From c29cdef21cfa8164375cd220e56e3651575117bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Espino=20Garc=C3=ADa?= Date: Thu, 26 Aug 2021 10:40:22 +0200 Subject: [PATCH] [MM-36758] Fix filter and locations (#5567) * Fix filter and locations * Fix lint * Only fetch form on final commands and some other minor fixes * Fix tests --- .../app_command_parser.test.ts | 39 +- .../app_command_parser/app_command_parser.ts | 35 +- .../tests/app_command_parser_test_data.ts | 8 +- .../entities/__snapshots__/apps.test.js.snap | 646 +++++++++--------- app/mm-redux/reducers/entities/apps.test.js | 590 ++++++++-------- app/utils/apps.ts | 28 +- assets/base/i18n/en.json | 1 + 7 files changed, 676 insertions(+), 671 deletions(-) diff --git a/app/components/autocomplete/slash_suggestion/app_command_parser/app_command_parser.test.ts b/app/components/autocomplete/slash_suggestion/app_command_parser/app_command_parser.test.ts index 943adfa30..1572fc453 100644 --- a/app/components/autocomplete/slash_suggestion/app_command_parser/app_command_parser.test.ts +++ b/app/components/autocomplete/slash_suggestion/app_command_parser/app_command_parser.test.ts @@ -141,25 +141,30 @@ describe('AppCommandParser', () => { { title: 'incomplete top command', command: '/jir', - autocomplete: {expectError: '`{command}`: No matching command found in this workspace.'}, + autocomplete: {verify: (parsed: ParsedCommand): void => { + expect(parsed.state).toBe(ParseState.Command); + expect(parsed.incomplete).toBe('jir'); + }}, submit: {expectError: '`{command}`: No matching command found in this workspace.'}, }, { title: 'no space after the top command', command: '/jira', - autocomplete: {expectError: '`{command}`: No matching command found in this workspace.'}, - submit: {verify: (parsed: ParsedCommand): void => { + autocomplete: {verify: (parsed: ParsedCommand): void => { expect(parsed.state).toBe(ParseState.Command); - expect(parsed.binding?.label).toBe('jira'); + expect(parsed.incomplete).toBe('jira'); }}, + submit: {expectError: 'You must select a subcommand.'}, }, { title: 'space after the top command', command: '/jira ', - submit: {verify: (parsed: ParsedCommand): void => { + autocomplete: {verify: (parsed: ParsedCommand): void => { expect(parsed.state).toBe(ParseState.Command); + expect(parsed.incomplete).toBe(''); expect(parsed.binding?.label).toBe('jira'); }}, + submit: {expectError: 'You must select a subcommand.'}, }, { title: 'middle of subcommand', @@ -170,12 +175,7 @@ describe('AppCommandParser', () => { expect(parsed.incomplete).toBe('iss'); expect(parsed.incompleteStart).toBe(9); }}, - submit: {verify: (parsed: ParsedCommand): void => { - expect(parsed.state).toBe(ParseState.EndCommand); - expect(parsed.binding?.label).toBe('jira'); - expect(parsed.incomplete).toBe('iss'); - expect(parsed.incompleteStart).toBe(9); - }}, + submit: {expectError: 'You must select a subcommand.'}, }, { title: 'second subcommand, no space', @@ -186,11 +186,7 @@ describe('AppCommandParser', () => { expect(parsed.incomplete).toBe('issue'); expect(parsed.incompleteStart).toBe(6); }}, - submit: {verify: (parsed: ParsedCommand): void => { - expect(parsed.state).toBe(ParseState.Command); - expect(parsed.binding?.label).toBe('issue'); - expect(parsed.location).toBe('/jira/issue'); - }}, + submit: {expectError: 'You must select a subcommand.'}, }, { title: 'token after the end of bindings, no space', @@ -304,7 +300,6 @@ describe('AppCommandParser', () => { expect(parsed.values?.epic).toBe('M'); }}, }, - { title: 'happy full view', command: '/jira issue view --project=`P 1` MM-123', @@ -874,6 +869,10 @@ describe('AppCommandParser', () => { const {call} = await parser.composeCallFromCommand(cmd); expect(call).toEqual({ ...base, + context: { + ...base.context, + location: '/command/jira/issue/create', + }, raw_command: cmd, expand: {}, query: undefined, @@ -897,6 +896,10 @@ describe('AppCommandParser', () => { const {call} = await parser.composeCallFromCommand(cmd); expect(call).toEqual({ ...base, + context: { + ...base.context, + location: '/command/jira/issue/create', + }, expand: {}, selected_field: undefined, query: undefined, @@ -928,7 +931,7 @@ describe('AppCommandParser', () => { context: { app_id: 'jira', channel_id: 'current_channel_id', - location: '/command', + location: '/command/jira/issue/create', root_id: 'root_id', team_id: 'team_id', }, diff --git a/app/components/autocomplete/slash_suggestion/app_command_parser/app_command_parser.ts b/app/components/autocomplete/slash_suggestion/app_command_parser/app_command_parser.ts index 409043c03..eda4d1116 100644 --- a/app/components/autocomplete/slash_suggestion/app_command_parser/app_command_parser.ts +++ b/app/components/autocomplete/slash_suggestion/app_command_parser/app_command_parser.ts @@ -217,6 +217,10 @@ export class ParsedCommand { } if (!this.binding) { + if (autocompleteMode) { + return this; + } + return this.asError(this.intl.formatMessage({ id: 'apps.error.parser.no_match', defaultMessage: '`{command}`: No matching command found in this workspace.', @@ -225,13 +229,22 @@ export class ParsedCommand { })); } - this.form = this.binding.form; - if (!this.form) { - const fetched = await this.formsCache.getForm(this.location, this.binding); - if (fetched?.error) { - return this.asError(fetched.error); + if (!autocompleteMode && this.binding.bindings?.length) { + return this.asError(this.intl.formatMessage({ + id: 'apps.error.parser.execute_non_leaf', + defaultMessage: 'You must select a subcommand.', + })); + } + + if (!this.binding.bindings?.length) { + this.form = this.binding?.form; + if (!this.form) { + const fetched = await this.formsCache.getForm(this.location, this.binding); + if (fetched?.error) { + return this.asError(fetched.error); + } + this.form = fetched?.form; } - this.form = fetched?.form; } return this; @@ -795,7 +808,7 @@ export class AppCommandParser { return {call: null, errorMessage}; } - const context = this.getAppContext(parsed.binding.app_id); + const context = this.getAppContext(parsed.binding); return {call: createCallRequest(call, context, {}, values, parsed.command)}; } @@ -954,10 +967,10 @@ export class AppCommandParser { } // getAppContext collects post/channel/team info for performing calls - private getAppContext = (appID: string): AppContext => { + private getAppContext = (binding: AppBinding): AppContext => { const context: AppContext = { - app_id: appID, - location: AppBindingLocations.COMMAND, + app_id: binding.app_id, + location: binding.location, root_id: this.rootPostID, }; @@ -980,7 +993,7 @@ export class AppCommandParser { const payload = createCallRequest( binding.call, - this.getAppContext(binding.app_id), + this.getAppContext(binding), ); const res = await this.store.dispatch(doAppCall(payload, AppCallTypes.FORM, this.intl)) as DoAppCallResult; diff --git a/app/components/autocomplete/slash_suggestion/app_command_parser/tests/app_command_parser_test_data.ts b/app/components/autocomplete/slash_suggestion/app_command_parser/tests/app_command_parser_test_data.ts index 6b977fb59..dde57a439 100644 --- a/app/components/autocomplete/slash_suggestion/app_command_parser/tests/app_command_parser_test_data.ts +++ b/app/components/autocomplete/slash_suggestion/app_command_parser/tests/app_command_parser_test_data.ts @@ -100,7 +100,7 @@ export const reduxTestState = { export const viewCommand: AppBinding = { app_id: 'jira', label: 'view', - location: 'view', + location: '/command/jira/issue/view', description: 'View details of a Jira issue', form: { call: { @@ -130,7 +130,7 @@ export const viewCommand: AppBinding = { export const createCommand: AppBinding = { app_id: 'jira', label: 'create', - location: 'create', + location: '/command/jira/issue/create', description: 'Create a new Jira issue', icon: 'Create icon', hint: 'Create hint', @@ -188,12 +188,14 @@ export const testBindings: AppBinding[] = [ location: '/command', bindings: [ { + location: '/command/jira', app_id: 'jira', label: 'jira', description: 'Interact with your Jira instance', icon: 'Jira icon', hint: 'Jira hint', bindings: [{ + location: '/command/jira/issue', app_id: 'jira', label: 'issue', description: 'Interact with Jira issues', @@ -206,12 +208,14 @@ export const testBindings: AppBinding[] = [ }], }, { + location: '/command/other', app_id: 'other', label: 'other', description: 'Other description', icon: 'Other icon', hint: 'Other hint', bindings: [{ + location: '/command/other/sub1', app_id: 'other', label: 'sub1', description: 'Some Description', diff --git a/app/mm-redux/reducers/entities/__snapshots__/apps.test.js.snap b/app/mm-redux/reducers/entities/__snapshots__/apps.test.js.snap index 934192dc2..1929a615c 100644 --- a/app/mm-redux/reducers/entities/__snapshots__/apps.test.js.snap +++ b/app/mm-redux/reducers/entities/__snapshots__/apps.test.js.snap @@ -1,340 +1,332 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`bindings Invalid channel header get filtered 1`] = ` -Object { - "bindings": Array [ - Object { - "app_id": "1", - "bindings": Array [ - Object { - "app_id": "1", - "bindings": undefined, - "call": Object {}, - "label": "a", - "location": "/post_menu/locA", - }, - ], - "call": Object {}, - "location": "/post_menu", - }, - Object { - "app_id": "2", - "bindings": Array [ - Object { - "app_id": "2", - "bindings": undefined, - "call": Object {}, - "label": "a", - "location": "/post_menu/locA", - }, - ], - "call": Object {}, - "location": "/post_menu", - }, - Object { - "app_id": "1", - "bindings": Array [ - Object { - "app_id": "1", - "bindings": undefined, - "call": Object {}, - "icon": "icon", - "label": "b", - "location": "/channel_header/locB", - }, - Object { - "app_id": "1", - "bindings": undefined, - "call": Object {}, - "label": "c", - "location": "/channel_header/locC", - }, - ], - "call": Object {}, - "location": "/channel_header", - }, - Object { - "app_id": "2", - "bindings": Array [ - Object { - "app_id": "2", - "bindings": undefined, - "call": Object {}, - "icon": "icon", - "label": "c", - "location": "/channel_header/locC", - }, - ], - "call": Object {}, - "location": "/channel_header", - }, - Object { - "app_id": "3", - "bindings": Array [ - Object { - "app_id": "3", - "bindings": undefined, - "call": Object {}, - "label": "c", - "location": "/channel_header/locC", - }, - ], - "call": Object {}, - "location": "/channel_header", - }, - Object { - "app_id": "3", - "bindings": Array [ - Object { - "app_id": "3", - "bindings": undefined, - "call": Object {}, - "label": "c", - "location": "/command/locC", - }, - ], - "call": Object {}, - "location": "/command", - }, - ], -} +Array [ + Object { + "app_id": "1", + "bindings": Array [ + Object { + "app_id": "1", + "bindings": undefined, + "call": Object {}, + "label": "a", + "location": "/post_menu/locA", + }, + ], + "call": Object {}, + "location": "/post_menu", + }, + Object { + "app_id": "2", + "bindings": Array [ + Object { + "app_id": "2", + "bindings": undefined, + "call": Object {}, + "label": "a", + "location": "/post_menu/locA", + }, + ], + "call": Object {}, + "location": "/post_menu", + }, + Object { + "app_id": "1", + "bindings": Array [ + Object { + "app_id": "1", + "bindings": undefined, + "call": Object {}, + "icon": "icon", + "label": "b", + "location": "/channel_header/locB", + }, + Object { + "app_id": "1", + "bindings": undefined, + "call": Object {}, + "label": "c", + "location": "/channel_header/locC", + }, + ], + "call": Object {}, + "location": "/channel_header", + }, + Object { + "app_id": "2", + "bindings": Array [ + Object { + "app_id": "2", + "bindings": undefined, + "call": Object {}, + "icon": "icon", + "label": "c", + "location": "/channel_header/locC", + }, + ], + "call": Object {}, + "location": "/channel_header", + }, + Object { + "app_id": "3", + "bindings": Array [ + Object { + "app_id": "3", + "bindings": undefined, + "call": Object {}, + "label": "c", + "location": "/channel_header/locC", + }, + ], + "call": Object {}, + "location": "/channel_header", + }, + Object { + "app_id": "3", + "bindings": Array [ + Object { + "app_id": "3", + "bindings": undefined, + "call": Object {}, + "label": "c", + "location": "/command/locC", + }, + ], + "call": Object {}, + "location": "/command", + }, +] `; exports[`bindings Invalid commands get filtered 1`] = ` -Object { - "bindings": Array [ - Object { - "app_id": "1", - "bindings": Array [ - Object { - "app_id": "1", - "bindings": undefined, - "call": Object {}, - "label": "a", - "location": "/post_menu/locA", - }, - Object { - "app_id": "1", - "bindings": undefined, - "call": Object {}, - "label": "a", - "location": "/post_menu/locB", - }, - ], - "call": Object {}, - "location": "/post_menu", - }, - Object { - "app_id": "1", - "bindings": Array [ - Object { - "app_id": "1", - "bindings": undefined, - "call": Object {}, - "icon": "icon", - "label": "b", - "location": "/channel_header/locB", - }, - ], - "call": Object {}, - "location": "/channel_header", - }, - Object { - "app_id": "3", - "bindings": Array [ - Object { - "app_id": "3", - "bindings": Array [ - Object { - "app_id": "3", - "bindings": undefined, - "call": Object {}, - "label": "c2", - "location": "/command/locC/subC2", - }, - ], - "call": Object {}, - "label": "c", - "location": "/command/locC", - }, - ], - "call": Object {}, - "location": "/command", - }, - Object { - "app_id": "1", - "bindings": Array [], - "location": "/command", - }, - Object { - "app_id": "2", - "bindings": Array [ - Object { - "app_id": "2", - "bindings": Array [ - Object { - "app_id": "2", - "bindings": undefined, - "call": Object {}, - "label": "c1", - "location": "/command/locC/subC1", - }, - Object { - "app_id": "2", - "bindings": undefined, - "call": Object {}, - "label": "c2", - "location": "/command/locC/subC2", - }, - ], - "call": Object {}, - "label": "c", - "location": "/command/locC", - }, - ], - "call": Object {}, - "location": "/command", - }, - ], -} +Array [ + Object { + "app_id": "1", + "bindings": Array [ + Object { + "app_id": "1", + "bindings": undefined, + "call": Object {}, + "label": "a", + "location": "/post_menu/locA", + }, + Object { + "app_id": "1", + "bindings": undefined, + "call": Object {}, + "label": "a", + "location": "/post_menu/locB", + }, + ], + "call": Object {}, + "location": "/post_menu", + }, + Object { + "app_id": "1", + "bindings": Array [ + Object { + "app_id": "1", + "bindings": undefined, + "call": Object {}, + "icon": "icon", + "label": "b", + "location": "/channel_header/locB", + }, + ], + "call": Object {}, + "location": "/channel_header", + }, + Object { + "app_id": "3", + "bindings": Array [ + Object { + "app_id": "3", + "bindings": Array [ + Object { + "app_id": "3", + "bindings": undefined, + "call": Object {}, + "label": "c2", + "location": "/command/locC/subC2", + }, + ], + "call": Object {}, + "label": "c", + "location": "/command/locC", + }, + ], + "call": Object {}, + "location": "/command", + }, + Object { + "app_id": "1", + "bindings": Array [], + "location": "/command", + }, + Object { + "app_id": "2", + "bindings": Array [ + Object { + "app_id": "2", + "bindings": Array [ + Object { + "app_id": "2", + "bindings": undefined, + "call": Object {}, + "label": "c1", + "location": "/command/locC/subC1", + }, + Object { + "app_id": "2", + "bindings": undefined, + "call": Object {}, + "label": "c2", + "location": "/command/locC/subC2", + }, + ], + "call": Object {}, + "label": "c", + "location": "/command/locC", + }, + ], + "call": Object {}, + "location": "/command", + }, +] `; exports[`bindings Invalid post menu get filtered 1`] = ` -Object { - "bindings": Array [ - Object { - "app_id": "1", - "bindings": Array [ - Object { - "app_id": "1", - "bindings": undefined, - "call": Object {}, - "label": "a", - "location": "/post_menu/locB", - }, - ], - "call": Object {}, - "location": "/post_menu", - }, - Object { - "app_id": "2", - "bindings": Array [ - Object { - "app_id": "2", - "bindings": undefined, - "call": Object {}, - "label": "a", - "location": "/post_menu/locA", - }, - Object { - "app_id": "2", - "bindings": undefined, - "call": Object {}, - "label": "b", - "location": "/post_menu/locB", - }, - ], - "call": Object {}, - "location": "/post_menu", - }, - Object { - "app_id": "3", - "bindings": Array [], - "location": "/post_menu", - }, - Object { - "app_id": "1", - "bindings": Array [ - Object { - "app_id": "1", - "bindings": undefined, - "call": Object {}, - "icon": "icon", - "label": "b", - "location": "/channel_header/locB", - }, - ], - "call": Object {}, - "location": "/channel_header", - }, - Object { - "app_id": "3", - "bindings": Array [ - Object { - "app_id": "3", - "bindings": undefined, - "call": Object {}, - "label": "c", - "location": "/command/locC", - }, - ], - "call": Object {}, - "location": "/command", - }, - ], -} +Array [ + Object { + "app_id": "1", + "bindings": Array [ + Object { + "app_id": "1", + "bindings": undefined, + "call": Object {}, + "label": "a", + "location": "/post_menu/locB", + }, + ], + "call": Object {}, + "location": "/post_menu", + }, + Object { + "app_id": "2", + "bindings": Array [ + Object { + "app_id": "2", + "bindings": undefined, + "call": Object {}, + "label": "a", + "location": "/post_menu/locA", + }, + Object { + "app_id": "2", + "bindings": undefined, + "call": Object {}, + "label": "b", + "location": "/post_menu/locB", + }, + ], + "call": Object {}, + "location": "/post_menu", + }, + Object { + "app_id": "3", + "bindings": Array [], + "location": "/post_menu", + }, + Object { + "app_id": "1", + "bindings": Array [ + Object { + "app_id": "1", + "bindings": undefined, + "call": Object {}, + "icon": "icon", + "label": "b", + "location": "/channel_header/locB", + }, + ], + "call": Object {}, + "location": "/channel_header", + }, + Object { + "app_id": "3", + "bindings": Array [ + Object { + "app_id": "3", + "bindings": undefined, + "call": Object {}, + "label": "c", + "location": "/command/locC", + }, + ], + "call": Object {}, + "location": "/command", + }, +] `; exports[`bindings No element get filtered 1`] = ` -Object { - "bindings": Array [ - Object { - "app_id": "1", - "bindings": Array [ - Object { - "app_id": "1", - "bindings": undefined, - "call": Object {}, - "label": "a", - "location": "/post_menu/locA", - }, - ], - "call": Object {}, - "location": "/post_menu", - }, - Object { - "app_id": "2", - "bindings": Array [ - Object { - "app_id": "2", - "bindings": undefined, - "call": Object {}, - "label": "a", - "location": "/post_menu/locA", - }, - ], - "call": Object {}, - "location": "/post_menu", - }, - Object { - "app_id": "1", - "bindings": Array [ - Object { - "app_id": "1", - "bindings": undefined, - "call": Object {}, - "icon": "icon", - "label": "b", - "location": "/channel_header/locB", - }, - ], - "call": Object {}, - "location": "/channel_header", - }, - Object { - "app_id": "3", - "bindings": Array [ - Object { - "app_id": "3", - "bindings": undefined, - "call": Object {}, - "label": "c", - "location": "/command/locC", - }, - ], - "call": Object {}, - "location": "/command", - }, - ], -} +Array [ + Object { + "app_id": "1", + "bindings": Array [ + Object { + "app_id": "1", + "bindings": undefined, + "call": Object {}, + "label": "a", + "location": "/post_menu/locA", + }, + ], + "call": Object {}, + "location": "/post_menu", + }, + Object { + "app_id": "2", + "bindings": Array [ + Object { + "app_id": "2", + "bindings": undefined, + "call": Object {}, + "label": "a", + "location": "/post_menu/locA", + }, + ], + "call": Object {}, + "location": "/post_menu", + }, + Object { + "app_id": "1", + "bindings": Array [ + Object { + "app_id": "1", + "bindings": undefined, + "call": Object {}, + "icon": "icon", + "label": "b", + "location": "/channel_header/locB", + }, + ], + "call": Object {}, + "location": "/channel_header", + }, + Object { + "app_id": "3", + "bindings": Array [ + Object { + "app_id": "3", + "bindings": undefined, + "call": Object {}, + "label": "c", + "location": "/command/locC", + }, + ], + "call": Object {}, + "location": "/command", + }, +] `; diff --git a/app/mm-redux/reducers/entities/apps.test.js b/app/mm-redux/reducers/entities/apps.test.js index fd40eaac4..e1d99719e 100644 --- a/app/mm-redux/reducers/entities/apps.test.js +++ b/app/mm-redux/reducers/entities/apps.test.js @@ -9,55 +9,53 @@ describe('bindings', () => { const initialState = []; test('No element get filtered', () => { - const data = { - bindings: [ - { - app_id: '1', - location: '/post_menu', - bindings: [ - { - location: 'locA', - label: 'a', - call: {}, - }, - ], - }, - { - app_id: '2', - location: '/post_menu', - bindings: [ - { - location: 'locA', - label: 'a', - call: {}, - }, - ], - }, - { - app_id: '1', - location: '/channel_header', - bindings: [ - { - location: 'locB', - label: 'b', - icon: 'icon', - call: {}, - }, - ], - }, - { - app_id: '3', - location: '/command', - bindings: [ - { - location: 'locC', - label: 'c', - call: {}, - }, - ], - }, - ], - }; + const data = [ + { + app_id: '1', + location: '/post_menu', + bindings: [ + { + location: 'locA', + label: 'a', + call: {}, + }, + ], + }, + { + app_id: '2', + location: '/post_menu', + bindings: [ + { + location: 'locA', + label: 'a', + call: {}, + }, + ], + }, + { + app_id: '1', + location: '/channel_header', + bindings: [ + { + location: 'locB', + label: 'b', + icon: 'icon', + call: {}, + }, + ], + }, + { + app_id: '3', + location: '/command', + bindings: [ + { + location: 'locC', + label: 'c', + call: {}, + }, + ], + }, + ]; const state = Reducers.bindings( initialState, @@ -71,92 +69,90 @@ describe('bindings', () => { }); test('Invalid channel header get filtered', () => { - const data = { - bindings: [ - { - app_id: '1', - location: '/post_menu', - bindings: [ - { - location: 'locA', - label: 'a', - call: {}, - }, - ], - }, - { - app_id: '2', - location: '/post_menu', - bindings: [ - { - location: 'locA', - label: 'a', - call: {}, - }, - ], - }, - { - app_id: '1', - location: '/channel_header', - bindings: [ - { - location: 'locB', - label: 'b', - icon: 'icon', - call: {}, - }, - { - location: 'locC', - label: 'c', - call: {}, - }, - ], - }, - { - app_id: '2', - location: '/channel_header', - bindings: [ - { - location: 'locB', - icon: 'icon', - call: {}, - }, - { - location: 'locC', - label: 'c', - icon: 'icon', - call: {}, - }, - ], - }, - { - app_id: '3', - location: '/channel_header', - bindings: [ - { - location: 'locB', - call: {}, - }, - { - location: 'locC', - label: 'c', - call: {}, - }, - ], - }, - { - app_id: '3', - location: '/command', - bindings: [ - { - location: 'locC', - label: 'c', - call: {}, - }, - ], - }, - ], - }; + const data = [ + { + app_id: '1', + location: '/post_menu', + bindings: [ + { + location: 'locA', + label: 'a', + call: {}, + }, + ], + }, + { + app_id: '2', + location: '/post_menu', + bindings: [ + { + location: 'locA', + label: 'a', + call: {}, + }, + ], + }, + { + app_id: '1', + location: '/channel_header', + bindings: [ + { + location: 'locB', + label: 'b', + icon: 'icon', + call: {}, + }, + { + location: 'locC', + label: 'c', + call: {}, + }, + ], + }, + { + app_id: '2', + location: '/channel_header', + bindings: [ + { + location: 'locB', + icon: 'icon', + call: {}, + }, + { + location: 'locC', + label: 'c', + icon: 'icon', + call: {}, + }, + ], + }, + { + app_id: '3', + location: '/channel_header', + bindings: [ + { + location: 'locB', + call: {}, + }, + { + location: 'locC', + label: 'c', + call: {}, + }, + ], + }, + { + app_id: '3', + location: '/command', + bindings: [ + { + location: 'locC', + label: 'c', + call: {}, + }, + ], + }, + ]; const state = Reducers.bindings( initialState, @@ -170,74 +166,72 @@ describe('bindings', () => { }); test('Invalid post menu get filtered', () => { - const data = { - bindings: [ - { - app_id: '1', - location: '/post_menu', - bindings: [ - { - location: 'locA', - call: {}, - }, - { - location: 'locB', - label: 'a', - call: {}, - }, - ], - }, - { - app_id: '2', - location: '/post_menu', - bindings: [ - { - location: 'locA', - label: 'a', - call: {}, - }, - { - location: 'locB', - label: 'b', - call: {}, - }, - ], - }, - { - app_id: '3', - location: '/post_menu', - bindings: [ - { - location: 'locA', - call: {}, - }, - ], - }, - { - app_id: '1', - location: '/channel_header', - bindings: [ - { - location: 'locB', - label: 'b', - icon: 'icon', - call: {}, - }, - ], - }, - { - app_id: '3', - location: '/command', - bindings: [ - { - location: 'locC', - label: 'c', - call: {}, - }, - ], - }, - ], - }; + const data = [ + { + app_id: '1', + location: '/post_menu', + bindings: [ + { + location: 'locA', + call: {}, + }, + { + location: 'locB', + label: 'a', + call: {}, + }, + ], + }, + { + app_id: '2', + location: '/post_menu', + bindings: [ + { + location: 'locA', + label: 'a', + call: {}, + }, + { + location: 'locB', + label: 'b', + call: {}, + }, + ], + }, + { + app_id: '3', + location: '/post_menu', + bindings: [ + { + location: 'locA', + call: {}, + }, + ], + }, + { + app_id: '1', + location: '/channel_header', + bindings: [ + { + location: 'locB', + label: 'b', + icon: 'icon', + call: {}, + }, + ], + }, + { + app_id: '3', + location: '/command', + bindings: [ + { + location: 'locC', + label: 'c', + call: {}, + }, + ], + }, + ]; const state = Reducers.bindings( initialState, @@ -251,102 +245,100 @@ describe('bindings', () => { }); test('Invalid commands get filtered', () => { - const data = { - bindings: [ - { - app_id: '1', - location: '/post_menu', - bindings: [ - { - location: 'locA', - label: 'a', - call: {}, - }, - { - location: 'locB', - label: 'a', - call: {}, - }, - ], - }, - { - app_id: '1', - location: '/channel_header', - bindings: [ - { - location: 'locB', - label: 'b', - icon: 'icon', - call: {}, - }, - ], - }, - { - app_id: '3', - location: '/command', - bindings: [ - { - location: 'locC', - label: 'c', - bindings: [ - { - location: 'subC1', - call: {}, - }, - { - location: 'subC2', - label: 'c2', - call: {}, - }, - ], - }, - { - location: 'locD', - label: 'd', - bindings: [ - { - location: 'subC1', - call: {}, - }, - ], - }, - ], - }, - { - app_id: '1', - location: '/command', - bindings: [ - { - location: 'locC', - call: {}, - }, - ], - }, - { - app_id: '2', - location: '/command', - bindings: [ - { - location: 'locC', - label: 'c', - call: {}, - bindings: [ - { - location: 'subC1', - label: 'c1', - call: {}, - }, - { - location: 'subC2', - label: 'c2', - call: {}, - }, - ], - }, - ], - }, - ], - }; + const data = [ + { + app_id: '1', + location: '/post_menu', + bindings: [ + { + location: 'locA', + label: 'a', + call: {}, + }, + { + location: 'locB', + label: 'a', + call: {}, + }, + ], + }, + { + app_id: '1', + location: '/channel_header', + bindings: [ + { + location: 'locB', + label: 'b', + icon: 'icon', + call: {}, + }, + ], + }, + { + app_id: '3', + location: '/command', + bindings: [ + { + location: 'locC', + label: 'c', + bindings: [ + { + location: 'subC1', + call: {}, + }, + { + location: 'subC2', + label: 'c2', + call: {}, + }, + ], + }, + { + location: 'locD', + label: 'd', + bindings: [ + { + location: 'subC1', + call: {}, + }, + ], + }, + ], + }, + { + app_id: '1', + location: '/command', + bindings: [ + { + location: 'locC', + call: {}, + }, + ], + }, + { + app_id: '2', + location: '/command', + bindings: [ + { + location: 'locC', + label: 'c', + call: {}, + bindings: [ + { + location: 'subC1', + label: 'c1', + call: {}, + }, + { + location: 'subC2', + label: 'c2', + call: {}, + }, + ], + }, + ], + }, + ]; const state = Reducers.bindings( initialState, diff --git a/app/utils/apps.ts b/app/utils/apps.ts index 65beda98c..0e72d6c2b 100644 --- a/app/utils/apps.ts +++ b/app/utils/apps.ts @@ -69,16 +69,16 @@ export function fillAndTrimBindingsInformation(binding?: AppBinding) { } } -export function validateBindings(binding?: AppBinding) { - filterInvalidChannelHeaderBindings(binding); - filterInvalidCommands(binding); - filterInvalidPostMenuBindings(binding); - binding?.bindings?.forEach(fillAndTrimBindingsInformation); +export function validateBindings(bindings?: AppBinding[]) { + filterInvalidChannelHeaderBindings(bindings); + filterInvalidCommands(bindings); + filterInvalidPostMenuBindings(bindings); + bindings?.forEach(fillAndTrimBindingsInformation); } // filterInvalidCommands remove commands without a label -function filterInvalidCommands(binding?: AppBinding) { - if (!binding) { +function filterInvalidCommands(bindings?: AppBinding[]) { + if (!bindings) { return; } @@ -91,13 +91,13 @@ function filterInvalidCommands(binding?: AppBinding) { b.bindings?.forEach(validateCommand); }; - binding.bindings?.filter((b) => b.location === AppBindingLocations.COMMAND).forEach(validateCommand); + bindings?.filter((b) => b.location === AppBindingLocations.COMMAND).forEach(validateCommand); } // filterInvalidChannelHeaderBindings remove bindings // without a label. -function filterInvalidChannelHeaderBindings(binding?: AppBinding) { - if (!binding) { +function filterInvalidChannelHeaderBindings(bindings?: AppBinding[]) { + if (!bindings) { return; } @@ -110,13 +110,13 @@ function filterInvalidChannelHeaderBindings(binding?: AppBinding) { b.bindings?.forEach(validateChannelHeaderBinding); }; - binding.bindings?.filter((b) => b.location === AppBindingLocations.CHANNEL_HEADER_ICON).forEach(validateChannelHeaderBinding); + bindings?.filter((b) => b.location === AppBindingLocations.CHANNEL_HEADER_ICON).forEach(validateChannelHeaderBinding); } // filterInvalidPostMenuBindings remove bindings // without a label. -function filterInvalidPostMenuBindings(binding?: AppBinding) { - if (!binding) { +function filterInvalidPostMenuBindings(bindings?: AppBinding[]) { + if (!bindings) { return; } @@ -129,7 +129,7 @@ function filterInvalidPostMenuBindings(binding?: AppBinding) { b.bindings?.forEach(validatePostMenuBinding); }; - binding.bindings?.filter((b) => b.location === AppBindingLocations.POST_MENU_ITEM).forEach(validatePostMenuBinding); + bindings?.filter((b) => b.location === AppBindingLocations.POST_MENU_ITEM).forEach(validatePostMenuBinding); } export function createCallContext( diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 6bc60a6ba..445ce02a1 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -26,6 +26,7 @@ "apps.error.lookup.error_preparing_request": "Error preparing lookup request: {errorMessage}", "apps.error.parser": "Parsing error: {error}", "apps.error.parser.empty_value": "empty values are not allowed", + "apps.error.parser.execute_non_leaf": "You must select a subcommand.", "apps.error.parser.missing_call": "Missing binding call.", "apps.error.parser.missing_field_value": "Field value is missing.", "apps.error.parser.missing_quote": "Matching double quote expected before end of input.",