@hexagramio/saga-ts
v0.9.434-0
Published
Saga SDK
Downloads
254
Maintainers
Readme
Table of Contents
Saga Typescript SDK
Install
npm install @hexagramio/saga-ts
Description
Typescript SDK to interface with a Saga API. The implementation is inspired by the Command Design Pattern. A command is send to a Reveiver. A command encapsulates all the data needed to call the API except the domain name. Currently there two kinds of command receivers, HTTP and Socket. Almost all commands except log in and some user creation methods require a accessToken. If a command fails an exception is being thrown that mirrors SAGA API errors.
Fundamental Types
Both HTTPCommand and SocketCommand below show the entire complexity of both types. Easy to adopt for specialized and new commands or to use for HTTP Scripts.
HTTPCommand
/**
* Interfaces to represent the various mutations of a HTTP Command profile.
*/
export interface HTTPGetCommand<T> {
method: "GET" ,
path: string,
params?: URLSearchParams,
accessToken?: string
}
export interface HTTPPostPutCommand<T> {
method: "PUT" | "POST",
path: string,
data: any ,
accessToken?: string
}
export interface HTTPDeleteCommand<T> {
method: "DELETE",
path: string,
params?: URLSearchParams,
accessToken?: string
}
export type HTTPCommand<T> = HTTPGetCommand<T> | HTTPPostPutCommand<T> | HTTPDeleteCommand<T>
Example Custom Command
Sends HTTP post to requests/slack with the assembled body.
/**
* Get a bot command
* @param id the id of the bot
* @param accessToken the required accessToken
* @group HTTP Commands
*/
export const PostSlackMessageCommand = (accessToken: string, slack_id: string, slack_channel: string, message: string):HTTPCommand<{ok:boolean}> =>{
return {
method:"POST",
path:`requests/slack`,
accessToken,
data: {slack_id,slack_channel,message}
}
}
SocketCommand
/**
* A socket join command
*/
export interface SocketJoinCommand {
method: "/users" | "/bots" | "/globals" |
"/jobs/runnable_calls" | "/jobs/versions" |
"/scripts/runnable_calls" | "/scripts/versions" |
"/system" | "/npm_packages"
id: string,
join: boolean,
}
Quickstart
For the unhurried among us ;)
Register User
import {
RegisterUserCommand,
sendHTTPCommand} from "saga-ts";
const baseURL=new URL("https://saga-api.com");
const user = await sendHTTPCommand(baseUrl,RegisterUserCommand({username:`foo`,password:`bar`}))
Login User and Add User Property
import {
AddUserPropertyCommand,
LoginUserCommand,
sendHTTPCommand} from "saga-ts";
const baseURL=new URL("https://saga-api.com");
const user = await sendHTTPCommand(baseUrl,LoginUserCommand({username:`foo`,password:`bar`}))
await sendHTTPCommand(baseUrl,AddUserPropertyCommand(user.accessToken,{parent_id:user._id,name:"hello",value:"world"}))
Paginate Listing, example using Listing
import {
ListBotsCommand,
sendHTTPCommand} from "saga-ts";
const baseURL=new URL("https://saga-api.com");
const accessToken="123213132";//from cache
const botsListing = await sendHTTPCommand(baseUrl,ListBotsCommand(accessToken))
//do we have more? nextCommand contains all the data needed for the call
if (botsListing.nextCommand) {
const botsNext = await sendHTTPCommand(baseUrl, botsListing.nextCommand);
}
//OR
//do we have some previous? prevcommand contains all the data needed for the call
if (botsListing.prevCommand) {
const botsPrev = await sendHTTPCommand(baseUrl, botsListing.prevCommand);
Handle HTTP Error
import {
ListUserCommand,
RegisterUserCommand,
sendHTTPCommand} from "saga-ts";
const baseURL=new URL("https://saga-api.com");
const accessToken="89012y417114211";//from cache
try{
await sendHTTPCommand(baseUrl,CreateBot(user.accessToken,{name:"ALREADY TAKEN"}))
} catch(e){
if(e.statusCode===422){
//e.errors contains [fieldName:string]: message
//handle the specific error messages
}
//...
}
Inspect Missing Fields in failed HTTP Call
import {
ListUserCommand,
RegisterUserCommand,
sendHTTPCommand} from "saga-ts";
const baseURL=new URL("https://saga-api.com");
const user = await sendHTTPCommand(baseUrl,RegisterUserCommand({username:`foo`,password:`bar`}))
try{
await sendHTTPCommand(baseUrl,ListUserCommand(user.accessToken,{parent_id:user._id,name:"hello",value:"world"}))
} catch(e){
//handle 401 and 403 for
}
Subscribe to Socket Bot Property Changes
import {
Authentication,
SocketSession} from "saga-ts";
const baseURL=new URL("https://saga-api.com");
const socketSession = new SocketSession(
baseUrl,
accessToken, //assumed existing
(error)=>console.error //disconnect errors need to be handled asynchronously
)
socketSession.on("/properties", (property)=>{
//do something with propterties
if(property.name="location"){
updateMapLocation(property);
}//....
})
//join bot command
await socketSession.emitCommand(JoinBotCommand("ID OF BOT TO JOIN"))
Handle Socket Emit Comand Errors
import {
Authentication,
SocketSession} from "saga-ts";
const baseURL=new URL("https://saga-api.com");
const socketSession = new SocketSession(
baseUrl,
accessToken, //assumed existing
(error)=>console.error //disconnect errors need to be handled asynchronously
)
try{
await socketSession.emitCommand(JoinBotCommand("Not access to id"))
} catch(e){
//deal with API Error like 401,403,404 and 422 depending on the comand
}
Handle Socket Connection Error
import {SocketSession} from "saga-ts";
const baseURL=new URL("https://saga-api.com");
new SocketSession(
baseUrl,
"bad token",
(err)=>{
//handleError
}
)