// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import {AppBindingLocations, AppCallResponseTypes} from '@constants/apps'; export function copyAndFillBindings(binding?: AppBinding): AppBinding | undefined { if (!binding) { return undefined; } const copy = JSON.parse(JSON.stringify(binding)); fillAndTrimBindingsInformation(copy); return copy; } // fillAndTrimBindingsInformation does: // - Build the location (e.g. channel_header/binding) // - Inherit app calls // - Inherit app ids // - Trim invalid bindings (do not have an app call or app id at the leaf) export function fillAndTrimBindingsInformation(binding?: AppBinding) { if (!binding) { return; } binding.bindings?.forEach((b) => { // Propagate id down if not defined if (!b.app_id) { b.app_id = binding.app_id; } // Compose location b.location = binding.location + '/' + b.location; // Propagate call down if not defined if (!b.call) { b.call = binding.call; } fillAndTrimBindingsInformation(b); }); // Trim branches without app_id if (!binding.app_id) { binding.bindings = binding.bindings?.filter((v) => v.app_id); } // Trim branches without calls if (!binding.call) { binding.bindings = binding.bindings?.filter((v) => v.call); } // Pull up app_id if needed if (binding.bindings?.length && !binding.app_id) { binding.app_id = binding.bindings[0].app_id; } // Pull up call if needed if (binding.bindings?.length && !binding.call) { binding.call = binding.bindings[0].call; } } export function validateBindings(binding?: AppBinding) { filterInvalidChannelHeaderBindings(binding); filterInvalidCommands(binding); filterInvalidPostMenuBindings(binding); binding?.bindings?.forEach(fillAndTrimBindingsInformation); } // filterInvalidCommands remove commands without a label function filterInvalidCommands(binding?: AppBinding) { if (!binding) { return; } const isValidCommand = (b: AppBinding): boolean => { return Boolean(b.label); }; const validateCommand = (b: AppBinding) => { b.bindings = b.bindings?.filter(isValidCommand); b.bindings?.forEach(validateCommand); }; binding.bindings?.filter((b) => b.location === AppBindingLocations.COMMAND).forEach(validateCommand); } // filterInvalidChannelHeaderBindings remove bindings // without a label. function filterInvalidChannelHeaderBindings(binding?: AppBinding) { if (!binding) { return; } const isValidChannelHeaderBindings = (b: AppBinding): boolean => { return Boolean(b.label); }; const validateChannelHeaderBinding = (b: AppBinding) => { b.bindings = b.bindings?.filter(isValidChannelHeaderBindings); b.bindings?.forEach(validateChannelHeaderBinding); }; binding.bindings?.filter((b) => b.location === AppBindingLocations.CHANNEL_HEADER_ICON).forEach(validateChannelHeaderBinding); } // filterInvalidPostMenuBindings remove bindings // without a label. function filterInvalidPostMenuBindings(binding?: AppBinding) { if (!binding) { return; } const isValidPostMenuBinding = (b: AppBinding): boolean => { return Boolean(b.label); }; const validatePostMenuBinding = (b: AppBinding) => { b.bindings = b.bindings?.filter(isValidPostMenuBinding); b.bindings?.forEach(validatePostMenuBinding); }; binding.bindings?.filter((b) => b.location === AppBindingLocations.POST_MENU_ITEM).forEach(validatePostMenuBinding); } export function createCallContext( appID: string, location?: string, channelID?: string, teamID?: string, postID?: string, rootID?: string, ): AppContext { return { app_id: appID, location, channel_id: channelID, team_id: teamID, post_id: postID, root_id: rootID, }; } export function createCallRequest( call: AppCall, context: AppContext, defaultExpand: AppExpand = {}, values?: AppCallValues, rawCommand?: string, query?: string, selectedField?: string, ): AppCallRequest { return { ...call, context, values, expand: { ...defaultExpand, ...call.expand, }, raw_command: rawCommand, query, selected_field: selectedField, }; } export const makeCallErrorResponse = (errMessage: string) => { return { type: AppCallResponseTypes.ERROR, error: errMessage, }; }; export const filterEmptyOptions = (option: AppSelectOption): boolean => Boolean(option.value && !option.value.match(/^[ \t]+$/));