Nanyx

Event ingestion client

A small API client with optional fields and a record of functions as an object.

module highstorm

import (
  nanyx.web/http
  nanyx.web/json
)

-- A small client for an event-ingestion API, showing optional fields,
-- a record of functions as an "object", and `proc` for error chaining.

type Event = (
  event: string
  time?: int
  content?: string
  metadata?: map(string, string)
)

type Options = (
  token: string
  baseUrl?: string
)

type IngestError = #httpError(int) | #badResponse

type Client = (
  baseUrl: string
  ingest: [Http] (channel: string, event: Event) -> #some(id: string) | #error(IngestError)
)

def checkOk: Response -> #some(Response) | #error(#httpError(int)) = { response ->
  if response.ok then #some(response) else #error(#httpError(response.status))
}

def idDecoder = Json.record(id = Json.field("id", Json.string))

export def Client.new: Options -> Client = { options ->
  def #some(baseUrl) = options.baseUrl except #none -> #some("https://highstorm.app")

  Client(
    baseUrl = baseUrl

    ingest = { channel, event -> proc {
      def response = try Http.post(
        url = "{baseUrl}/api/v1/events/{channel}"
        headers = [
          "Content-Type" => "application/json"
          "Authorization" => "Bearer {options.token}"
        ]
        body = Json.encode(event)
      ) \Result.then(checkOk)

      def body = try Json.decode(response.body, idDecoder) \Result.mapError { _ -> #badResponse }

      return body
    }}
  )
}

@entry
def main: [Console & Http] () -> () = {
  def client = Client.new(Options(token = "secret"))

  match client.ingest(channel = "deploys", event = Event(event = "deploy.finished", content = "v1.2.0"))
    | #some(id = id) -> println("Ingested event {id}")
    | #error(#httpError(status)) -> println("HTTP error {status}")
    | #error(#badResponse) -> println("Could not read the response")
}

This program lives in the repository as examples/EventSubscription.nyx.