@slack/bolt
Version:
A framework for building Slack apps, fast.
144 lines • 5.99 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.enrichFunctionArgs = exports.isFunctionEvent = exports.processFunctionMiddleware = exports.validate = exports.CustomFunction = void 0;
const web_api_1 = require("@slack/web-api");
const errors_1 = require("./errors");
const process_1 = __importDefault(require("./middleware/process"));
/** Constants */
const VALID_PAYLOAD_TYPES = new Set(['function_executed']);
/** Class */
class CustomFunction {
/** Function callback_id */
callbackId;
appWebClientOptions;
middleware;
constructor(callbackId, middleware, clientOptions) {
validate(callbackId, middleware);
this.appWebClientOptions = clientOptions;
this.callbackId = callbackId;
this.middleware = middleware;
}
getMiddleware() {
return async (args) => {
if (isFunctionEvent(args) && this.matchesConstraints(args)) {
return this.processEvent(args);
}
return args.next();
};
}
matchesConstraints(args) {
return args.payload.function.callback_id === this.callbackId;
}
async processEvent(args) {
const functionArgs = enrichFunctionArgs(args, this.appWebClientOptions);
const functionMiddleware = this.getFunctionMiddleware();
return processFunctionMiddleware(functionArgs, functionMiddleware);
}
getFunctionMiddleware() {
return this.middleware;
}
/**
* Factory for `complete()` utility
*/
static createFunctionComplete(context, client) {
const token = selectToken(context);
const { functionExecutionId } = context;
if (!functionExecutionId) {
const errorMsg = 'No function_execution_id found';
throw new errors_1.CustomFunctionCompleteSuccessError(errorMsg);
}
return (params = {}) => client.functions.completeSuccess({
token,
outputs: params.outputs || {},
function_execution_id: functionExecutionId,
});
}
/**
* Factory for `fail()` utility
*/
static createFunctionFail(context, client) {
const token = selectToken(context);
const { functionExecutionId } = context;
if (!functionExecutionId) {
const errorMsg = 'No function_execution_id found';
throw new errors_1.CustomFunctionCompleteFailError(errorMsg);
}
return (params) => {
const { error } = params ?? {};
return client.functions.completeError({
token,
error,
function_execution_id: functionExecutionId,
});
};
}
}
exports.CustomFunction = CustomFunction;
/** Helper Functions */
function validate(callbackId, middleware) {
// Ensure callbackId is valid
if (typeof callbackId !== 'string') {
const errorMsg = 'CustomFunction expects a callback_id as the first argument';
throw new errors_1.CustomFunctionInitializationError(errorMsg);
}
// Ensure middleware argument is either a function or an array
if (typeof middleware !== 'function' && !Array.isArray(middleware)) {
const errorMsg = 'CustomFunction expects a function or array of functions as the second argument';
throw new errors_1.CustomFunctionInitializationError(errorMsg);
}
// Ensure array includes only functions
if (Array.isArray(middleware)) {
for (const fn of middleware) {
if (!(fn instanceof Function)) {
const errorMsg = 'All CustomFunction middleware must be functions';
throw new errors_1.CustomFunctionInitializationError(errorMsg);
}
}
}
}
exports.validate = validate;
/**
* `processFunctionMiddleware()` invokes each listener middleware
*/
async function processFunctionMiddleware(args, middleware) {
const { context, client, logger } = args;
const callbacks = [...middleware];
const lastCallback = callbacks.pop();
if (lastCallback !== undefined) {
await (0, process_1.default)(callbacks, args, context, client, logger, async () => lastCallback({ ...args, context, client, logger }));
}
}
exports.processFunctionMiddleware = processFunctionMiddleware;
function isFunctionEvent(args) {
return VALID_PAYLOAD_TYPES.has(args.payload.type);
}
exports.isFunctionEvent = isFunctionEvent;
function selectToken(context) {
// If attachFunctionToken = false, fallback to botToken or userToken
return context.functionBotAccessToken ? context.functionBotAccessToken : context.botToken || context.userToken;
}
/**
* `enrichFunctionArgs()` takes in a function's args and:
* 1. removes the next() passed in from App-level middleware processing
* - events will *not* continue down global middleware chain to subsequent listeners
* 2. augments args with step lifecycle-specific properties/utilities
* */
function enrichFunctionArgs(args, webClientOptions) {
const { next: _next, ...functionArgs } = args;
const enrichedArgs = { ...functionArgs };
const token = selectToken(functionArgs.context);
// Making calls with a functionBotAccessToken establishes continuity between
// a function_executed event and subsequent interactive events (actions)
const client = new web_api_1.WebClient(token, webClientOptions);
enrichedArgs.client = client;
// Utility args
enrichedArgs.inputs = enrichedArgs.event.inputs;
enrichedArgs.complete = CustomFunction.createFunctionComplete(enrichedArgs.context, client);
enrichedArgs.fail = CustomFunction.createFunctionFail(enrichedArgs.context, client);
return enrichedArgs; // TODO: dangerous casting as it obfuscates missing `next()`
}
exports.enrichFunctionArgs = enrichFunctionArgs;
//# sourceMappingURL=CustomFunction.js.map