console: console support for actions response transform

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6455
Co-authored-by: Sooraj <8408875+soorajshankar@users.noreply.github.com>
GitOrigin-RevId: 82040e5a05c035f3319a94cfb6dd94acb5259505
This commit is contained in:
Varun Choudhary 2022-10-27 19:36:06 +05:30 committed by hasura-bot
parent 4589af44b4
commit bdf1aee641
10 changed files with 693 additions and 312 deletions

View File

@ -9,16 +9,20 @@ import {
KeyValuePair, KeyValuePair,
RequestTransformState, RequestTransformState,
RequestTransformStateBody, RequestTransformStateBody,
ResponseTransformState,
ResponseTransformStateBody,
TransformationType, TransformationType,
} from './stateDefaults'; } from './stateDefaults';
import RequestOptionsTransforms from './RequestOptionsTransforms'; import RequestOptionsTransforms from './RequestOptionsTransforms';
import PayloadOptionsTransforms from './PayloadOptionsTransforms'; import PayloadOptionsTransforms from './PayloadOptionsTransforms';
import SampleContextTransforms from './SampleContextTransforms'; import SampleContextTransforms from './SampleContextTransforms';
import AddIcon from '../Icons/Add'; import AddIcon from '../Icons/Add';
import ResponseTransforms from './ResponseTransform';
type ConfigureTransformationProps = { type ConfigureTransformationProps = {
transformationType: TransformationType; transformationType: TransformationType;
state: RequestTransformState; requestTransfromState: RequestTransformState;
responseTransformState?: ResponseTransformState;
resetSampleInput: () => void; resetSampleInput: () => void;
envVarsOnChange: (envVars: KeyValuePair[]) => void; envVarsOnChange: (envVars: KeyValuePair[]) => void;
sessionVarsOnChange: (sessionVars: KeyValuePair[]) => void; sessionVarsOnChange: (sessionVars: KeyValuePair[]) => void;
@ -33,187 +37,241 @@ type ConfigureTransformationProps = {
) => void; ) => void;
requestUrlTransformOnChange: (data: boolean) => void; requestUrlTransformOnChange: (data: boolean) => void;
requestPayloadTransformOnChange: (data: boolean) => void; requestPayloadTransformOnChange: (data: boolean) => void;
responsePayloadTransformOnChange?: (data: boolean) => void;
responseBodyOnChange?: (responseBody: ResponseTransformStateBody) => void;
}; };
const ConfigureTransformation: React.FC<ConfigureTransformationProps> = ({ const ConfigureTransformation: React.FC<ConfigureTransformationProps> =
transformationType, props => {
state, const {
resetSampleInput, transformationType,
envVarsOnChange, requestTransfromState,
sessionVarsOnChange, responseTransformState,
requestMethodOnChange, resetSampleInput,
requestUrlOnChange, envVarsOnChange,
requestQueryParamsOnChange, sessionVarsOnChange,
requestAddHeadersOnChange, requestMethodOnChange,
requestBodyOnChange, requestUrlOnChange,
requestSampleInputOnChange, requestQueryParamsOnChange,
requestUrlTransformOnChange, requestAddHeadersOnChange,
requestPayloadTransformOnChange, requestBodyOnChange,
}) => { requestSampleInputOnChange,
const { requestUrlTransformOnChange,
envVars, requestPayloadTransformOnChange,
sessionVars, responsePayloadTransformOnChange,
requestMethod, responseBodyOnChange,
requestUrl, } = props;
requestUrlError, const {
requestUrlPreview, envVars,
requestQueryParams, sessionVars,
requestAddHeaders, requestMethod,
requestBody, requestUrl,
requestBodyError, requestUrlError,
requestSampleInput, requestUrlPreview,
requestTransformedBody, requestQueryParams,
isRequestUrlTransform, requestAddHeaders,
isRequestPayloadTransform, requestBody,
} = state; requestBodyError,
requestSampleInput,
requestTransformedBody,
isRequestUrlTransform,
isRequestPayloadTransform,
} = requestTransfromState;
const [isContextAreaActive, toggleContextArea] = useState<boolean>(false); const [isContextAreaActive, toggleContextArea] = useState<boolean>(false);
const contextAreaText = isContextAreaActive const contextAreaText = isContextAreaActive
? `Hide Sample Context` ? `Hide Sample Context`
: `Show Sample Context`; : `Show Sample Context`;
const requestUrlTransformText = isRequestUrlTransform const requestUrlTransformText = isRequestUrlTransform
? `Remove Request Options Transform` ? `Remove Request Options Transform`
: `Add Request Options Transform`; : `Add Request Options Transform`;
const requestPayloadTransformText = isRequestPayloadTransform
? `Remove Payload Transform`
: `Add Payload Transform`;
const requestPayloadTransformText = isRequestPayloadTransform const responsePayloadTransformText =
? `Remove Payload Transform` responseTransformState?.isResponsePayloadTransform
: `Add Payload Transform`; ? `Remove Response Transform`
: `Add Response Transform`;
return (
<>
<h2 className="text-lg font-semibold mb-sm flex items-center">
Configure REST Connectors
</h2>
return ( <div className="mb-lg">
<> <label className="block text-gray-600 font-medium mb-xs">
<h2 className="text-lg font-semibold mb-sm flex items-center"> Sample Context
Configure REST Connectors </label>
</h2> <p className="text-sm text-gray-600 mb-sm">
Add sample env vars and session vars for testing the connector
<div className="mb-lg"> </p>
<label className="block text-gray-600 font-medium mb-xs"> <Analytics
Sample Context name={
</label> isContextAreaActive
<p className="text-sm text-gray-600 mb-sm"> ? 'actions-tab-hide-sample-context-button'
Add sample env vars and session vars for testing the connector : 'actions-tab-show-sample-context-button'
</p> }
<Analytics passHtmlAttributesToChildren
name={
isContextAreaActive
? 'actions-tab-hide-sample-context-button'
: 'actions-tab-show-sample-context-button'
}
passHtmlAttributesToChildren
>
<Button
color="white"
size="sm"
data-test="toggle-context-area"
onClick={() => {
toggleContextArea(!isContextAreaActive);
}}
> >
{!isContextAreaActive ? <AddIcon /> : null} <Button
{contextAreaText} color="white"
</Button> size="sm"
</Analytics> data-test="toggle-context-area"
onClick={() => {
toggleContextArea(!isContextAreaActive);
}}
>
{!isContextAreaActive ? <AddIcon /> : null}
{contextAreaText}
</Button>
</Analytics>
{isContextAreaActive ? ( {isContextAreaActive ? (
<SampleContextTransforms <SampleContextTransforms
transformationType={transformationType} transformationType={transformationType}
envVars={envVars} envVars={envVars}
sessionVars={sessionVars} sessionVars={sessionVars}
envVarsOnChange={envVarsOnChange} envVarsOnChange={envVarsOnChange}
sessionVarsOnChange={sessionVarsOnChange} sessionVarsOnChange={sessionVarsOnChange}
/> />
) : null} ) : null}
</div> </div>
<div className="mb-lg"> <div className="mb-lg">
<label className="block text-gray-600 font-medium mb-xs"> <label className="block text-gray-600 font-medium mb-xs">
Change Request Options Change Request Options
</label> </label>
<p className="text-sm text-gray-600 mb-sm"> <p className="text-sm text-gray-600 mb-sm">
Change the method and URL to adapt to your API&apos;s expected format. Change the method and URL to adapt to your API&apos;s expected
</p> format.
<Analytics </p>
name={ <Analytics
isRequestUrlTransform name={
? 'actions-tab-hide-request-transform-button' isRequestUrlTransform
: 'actions-tab-show-request-transform-button' ? 'actions-tab-hide-request-transform-button'
} : 'actions-tab-show-request-transform-button'
passHtmlAttributesToChildren }
> passHtmlAttributesToChildren
<Button
size="sm"
icon={!isRequestUrlTransform ? <AddIcon /> : undefined}
iconPosition="start"
data-test="toggle-request-transform"
onClick={() => {
requestUrlTransformOnChange(!isRequestUrlTransform);
resetSampleInput();
}}
> >
{requestUrlTransformText} <Button
</Button> size="sm"
</Analytics> icon={!isRequestUrlTransform ? <AddIcon /> : undefined}
iconPosition="start"
data-test="toggle-request-transform"
onClick={() => {
requestUrlTransformOnChange(!isRequestUrlTransform);
resetSampleInput();
}}
>
{requestUrlTransformText}
</Button>
</Analytics>
{isRequestUrlTransform ? (
<RequestOptionsTransforms
requestMethod={requestMethod}
requestUrl={requestUrl}
requestUrlError={requestUrlError}
requestUrlPreview={requestUrlPreview}
requestQueryParams={requestQueryParams}
requestAddHeaders={requestAddHeaders}
requestMethodOnChange={requestMethodOnChange}
requestUrlOnChange={requestUrlOnChange}
requestQueryParamsOnChange={requestQueryParamsOnChange}
requestAddHeadersOnChange={requestAddHeadersOnChange}
/>
) : null}
</div>
{isRequestUrlTransform ? ( <div className="mb-lg">
<RequestOptionsTransforms <label className="block text-gray-600 font-medium mb-xs">
requestMethod={requestMethod} Change Payload
requestUrl={requestUrl} </label>
requestUrlError={requestUrlError} <p className="text-sm text-gray-600 mb-sm">
requestUrlPreview={requestUrlPreview} Change the payload to adapt to your API&apos;s expected format.
requestQueryParams={requestQueryParams} </p>
requestAddHeaders={requestAddHeaders} <Analytics
requestMethodOnChange={requestMethodOnChange} name={
requestUrlOnChange={requestUrlOnChange} isRequestPayloadTransform
requestQueryParamsOnChange={requestQueryParamsOnChange} ? 'actions-tab-hide-payload-transform-button'
requestAddHeadersOnChange={requestAddHeadersOnChange} : 'actions-tab-show-payload-transform-button'
/> }
) : null} passHtmlAttributesToChildren
</div>
<div className="mb-lg">
<label className="block text-gray-600 font-medium mb-xs">
Change Payload
</label>
<p className="text-sm text-gray-600 mb-sm">
Change the payload to adapt to your API&apos;s expected format.
</p>
<Analytics
name={
isRequestPayloadTransform
? 'actions-tab-hide-payload-transform-button'
: 'actions-tab-show-payload-transform-button'
}
passHtmlAttributesToChildren
>
<Button
color="white"
size="sm"
data-test="toggle-payload-transform"
onClick={() => {
requestPayloadTransformOnChange(!isRequestPayloadTransform);
resetSampleInput();
}}
> >
{!isRequestPayloadTransform ? <AddIcon /> : null} <Button
{requestPayloadTransformText} color="white"
</Button> size="sm"
</Analytics> data-test="toggle-payload-transform"
{isRequestPayloadTransform ? ( onClick={() => {
<PayloadOptionsTransforms requestPayloadTransformOnChange(!isRequestPayloadTransform);
transformationType={transformationType} resetSampleInput();
requestBody={requestBody} }}
requestBodyError={requestBodyError} >
requestSampleInput={requestSampleInput} {!isRequestPayloadTransform ? <AddIcon /> : null}
requestTransformedBody={requestTransformedBody} {requestPayloadTransformText}
resetSampleInput={resetSampleInput} </Button>
requestBodyOnChange={requestBodyOnChange} </Analytics>
requestSampleInputOnChange={requestSampleInputOnChange} {isRequestPayloadTransform ? (
/> <PayloadOptionsTransforms
) : null} transformationType={transformationType}
</div> requestBody={requestBody}
</> requestBodyError={requestBodyError}
); requestSampleInput={requestSampleInput}
}; requestTransformedBody={requestTransformedBody}
resetSampleInput={resetSampleInput}
requestBodyOnChange={requestBodyOnChange}
requestSampleInputOnChange={requestSampleInputOnChange}
/>
) : null}
</div>
{responseTransformState && responsePayloadTransformOnChange && (
<div className="mb-lg">
<label className="block text-gray-600 font-medium mb-xs">
Change Response
</label>
<p className="text-sm text-gray-600 mb-sm">
Change the incoming response to adapt to your declared types.
</p>
<Analytics
name={
isRequestPayloadTransform
? 'actions-tab-hide-response-transform-button'
: 'actions-tab-show-response-transform-button'
}
passHtmlAttributesToChildren
>
<Button
color="white"
size="sm"
data-test="toggle-response-transform"
onClick={() => {
responsePayloadTransformOnChange(
!responseTransformState.isResponsePayloadTransform
);
resetSampleInput();
}}
>
{!responseTransformState.isResponsePayloadTransform ? (
<AddIcon />
) : null}
{responsePayloadTransformText}
</Button>
</Analytics>
{responseTransformState.isResponsePayloadTransform &&
responseBodyOnChange ? (
<ResponseTransforms
requestSampleInput={responseTransformState.responseSampleInput}
requestBody={responseTransformState.responseBody}
requestBodyError={responseTransformState.responseBodyError}
requestBodyOnChange={responseBodyOnChange}
/>
) : null}
</div>
)}
</>
);
};
export default ConfigureTransformation; export default ConfigureTransformation;

