Compare commits

...

11 commits
v1.4.0 ... main

Author SHA1 Message Date
050cef92e4 Bump version to 1.5.5
Some checks failed
test / test (push) Has been cancelled
2025-06-25 12:03:23 +10:00
de20cb5001 Handle float values without decimal point 2025-06-25 12:03:04 +10:00
aed9b6413f Bump version to 1.5.4
Some checks are pending
test / test (push) Waiting to run
2025-06-25 10:24:00 +10:00
a4edaa71e1 Add to_string functions for tokens 2025-06-25 10:23:38 +10:00
89ff1b3520 Bump version to 1.5.3
Some checks failed
test / test (push) Has been cancelled
2025-06-20 00:16:52 +10:00
59ecc25dc7 Fix code example in readme 2025-06-20 00:16:24 +10:00
35b1d0c1fc Bump version to 1.5.2
Some checks are pending
test / test (push) Waiting to run
2025-06-19 23:38:35 +10:00
f9944d6878 Fix incorrect auth on supply_construction_site endpoint 2025-06-19 23:38:22 +10:00
70a30ff132 Bump version to 1.5.1
Some checks are pending
test / test (push) Waiting to run
2025-06-18 20:30:53 +10:00
bc25e15faf Bump version to 1.5.0
Some checks are pending
test / test (push) Waiting to run
2025-06-18 20:04:27 +10:00
c1a7debb84 Remove reexports 2025-06-18 20:04:15 +10:00
7 changed files with 122 additions and 119 deletions

View file

@ -7,12 +7,11 @@
gleam add spacetraders_sdk@1 gleam add spacetraders_sdk@1
``` ```
```gleam ```gleam
import spacetraders_sdk import spacetraders_api
pub fn main() -> Nil { pub fn main() -> Nil {
let assert Ok(server_status) = get_server_status() let assert Ok(server_status) = spacetraders_api.get_server_status()
echo server_status io.println(server_status.status)
Nil
} }
``` ```

View file

@ -1,5 +1,5 @@
name = "spacetraders_sdk" name = "spacetraders_sdk"
version = "1.4.0" version = "1.5.5"
gleam = ">= 1.11.0" gleam = ">= 1.11.0"
description = "A Gleam SDK for the spacetraders.io game API" description = "A Gleam SDK for the spacetraders.io game API"
licences = ["MIT"] licences = ["MIT"]

View file

