Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,7 @@ ow.actions.create({name, action}).then(result => {
const actionName = '/mynamespace/reverseWords'
const name = 'reverse'

ow.actions.create({ name, action: {
exec: {
kind: 'sequence',
components: [ actionName ]
}
}})
ow.actions.create({ name, sequence: [ actionName ] })
```

### retrieve action resource
Expand Down Expand Up @@ -459,6 +454,32 @@ If you pass in an array for the first parameter, the `create` call will be execu
ow.actions.create([{...}, {...}])
```

### create & update action sequence

```javascript
ow.actions.create({name: '...', sequence: ["action_name", "next_action", ...]})
ow.actions.update({name: '...', sequence: ["action_name", "next_action", ...]})
```

The following mandatory parameters are supported:

- `name` - action identifier
- `sequence` - Array containing JS strings with action identifiers to use in sequence. This can be a full or relative action identifier (e.g. `action-name` or `/namespace/package/action-name`).

The following optional parameters are supported:

- `namespace` - set custom namespace for endpoint
- `params` - object containing default parameters for the action (default: `{}`)
- `annotations` - object containing annotations for the action (default: `{}`)
- `limits` - object containing limits for the action (default: `{}`)
- `version` - set semantic version of the action. If parameter is empty when create new action openwisk generate 0.0.1 value when update an action increase the patch version.

If you pass in an array for the first parameter, the `create` call will be executed for each array item. The function returns a Promise which resolves with the results when all operations have finished.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t understand this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you pass ow.actions.create([{...}, {...}]) it will provide a mechanism for creating multiple actions without having to do the calls manually. This is the doc copied from the section above: https://github.com/apache/incubator-openwhisk-client-js#create--update-action

```javascript
ow.actions.create([{...}, {...}])
```

### fire trigger

```javascript
Expand Down
42 changes: 37 additions & 5 deletions lib/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@ class Actions extends Resources {
return super.create(options)
}

actionBody (options) {
if (!options.hasOwnProperty('action')) {
throw new Error(messages.MISSING_ACTION_BODY_ERROR)
}
actionBodyWithCode (options) {
const body = {exec: {kind: options.kind || 'nodejs:default', code: options.action}}

// allow options to override the derived exec object
Expand All @@ -57,10 +54,45 @@ class Actions extends Resources {

if (options.action instanceof Buffer) {
body.exec.code = options.action.toString('base64')
} else if (typeof options.action === 'object') {
}

return body
}

actionBodyWithSequence (options) {
if (!(options.sequence instanceof Array)) {
throw new Error(messages.INVALID_SEQ_PARAMETER)
}

if (options.sequence.length === 0) {
throw new Error(messages.INVALID_SEQ_PARAMETER_LENGTH)
}

const body = {exec: {kind: 'sequence', components: options.sequence}}
return body
}

actionBody (options) {
const isCodeAction = options.hasOwnProperty('action')
const isSequenceAction = options.hasOwnProperty('sequence')

if (!isCodeAction && !isSequenceAction) {
throw new Error(messages.MISSING_ACTION_OR_SEQ_BODY_ERROR)
}

if (isCodeAction && isSequenceAction) {
throw new Error(messages.INVALID_ACTION_AND_SEQ_PARAMETERS)
}

// user can manually define & control exact action definition by passing in an object
if (isCodeAction && typeof options.action === 'object' &&
(!(options.action instanceof Buffer))) {
return options.action
}

const body = isCodeAction
? this.actionBodyWithCode(options) : this.actionBodyWithSequence(options)

if (typeof options.params === 'object') {
body.parameters = Object.keys(options.params).map(key => ({key, value: options.params[key]}))
}
Expand Down
5 changes: 4 additions & 1 deletion lib/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ module.exports = {
MISSING_ACTION_ERROR: 'Missing mandatory actionName parameter from options.',
INVALID_ACTION_ERROR: 'Invalid actionName parameter from options. Should be "action", "/namespace/action" or "/namespace/package/action".',
INVALID_RESOURCE_ERROR: 'Invalid resource identifier from options. Should be "resource", "/namespace/resource" or "/namespace/package/resource".',
MISSING_ACTION_BODY_ERROR: 'Missing mandatory action parameter from options.',
INVALID_SEQ_PARAMETER: 'Invalid sequence parameter from options. Must be an array.',
INVALID_SEQ_PARAMETER_LENGTH: 'Invalid sequence parameter from options. Array must not be empty.',
INVALID_ACTION_AND_SEQ_PARAMETERS: 'Invalid options parameters, contains both "action" and "sequence" parameters in options.',
MISSING_ACTION_OR_SEQ_BODY_ERROR: 'Missing mandatory action or sequence parameter from options.',
MISSING_RULE_ERROR: 'Missing mandatory ruleName parameter from options.',
MISSING_TRIGGER_ERROR: 'Missing mandatory triggerName parameter from options.',
MISSING_PACKAGE_ERROR: 'Missing mandatory packageName parameter from options.',
Expand Down
24 changes: 24 additions & 0 deletions test/integration/actions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,27 @@ test('create, invoke and remove package action', t => {
})
}).catch(errors)
})

test('create, get and delete sequence action', t => {
const errors = err => {
console.log(err)
t.fail()
}

const actions = new Actions(new Client(options))
return actions.create({
actionName: 'my_sequence',
sequence: ['/whisk.system/utils/echo']
}).then(result => {
t.is(result.name, 'my_sequence')
t.is(result.namespace, NAMESPACE)
t.is(result.exec.kind, 'sequence')
t.deepEqual(result.exec.components, ['/whisk.system/utils/echo'])
return actions.get({actionName: 'my_sequence'}).then(actionResult => {
t.is(actionResult.name, 'my_sequence')
t.is(actionResult.namespace, NAMESPACE)
t.pass()
return actions.delete({actionName: 'my_sequence'}).catch(errors)
}).catch(errors)
}).catch(errors)
})
76 changes: 76 additions & 0 deletions test/unit/actions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,82 @@ test('create a new action with version parameter', t => {
return actions.create({name: '12345', action, version})
})

test('create a new sequence action', t => {
t.plan(4)
const ns = '_'
const client = {}
const sequence = ['/ns/action', '/ns/another_action', '/ns/final_action']

const actions = new Actions(client)

client.request = (method, path, options) => {
t.is(method, 'PUT')
t.is(path, `namespaces/${ns}/actions/12345`)
t.deepEqual(options.qs, {})
t.deepEqual(options.body, {exec: {kind: 'sequence', components: sequence}})
}

return actions.create({name: '12345', sequence})
})

test('create a new sequence action with additional options', t => {
t.plan(4)
const ns = '_'
const client = {}
const sequence = ['/ns/action', '/ns/another_action', '/ns/final_action']
const annotations = {
foo: 'bar'
}
const params = {
foo: 'bar'
}
const limits = {
timeout: 300000
}

const actions = new Actions(client)

client.request = (method, path, options) => {
t.is(method, 'PUT')
t.is(path, `namespaces/${ns}/actions/12345`)
t.deepEqual(options.qs, {})
t.deepEqual(options.body, {exec: {kind: 'sequence', components: sequence},
limits,
parameters: [
{key: 'foo', value: 'bar'}
],
annotations: [
{ key: 'foo', value: 'bar' }
]})
}

return actions.create({name: '12345', sequence, annotations, params, limits})
})

test('creating sequence action with invalid sequence parameter', t => {
const client = {}

const actions = new Actions(client)

t.throws(() => actions.create({name: '12345', sequence: 'string'}), /Invalid sequence parameter/)
t.throws(() => actions.create({name: '12345', sequence: { foo: 'bar' }}), /Invalid sequence parameter/)
})

test('creating sequence action with empty array', t => {
const client = {}

const actions = new Actions(client)

t.throws(() => actions.create({name: '12345', sequence: []}), /Invalid sequence parameter/)
})

test('creating action with both sequence and action parameters', t => {
const client = {}
const actions = new Actions(client)

t.throws(() => actions.create({name: '12345', action: 'function main() {}', sequence: 'string'}), /Invalid options parameters/)
})

test('should pass through requested User-Agent header', t => {
t.plan(1)
const userAgent = 'userAgentShouldPassThroughPlease'
Expand Down