Skip to content

Commit

Permalink
Move getApplicatio to the backend
Browse files Browse the repository at this point in the history
  • Loading branch information
cnasikas committed May 24, 2021
1 parent 060531a commit dcaab5b
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,16 @@ import {
ExternalServiceApi,
Incident,
PushToServiceApiHandlerArgs,
GetApplicationResponse,
GetApplicationHandlerArgs,
} from './types';

const getApplicationHandler = async ({
externalService,
}: GetApplicationHandlerArgs): Promise<GetApplicationResponse> => {
return await externalService.getApplication();
};

const createRecordHandler = async ({
externalService,
params,
Expand Down Expand Up @@ -52,6 +60,7 @@ const pushToServiceHandler = async ({
};

export const api: ExternalServiceApi = {
getApplication: getApplicationHandler,
createRecord: createRecordHandler,
pushToService: pushToServiceHandler,
};
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ interface GetActionTypeParams {
configurationUtilities: ActionsConfigurationUtilities;
}

const supportedSubActions: string[] = ['application', 'createRecord', 'pushToService'];
const supportedSubActions: string[] = ['getApplication', 'createRecord', 'pushToService'];

// action type definition
export function getActionType(
Expand Down Expand Up @@ -101,6 +101,12 @@ async function executor(
throw new Error(errorMessage);
}

if (subAction === 'getApplication') {
data = await api.getApplication({
externalService,
});
}

if (subAction === 'createRecord') {
const createRecordParams = subActionParams as ExecutorSubActionCreateRecordParams;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const SwimlaneFields = {
severity: schema.nullable(schema.string()),
};

export const ExecutorSubActionGetApplicationParamsSchema = schema.object({});
export const ExecutorSubActionCreateRecordParamsSchema = schema.object(SwimlaneFields);

export const ExecutorSubActionPushParamsSchema = schema.object({
Expand All @@ -68,6 +69,10 @@ export const ExecutorSubActionPushParamsSchema = schema.object({
});

export const ExecutorParamsSchema = schema.oneOf([
schema.object({
subAction: schema.literal('getApplication'),
subActionParams: ExecutorSubActionGetApplicationParamsSchema,
}),
schema.object({
subAction: schema.literal('createRecord'),
subActionParams: ExecutorSubActionCreateRecordParamsSchema,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ExternalService,
ExternalServiceCredentials,
ExternalServiceIncidentResponse,
GetApplicationResponse,
MappingConfigType,
SwimlaneComment,
SwimlanePublicConfigurationType,
Expand Down Expand Up @@ -51,6 +52,8 @@ export const createExternalService = (
? urlWithoutTrailingSlash
: urlWithoutTrailingSlash + '/api';

const applicationUrl = `${apiUrl}/app/${appId}`;

const getPostRecordUrl = (id: string) => `${apiUrl}/app/${id}/record`;

const getPostRecordIdUrl = (id: string, recordId: string) =>
Expand All @@ -65,6 +68,28 @@ export const createExternalService = (
const getCommentFieldId = (fieldMappings: MappingConfigType): string | null =>
fieldMappings.commentsConfig?.id || null;

const getApplication = async (): Promise<GetApplicationResponse> => {
try {
const res = await request({
axios: axiosInstance,
configurationUtilities,
headers,
logger,
method: 'get',
url: applicationUrl,
});

return res.data;
} catch (error) {
throw new Error(
getErrorMessage(
i18n.NAME,
`Unable to get application with id ${appId}. Error: ${error.message}`
)
);
}
};

const createRecord = async (
params: CreateRecordParams
): Promise<ExternalServiceIncidentResponse> => {
Expand Down Expand Up @@ -209,5 +234,6 @@ export const createExternalService = (
createComment,
createRecord,
updateRecord,
getApplication,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export interface FieldConfig {
fieldType: string;
}

export interface GetApplicationResponse {
fields: FieldConfig[];
}

export interface SwimlaneRecordPayload {
applicationId: string;
id?: string;
Expand All @@ -78,6 +82,7 @@ export interface SwimlaneRecordPayload {
}

export interface ExternalService {
getApplication: () => Promise<GetApplicationResponse>;
createComment: (params: CreateCommentParams) => Promise<ExternalServiceCommentResponse>;
createRecord: (params: CreateRecordParams) => Promise<ExternalServiceIncidentResponse>;
updateRecord: (params: UpdateRecordParams) => Promise<ExternalServiceIncidentResponse>;
Expand All @@ -101,11 +106,12 @@ export interface GetApplicationHandlerArgs {
}

export interface ExternalServiceApi {
getApplication: (args: GetApplicationHandlerArgs) => Promise<GetApplicationResponse>;
createRecord: (args: CreateRecordApiHandlerArgs) => Promise<ExternalServiceIncidentResponse>;
pushToService: (args: PushToServiceApiHandlerArgs) => Promise<ExternalServiceIncidentResponse>;
}

export type SwimlaneExecutorResultData = ExternalServiceIncidentResponse;
export type SwimlaneExecutorResultData = ExternalServiceIncidentResponse | GetApplicationResponse;
export type SwimlaneDataValues = Record<string, string | number>;
export interface SwimlaneComment {
fieldId: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@ import { fieldLabels } from './index';
const SwimlaneFieldsComponent: React.FunctionComponent<
ConnectorFieldsProps<SwimlaneFieldsType>
> = ({ isEdit = true, fields, connector, onChange }) => {
const { alertSource, caseId, caseName, severity } = fields || {
alertSource: null,
caseId: null,
caseName: null,
severity: null,
};
const { alertSource = null, caseId = null, caseName = null, severity = null } = fields ?? {};

const onFieldChange = useCallback(
(key, value) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,25 @@
*/

import { HttpSetup } from 'kibana/public';
import { BASE_ACTION_API_PATH } from '../../../constants';
import { SwimlaneFieldMappingConfig } from './types';

export async function getApplication({
http,
url,
appId,
apiToken,
signal,
connectorId,
}: {
http: HttpSetup;
url: string;
appId: string;
apiToken: string;
}): Promise<Record<string, any>> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'Private-Token': `${apiToken}`,
};

const urlWithoutTrailingSlash = url.endsWith('/') ? url.slice(0, -1) : url;
const apiUrl = urlWithoutTrailingSlash.endsWith('api')
? urlWithoutTrailingSlash
: urlWithoutTrailingSlash + '/api';
const applicationUrl = `${apiUrl}/app/{appId}`;

const getApplicationUrl = (id: string) => applicationUrl.replace('{appId}', id);
try {
return await http.get(getApplicationUrl(appId), {
headers,
});
} catch (error) {
throw new Error(`Unable to get application with id ${appId}. Error: ${error.message}`);
}
signal: AbortSignal;
connectorId: string;
}): Promise<{ fields: SwimlaneFieldMappingConfig[] }> {
return await http.post(
`${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`,
{
body: JSON.stringify({
params: { subAction: 'getApplication', subActionParams: {} },
}),
signal,
}
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { FormattedMessage } from 'react-intl';
import * as i18n from '../translations';
import { StepProps } from './';
import { useKibana } from '../../../../../common/lib/kibana';
import { getApplication } from '../api';
import { useGetApplication } from '../use_get_application';

export const SwimlaneConnection: React.FunctionComponent<StepProps> = ({
action,
Expand All @@ -29,22 +29,31 @@ export const SwimlaneConnection: React.FunctionComponent<StepProps> = ({
updateCurrentStep,
updateFields,
}) => {
const { http } = useKibana().services;
const {
http,
notifications: { toasts },
} = useKibana().services;
const { apiUrl, appId } = action.config;
const { apiToken } = action.secrets;
const { docLinks } = useKibana().services;
const { getApplication } = useGetApplication({
http,
toastNotifications: toasts,
action,
});
const isValid = useMemo(() => apiUrl && apiToken && appId, [apiToken, apiUrl, appId]);

const connectSwimlane = useCallback(async () => {
// fetch swimlane application configuration
const application = await getApplication({ http, url: apiUrl, appId, apiToken });
const application = await getApplication();

if (!application) {
throw new Error(i18n.SW_GET_APPLICATION_API_ERROR(appId));
if (application != null) {
const allFields = application.fields;
updateFields(allFields);
updateCurrentStep(2);
}
const allFields = application.fields;
updateFields(allFields);
updateCurrentStep(2);
}, [apiToken, apiUrl, appId, http, updateCurrentStep, updateFields]);
}, [getApplication, updateCurrentStep, updateFields]);

const onChangeConfig = useCallback(
(e: React.ChangeEvent<HTMLInputElement>, key: 'apiUrl' | 'appId') => {
editActionConfig(key, e.target.value);
Expand Down Expand Up @@ -142,7 +151,7 @@ export const SwimlaneConnection: React.FunctionComponent<StepProps> = ({
fullWidth
isInvalid={isInvalid}
readOnly={readOnly}
value={apiToken || ''}
value={apiToken ?? ''}
data-test-subj="swimlaneApiTokenInput"
onChange={onChangeSecrets}
onBlur={onBlurSecrets}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { ActionConnectorFieldsProps } from '../../../../types';
import { SwimlaneActionConnector, SwimlaneFieldMappingConfig } from './types';
import { SwimlaneConnection, SwimlaneFields } from './steps';

const SwimlaneActionConnectorFields: React.FunctionComponent<
const SwimlaneActionConnectorFields: React.FC<
ActionConnectorFieldsProps<SwimlaneActionConnector>
> = ({ errors, action, editActionConfig, editActionSecrets, readOnly }) => {
const [currentStep, setCurrentStep] = useState<number>(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,12 @@ export const SW_REQUIRED_API_TOKEN_TEXT = i18n.translate(
}
);

export const SW_GET_APPLICATION_API_ERROR = (id: string | null) =>
i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationMessage',
{
defaultMessage: 'Unable to get application with id {id}',
values: { id },
}
);
export const SW_GET_APPLICATION_API_ERROR = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationMessage',
{
defaultMessage: 'Unable to get application',
}
);

export const SW_API_URL_TEXT_FIELD_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiUrlTextFieldLabel',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useState, useCallback, useRef } from 'react';
import { HttpSetup, ToastsApi } from 'kibana/public';
import { getApplication as getApplicationApi } from './api';
import * as i18n from './translations';
import { SwimlaneActionConnector, SwimlaneFieldMappingConfig } from './types';

interface Props {
http: HttpSetup;
toastNotifications: Pick<
ToastsApi,
'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError'
>;
action: SwimlaneActionConnector;
}

export interface UseGetApplication {
getApplication: () => Promise<{ fields: SwimlaneFieldMappingConfig[] } | undefined>;
isLoading: boolean;
}

export const useGetApplication = ({
http,
action,
toastNotifications,
}: Props): UseGetApplication => {
const [isLoading, setIsLoading] = useState(false);
const isCancelledRef = useRef(false);
const abortCtrlRef = useRef(new AbortController());

const getApplication = useCallback(async () => {
try {
isCancelledRef.current = false;
abortCtrlRef.current.abort();
abortCtrlRef.current = new AbortController();
setIsLoading(true);

const response = await getApplicationApi({
http,
signal: abortCtrlRef.current.signal,
connectorId: action.id,
});

setIsLoading(false);

if (!isCancelledRef.current) {
toastNotifications.addDanger({
title: i18n.SW_GET_APPLICATION_API_ERROR,
});
}

return response;
} catch (error) {
if (!isCancelledRef.current) {
if (error.name !== 'AbortError') {
toastNotifications.addDanger({
title: i18n.SW_GET_APPLICATION_API_ERROR,
text: error.message,
});
}
setIsLoading(false);
}
}
}, [action, http, toastNotifications]);

return {
isLoading,
getApplication,
};
};

0 comments on commit dcaab5b

Please sign in to comment.