actionhero
Version:
The reusable, scalable, and quick node.js API server for stateless and stateful applications
109 lines (101 loc) • 3.62 kB
text/typescript
import { Inputs } from "./inputs";
import { api } from "../index";
/**
* Create a new Actionhero Action. The required properties of an action. These can be defined statically (this.name) or as methods which return a value.
*```js
* import { Action } from "actionhero";
*
* export default class RandomNumber extends Action {
* constructor () {
* super()
* this.name = 'randomNumber'
* this.description = 'I am an API method which will generate a random number'
* this.outputExample = { randomNumber: 0.1234 }
* }
* async run ({ response }) {
* response.randomNumber = Math.random()
* }
*}
*```
*/
export abstract class Action {
/**The name of the Action*/
name: string;
/**The description of the Action (default this.name)*/
description: string;
/**The version of this Action (default: 1) */
version: number | string;
//*An example response payload (default: {})
outputExample?: object;
/**The inputs of the Action (default: {}) */
inputs: Inputs;
/**The Middleware specific to this Action (default: []). Middleware is described by the string names of the middleware. */
middleware: Array<string>;
/**Are there connections from any servers which cannot use this Action (default: [])? */
blockedConnectionTypes: Array<string>;
/**Under what level should connections to this Action be logged (default 'info')? */
logLevel: string;
/**If this Action is responding to a `web` request, and that request has a file extension like *.jpg, should Actionhero set the response headers to match that extension (default: true)? */
matchExtensionMimeType: boolean;
/**Should this Action appear in api.documentation.documentation? (default: true)? */
toDocument: boolean;
constructor() {
const coreProperties = this.defaults();
for (const key in coreProperties) {
if (!this[key]) {
this[key] = coreProperties[key];
}
if (typeof this[key] === "function") {
this[key] = this[key]();
}
}
}
/**
* The main "do something" method for this action. It can be `async`. Usually the goal of this run method is to set properties on `data.response`. If error is thrown in this method, it will be logged, caught, and appended to `data.response.error`
* @param data The data about this connection, response, and params.
*/
abstract run(data: { [key: string]: any }): Promise<ActionResponse>;
private defaults() {
return {
name: null,
version: 1,
description: this.name,
outputExample: {},
inputs: {},
middleware: [],
blockedConnectionTypes: [],
logLevel: "info",
matchExtensionMimeType: true,
toDocument: true,
};
}
validate() {
if (!this.name) {
throw new Error("name is required for this action");
}
if (!this.description) {
throw new Error(
`description is required for the action \`${this.name}\``
);
}
if (!this.run || typeof this.run !== "function") {
throw new Error(`action \`${this.name}\` has no run method`);
}
if (
api.connections &&
api.connections.allowedVerbs.indexOf(this.name) >= 0
) {
throw new Error(
`action \`${this.name}\` is a reserved verb for connections. choose a new name`
);
}
Object.keys(this.inputs).forEach((input) => {
if (api.params.globalSafeParams.indexOf(input) >= 0) {
throw new Error(
`input \`${input}\` in action \`${this.name}\` is a reserved param`
);
}
});
}
}
export type ActionResponse = { [key: string]: any } | null | void;