UNPKG

@slack/bolt

Version:

A framework for building Slack apps, fast.

144 lines 5.99 kB
"use strict"; 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