前回で傾向が分かったので対策する。

yohfee.hatenadiary.org

ライブラリっぽい部分。

namespace Mackerel

open System.Net
open System.Text.Json.Serialization
open FsHttp

type Options = { BaseUri: string; ApiKey: string }

module HttpClient =
    let send<'t> request =
        async {
            let! response = request |> Request.sendAsync

            match response.statusCode with
            | HttpStatusCode.OK ->
                let! data = response |> Response.deserializeJsonAsync<'t>
                return Ok data
            | e -> return Error e
        }

    let get<'t> (path: string) param options =
        http {
            GET $"{options.BaseUri}{path}"
            header "X-Api-Key" options.ApiKey
            query param
        }
        |> send<'t>

    let post<'t> (path: string) param options =
        http {
            POST $"{options.BaseUri}{path}"
            header "X-Api-Key" options.ApiKey
            body
            jsonSerialize param
        }
        |> send<'t>

    let put<'t> (path: string) param options =
        http {
            PUT $"{options.BaseUri}{path}"
            header "X-Api-Key" options.ApiKey
            body
            jsonSerialize param
        }
        |> send<'t>

    let delete<'t> (path: string) options =
        http {
            DELETE $"{options.BaseUri}{path}"
            header "X-Api-Key" options.ApiKey
            body
            json ""
        }
        |> send<'t>

type Org =
    { [<JsonPropertyName("name")>]
      Name: string }

module Org =
    let get = HttpClient.get<Org> "/api/v0/org" []

type Service =
    { [<JsonPropertyName("name")>]
      Name: string

      [<JsonPropertyName("memo")>]
      Memo: string

      [<JsonPropertyName("roles")>]
      Roles: string list }

module Service =

    let findAll = HttpClient.get<{| services: Service list |}> "/api/v0/services" []

    type CreateParam =
        { [<JsonPropertyName("name")>]
          Name: string

          [<JsonPropertyName("memo")>]
          Memo: string }

    let create (param: CreateParam) =
        HttpClient.post<Service> "/api/v0/services" param

    let delete (serviceName: string) =
        HttpClient.delete<Service> $"/api/v0/services/{serviceName}"

    let listMetricNames (serviceName: string) =
        HttpClient.get<{| names: string list |}> $"/api/v0/services/{serviceName}/metric-names" []

type User =
    { [<JsonPropertyName("id")>]
      ID: string

      [<JsonPropertyName("screenName")>]
      ScreenName: string

      [<JsonPropertyName("email")>]
      Email: string

      [<JsonPropertyName("authority")>]
      Authority: string

      [<JsonPropertyName("isInRegistrationProcess")>]
      IsInRegistrationProcess: bool

      [<JsonPropertyName("isMFAEnabled")>]
      IsMFAEnabled: bool

      [<JsonPropertyName("authenticationMethods")>]
      AuthenticationMethods: string list

      [<JsonPropertyName("joinedAt")>]
      JoinedAt: int64 }

module User =

    let findAll = HttpClient.get<{| users: User list |}> "/api/v0/users" []

    let delete (userId: string) =
        HttpClient.delete<User> $"/api/v0/users/{userId}"

アプリケーションっぽい部分。

open Mackerel

let run t =
    t |> Async.RunSynchronously |> printfn "%A"

let options =
    { BaseUri = "https://api.mackerelio.com"
      ApiKey = "*********************************************" }

module Org =
    open Org

    get options |> run

module Service =
    open Service

    findAll options |> run

    ({ Name = "Service888"
       Memo = "Service 888" },
     options)
    ||> create
    |> run

    ("Service888", options) ||> listMetricNames |> run

    ("Service888", options) ||> delete |> run

module User =
    open User

    findAll options |> run

FsHttp 便利~。