Saga SDK
Table of Contents
- Saga Typescript SDK
- Install
- Description
- Fundamental Types
- HTTPCommand
- Example Custom Command
- SocketCommand
- Quickstart
- Saga API URL
- Register User
- Login User and Add User Property
- Paginate Listing, example using Listing
- Handle HTTP Error
- Inspect Missing Fields in a failed HTTP Call
- Subscribe to Socket Bot Property Changes
- Handle Socket Emit Comand Errors
- Handle Socket Connection Error
``shell`
npm install @hexagramio/saga-ts
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.
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.
ts
/**
* Interfaces to represent the various mutations of a HTTP Command profile.
*/
export interface HTTPGetCommand {
method: "GET" ,
path: string,
params?: URLSearchParams,
accessToken?: string
}
export interface HTTPPostPutCommand {
method: "PUT" | "POST",
path: string,
data: any ,
accessToken?: string
}export interface HTTPDeleteCommand {
method: "DELETE",
path: string,
params?: URLSearchParams,
accessToken?: string
}
export type HTTPCommand = HTTPGetCommand | HTTPPostPutCommand | HTTPDeleteCommand
`$3
Sends HTTP post to requests/slack with the assembled body.
`ts
/**
* 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}
}
}
`$3
`ts
/**
* 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 ;)
$3
Can be either string or URL.
`tslet url_string = "https://saga-api.com"
let let_object = new URL("https://saga-api.com")
`$3
`ts
import {
RegisterUserCommand,
sendHTTPCommand} from "saga-ts";const baseURL=new URL("https://saga-api.com");
const user = await sendHTTPCommand(baseUrl,RegisterUserCommand({username:
foo,password:bar}))
`$3
Logging as a user and using the users access token for further commands
`ts
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"}))
`$3
Pagination in saga using nextCommand and prevCommand.
`ts
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);
`$3
How to handle the various saga errors
`ts
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
}
//...
}
`$3
`ts
import {
ListUserCommand,
RegisterUserCommand,
sendHTTPCommand} from "saga-ts";const baseURL=new URL("https://saga-api.com");
try{
const user = await sendHTTPCommand(baseUrl,RegisterUserCommand({password:
bar})) await sendHTTPCommand(baseUrl,ListUserCommand(user.accessToken,{parent_id:user._id,name:"hello",value:"world"}))
} catch(e){
//handle 401 and 403 for
}
`$3
`ts
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"))
`$3
`ts
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
}
`$3
`ts
import {SocketSession} from "saga-ts";
const baseURL=new URL("https://saga-api.com");
new SocketSession(
baseUrl,
"bad token",
(err)=>{
//handleError
}
)
``