View File

@ -0,0 +1,101 @@
import React, { useRef } from 'react';
import { useDebouncedEffect } from '@/hooks/useDebounceEffect';
import CrossIcon from '../Icons/Cross';
import TemplateEditor from './CustomEditors/TemplateEditor';
import { editorDebounceTime } from './utils';
import NumberedSidebar from './CustomEditors/NumberedSidebar';
import { KeyValuePair, RequestTransformStateBody } from './stateDefaults';
import KeyValueInput from './CustomEditors/KeyValueInput';
import { isEmpty } from '../utils/jsUtils';
import { requestBodyActionState } from './requestTransformState';
type PayloadOptionsTransformsProps = {
requestBody: RequestTransformStateBody;
requestBodyError: string;
requestSampleInput: string;
requestBodyOnChange: (requestBody: RequestTransformStateBody) => void;
};
const ResponseTransforms: React.FC<PayloadOptionsTransformsProps> = ({
requestSampleInput,
requestBody,
requestBodyError,
requestBodyOnChange,
}) => {
const editorRef = useRef<any>();
const [localFormElements, setLocalFormElements] = React.useState<
KeyValuePair[]
>(requestBody.form_template ?? [{ name: '', value: '' }]);
React.useEffect(() => {
setLocalFormElements(
requestBody.form_template ?? [{ name: '', value: '' }]
);
}, [requestBody]);
useDebouncedEffect(
() => {
requestBodyOnChange({ ...requestBody, form_template: localFormElements });
},
editorDebounceTime,
[localFormElements]
);
if (editorRef?.current?.editor?.renderer?.$cursorLayer?.element?.style) {
editorRef.current.editor.renderer.$cursorLayer.element.style.display =
'none';
}
return (
<div
className="m-md pl-lg pr-sm border-l border-l-gray-400"
data-cy="Change Payload"
>
<div className="mb-md">
<NumberedSidebar
title="Configure Response Body"
description={
<span>
The template which will transform your response body into the
required specification. You can use{' '}
<code className="text-xs">&#123;&#123;$body&#125;&#125;</code> to
access the original response body
</span>
}
number="1"
/>
{requestBody.action ===
requestBodyActionState.transformApplicationJson ? (
<TemplateEditor
requestBody={requestBody}
requestBodyError={requestBodyError}
requestSampleInput={requestSampleInput}
requestBodyOnChange={requestBodyOnChange}
/>
) : null}
{requestBody.action ===
requestBodyActionState.transformFormUrlEncoded ? (
<>
{!isEmpty(requestBodyError) && (
<div className="mb-sm" data-test="transform-requestBody-error">
<CrossIcon />
<span className="text-red-500 ml-sm">{requestBodyError}</span>
</div>
)}
<div className="grid gap-3 grid-cols-3 mb-sm">
<KeyValueInput
pairs={localFormElements}
setPairs={setLocalFormElements}
testId="add-url-encoded-body"
/>
</div>
</>
) : null}
</div>
</div>
);
};
export default ResponseTransforms;

