UNPKG

simple-swf

Version:

Actually simple API layer for AWS SWF

113 lines (101 loc) 4.13 kB
import { SWF, Request } from 'aws-sdk' import * as _ from 'lodash' import * as async from 'async' import { Activity, ActivityType, Workflow } from '../entities' import { ActivityTask } from '../tasks' import { FieldSerializer } from '../util' import { Worker } from './Worker' import { buildIdentity } from '../util/buildIdentity' import { SWFConfig, ConfigOverride } from '../SWFConfig' import { UnknownResourceFault, StopReasons, TaskInput } from '../interfaces' export interface ActivityTypeCreated { activity: ActivityType, created: boolean } export class ActivityWorker extends Worker<SWF.ActivityTask, ActivityTask> { swfClient: SWF config: SWFConfig opts: ConfigOverride activityRegistry: {[name: string]: ActivityType} activeActivities: {[activeId: string]: Activity } fieldSerializer: FieldSerializer constructor(workflow: Workflow, opts: ConfigOverride = {}) { // ensure string from overrides as ConfigOverride allows numbers let identity = (opts['identity'] || buildIdentity('activity')).toString() super(workflow, identity) this.config = workflow.config this.fieldSerializer = workflow.fieldSerializer this.opts = opts this.activityRegistry = {} this.activeActivities = {} this.swfClient = workflow.swfClient } buildApiRequest(): Request<any, any> { let defaults = this.config.populateDefaults({entities: ['activity'], api: 'pollForActivityTask'}, this.opts) let taskListKey = this.config.getMappingName('taskList', {entities: ['activity'], api: 'pollForActivityTask'}) let taskList = defaults[taskListKey!] let params: SWF.PollForActivityTaskInput = { domain: this.workflow.domain.name, identity: this.identity, taskList: taskList } return this.swfClient.pollForActivityTask(_.defaults<SWF.PollForActivityTaskInput>(params, defaults)) } handleError(err: Error): boolean { return false } wrapTask(workflow: Workflow, task: SWF.ActivityTask, cb: {(err: Error | null, task: ActivityTask | null)}) { this.fieldSerializer.deserialize(task.input || null, (err, input) => { if (err) return cb(err, null) let sInput = input as TaskInput let actTask = new ActivityTask(workflow, task, sInput) cb(null, actTask) }) } performTask(task: ActivityTask) { let activityType = this.getActivityType(task.activityName()) let execution = activityType.createExecution(this.workflow, task) this.emit('startTask', task, execution) this.activeActivities[execution.id] = execution execution.on('failedToStop', (err) => { this.emit('error', err) }) execution._start((err, status, details) => { // this error should only indicate AWS errors, the actual result of the task // is handler by the activity if (err && err.code !== UnknownResourceFault) this.emit('error', err) if (err) this.emit('warn', err) this.emit('finished', task, execution, status, details) delete this.activeActivities[execution.id] }) } stop(cb: {(err?: Error)}) { async.forEachOf(this.activeActivities, (execution: Activity, keyName, cb: {(err?: Error | null)}) => { delete this.activeActivities[keyName] execution._requestStop(StopReasons.ProcessExit, false, cb) }, (err) => { // even if we have an error, we want still stop the polling this._stop((stopError) => { cb(err || stopError) }) }) } start(cb: {(Error?, res?: ActivityTypeCreated[])}) { let activities = _.values<ActivityType>(this.activityRegistry) async.map( activities, (act, cb: {(err?: Error | null, s?: boolean)}) => act.ensureActivityType(this.workflow.domain, cb), (err, results) => { if (err) return cb(err) const withCreated = activities.map((act, index) => ({activity: act, created: results[index] as boolean})) this._start() cb(null!, withCreated) }) } registerActivityType(activity: ActivityType) { this.activityRegistry[activity.name] = activity } getActivityType(name: string): ActivityType { return this.activityRegistry[name] } }