mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-10-05 06:18:04 +03:00
console: add support for array as query param in import from OAS
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7630 GitOrigin-RevId: ce8a374b055c4ced7bb6fddebdc11016a5787d23
This commit is contained in:
parent
9138a09d3e
commit
bd0533fcf3
@ -35,9 +35,10 @@ const RequestUrlEditor: React.FC<RequestUrlEditorProps> = ({
|
||||
requestQueryParamsOnChange,
|
||||
}) => {
|
||||
const [localUrl, setLocalUrl] = useState<string>(requestUrl);
|
||||
const [queryParamsType, setQueryParamstype] = useState(
|
||||
typeof requestQueryParams === 'string' ? 'url-string' : 'key-value'
|
||||
);
|
||||
const [queryParamsType, setQueryParamstype] = useState<
|
||||
QueryParmsOptions['value']
|
||||
>(typeof requestQueryParams === 'string' ? 'url-string' : 'key-value');
|
||||
|
||||
const [keyValueQueryParams, setKeyValueQueryParams] = useState(
|
||||
typeof requestQueryParams !== 'string'
|
||||
? requestQueryParams
|
||||
@ -51,6 +52,22 @@ const RequestUrlEditor: React.FC<RequestUrlEditorProps> = ({
|
||||
const [localQueryParams, setLocalQueryParams] =
|
||||
useState<QueryParams>(requestQueryParams);
|
||||
|
||||
useEffect(() => {
|
||||
setQueryParamstype(
|
||||
typeof requestQueryParams === 'string' ? 'url-string' : 'key-value'
|
||||
);
|
||||
setKeyValueQueryParams(
|
||||
typeof requestQueryParams !== 'string'
|
||||
? requestQueryParams
|
||||
: keyValueQueryParams
|
||||
);
|
||||
setStringQueryParams(
|
||||
typeof requestQueryParams === 'string'
|
||||
? requestQueryParams
|
||||
: stringQueryParams
|
||||
);
|
||||
}, [requestQueryParams]);
|
||||
|
||||
const executionOptions: QueryParmsOptions[] = [
|
||||
{
|
||||
value: 'key-value',
|
||||
|
@ -394,13 +394,8 @@ const AddAction: React.FC<AddActionProps> = ({
|
||||
transformDispatch(setRequestSampleInput(sampleInput));
|
||||
requestMethodOnChange(method);
|
||||
requestUrlTransformOnChange(true);
|
||||
requestUrlOnChange(path);
|
||||
requestQueryParamsOnChange(
|
||||
queryParams.map(name => ({
|
||||
name,
|
||||
value: `{{$body.input.${name}}}`,
|
||||
}))
|
||||
);
|
||||
requestUrlOnChange(path.replace(/\{([^}]+)\}/g, '{{$body.input.$1}}'));
|
||||
requestQueryParamsOnChange(queryParams);
|
||||
setHeaders(
|
||||
actionHeaders.map(name => ({
|
||||
name,
|
||||
|
@ -26,6 +26,12 @@ const getArgObjFromDefinition = (
|
||||
if (type === 'Int' || type === 'Float' || type === 'BigInt')
|
||||
return { [name]: 10 };
|
||||
if (type === 'Boolean') return { [name]: false };
|
||||
if (type === '[String]' || type === '[ID]') {
|
||||
return { [name]: ['foo', 'bar'] };
|
||||
}
|
||||
if (type === '[Int]' || type === '[Float]' || type === '[BigInt]') {
|
||||
return { [name]: [10, 20] };
|
||||
}
|
||||
|
||||
const userDefType = typesdef?.types.find(
|
||||
(t: Record<string, any>) => t.name === type
|
||||
|
@ -17,7 +17,7 @@ export type GeneratedAction = {
|
||||
responseTransforms: string;
|
||||
sampleInput: string;
|
||||
headers: string[];
|
||||
queryParams: string[];
|
||||
queryParams: string | { name: string; value: string }[];
|
||||
};
|
||||
|
||||
export type Result = Awaited<ReturnType<typeof createGraphQLSchema>>;
|
||||
@ -25,3 +25,5 @@ export type Operation = Result['data']['operations'][0];
|
||||
|
||||
export type DataDefinition = Operation['responseDefinition'];
|
||||
export type SubDefinition = Operation['responseDefinition']['subDefinitions'];
|
||||
|
||||
export type OperationParameters = Operation['parameters'];
|
||||
|
@ -1,6 +1,34 @@
|
||||
import { Oas3 } from 'openapi-to-graphql';
|
||||
import { ParameterObject } from 'openapi-to-graphql/dist/types/oas3';
|
||||
import petStore from './petstore.json';
|
||||
import { getOperations, generateAction } from './utils';
|
||||
import { getOperations, generateAction, generateQueryParams } from './utils';
|
||||
|
||||
const tags: ParameterObject = {
|
||||
name: 'tags',
|
||||
in: 'query',
|
||||
description: 'Tags to filter by',
|
||||
required: false,
|
||||
explode: true,
|
||||
schema: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const status: ParameterObject = {
|
||||
name: 'status',
|
||||
in: 'query',
|
||||
description: 'Status values that need to be considered for filter',
|
||||
required: false,
|
||||
explode: true,
|
||||
schema: {
|
||||
type: 'string',
|
||||
default: 'available',
|
||||
enum: ['available', 'pending', 'sold'],
|
||||
},
|
||||
};
|
||||
|
||||
describe('getOperations', () => {
|
||||
it('should return an array of operations', async () => {
|
||||
@ -44,17 +72,22 @@ describe('generateAction', () => {
|
||||
path: '/pets',
|
||||
requestTransforms: '',
|
||||
responseTransforms: '',
|
||||
sampleInput:
|
||||
'{\n' +
|
||||
' "action": {\n' +
|
||||
' "name": "findPets"\n' +
|
||||
' },\n' +
|
||||
' "input": {\n' +
|
||||
' "limit": 10\n' +
|
||||
' }\n' +
|
||||
'}',
|
||||
sampleInput: JSON.stringify(
|
||||
{
|
||||
action: {
|
||||
name: 'findPets',
|
||||
},
|
||||
input: {
|
||||
limit: 10,
|
||||
tags: ['foo', 'bar'],
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
headers: [],
|
||||
queryParams: ['tags', 'limit'],
|
||||
queryParams:
|
||||
'{{ concat ([concat({{ range _, x := $body.input.tags }} "tags={{x}}&" {{ end }}), "limit={{$body.input.limit}}&"]) }}',
|
||||
});
|
||||
});
|
||||
|
||||
@ -64,3 +97,24 @@ describe('generateAction', () => {
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateQueryParams', () => {
|
||||
it('should generate query params with one non-array param', async () => {
|
||||
const queryParams = await generateQueryParams([status]);
|
||||
expect(queryParams).toStrictEqual([
|
||||
{ name: 'status', value: '{{$body.input.status}}' },
|
||||
]);
|
||||
});
|
||||
it('should generate query params with one non-array param and one array param', async () => {
|
||||
const queryParams = await generateQueryParams([status, tags]);
|
||||
expect(queryParams).toBe(
|
||||
'{{ concat (["status={{$body.input.status}}&", concat({{ range _, x := $body.input.tags }} "tags={{x}}&" {{ end }})]) }}'
|
||||
);
|
||||
});
|
||||
it('should generate query params with one non-array param and one array param (reversed)', async () => {
|
||||
const queryParams = await generateQueryParams([tags, status]);
|
||||
expect(queryParams).toBe(
|
||||
'{{ concat ([concat({{ range _, x := $body.input.tags }} "tags={{x}}&" {{ end }}), "status={{$body.input.status}}&"]) }}'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -22,6 +22,7 @@ import {
|
||||
DataDefinition,
|
||||
GeneratedAction,
|
||||
Operation,
|
||||
OperationParameters,
|
||||
Result,
|
||||
SubDefinition,
|
||||
} from './types';
|
||||
@ -55,6 +56,37 @@ export const formatQuery = (query?: string): string => {
|
||||
}
|
||||
};
|
||||
|
||||
const isSchemaObject = (
|
||||
schema: SchemaObject | ReferenceObject | undefined
|
||||
): schema is SchemaObject => schema !== undefined && 'type' in schema;
|
||||
|
||||
// {{ concat ([concat({{ range _, x := ["apple", "banana"] }} "tags={{x}}&" {{ end }})]) }}
|
||||
export const generateQueryParams = (parameters: OperationParameters) => {
|
||||
const isThereArray = parameters.some(parameter => {
|
||||
return (
|
||||
isSchemaObject(parameter?.schema) && parameter.schema.type === 'array'
|
||||
);
|
||||
});
|
||||
if (isThereArray) {
|
||||
const stringParams = parameters.map(param => {
|
||||
if (isSchemaObject(param?.schema) && param.schema.type === 'array') {
|
||||
return `concat({{ range _, x := $body.input.${param.name} }} "${param.name}={{x}}&" {{ end }})`;
|
||||
}
|
||||
return `"${param.name}={{$body.input.${param.name}}}&"`;
|
||||
});
|
||||
|
||||
return `{{ concat ([${stringParams.join(', ')}]) }}`.replace(/&&/, '&');
|
||||
}
|
||||
const parameterNames =
|
||||
parameters
|
||||
?.filter(param => param.in === 'query')
|
||||
?.map(param => param.name) || [];
|
||||
return parameterNames.map(name => ({
|
||||
name,
|
||||
value: `{{$body.input.${name}}}`,
|
||||
}));
|
||||
};
|
||||
|
||||
const lowerCaseFirstLetter = (str: string): string => {
|
||||
return str ? str.charAt(0).toLowerCase() + str.slice(1) : '';
|
||||
};
|
||||
@ -370,10 +402,8 @@ export const translateAction = (
|
||||
?.filter(param => param.in === 'header')
|
||||
?.map(param => param.name) || [];
|
||||
|
||||
const queryParams =
|
||||
operation.parameters
|
||||
?.filter(param => param.in === 'query')
|
||||
?.map(param => param.name) || [];
|
||||
const queryParams = generateQueryParams(operation.parameters ?? []);
|
||||
|
||||
return {
|
||||
operationId: operation.operationId,
|
||||
actionType:
|
||||
|
@ -134,8 +134,10 @@ export const getTypesFromSdl = sdl => {
|
||||
|
||||
schemaAst.definitions.forEach(def => {
|
||||
const typeDef = getTypeFromAstDef(def);
|
||||
typeDefinition.error = typeDef.error;
|
||||
typeDefinition.types.push(typeDef);
|
||||
if (typeDef) {
|
||||
typeDefinition.error = typeDef.error;
|
||||
typeDefinition.types.push(typeDef);
|
||||
}
|
||||
});
|
||||
|
||||
return typeDefinition;
|
||||
|
Loading…
Reference in New Issue
Block a user