View File

@ -46,6 +46,18 @@ import {
defaultEventRequestBody, defaultEventRequestBody,
defaultEventRequestSampleInput, defaultEventRequestSampleInput,
RequestTransformStateBody, RequestTransformStateBody,
SET_RESPONSE_PAYLOAD_TRANSFORM,
SetResponsePayloadTransform,
ResponseTransformStateBody,
SET_RESPONSE_BODY,
SetResponseBody,
SET_RESPONSE_BODY_ERROR,
SetResponseBodyError,
ResponseTransformState,
ResponseTransformEvents,
SET_RESPONSE_TRANSFORM_STATE,
defaultActionResponseBody,
SetResponseTransformState,
} from './stateDefaults'; } from './stateDefaults';
import { getSessionVarsFromLS, getEnvVarsFromLS } from './utils'; import { getSessionVarsFromLS, getEnvVarsFromLS } from './utils';
@ -108,6 +120,13 @@ export const setRequestBody = (
requestBody, requestBody,
}); });
export const setResponseBody = (
responseBody: ResponseTransformStateBody
): SetResponseBody => ({
type: SET_RESPONSE_BODY,
responseBody,
});
export const setRequestBodyError = ( export const setRequestBodyError = (
requestBodyError: string requestBodyError: string
): SetRequestBodyError => ({ ): SetRequestBodyError => ({
@ -115,6 +134,13 @@ export const setRequestBodyError = (
requestBodyError, requestBodyError,
}); });
export const setResponseBodyError = (
responseBodyError: string
): SetResponseBodyError => ({
type: SET_RESPONSE_BODY_ERROR,
responseBodyError,
});
export const setRequestSampleInput = ( export const setRequestSampleInput = (
requestSampleInput: string requestSampleInput: string
): SetRequestSampleInput => ({ ): SetRequestSampleInput => ({
@ -150,6 +176,13 @@ export const setRequestPayloadTransform = (
isRequestPayloadTransform, isRequestPayloadTransform,
}); });
export const setResponsePayloadTransform = (
isResponsePayloadTransform: boolean
): SetResponsePayloadTransform => ({
type: SET_RESPONSE_PAYLOAD_TRANSFORM,
isResponsePayloadTransform,
});
export const setRequestTransformState = ( export const setRequestTransformState = (
newState: RequestTransformState newState: RequestTransformState
): SetRequestTransformState => ({ ): SetRequestTransformState => ({
@ -157,6 +190,13 @@ export const setRequestTransformState = (
newState, newState,
}); });
export const setResponseTransformState = (
newState: ResponseTransformState
): SetResponseTransformState => ({
type: SET_RESPONSE_TRANSFORM_STATE,
newState,
});
const currentVersion = 2; const currentVersion = 2;
export const requestBodyActionState = { export const requestBodyActionState = {
@ -166,6 +206,10 @@ export const requestBodyActionState = {
'x_www_form_urlencoded' as RequestTransformBodyActions, 'x_www_form_urlencoded' as RequestTransformBodyActions,
}; };
export const responseBodyActionState = {
transformApplicationJson: 'transform' as RequestTransformBodyActions,
};
export const requestTransformState: RequestTransformState = { export const requestTransformState: RequestTransformState = {
version: currentVersion, version: currentVersion,
envVars: [], envVars: [],
@ -186,6 +230,15 @@ export const requestTransformState: RequestTransformState = {
templatingEngine: 'Kriti', templatingEngine: 'Kriti',
}; };
export const responseTransformState: ResponseTransformState = {
version: currentVersion,
isResponsePayloadTransform: false,
responseSampleInput: '',
responseBody: { action: responseBodyActionState.transformApplicationJson },
responseBodyError: '',
templatingEngine: 'Kriti',
};
export const getActionRequestTransformDefaultState = export const getActionRequestTransformDefaultState =
(): RequestTransformState => { (): RequestTransformState => {
return { return {
@ -203,6 +256,19 @@ export const getActionRequestTransformDefaultState =
}; };
}; };
export const getActionResponseTransformDefaultState =
(): ResponseTransformState => {
return {
...responseTransformState,
responseBody: {
action: responseBodyActionState.transformApplicationJson,
template: defaultActionResponseBody,
form_template: [{ name: 'name', value: '{{$body.action.name}}' }],
},
responseSampleInput: defaultActionRequestSampleInput,
};
};
export const getEventRequestTransformDefaultState = export const getEventRequestTransformDefaultState =
(): RequestTransformState => { (): RequestTransformState => {
return { return {
@ -311,3 +377,32 @@ export const requestTransformReducer = (
return state; return state;
} }
}; };
export const responseTransformReducer = (
state = responseTransformState,
action: ResponseTransformEvents
): ResponseTransformState => {
switch (action.type) {
case SET_RESPONSE_BODY:
return {
...state,
responseBody: action.responseBody,
};
case SET_RESPONSE_BODY_ERROR:
return {
...state,
responseBodyError: action.responseBodyError,
};
case SET_RESPONSE_PAYLOAD_TRANSFORM:
return {
...state,
isResponsePayloadTransform: action.isResponsePayloadTransform,
};
case SET_RESPONSE_TRANSFORM_STATE:
return {
...action.newState,
};
default:
return state;
}
};

View File

@ -10,6 +10,7 @@ import {
RequestTransformContentType, RequestTransformContentType,
RequestTransformTemplateEngine, RequestTransformTemplateEngine,
RequestTransformBody, RequestTransformBody,
ResponseTransformBody,
} from '../../../metadata/types'; } from '../../../metadata/types';
import { Nullable } from '../utils/tsUtils'; import { Nullable } from '../utils/tsUtils';
@ -40,6 +41,13 @@ export const SET_REQUEST_PAYLOAD_TRANSFORM =
'RequestTransform/SET_REQUEST_PAYLOAD_TRANSFORM'; 'RequestTransform/SET_REQUEST_PAYLOAD_TRANSFORM';
export const SET_REQUEST_TRANSFORM_STATE = export const SET_REQUEST_TRANSFORM_STATE =
'RequestTransform/SET_REQUEST_TRANSFORM_STATE'; 'RequestTransform/SET_REQUEST_TRANSFORM_STATE';
export const SET_RESPONSE_BODY = 'ResponseTransform/SET_RESPONSE_BODY';
export const SET_RESPONSE_BODY_ERROR =
'ResponseTransform/SET_RESPONSE_BODY_ERROR';
export const SET_RESPONSE_PAYLOAD_TRANSFORM =
'ResponseTransform/SET_RESPONSE_PAYLOAD_TRANSFORM';
export const SET_RESPONSE_TRANSFORM_STATE =
'RequestTransform/SET_REQUEST_TRANSFORM_STATE';
export interface SetEnvVars extends ReduxAction { export interface SetEnvVars extends ReduxAction {
type: typeof SET_ENV_VARS; type: typeof SET_ENV_VARS;
@ -86,11 +94,21 @@ export interface SetRequestBody extends ReduxAction {
requestBody: RequestTransformStateBody; requestBody: RequestTransformStateBody;
} }
export interface SetResponseBody extends ReduxAction {
type: typeof SET_RESPONSE_BODY;
responseBody: ResponseTransformStateBody;
}
export interface SetRequestBodyError extends ReduxAction { export interface SetRequestBodyError extends ReduxAction {
type: typeof SET_REQUEST_BODY_ERROR; type: typeof SET_REQUEST_BODY_ERROR;
requestBodyError: string; requestBodyError: string;
} }
export interface SetResponseBodyError extends ReduxAction {
type: typeof SET_RESPONSE_BODY_ERROR;
responseBodyError: string;
}
export interface SetRequestSampleInput extends ReduxAction { export interface SetRequestSampleInput extends ReduxAction {
type: typeof SET_REQUEST_SAMPLE_INPUT; type: typeof SET_REQUEST_SAMPLE_INPUT;
requestSampleInput: string; requestSampleInput: string;
@ -116,11 +134,21 @@ export interface SetRequestPayloadTransform extends ReduxAction {
isRequestPayloadTransform: boolean; isRequestPayloadTransform: boolean;
} }
export interface SetResponsePayloadTransform extends ReduxAction {
type: typeof SET_RESPONSE_PAYLOAD_TRANSFORM;
isResponsePayloadTransform: boolean;
}
export interface SetRequestTransformState extends ReduxAction { export interface SetRequestTransformState extends ReduxAction {
type: typeof SET_REQUEST_TRANSFORM_STATE; type: typeof SET_REQUEST_TRANSFORM_STATE;
newState: RequestTransformState; newState: RequestTransformState;
} }
export interface SetResponseTransformState extends ReduxAction {
type: typeof SET_RESPONSE_TRANSFORM_STATE;
newState: ResponseTransformState;
}
export type RequestTransformEvents = export type RequestTransformEvents =
| SetEnvVars | SetEnvVars
| SetSessionVars | SetSessionVars
@ -139,6 +167,12 @@ export type RequestTransformEvents =
| SetRequestPayloadTransform | SetRequestPayloadTransform
| SetRequestTransformState; | SetRequestTransformState;
export type ResponseTransformEvents =
| SetResponseBody
| SetResponseBodyError
| SetResponsePayloadTransform
| SetResponseTransformState;
export type RequestTransformState = { export type RequestTransformState = {
version: 1 | 2; version: 1 | 2;
envVars: KeyValuePair[]; envVars: KeyValuePair[];
@ -161,8 +195,11 @@ export type RequestTransformState = {
export type ResponseTransformState = { export type ResponseTransformState = {
version: 1 | 2; version: 1 | 2;
requestBody: RequestTransformStateBody;
templatingEngine: RequestTransformTemplateEngine; templatingEngine: RequestTransformTemplateEngine;
isResponsePayloadTransform: boolean;
responseSampleInput: string;
responseBody: ResponseTransformStateBody;
responseBodyError: string;
}; };
export type RequestTransformStateBody = Omit< export type RequestTransformStateBody = Omit<
@ -170,6 +207,11 @@ export type RequestTransformStateBody = Omit<
'form_template' 'form_template'
> & { form_template?: KeyValuePair[] }; > & { form_template?: KeyValuePair[] };
export type ResponseTransformStateBody = Omit<
ResponseTransformBody,
'form_template'
> & { form_template?: KeyValuePair[] };
export type KeyValuePair = { export type KeyValuePair = {
name: string; name: string;
value: string; value: string;
@ -199,6 +241,10 @@ export const defaultActionRequestBody = `{
} }
}`; }`;
export const defaultActionResponseBody = `{
"response": {{$body}},
}`;
export const defaultEventRequestBody = `{ export const defaultEventRequestBody = `{
"table": { "table": {
"name": {{$body.table.name}}, "name": {{$body.table.name}},

View File

@ -12,10 +12,14 @@ import {
RequestTransformState, RequestTransformState,
RequestTransformStateBody, RequestTransformStateBody,
ResponseTransformState, ResponseTransformState,
ResponseTransformStateBody,
} from './stateDefaults'; } from './stateDefaults';
import { isEmpty, isJsonString } from '../utils/jsUtils'; import { isEmpty, isJsonString } from '../utils/jsUtils';
import { Nullable } from '../utils/tsUtils'; import { Nullable } from '../utils/tsUtils';
import { requestBodyActionState } from './requestTransformState'; import {
requestBodyActionState,
responseBodyActionState,
} from './requestTransformState';
export const getPairsObjFromArray = ( export const getPairsObjFromArray = (
pairs: KeyValuePair[] pairs: KeyValuePair[]
@ -177,12 +181,21 @@ export const getRequestTransformObject = (
}; };
export const getResponseTransformObject = ( export const getResponseTransformObject = (
transformState: ResponseTransformState responseTransformState: ResponseTransformState
): ResponseTranform => ({ ) => {
version: 2, const isResponsePayloadTransform =
body: getTransformBodyServer(transformState.requestBody), responseTransformState.isResponsePayloadTransform;
template_engine: transformState.templatingEngine,
}); if (!isResponsePayloadTransform) return null;
const obj: ResponseTranform = {
version: 2,
body: getTransformBodyServer(responseTransformState.responseBody),
template_engine: responseTransformState.templatingEngine,
};
return obj;
};
const getErrorFromCode = (data: Record<string, any>) => { const getErrorFromCode = (data: Record<string, any>) => {
const errorCode = data.code ? data.code : ''; const errorCode = data.code ? data.code : '';
@ -439,6 +452,15 @@ const getRequestTransformBody = (
}; };
}; };
const getResponseTransformBody = (
responseTransform: ResponseTranform
): ResponseTransformStateBody => {
return {
action: responseBodyActionState.transformApplicationJson,
template: responseTransform.body?.template ?? '',
};
};
export const getTransformState = ( export const getTransformState = (
transform: RequestTransform, transform: RequestTransform,
sampleInput: string sampleInput: string
@ -469,6 +491,17 @@ export const getTransformState = (
templatingEngine: transform?.template_engine ?? 'Kriti', templatingEngine: transform?.template_engine ?? 'Kriti',
}); });
export const getResponseTransformState = (
responseTransform: ResponseTranform
): ResponseTransformState => ({
version: responseTransform?.version,
templatingEngine: responseTransform?.template_engine ?? 'Kriti',
isResponsePayloadTransform: !!responseTransform?.body,
responseBody: getResponseTransformBody(responseTransform),
responseSampleInput: '',
responseBodyError: '',
});
export const capitaliseFirstLetter = (val: string) => export const capitaliseFirstLetter = (val: string) =>
`${val[0].toUpperCase()}${val.slice(1)}`; `${val[0].toUpperCase()}${val.slice(1)}`;

View File

@ -29,6 +29,10 @@ import {
setRequestContentType, setRequestContentType,
setRequestUrlTransform, setRequestUrlTransform,
setRequestPayloadTransform, setRequestPayloadTransform,
setResponsePayloadTransform,
setResponseBody,
responseTransformReducer,
getActionResponseTransformDefaultState,
} from '@/components/Common/ConfigureTransformation/requestTransformState'; } from '@/components/Common/ConfigureTransformation/requestTransformState';
import { import {
RequestTransformContentType, RequestTransformContentType,
@ -37,6 +41,7 @@ import {
import { import {
KeyValuePair, KeyValuePair,
RequestTransformStateBody, RequestTransformStateBody,
ResponseTransformStateBody,
} from '@/components/Common/ConfigureTransformation/stateDefaults'; } from '@/components/Common/ConfigureTransformation/stateDefaults';
import ConfigureTransformation from '@/components/Common/ConfigureTransformation/ConfigureTransformation'; import ConfigureTransformation from '@/components/Common/ConfigureTransformation/ConfigureTransformation';
import ActionEditor from '../Common/components/ActionEditor'; import ActionEditor from '../Common/components/ActionEditor';
@ -82,6 +87,11 @@ const AddAction: React.FC<AddActionProps> = ({
getActionRequestTransformDefaultState() getActionRequestTransformDefaultState()
); );
const [responseTransformState, responseTransformDispatch] = useReducer(
responseTransformReducer,
getActionResponseTransformDefaultState()
);
useEffect(() => { useEffect(() => {
if (readOnlyMode) if (readOnlyMode)
dispatch( dispatch(
@ -120,7 +130,7 @@ const AddAction: React.FC<AddActionProps> = ({
} = actionDefinition; } = actionDefinition;
const onSubmit = () => { const onSubmit = () => {
dispatch(createAction(transformState)); dispatch(createAction(transformState, responseTransformState));
}; };
const setHeaders = (hs: Header[]) => { const setHeaders = (hs: Header[]) => {
@ -222,6 +232,14 @@ const AddAction: React.FC<AddActionProps> = ({
transformDispatch(setRequestPayloadTransform(data)); transformDispatch(setRequestPayloadTransform(data));
}; };
const responsePayloadTransformOnChange = (data: boolean) => {
responseTransformDispatch(setResponsePayloadTransform(data));
};
const responseBodyOnChange = (responseBody: ResponseTransformStateBody) => {
responseTransformDispatch(setResponseBody(responseBody));
};
// we send separate requests for the `url` preview and `body` preview, as in case of error, // we send separate requests for the `url` preview and `body` preview, as in case of error,
// we will not be able to resolve if the error is with url or body transform, with the current state of `test_webhook_transform` api // we will not be able to resolve if the error is with url or body transform, with the current state of `test_webhook_transform` api
useEffect(() => { useEffect(() => {
@ -357,7 +375,8 @@ const AddAction: React.FC<AddActionProps> = ({
<ConfigureTransformation <ConfigureTransformation
transformationType="action" transformationType="action"
state={transformState} requestTransfromState={transformState}
responseTransformState={responseTransformState}
resetSampleInput={resetSampleInput} resetSampleInput={resetSampleInput}
envVarsOnChange={envVarsOnChange} envVarsOnChange={envVarsOnChange}
sessionVarsOnChange={sessionVarsOnChange} sessionVarsOnChange={sessionVarsOnChange}
@ -370,6 +389,8 @@ const AddAction: React.FC<AddActionProps> = ({
requestContentTypeOnChange={requestContentTypeOnChange} requestContentTypeOnChange={requestContentTypeOnChange}
requestUrlTransformOnChange={requestUrlTransformOnChange} requestUrlTransformOnChange={requestUrlTransformOnChange}
requestPayloadTransformOnChange={requestPayloadTransformOnChange} requestPayloadTransformOnChange={requestPayloadTransformOnChange}
responsePayloadTransformOnChange={responsePayloadTransformOnChange}
responseBodyOnChange={responseBodyOnChange}
/> />
<div> <div>

View File

@ -12,6 +12,7 @@ import {
parseValidateApiData, parseValidateApiData,
getValidateTransformOptions, getValidateTransformOptions,
getTransformState, getTransformState,
getResponseTransformState,
} from '@/components/Common/ConfigureTransformation/utils'; } from '@/components/Common/ConfigureTransformation/utils';
import { Button } from '@/new-components/Button'; import { Button } from '@/new-components/Button';
import requestAction from '@/utils/requestAction'; import requestAction from '@/utils/requestAction';
@ -34,10 +35,16 @@ import {
setRequestUrlTransform, setRequestUrlTransform,
setRequestPayloadTransform, setRequestPayloadTransform,
setRequestTransformState, setRequestTransformState,
responseTransformReducer,
getActionResponseTransformDefaultState,
setResponsePayloadTransform,
setResponseBody,
setResponseTransformState,
} from '@/components/Common/ConfigureTransformation/requestTransformState'; } from '@/components/Common/ConfigureTransformation/requestTransformState';
import { import {
KeyValuePair, KeyValuePair,
RequestTransformStateBody, RequestTransformStateBody,
ResponseTransformStateBody,
} from '@/components/Common/ConfigureTransformation/stateDefaults'; } from '@/components/Common/ConfigureTransformation/stateDefaults';
import { import {
RequestTransformContentType, RequestTransformContentType,
@ -106,6 +113,11 @@ const ModifyAction: React.FC<ModifyProps> = ({
getActionRequestTransformDefaultState() getActionRequestTransformDefaultState()
); );
const [responseTransformState, responseTransformDispatch] = useReducer(
responseTransformReducer,
getActionResponseTransformDefaultState()
);
// initialize action state // initialize action state
const init = () => { const init = () => {
const modifyState = getModifyState(currentAction, allTypes); const modifyState = getModifyState(currentAction, allTypes);
@ -128,6 +140,16 @@ const ModifyAction: React.FC<ModifyProps> = ({
setRequestTransformState(getActionRequestTransformDefaultState()) setRequestTransformState(getActionRequestTransformDefaultState())
); );
} }
if (currentAction?.definition?.response_transform) {
const responseState = getResponseTransformState(
currentAction?.definition?.response_transform
);
responseTransformDispatch(setResponseTransformState(responseState));
} else {
responseTransformDispatch(
setResponseTransformState(getActionResponseTransformDefaultState())
);
}
}; };
useEffect(init, [currentAction, allTypes, dispatch]); useEffect(init, [currentAction, allTypes, dispatch]);
@ -155,9 +177,8 @@ const ModifyAction: React.FC<ModifyProps> = ({
) => { ) => {
dispatch(setTypeDefinition(value, error as any, timer, ast)); dispatch(setTypeDefinition(value, error as any, timer, ast));
}; };
const onSave = () => { const onSave = () => {
dispatch(saveAction(currentAction, transformState)); dispatch(saveAction(currentAction, transformState, responseTransformState));
}; };
const onDelete = () => { const onDelete = () => {
@ -245,6 +266,14 @@ const ModifyAction: React.FC<ModifyProps> = ({
transformDispatch(setRequestPayloadTransform(data)); transformDispatch(setRequestPayloadTransform(data));
}; };
const responsePayloadTransformOnChange = (data: boolean) => {
responseTransformDispatch(setResponsePayloadTransform(data));
};
const responseBodyOnChange = (responseBody: ResponseTransformStateBody) => {
responseTransformDispatch(setResponseBody(responseBody));
};
useEffect(() => { useEffect(() => {
requestUrlErrorOnChange(''); requestUrlErrorOnChange('');
requestUrlPreviewOnChange(''); requestUrlPreviewOnChange('');
@ -405,7 +434,8 @@ const ModifyAction: React.FC<ModifyProps> = ({
<ConfigureTransformation <ConfigureTransformation
transformationType="action" transformationType="action"
state={transformState} requestTransfromState={transformState}
responseTransformState={responseTransformState}
resetSampleInput={resetSampleInput} resetSampleInput={resetSampleInput}
envVarsOnChange={envVarsOnChange} envVarsOnChange={envVarsOnChange}
sessionVarsOnChange={sessionVarsOnChange} sessionVarsOnChange={sessionVarsOnChange}
@ -418,6 +448,10 @@ const ModifyAction: React.FC<ModifyProps> = ({
requestContentTypeOnChange={requestContentTypeOnChange} requestContentTypeOnChange={requestContentTypeOnChange}
requestUrlTransformOnChange={requestUrlTransformOnChange} requestUrlTransformOnChange={requestUrlTransformOnChange}
requestPayloadTransformOnChange={requestPayloadTransformOnChange} requestPayloadTransformOnChange={requestPayloadTransformOnChange}
responsePayloadTransformOnChange={
responsePayloadTransformOnChange
}
responseBodyOnChange={responseBodyOnChange}
/> />
<div className="flex items-start mb-lg"> <div className="flex items-start mb-lg">

View File

@ -64,141 +64,139 @@ import {
getResponseTransformObject, getResponseTransformObject,
} from '../../Common/ConfigureTransformation/utils'; } from '../../Common/ConfigureTransformation/utils';
export const createAction = transformState => (dispatch, getState) => { export const createAction =
const { add: rawState } = getState().actions; (transformState, responseTransformState) => (dispatch, getState) => {
const existingTypesList = customTypesSelector(getState()); const { add: rawState } = getState().actions;
const allActions = actionsSelector(getState()); const existingTypesList = customTypesSelector(getState());
const allActions = actionsSelector(getState());
const actionComment = rawState.comment ? rawState.comment.trim() : null; const actionComment = rawState.comment ? rawState.comment.trim() : null;
const { const {
name: actionName, name: actionName,
arguments: args, arguments: args,
outputType, outputType,
error: actionDefError, error: actionDefError,
type: actionType, type: actionType,
} = getActionDefinitionFromSdl(rawState.actionDefinition.sdl); } = getActionDefinitionFromSdl(rawState.actionDefinition.sdl);
if (actionDefError) { if (actionDefError) {
return dispatch( return dispatch(
showErrorNotification('Invalid Action Definition', actionDefError) showErrorNotification('Invalid Action Definition', actionDefError)
);
}
const { types, error: typeDefError } = getTypesFromSdl(
rawState.typeDefinition.sdl
); );
}
const { types, error: typeDefError } = getTypesFromSdl( if (typeDefError) {
rawState.typeDefinition.sdl return dispatch(
); showErrorNotification('Invalid Types Definition', typeDefError)
);
}
if (typeDefError) { const state = {
return dispatch( handler: rawState.handler.trim(),
showErrorNotification('Invalid Types Definition', typeDefError) kind: rawState.kind,
types,
actionType,
name: actionName,
arguments: args,
outputType,
headers: rawState.headers,
comment: actionComment,
timeout: parseInt(rawState.timeout, 10),
};
const requestTransform = getRequestTransformObject(transformState);
const responseTransform = getResponseTransformObject(
responseTransformState
); );
} const validationError = getStateValidationError(state, existingTypesList);
if (validationError) {
return dispatch(showErrorNotification(validationError));
}
const state = { const typesWithRelationships = hydrateTypeRelationships(
handler: rawState.handler.trim(), state.types,
kind: rawState.kind, existingTypesList
types, );
actionType,
name: actionName,
arguments: args,
outputType,
headers: rawState.headers,
comment: actionComment,
timeout: parseInt(rawState.timeout, 10),
};
const requestTransform = getRequestTransformObject(transformState); const { types: mergedTypes, overlappingTypenames } = mergeCustomTypes(
typesWithRelationships,
existingTypesList
);
const validationError = getStateValidationError(state, existingTypesList); if (overlappingTypenames) {
if (validationError) { const isOk = getOverlappingTypeConfirmation(
return dispatch(showErrorNotification(validationError)); state.name,
} allActions,
existingTypesList,
overlappingTypenames
);
if (!isOk) {
return;
}
}
// Migration queries start
const migration = new Migration();
const typesWithRelationships = hydrateTypeRelationships( const customFieldsQueryUp = generateSetCustomTypesQuery(
state.types, reformCustomTypes(mergedTypes)
existingTypesList );
);
const { types: mergedTypes, overlappingTypenames } = mergeCustomTypes( const customFieldsQueryDown = generateSetCustomTypesQuery(
typesWithRelationships, reformCustomTypes(existingTypesList)
existingTypesList );
); migration.add(customFieldsQueryUp, customFieldsQueryDown);
if (overlappingTypenames) { const actionQueryUp = generateCreateActionQuery(
const isOk = getOverlappingTypeConfirmation(
state.name, state.name,
allActions, generateActionDefinition(state, requestTransform, responseTransform),
existingTypesList, actionComment
overlappingTypenames
); );
if (!isOk) {
return;
}
}
// Migration queries start
const migration = new Migration();
const customFieldsQueryUp = generateSetCustomTypesQuery( const actionQueryDown = generateDropActionQuery(state.name);
reformCustomTypes(mergedTypes)
);
const customFieldsQueryDown = generateSetCustomTypesQuery( migration.add(actionQueryUp, actionQueryDown);
reformCustomTypes(existingTypesList) // Migration queries end
);
migration.add(customFieldsQueryUp, customFieldsQueryDown);
const actionQueryUp = requestTransform const migrationName = `create_action_${state.name}`;
? generateCreateActionQuery( const requestMsg = 'Creating action...';
state.name, const successMsg = 'Created action successfully';
generateActionDefinition(state, requestTransform), const errorMsg = 'Creating action failed';
actionComment const customOnSuccess = () => {
) if (rawState.derive.operation) {
: generateCreateActionQuery( persistDerivedAction(state.name, rawState.derive.operation);
state.name, }
generateActionDefinition(state), dispatch(exportMetadata()).then(() => {
actionComment dispatch(createActionRequestComplete());
); dispatch(
push(`${globals.urlPrefix}${appPrefix}/manage/${state.name}/modify`)
const actionQueryDown = generateDropActionQuery(state.name); );
});
migration.add(actionQueryUp, actionQueryDown); };
// Migration queries end const customOnError = () => {
const migrationName = `create_action_${state.name}`;
const requestMsg = 'Creating action...';
const successMsg = 'Created action successfully';
const errorMsg = 'Creating action failed';
const customOnSuccess = () => {
if (rawState.derive.operation) {
persistDerivedAction(state.name, rawState.derive.operation);
}
dispatch(exportMetadata()).then(() => {
dispatch(createActionRequestComplete()); dispatch(createActionRequestComplete());
dispatch( };
push(`${globals.urlPrefix}${appPrefix}/manage/${state.name}/modify`) dispatch(createActionRequestInProgress());
); makeMigrationCall(
}); dispatch,
getState,
migration.upMigration,
migration.downMigration,
migrationName,
customOnSuccess,
customOnError,
requestMsg,
successMsg,
errorMsg
);
}; };
const customOnError = () => {
dispatch(createActionRequestComplete());
};
dispatch(createActionRequestInProgress());
makeMigrationCall(
dispatch,
getState,
migration.upMigration,
migration.downMigration,
migrationName,
customOnSuccess,
customOnError,
requestMsg,
successMsg,
errorMsg
);
};
export const saveAction = export const saveAction =
(currentAction, transformState) => (dispatch, getState) => { (currentAction, transformState, responseTransformState) =>
(dispatch, getState) => {
const { modify: rawState } = getState().actions; const { modify: rawState } = getState().actions;
const existingTypesList = customTypesSelector(getState()); const existingTypesList = customTypesSelector(getState());
const { const {
@ -241,7 +239,9 @@ export const saveAction =
}; };
const requestTransform = getRequestTransformObject(transformState); const requestTransform = getRequestTransformObject(transformState);
const responseTransform = getResponseTransformObject(transformState); const responseTransform = getResponseTransformObject(
responseTransformState
);
const validationError = getStateValidationError(state); const validationError = getStateValidationError(state);
if (validationError) { if (validationError) {
@ -269,7 +269,6 @@ export const saveAction =
); );
const dropCurrentActionQuery = generateDropActionQuery(currentAction.name); const dropCurrentActionQuery = generateDropActionQuery(currentAction.name);
const updateCurrentActionQuery = getUpdateActionQuery( const updateCurrentActionQuery = getUpdateActionQuery(
generateActionDefinition(state, requestTransform, responseTransform), generateActionDefinition(state, requestTransform, responseTransform),
currentAction.name, currentAction.name,
@ -281,17 +280,11 @@ export const saveAction =
currentAction.comment currentAction.comment
); );
const createNewActionQuery = requestTransform const createNewActionQuery = generateCreateActionQuery(
? generateCreateActionQuery( state.name,
state.name, generateActionDefinition(state, requestTransform, responseTransform),
generateActionDefinition(state, requestTransform), actionComment
actionComment );
)
: generateCreateActionQuery(
state.name,
generateActionDefinition(state),
actionComment
);
const actionQueryDown = generateDropActionQuery(state.name); const actionQueryDown = generateDropActionQuery(state.name);
const oldActionQueryUp = generateCreateActionQuery( const oldActionQueryUp = generateCreateActionQuery(

View File

@ -374,7 +374,7 @@ const Add: React.FC<Props> = props => {
/> />
<ConfigureTransformation <ConfigureTransformation
transformationType="event" transformationType="event"
state={transformState} requestTransfromState={transformState}
resetSampleInput={resetSampleInput} resetSampleInput={resetSampleInput}
envVarsOnChange={envVarsOnChange} envVarsOnChange={envVarsOnChange}
sessionVarsOnChange={sessionVarsOnChange} sessionVarsOnChange={sessionVarsOnChange}

View File

@ -388,7 +388,7 @@ const Modify: React.FC<Props> = props => {
/> />
<ConfigureTransformation <ConfigureTransformation
transformationType="event" transformationType="event"
state={transformState} requestTransfromState={transformState}
resetSampleInput={resetSampleInput} resetSampleInput={resetSampleInput}
envVarsOnChange={envVarsOnChange} envVarsOnChange={envVarsOnChange}
sessionVarsOnChange={sessionVarsOnChange} sessionVarsOnChange={sessionVarsOnChange}