Skip to content

Commit d87de06

Browse files
committed
ResponseType on actor definition
1 parent d913f89 commit d87de06

File tree

5 files changed

+55
-26
lines changed

5 files changed

+55
-26
lines changed

README.md

+8-9
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ This lib supports both Bun and NodeJS runtimes, Bun performs invocations ~2x fas
2222
import spawn, { ActorContext, Value } from '@eigr/spawn-sdk'
2323
import { UserState, ChangeUserNamePayload, ChangeUserNameStatus } from 'src/protos/examples/user_example'
2424

25-
const system = spawn.createSystem('SpawnSystemName')
25+
const system = spawn.createSystem('spawn-system')
2626

2727
// You can register multiple actors with different options
2828
const actor = system.buildActor({
2929
name: 'exampleActor',
30-
stateType: UserState, // or 'json' if you don't want to use protobufs
30+
stateType: UserState,
3131
stateful: true,
3232
snapshotTimeout: 10_000n,
3333
deactivatedTimeout: 60_000n
@@ -37,12 +37,11 @@ const actor = system.buildActor({
3737
const setNameHandler = async (context: ActorContext<UserState>, payload: ChangeUserNamePayload) => {
3838
return Value.of<UserState, ChangeUserNameResponse>()
3939
.state({ name: payload.newName })
40-
.response(ChangeUserNameResponse, { status: ChangeUserNameStatus.OK })
40+
.response({ status: ChangeUserNameStatus.OK })
4141
}
4242

4343
// This is similar to a Route definition in REST
44-
// the default payloadType is 'json'
45-
actor.addAction({ name: 'setName', payloadType: ChangeUserNamePayload }, setNameHandler)
44+
actor.addAction({ name: 'SetName', payloadType: ChangeUserNamePayload, responseType: ChangeUserNameResponse }, setNameHandler)
4645

4746
system.register()
4847
.then(() => console.log('Spawn System registered'))
@@ -59,14 +58,14 @@ import { UserState, ChangeUserNamePayload, ChangeUserNameResponse } from 'src/pr
5958
const response: ChangeUserNameResponse = await spawn.invoke('exampleActor', {
6059
action: 'setName',
6160
response: ChangeUserNameResponse,
62-
payload: payloadFor(ChangeUserNamePayload, payload)
63-
// system: 'SpawnSystemName'
61+
payload: payloadFor(ChangeUserNamePayload, payload),
62+
system: 'spawn-system'
6463
})
6564

6665
const state: UserState = await spawn.invoke('exampleActor', {
6766
action: 'getState',
6867
response: UserState,
69-
// system: 'SpawnSystemName'
68+
system: 'spawn-system'
7069
})
7170

7271
console.log(state) // { name: 'changedName' }
@@ -113,7 +112,7 @@ With this, it should generate a file at `src/protos/examples/user_example.ts`, w
113112
You'll need to make sure Spawn Proxy service is up and running.
114113
With `docker-compose` you can define:
115114

116-
> **_NOTE:_** _using docker is recommended for `dev purposes only`, see [spawn deploy](https://github.com/eigr/spawn#getting-started) for production examples._
115+
> **_NOTE:_** _you can start the proxy using the `spawn cli`, see [spawn deploy](https://github.com/eigr/spawn#getting-started) for production examples._
117116
118117
```YML
119118
version: "3.8"

src/client-actor/value.ts

+32-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Broadcast, Effect, Forward, Pipe } from './workflows'
22
import { PayloadRef } from '../integration/parsers'
33
import { MessageType } from '@protobuf-ts/runtime'
4+
import { SpawnActorError } from '../integration/errors'
5+
import { Noop } from '../protos/eigr/functions/protocol/actors/protocol'
46
import { payloadFor } from '../spawn'
57

68
export class Value<T extends object = object, K extends object = object> {
@@ -58,15 +60,43 @@ export class Value<T extends object = object, K extends object = object> {
5860
return this
5961
}
6062

61-
parse() {
63+
parse(responseType?: MessageType<any> | 'json') {
6264
return {
6365
state: this._state,
64-
value: this._response,
66+
value: this.buildResponse(responseType),
6567
broadcast: this._broadcast,
6668
pipe: this._pipe,
6769
forward: this._forward,
6870
effects: this._effects,
6971
tags: this._tags
7072
}
7173
}
74+
75+
private buildResponse(responseType?: MessageType<any> | 'json') {
76+
if (!this._response) {
77+
return
78+
}
79+
80+
if (Noop.is(this._response)) {
81+
return this._response
82+
}
83+
84+
if ((this._response as PayloadRef<any>).ref !== undefined && (this._response as PayloadRef<any>).instance) {
85+
return this._response
86+
}
87+
88+
if (!responseType) {
89+
throw new SpawnActorError('You have to define a valid responseType in your actor action and set response with a correct type')
90+
}
91+
92+
if (responseType === 'json') {
93+
return this._response
94+
}
95+
96+
if (responseType && responseType.is(this._response)) {
97+
return payloadFor(responseType, this._response)
98+
}
99+
100+
throw new SpawnActorError('Specified responseType and response missmatch')
101+
}
72102
}

src/integration/controller/invoke-actions-controller.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ async function register(
4242
return sendResponse(200, res, resp)
4343
}
4444

45-
const { stateType, payloadType, callback } = callbackData
45+
const { stateType, payloadType, responseType, callback } = callbackData
4646

4747
const state = currentContext!.state && unpack(currentContext!.state, stateType)
4848
const context: ActorContext<any> = {
@@ -69,7 +69,7 @@ async function register(
6969

7070
try {
7171
const value = await callback(context, finalPayload)
72-
const parsedValue = value.parse()
72+
const parsedValue = value.parse(responseType)
7373

7474
const response = ActorInvocationResponse.create({
7575
actorName: actor?.name,
@@ -91,7 +91,7 @@ async function register(
9191

9292
return sendResponse(200, res, response)
9393
} catch (error) {
94-
console.error(error)
94+
console.error(`Error when calling: ${actor?.name}.${actionName} | ${(error as any)?.message}`)
9595

9696
return sendResponse(400, res)
9797
}

src/spawn.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { Value } from './client-actor/value'
3636
export type ActorActionOpts = {
3737
name: string
3838
payloadType?: MessageType<any> | 'json'
39+
responseType?: MessageType<any> | 'json'
3940
timer?: number
4041
}
4142

@@ -47,6 +48,7 @@ export type ActorActionCallback<T extends object = any, K extends object = any>
4748
export type ActorCallbackConnector = {
4849
stateType: MessageType<any> | 'json'
4950
payloadType: MessageType<any> | 'json'
51+
responseType: MessageType<any> | 'json'
5052
callback: ActorActionCallback
5153
}
5254

@@ -173,22 +175,20 @@ const createSystem = (system: string = uniqueDefaultSystem): SpawnSystem => {
173175
addAction: (actionOpts: ActorActionOpts, callback: ActorActionCallback) => {
174176
if (registered) throw new SpawnSystemRegisteredError(registeredErrorMsg)
175177

176-
const derfaultActionOpts = { payloadType: 'json' }
177-
const overridenActionOpts = { ...derfaultActionOpts, ...actionOpts } as ActorActionOpts
178-
179-
if (overridenActionOpts.timer) {
178+
if (actionOpts.timer) {
180179
actor.timerActions.push({
181-
action: { name: overridenActionOpts.name },
182-
seconds: overridenActionOpts.timer
180+
action: { name: actionOpts.name },
181+
seconds: actionOpts.timer
183182
})
184183
} else {
185-
actor.actions.push({ name: overridenActionOpts.name })
184+
actor.actions.push({ name: actionOpts.name })
186185
}
187186

188-
registeredCallbacks.set(`${system}${actorName}${overridenActionOpts.name}`, {
187+
registeredCallbacks.set(`${system}${actorName}${actionOpts.name}`, {
189188
callback,
190189
stateType: overridenOpts.stateType || Noop,
191-
payloadType: overridenActionOpts.payloadType || Noop
190+
payloadType: actionOpts.payloadType || Noop,
191+
responseType: actionOpts.responseType || Noop
192192
})
193193
}
194194
}

test/stubs/actors.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ export const createUserActor = (system: SpawnSystem) => {
5050
})
5151

5252
actor.addAction(
53-
{ name: 'pipeTest', payloadType: ChangeUserName },
53+
{ name: 'pipeTest', payloadType: ChangeUserName, responseType: ChangeUserName },
5454
async (context: ActorContext<UserState>, payload: ChangeUserName) => {
5555
const name = 'pipe_initial'
5656

5757
return Value.of<UserState, ChangeUserName>()
5858
.state({ ...context.state, name })
59-
.response(ChangeUserName, { newName: `${payload.newName}Internal` })
59+
.response({ newName: `${payload.newName}Internal` })
6060
.pipe({ actorName: 'userActorTransformerTest', action: 'transform' })
6161
}
6262
)
@@ -166,7 +166,7 @@ export const createJsonActor = (system: SpawnSystem) => {
166166
})
167167

168168
actor.addAction(
169-
{ name: 'plusOne' },
169+
{ name: 'plusOne', responseType: 'json' },
170170
async (context: ActorContext<ActorState>, { value }: any) => {
171171
const sum = (value || 0) + 1
172172

0 commit comments

Comments
 (0)