snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
271
webapp/channels/src/actions/apps.ts
Normal file
271
webapp/channels/src/actions/apps.ts
Normal file
@@ -0,0 +1,271 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {IntlShape} from 'react-intl';
|
||||
import type {AnyAction} from 'redux';
|
||||
|
||||
import type {AppCallResponse, AppForm, AppCallRequest, AppContext, AppBinding} from '@mattermost/types/apps';
|
||||
import type {CommandArgs} from '@mattermost/types/integrations';
|
||||
import type {Post} from '@mattermost/types/posts';
|
||||
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
import {AppCallResponseTypes} from 'mattermost-redux/constants/apps';
|
||||
import {cleanForm} from 'mattermost-redux/utils/apps';
|
||||
|
||||
import {openModal} from 'actions/views/modals';
|
||||
|
||||
import AppsForm from 'components/apps_form';
|
||||
|
||||
import {createCallRequest, makeCallErrorResponse} from 'utils/apps';
|
||||
import {getHistory} from 'utils/browser_history';
|
||||
import {ModalIdentifiers} from 'utils/constants';
|
||||
import {getSiteURL, shouldOpenInNewTab} from 'utils/url';
|
||||
|
||||
import type {DoAppCallResult} from 'types/apps';
|
||||
import type {ActionFuncAsync, ThunkActionFunc} from 'types/store';
|
||||
|
||||
import {sendEphemeralPost} from './global_actions';
|
||||
|
||||
export function handleBindingClick<Res=unknown>(binding: AppBinding, context: AppContext, intl: any): ThunkActionFunc<Promise<DoAppCallResult<Res>>> {
|
||||
return async (dispatch) => {
|
||||
// Fetch form
|
||||
let form = binding.form;
|
||||
if (form?.source) {
|
||||
const callRequest = createCallRequest(form.source, context);
|
||||
const res = await dispatch(doAppFetchForm<Res>(callRequest, intl));
|
||||
if (res.error) {
|
||||
return res;
|
||||
}
|
||||
form = res.data!.form;
|
||||
}
|
||||
|
||||
// Open form
|
||||
if (form) {
|
||||
// This should come properly formed, but using preventive checks
|
||||
if (!form?.submit) {
|
||||
const errMsg = intl.formatMessage({
|
||||
id: 'apps.error.malformed_binding',
|
||||
defaultMessage: 'This binding is not properly formed. Contact the App developer.',
|
||||
});
|
||||
return {error: makeCallErrorResponse(errMsg)};
|
||||
}
|
||||
|
||||
const res: AppCallResponse<Res> = {
|
||||
type: AppCallResponseTypes.FORM,
|
||||
form,
|
||||
};
|
||||
return {data: res};
|
||||
}
|
||||
|
||||
// Submit binding
|
||||
// This should come properly formed, but using preventive checks
|
||||
if (!binding.submit) {
|
||||
const errMsg = intl.formatMessage({
|
||||
id: 'apps.error.malformed_binding',
|
||||
defaultMessage: 'This binding is not properly formed. Contact the App developer.',
|
||||
});
|
||||
return {error: makeCallErrorResponse(errMsg)};
|
||||
}
|
||||
|
||||
const callRequest = createCallRequest(
|
||||
binding.submit,
|
||||
context,
|
||||
);
|
||||
|
||||
const res = await dispatch(doAppSubmit<Res>(callRequest, intl));
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
export function doAppSubmit<Res=unknown>(inCall: AppCallRequest, intl: IntlShape): ThunkActionFunc<Promise<DoAppCallResult<Res>>> {
|
||||
return async () => {
|
||||
try {
|
||||
const call: AppCallRequest = {
|
||||
...inCall,
|
||||
context: {
|
||||
...inCall.context,
|
||||
track_as_submit: true,
|
||||
},
|
||||
};
|
||||
const res = await Client4.executeAppCall(call, true) as AppCallResponse<Res>;
|
||||
const responseType = res.type || AppCallResponseTypes.OK;
|
||||
|
||||
switch (responseType) {
|
||||
case AppCallResponseTypes.OK:
|
||||
return {data: res};
|
||||
case AppCallResponseTypes.ERROR:
|
||||
return {error: res};
|
||||
case AppCallResponseTypes.FORM:
|
||||
if (!res.form?.submit) {
|
||||
const errMsg = intl.formatMessage({
|
||||
id: 'apps.error.responses.form.no_form',
|
||||
defaultMessage: 'Response type is `form`, but no form was included in response.',
|
||||
});
|
||||
return {error: makeCallErrorResponse(errMsg)};
|
||||
}
|
||||
|
||||
cleanForm(res.form);
|
||||
return {data: res};
|
||||
|
||||
case AppCallResponseTypes.NAVIGATE: {
|
||||
if (!res.navigate_to_url) {
|
||||
const errMsg = intl.formatMessage({
|
||||
id: 'apps.error.responses.navigate.no_url',
|
||||
defaultMessage: 'Response type is `navigate`, but no url was included in response.',
|
||||
});
|
||||
return {error: makeCallErrorResponse(errMsg)};
|
||||
}
|
||||
if (shouldOpenInNewTab(res.navigate_to_url, getSiteURL())) {
|
||||
window.open(res.navigate_to_url);
|
||||
return {data: res};
|
||||
}
|
||||
const navigateURL = res.navigate_to_url.startsWith(getSiteURL()) ? res.navigate_to_url.slice(getSiteURL().length) : res.navigate_to_url;
|
||||
getHistory().push(navigateURL);
|
||||
return {data: res};
|
||||
}
|
||||
default: {
|
||||
const errMsg = intl.formatMessage({
|
||||
id: 'apps.error.responses.unknown_type',
|
||||
defaultMessage: 'App response type not supported. Response type: {type}.',
|
||||
}, {type: responseType});
|
||||
return {error: makeCallErrorResponse(errMsg)};
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
const errMsg = error.message || intl.formatMessage({
|
||||
id: 'apps.error.responses.unexpected_error',
|
||||
defaultMessage: 'Received an unexpected error.',
|
||||
});
|
||||
return {error: makeCallErrorResponse(errMsg)};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function doAppFetchForm<Res=unknown>(call: AppCallRequest, intl: any): ThunkActionFunc<Promise<DoAppCallResult<Res>>> {
|
||||
return async () => {
|
||||
try {
|
||||
const res = await Client4.executeAppCall(call, false) as AppCallResponse<Res>;
|
||||
const responseType = res.type || AppCallResponseTypes.OK;
|
||||
|
||||
switch (responseType) {
|
||||
case AppCallResponseTypes.ERROR:
|
||||
return {error: res};
|
||||
case AppCallResponseTypes.FORM:
|
||||
if (!res.form?.submit) {
|
||||
const errMsg = intl.formatMessage({
|
||||
id: 'apps.error.responses.form.no_form',
|
||||
defaultMessage: 'Response type is `form`, but no form was included in response.',
|
||||
});
|
||||
return {error: makeCallErrorResponse(errMsg)};
|
||||
}
|
||||
cleanForm(res.form);
|
||||
return {data: res};
|
||||
default: {
|
||||
const errMsg = intl.formatMessage({
|
||||
id: 'apps.error.responses.unknown_type',
|
||||
defaultMessage: 'App response type not supported. Response type: {type}.',
|
||||
}, {type: responseType});
|
||||
return {error: makeCallErrorResponse(errMsg)};
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
const errMsg = error.message || intl.formatMessage({
|
||||
id: 'apps.error.responses.unexpected_error',
|
||||
defaultMessage: 'Received an unexpected error.',
|
||||
});
|
||||
return {error: makeCallErrorResponse(errMsg)};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function doAppLookup<Res=unknown>(call: AppCallRequest, intl: any): ThunkActionFunc<Promise<DoAppCallResult<Res>>> {
|
||||
return async () => {
|
||||
try {
|
||||
const res = await Client4.executeAppCall(call, false) as AppCallResponse<Res>;
|
||||
const responseType = res.type || AppCallResponseTypes.OK;
|
||||
|
||||
switch (responseType) {
|
||||
case AppCallResponseTypes.OK:
|
||||
return {data: res};
|
||||
case AppCallResponseTypes.ERROR:
|
||||
return {error: res};
|
||||
|
||||
default: {
|
||||
const errMsg = intl.formatMessage({
|
||||
id: 'apps.error.responses.unknown_type',
|
||||
defaultMessage: 'App response type not supported. Response type: {type}.',
|
||||
}, {type: responseType});
|
||||
return {error: makeCallErrorResponse(errMsg)};
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
const errMsg = error.message || intl.formatMessage({
|
||||
id: 'apps.error.responses.unexpected_error',
|
||||
defaultMessage: 'Received an unexpected error.',
|
||||
});
|
||||
return {error: makeCallErrorResponse(errMsg)};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function makeFetchBindings(location: string): (channelId: string, teamId: string) => ActionFuncAsync<AppBinding[]> {
|
||||
return (channelId: string, teamId: string): ActionFuncAsync<AppBinding[]> => {
|
||||
return async () => {
|
||||
try {
|
||||
const allBindings = await Client4.getAppsBindings(channelId, teamId);
|
||||
const headerBindings = allBindings.filter((b) => b.location === location);
|
||||
const bindings = headerBindings.reduce((accum: AppBinding[], current: AppBinding) => accum.concat(current.bindings || []), []);
|
||||
return {data: bindings};
|
||||
} catch {
|
||||
return {data: []};
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function openAppsModal(form: AppForm, context: AppContext): AnyAction {
|
||||
return openModal({
|
||||
modalId: ModalIdentifiers.APPS_MODAL,
|
||||
dialogType: AppsForm,
|
||||
dialogProps: {
|
||||
form,
|
||||
appContext: context,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function postEphemeralCallResponseForPost(response: AppCallResponse, message: string, post: Post) {
|
||||
return sendEphemeralPost(
|
||||
message,
|
||||
post.channel_id,
|
||||
post.root_id || post.id,
|
||||
response.app_metadata?.bot_user_id,
|
||||
);
|
||||
}
|
||||
|
||||
export function postEphemeralCallResponseForChannel(response: AppCallResponse, message: string, channelID: string) {
|
||||
return sendEphemeralPost(
|
||||
message,
|
||||
channelID,
|
||||
'',
|
||||
response.app_metadata?.bot_user_id,
|
||||
);
|
||||
}
|
||||
|
||||
export function postEphemeralCallResponseForContext(response: AppCallResponse, message: string, context: AppContext) {
|
||||
return sendEphemeralPost(
|
||||
message,
|
||||
context.channel_id,
|
||||
context.root_id || context.post_id,
|
||||
response.app_metadata?.bot_user_id,
|
||||
);
|
||||
}
|
||||
|
||||
export function postEphemeralCallResponseForCommandArgs(response: AppCallResponse, message: string, args: CommandArgs) {
|
||||
return sendEphemeralPost(
|
||||
message,
|
||||
args.channel_id,
|
||||
args.root_id,
|
||||
response.app_metadata?.bot_user_id,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user