rescript-openapi 0.1.0

Generate type-safe ReScript clients from OpenAPI specifications
Documentation
---
source: tests/codegen_tests.rs
assertion_line: 44
expression: client
---
// SPDX-License-Identifier: PMPL-1.0-or-later
// Generated by rescript-openapi - DO NOT EDIT
// Source: Swagger Petstore v1.0.0

open RescriptCore
open ApiTypes
open ApiSchema

/** API error type */
type apiError =
  | HttpError({status: int, message: string, body: option<Js.Json.t>})
  | ParseError({message: string, body: option<Js.Json.t>})
  | HttpClientError({message: string})

/** HTTP method (polymorphic variant for Fetch API) */
type httpMethod = [#GET | #POST | #PUT | #PATCH | #DELETE | #HEAD | #OPTIONS]

/** HTTP request configuration */
type httpRequest = {
  method: httpMethod,
  url: string,
  headers: Dict.t<string>,
  body: option<Js.Json.t>,
}

/** HTTP client module signature - implement this to use any HTTP library */
module type HttpClient = {
  let request: httpRequest => promise<result<Js.Json.t, apiError>>
}

/** Default fetch-based HTTP client using @glennsl/rescript-fetch */
module FetchClient: HttpClient = {
  open Fetch

  let request = async (req: httpRequest): result<Js.Json.t, apiError> => {
    try {
      let init: Request.init = {
        method: (req.method :> Fetch.method),
        headers: Headers.fromObject(req.headers->Obj.magic),
      }
      let init = switch req.body {
      | Some(b) => {...init, body: b->JSON.stringify->Body.string}
      | None => init
      }
      let response = await fetch(req.url, init)

      if response->Response.ok {
        let json = await response->Response.json
        Ok(json)
      } else {
        let status = response->Response.status
        let message = response->Response.statusText
        let body = try {
          Some(await response->Response.json)
        } catch {
        | _ => None
        }
        Error(HttpError({status, message, body}))
      }
    } catch {
    | Exn.Error(e) => Error(HttpClientError({
        message: Exn.message(e)->Option.getOr("Network error"),
      }))
    }
  }
}

/** Authentication configuration */
type authConfig =
  | NoAuth
  | BearerToken(string)
  | ApiKey({key: string, headerName: string})

/** Client configuration */
type config = {
  baseUrl: string,
  headers: Dict.t<string>,
  auth: authConfig,
}

/** Create client configuration with optional authentication
 *
 * Bearer token auth:
 * ```rescript
 * let config = makeConfig(
 *   ~baseUrl="https://api.example.com",
 *   ~bearerToken="my-jwt-token",
 *   ()
 * )
 * ```
 *
 * API key auth:
 * ```rescript
 * let config = makeConfig(
 *   ~baseUrl="https://api.example.com",
 *   ~apiKey="my-api-key",
 *   ~apiKeyHeader="X-API-Key",
 *   ()
 * )
 * ```
 */
let makeConfig = (
  ~baseUrl: string,
  ~headers=Dict.make(),
  ~bearerToken: option<string>=?,
  ~apiKey: option<string>=?,
  ~apiKeyHeader: string="X-API-Key",
  ()
): config => {
  let auth = switch (bearerToken, apiKey) {
  | (Some(token), _) => BearerToken(token)
  | (_, Some(key)) => ApiKey({key, headerName: apiKeyHeader})
  | (None, None) => NoAuth
  }
  {
    baseUrl,
    headers,
    auth,
  }
}

/** Apply authentication headers to a headers dict */
let applyAuth = (headers: Dict.t<string>, auth: authConfig): unit => {
  switch auth {
  | NoAuth => ()
  | BearerToken(token) => headers->Dict.set("Authorization", `Bearer ${token}`)
  | ApiKey({key, headerName}) => headers->Dict.set(headerName, key)
  }
}

/** Build URL with query parameters */
let buildUrl = (baseUrl: string, path: string, query: Dict.t<string>): string => {
  let url = baseUrl ++ path
  let params = query
    ->Dict.toArray
    ->Array.map(((k, v)) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
    ->Array.join("&")

  if params->String.length > 0 {
    url ++ "?" ++ params
  } else {
    url
  }
}

/** API client functor - provide your own HttpClient implementation */
module Make = (Http: HttpClient) => {
  /** List all pets */
  let listPets = async (config: config, ~limit=?, ()): result<pets, apiError> => {
    let path = "/pets"
    let query = Dict.make()
    switch limit { | Some(v) => query->Dict.set("limit", v->String.make) | None => () }
    let headers = Dict.fromArray(config.headers->Dict.toArray)
    headers->Dict.set("Content-Type", "application/json")
    applyAuth(headers, config.auth)

    let req: httpRequest = {
      method: #GET,
      url: buildUrl(config.baseUrl, path, query),
      headers,
      body: None,
    }

    switch await Http.request(req) {
    | Ok(json) => try {
      Ok(S.parseJsonOrThrow(json, petsSchema))
    } catch {
    | Exn.Error(e) => Error(ParseError({message: Exn.message(e)->Option.getOr("Parse error"), body: Some(json)}))
    }
    | Error(e) => Error(e)
    }
  }

}

/** Default client using fetch */
module Client = Make(FetchClient)

/** Operation aliases for convenience */
module Aliases = {
  let getPets = Client.listPets
}