@ -56,10 +56,11 @@ import spacetraders_models/waypoint_modifier.{type WaypointModifier}
import spacetraders_models/waypoint_symbol.{type WaypointSymbol} import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
import spacetraders_models/waypoint_trait_symbol.{type WaypointTraitSymbol} import spacetraders_models/waypoint_trait_symbol.{type WaypointTraitSymbol}
import spacetraders_models/waypoint_type.{type WaypointType} import spacetraders_models/waypoint_type.{type WaypointType}
import spacetraders_sdk/internal/api.{ import spacetraders_sdk.{
type AccountToken, type AgentToken, type ApiResponse, type PagedData, type AccountToken, type AgentToken, type ApiResponse, type PagedData,
AccountAuth, AgentAuth, NoAuth, AccountAuth, AgentAuth, NoAuth,
} }
import spacetraders_sdk/internal/api
import spacetraders_sdk/internal/time import spacetraders_sdk/internal/time
pub fn get_account(token: AgentToken) -> ApiResponse(Account) { pub fn get_account(token: AgentToken) -> ApiResponse(Account) {
@ -103,7 +104,10 @@ pub fn register_new_agent(
case response.status { case response.status {
201 -> 201 ->
api.parse_data_response(response, { api.parse_data_response(response, {
use token <- decode.field("token", api.agent_token_decoder()) use token <- decode.field(
"token",
spacetraders_sdk.agent_token_decoder(),
)
use agent <- decode.field("agent", agent.decoder()) use agent <- decode.field("agent", agent.decoder())
use faction <- decode.field("faction", faction.decoder()) use faction <- decode.field("faction", faction.decoder())
use contract <- decode.field("contract", contract.decoder()) use contract <- decode.field("contract", contract.decoder())
@ -1669,6 +1673,7 @@ pub type ConstructionSiteSupplied {
} }
pub fn supply_construction_site( pub fn supply_construction_site(
token: AgentToken,
system_symbol: SystemSymbol, system_symbol: SystemSymbol,
waypoint_symbol: WaypointSymbol, waypoint_symbol: WaypointSymbol,
ship_symbol: ShipSymbol, ship_symbol: ShipSymbol,
@ -1677,7 +1682,7 @@ pub fn supply_construction_site(
) -> ApiResponse(ConstructionSiteSupplied) { ) -> ApiResponse(ConstructionSiteSupplied) {
let request = let request =
api.post_json( api.post_json(
NoAuth, AgentAuth(token),
"/systems/" "/systems/"
<> system_symbol.to_string(system_symbol) <> system_symbol.to_string(system_symbol)
<> "/waypoints/" <> "/waypoints/"

View file

@ -1,4 +1,5 @@
import gleam/dynamic/decode.{type Decoder} import gleam/dynamic/decode.{type Decoder}
import gleam/int
import gleam/json.{type Json} import gleam/json.{type Json}
pub opaque type ShipComponentCondition { pub opaque type ShipComponentCondition {
@ -17,7 +18,11 @@ pub fn parse(value: Float) -> Result(ShipComponentCondition, Nil) {
} }
pub fn decoder() -> Decoder(ShipComponentCondition) { pub fn decoder() -> Decoder(ShipComponentCondition) {
use value <- decode.then(decode.float) use value <- decode.then(
decode.one_of(decode.float, [
decode.then(decode.int, fn(i) { decode.success(int.to_float(i)) }),
]),
)
case parse(value) { case parse(value) {
Ok(ship_component_condition) -> decode.success(ship_component_condition) Ok(ship_component_condition) -> decode.success(ship_component_condition)
Error(Nil) -> Error(Nil) ->

View file

@ -1,4 +1,5 @@
import gleam/dynamic/decode.{type Decoder} import gleam/dynamic/decode.{type Decoder}
import gleam/int
import gleam/json.{type Json} import gleam/json.{type Json}
pub opaque type ShipComponentIntegrity { pub opaque type ShipComponentIntegrity {
@ -17,7 +18,11 @@ pub fn parse(value: Float) -> Result(ShipComponentIntegrity, Nil) {
} }
pub fn decoder() -> Decoder(ShipComponentIntegrity) { pub fn decoder() -> Decoder(ShipComponentIntegrity) {
use value <- decode.then(decode.float) use value <- decode.then(
decode.one_of(decode.float, [
decode.then(decode.int, fn(i) { decode.success(int.to_float(i)) }),
]),
)
case parse(value) { case parse(value) {
Ok(ship_component_integrity) -> decode.success(ship_component_integrity) Ok(ship_component_integrity) -> decode.success(ship_component_integrity)
Error(Nil) -> Error(Nil) ->

View file

@ -1,33 +1,103 @@
import spacetraders_sdk/internal/api import gleam/dynamic.{type Dynamic}
import gleam/dynamic/decode.{type Decoder}
import gleam/http/request.{type Request}
import gleam/httpc.{type HttpError}
import gleam/json.{type DecodeError}
import gleam/option.{type Option}
import gleam/result
import spacetraders_models/meta.{type Meta}
import spacetraders_sdk/internal/jwt
pub type ApiResponse(data) = pub type ErrorResponse {
api.ApiResponse(data) ErrorResponse(
code: Int,
message: String,
data: Option(Dynamic),
request_id: Option(String),
)
}
pub type ApiError = pub type ApiError {
api.ApiError HttpcError(HttpError)
JsonDecodeError(DecodeError)
ResponseError(ErrorResponse)
}
pub type ErrorResponse = pub type ApiResponse(a) =
api.ErrorResponse Result(a, ApiError)
pub type PagedData(data) = pub type PagedData(data) {
api.PagedData(data) PagedData(data: data, meta: Meta)
}
pub type AccountToken = pub opaque type AccountToken {
api.AccountToken AccountToken(String)
}
pub type AgentToken = pub opaque type AgentToken {
api.AgentToken AgentToken(String)
}
pub type TokenParseError = pub type TokenParseError {
api.TokenParseError InvalidToken
IncorrectType
}
pub type AuthMethod = pub type AuthMethod {
api.AuthMethod AccountAuth(AccountToken)
AgentAuth(AgentToken)
NoAuth
}
pub const parse_account_token = api.parse_account_token pub fn parse_account_token(
value: String,
) -> Result(AccountToken, TokenParseError) {
use jwt <- result.try(jwt.parse(value) |> result.replace_error(InvalidToken))
case jwt.payload.subject {
"account-token" -> Ok(AccountToken(value))
_ -> Error(IncorrectType)
}
}
pub const account_token_decoder = api.account_token_decoder pub fn account_token_decoder() -> Decoder(AccountToken) {
use value <- decode.then(decode.string)
case parse_account_token(value) {
Ok(token) -> decode.success(token)
Error(_) -> decode.failure(AccountToken("invalid"), "AccountToken")
}
}
pub const parse_agent_token = api.parse_agent_token pub fn account_token_to_string(account_token: AccountToken) -> String {
let AccountToken(value) = account_token
value
}
pub const agent_token_decoder = api.agent_token_decoder pub fn parse_agent_token(value: String) -> Result(AgentToken, TokenParseError) {
use jwt <- result.try(jwt.parse(value) |> result.replace_error(InvalidToken))
case jwt.payload.subject {
"agent-token" -> Ok(AgentToken(value))
_ -> Error(IncorrectType)
}
}
pub fn agent_token_decoder() -> Decoder(AgentToken) {
use value <- decode.then(decode.string)
case parse_agent_token(value) {
Ok(token) -> decode.success(token)
Error(_) -> decode.failure(AgentToken("invalid"), "AgentToken")
}
}
pub fn agent_token_to_string(agent_token: AgentToken) -> String {
let AgentToken(value) = agent_token
value
}
@internal
pub fn set_request_auth(req: Request(a), auth_method: AuthMethod) -> Request(a) {
case auth_method {
NoAuth -> req
AccountAuth(AccountToken(token)) | AgentAuth(AgentToken(token)) ->
req |> request.set_header("Authorization", "Bearer " <> token)
}
}

View file

@ -1,99 +1,18 @@
import gleam/bit_array import gleam/bit_array
import gleam/dynamic.{type Dynamic}
import gleam/dynamic/decode.{type Decoder} import gleam/dynamic/decode.{type Decoder}
import gleam/http.{type Method} import gleam/http.{type Method}
import gleam/http/request.{type Request} import gleam/http/request.{type Request}
import gleam/http/response.{type Response} import gleam/http/response.{type Response}
import gleam/httpc.{type HttpError} import gleam/httpc
import gleam/int import gleam/int
import gleam/json.{type DecodeError, type Json} import gleam/json.{type Json}
import gleam/option.{type Option} import gleam/option.{type Option}
import gleam/result import gleam/result
import gleam/string_tree import gleam/string_tree
import spacetraders_models/meta.{type Meta} import spacetraders_models/meta
import spacetraders_sdk/internal/jwt import spacetraders_sdk.{
type ApiResponse, type AuthMethod, type ErrorResponse, type PagedData,
pub type ErrorResponse { ErrorResponse, HttpcError, JsonDecodeError, PagedData, ResponseError,
ErrorResponse(
code: Int,
message: String,
data: Option(Dynamic),
request_id: Option(String),
)
}
pub type ApiError {
HttpcError(HttpError)
JsonDecodeError(DecodeError)
ResponseError(ErrorResponse)
}
pub type ApiResponse(a) =
Result(a, ApiError)
pub type PagedData(data) {
PagedData(data: data, meta: Meta)
}
pub opaque type AccountToken {
AccountToken(token: String)
}
pub opaque type AgentToken {
AgentToken(token: String)
}
pub type TokenParseError {
InvalidToken
IncorrectType
}
pub type AuthMethod {
AccountAuth(AccountToken)
AgentAuth(AgentToken)
NoAuth
}
pub fn parse_account_token(
value: String,
) -> Result(AccountToken, TokenParseError) {
use jwt <- result.try(jwt.parse(value) |> result.replace_error(InvalidToken))
case jwt.payload.subject {
"account-token" -> Ok(AccountToken(value))
_ -> Error(IncorrectType)
}
}
pub fn account_token_decoder() -> Decoder(AccountToken) {
use value <- decode.then(decode.string)
case parse_account_token(value) {
Ok(token) -> decode.success(token)
Error(_) -> decode.failure(AccountToken("invalid"), "AccountToken")
}
}
pub fn parse_agent_token(value: String) -> Result(AgentToken, TokenParseError) {
use jwt <- result.try(jwt.parse(value) |> result.replace_error(InvalidToken))
case jwt.payload.subject {
"agent-token" -> Ok(AgentToken(value))
_ -> Error(IncorrectType)
}
}
pub fn agent_token_decoder() -> Decoder(AgentToken) {
use value <- decode.then(decode.string)
case parse_agent_token(value) {
Ok(token) -> decode.success(token)
Error(_) -> decode.failure(AgentToken("invalid"), "AgentToken")
}
}
fn set_auth(req: Request(a), auth_method: AuthMethod) -> Request(a) {
case auth_method {
NoAuth -> req
AccountAuth(AccountToken(token)) | AgentAuth(AgentToken(token)) ->
req |> request.set_header("Authorization", "Bearer " <> token)
}
} }
const base_request = request.Request( const base_request = request.Request(
@ -135,7 +54,7 @@ fn set_json_body(req: Request(a), json: Json) {
fn make_request(method: Method, auth_method: AuthMethod, path: String) { fn make_request(method: Method, auth_method: AuthMethod, path: String) {
base_request base_request
|> request.set_method(method) |> request.set_method(method)
|> set_auth(auth_method) |> spacetraders_sdk.set_request_auth(auth_method)
|> request.set_path(base_request.path <> path) |> request.set_path(base_request.path <> path)
} }