Skip to content

Commit

Permalink
feat(HTTPSend): return response data from HTTP SendCommand action (#334)
Browse files Browse the repository at this point in the history
  • Loading branch information
ianshade authored Jun 26, 2024
1 parent 96e0f6c commit d220c78
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,21 @@ export enum TimelineContentTypeHTTPParamType {
FORM = 'form'
}

export interface SendCommandResult {
statusCode: number
headers: {
[k: string]: string | string[]
}
body: string
}

export enum HttpSendActions {
Resync = 'resync',
SendCommand = 'sendCommand'
}
export interface HttpSendActionExecutionResults {
resync: () => void,
sendCommand: (payload: HTTPSendCommandContent) => void
sendCommand: (payload: HTTPSendCommandContent) => SendCommandResult
}
export type HttpSendActionExecutionPayload<A extends keyof HttpSendActionExecutionResults> = Parameters<
HttpSendActionExecutionResults[A]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,35 @@
},
"required": ["type", "url", "params"],
"additionalProperties": false
},
"result": {
"type": "object",
"properties": {
"statusCode": {
"type": "number"
},
"headers": {
"type": "object",
"additionalProperties": {
"oneOf": [
{ "type": "string" },
{
"type": "array",
"items": { "type": "string" }
}
]
}
},
"body": {
"type": "string"
}
},
"required": [
"statusCode",
"headers",
"body"
],
"additionalProperties": false
}
}
]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HTTPSendOptions } from 'timeline-state-resolver-types'
import { ActionExecutionResult, HTTPSendOptions, SendCommandResult } from 'timeline-state-resolver-types'
import { HTTPSendDevice, HttpSendDeviceCommand } from '.'
import { AccessToken, ClientCredentials } from 'simple-oauth2'

Expand Down Expand Up @@ -114,7 +114,11 @@ export class AuthenticatedHTTPSendDevice extends HTTPSendDevice {
return token
}

async sendCommand({ timelineObjId, context, command }: HttpSendDeviceCommand): Promise<void> {
async sendCommandWithResult({
timelineObjId,
context,
command,
}: HttpSendDeviceCommand): Promise<ActionExecutionResult<SendCommandResult>> {
if (this.authOptions) {
const bearerToken =
this.authOptions.method === AuthMethod.BEARER_TOKEN ? this.authOptions.bearerToken : await this.tokenPromise
Expand All @@ -129,6 +133,6 @@ export class AuthenticatedHTTPSendDevice extends HTTPSendDevice {
}
}
}
return super.sendCommand({ timelineObjId, context, command })
return super.sendCommandWithResult({ timelineObjId, context, command })
}
}
59 changes: 45 additions & 14 deletions packages/timeline-state-resolver/src/integrations/httpSend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
HTTPSendCommandContent,
HTTPSendOptions,
HttpSendActions,
SendCommandResult,
StatusCode,
TSRTimelineContent,
Timeline,
Expand Down Expand Up @@ -54,19 +55,22 @@ export class HTTPSendDevice extends Device<HTTPSendOptions, HttpSendDeviceState,
messages: [],
}
}

readonly actions: {
[id in HttpSendActions]: (id: string, payload?: Record<string, any>) => Promise<ActionExecutionResult>
[id in HttpSendActions]: (id: string, payload?: Record<string, any>) => Promise<ActionExecutionResult<any>>
} = {
[HttpSendActions.Resync]: async () => {
this.context.resetResolver()
return { result: ActionExecutionResultCode.Ok }
},
[HttpSendActions.Resync]: async (_id) => this.executeResyncAction(),
[HttpSendActions.SendCommand]: async (_id: string, payload?: Record<string, any>) =>
this.sendManualCommand(payload as HTTPSendCommandContent | undefined),
this.executeSendCommandAction(payload as HTTPSendCommandContent | undefined),
}

private async executeResyncAction(): Promise<ActionExecutionResult<undefined>> {
this.context.resetResolver()
return { result: ActionExecutionResultCode.Ok }
}

private async sendManualCommand(cmd?: HTTPSendCommandContent): Promise<ActionExecutionResult> {
private async executeSendCommandAction(
cmd?: HTTPSendCommandContent
): Promise<ActionExecutionResult<SendCommandResult>> {
if (!cmd)
return {
result: ActionExecutionResultCode.Error,
Expand Down Expand Up @@ -97,7 +101,7 @@ export class HTTPSendDevice extends Device<HTTPSendOptions, HttpSendDeviceState,
}
}

await this.sendCommand({
const response = await this.sendCommandWithResult({
timelineObjId: '',
context: 'makeReady',
command: {
Expand All @@ -107,9 +111,11 @@ export class HTTPSendDevice extends Device<HTTPSendOptions, HttpSendDeviceState,
},
}).catch(() => this.context.logger.warning('Manual command failed: ' + JSON.stringify(cmd)))

return {
result: ActionExecutionResultCode.Ok,
}
return (
response ?? {
result: ActionExecutionResultCode.Error,
}
)
}

convertTimelineStateToDeviceState(state: Timeline.TimelineState<TSRTimelineContent>): HttpSendDeviceState {
Expand Down Expand Up @@ -168,6 +174,13 @@ export class HTTPSendDevice extends Device<HTTPSendOptions, HttpSendDeviceState,
return commands
}
async sendCommand({ timelineObjId, context, command }: HttpSendDeviceCommand): Promise<void> {
await this.sendCommandWithResult({ timelineObjId, context, command })
}
async sendCommandWithResult({
timelineObjId,
context,
command,
}: HttpSendDeviceCommand): Promise<ActionExecutionResult<SendCommandResult>> {
const commandHash = this.getTrackedStateHash(command)

if (command.commandName === 'added' || command.commandName === 'changed') {
Expand All @@ -179,10 +192,15 @@ export class HTTPSendDevice extends Device<HTTPSendOptions, HttpSendDeviceState,
// Avoid sending multiple identical commands for the same state:
if (command.layer && command.commandName !== 'manual') {
const trackedHash = this.trackedState.get(command.layer)
if (commandHash !== trackedHash) return Promise.resolve() // command is no longer relevant to state
if (commandHash !== trackedHash)
return {
result: ActionExecutionResultCode.Error,
} // command is no longer relevant to state
}
if (this._terminated) {
return Promise.resolve()
return {
result: ActionExecutionResultCode.Error,
}
}

const cwc: CommandWithContext = {
Expand Down Expand Up @@ -245,6 +263,15 @@ export class HTTPSendDevice extends Device<HTTPSendOptions, HttpSendDeviceState,
`HTTPSend: ${command.content.type}: Bad statuscode response on url "${command.content.url}": ${response.statusCode} (${context})`
)
}

return {
result: ActionExecutionResultCode.Ok,
resultData: {
body: response.body,
statusCode: response.statusCode,
headers: response.headers as Record<string, string | string[]>,
},
}
} catch (error) {
const err = error as RequestError // make typescript happy

Expand Down Expand Up @@ -281,6 +308,10 @@ export class HTTPSendDevice extends Device<HTTPSendOptions, HttpSendDeviceState,
}, timeLeft)
}
}

return {
result: ActionExecutionResultCode.Error,
}
}
}
private getTrackedStateHash(command: HttpSendDeviceCommand['command']): string {
Expand Down

0 comments on commit d220c78

Please sign in to comment.