Skip to content

Commit

Permalink
Object query params (maps/dictionaries) support
Browse files Browse the repository at this point in the history
  • Loading branch information
mahamdan committed Dec 8, 2022
1 parent dd5c8ba commit 5f84938
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 23 deletions.
26 changes: 17 additions & 9 deletions src/components/TryOut/FormItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ArrayForm, containerStyle } from './ArrayForm';
import { SchemaSection } from './SchemaSection';
import { Dropdown, Input, Label } from './styled.elements';

const FormItemTypesSwitch = ({ item, onChange, discriminator, ancestors }) => {
const FormItemTypesSwitch = ({ item, onChange, discriminator, ancestors, location }) => {
const { schema, name, example, description, required, kind } = item;
const { oneOf, activeOneOf } = schema;
const oneOfSchema = oneOf?.[activeOneOf];
Expand All @@ -23,7 +23,9 @@ const FormItemTypesSwitch = ({ item, onChange, discriminator, ancestors }) => {
<DiscriminatorDropdown
parent={discriminator.parentSchema}
enumValues={schema.enum}
onChange={value => onChange && onChange(name, value, undefined, ancestors, item.in)}
onChange={value =>
onChange && onChange(name, value, undefined, ancestors, item.in || location)
}
/>
) : (
<>
Expand All @@ -34,15 +36,17 @@ const FormItemTypesSwitch = ({ item, onChange, discriminator, ancestors }) => {
color={'white'}
backgroundColor={'#326CD1'}
onChange={e =>
onChange && onChange(name, e.target.files?.[0], undefined, ancestors, item.in)
onChange &&
onChange(name, e.target.files?.[0], undefined, ancestors, item.in || location)
}
/>
) : (
<Input
placeholder={`${example || description || ''}`}
type={schema.format || 'text'}
onChange={e =>
onChange && onChange(name, e.target.value, undefined, ancestors, item.in)
onChange &&
onChange(name, e.target.value, undefined, ancestors, item.in || location)
}
/>
)}
Expand All @@ -61,7 +65,7 @@ const FormItemTypesSwitch = ({ item, onChange, discriminator, ancestors }) => {
!isNaN(Number(e.target.value)) ? Number(e.target.value) : e.target.value,
undefined,
ancestors,
item.in,
item.in || location,
)
}
/>
Expand All @@ -78,7 +82,7 @@ const FormItemTypesSwitch = ({ item, onChange, discriminator, ancestors }) => {
selectObject.target.value === 'true' ? true : false,
undefined,
ancestors,
item.in,
item.in || location,
);
}}
>
Expand All @@ -100,7 +104,7 @@ const FormItemTypesSwitch = ({ item, onChange, discriminator, ancestors }) => {
required={required}
onChange={onChange}
ancestors={ancestors}
location={item.in}
location={item.in || location}
/>
);
}
Expand Down Expand Up @@ -144,7 +148,8 @@ const FormItemTypesSwitch = ({ item, onChange, discriminator, ancestors }) => {
editable
hideButtons
setParsedJSON={jsonValue =>
onChange && onChange(dictionaryName, jsonValue, undefined, ancestors)
onChange &&
onChange(dictionaryName, jsonValue, undefined, ancestors, item.in || location)
}
/>
</div>
Expand Down Expand Up @@ -173,6 +178,7 @@ enum FormItemType {
interface FormItemProps {
item: FieldModel;
ancestors: string[];
location?: string;
onChange: () => void;
discriminator?: {
fieldName: string;
Expand All @@ -181,7 +187,7 @@ interface FormItemProps {
}

export const FormItem = observer(
({ item, onChange, discriminator, ancestors = [] }: FormItemProps) => {
({ item, onChange, discriminator, ancestors = [], location }: FormItemProps) => {
const { expanded, name, schema } = item;
const { activeOneOf, oneOf, isCircular, isPrimitive, type } = schema;
const oneOfSchema = oneOf?.[activeOneOf];
Expand Down Expand Up @@ -236,6 +242,7 @@ export const FormItem = observer(
onChange={onChange}
discriminator={discriminator}
ancestors={ancestors}
location={location}
/>
</div>
</div>
Expand All @@ -245,6 +252,7 @@ export const FormItem = observer(
schema={item.schema}
onChange={onChange}
ancestors={[...ancestors, name]}
location={item.in}
/>
)}
</div>
Expand Down
5 changes: 4 additions & 1 deletion src/components/TryOut/FormSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const Form = styled.div`
interface FormSectionProps {
items: FieldModel[];
ancestors?: string[];
location?: string;
onChange: () => void;
discriminator?: {
fieldName: string;
Expand All @@ -30,14 +31,16 @@ export const FormSection = ({
onChange,
discriminator,
ancestors = [],
location,
}: FormSectionProps) => {
return (
<Form>
{items.map((item, idx) => (
<FormItem
key={idx}
item={item}
ancestors={ancestors}
key={idx}
location={location}
onChange={onChange}
discriminator={discriminator}
/>
Expand Down
11 changes: 10 additions & 1 deletion src/components/TryOut/SchemaSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,18 @@ interface SchemaSectionProps {
ancestors?: string[];
onChange?: any;
requestPayload?: any;
location?: string;
}

export const SchemaSection = observer(
({ schema, contentType, onChange, ancestors = [], requestPayload }: SchemaSectionProps) => {
({
schema,
contentType,
onChange,
ancestors = [],
requestPayload,
location,
}: SchemaSectionProps) => {
if (!schema) return null;

switch (contentType) {
Expand Down Expand Up @@ -87,6 +95,7 @@ export const SchemaSection = observer(
<FormSection
items={fields}
ancestors={ancestors}
location={location}
onChange={onChange}
discriminator={{
fieldName: schema.discriminatorProp,
Expand Down
6 changes: 3 additions & 3 deletions src/components/TryOut/TryOut.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export const TryOut = observer(
setRequest(request => {
if (arrayIndex !== undefined) {
const updatedObject = getUpdatedObject(
request.queryParams,
request.pathParams,
fieldName,
value,
arrayIndex,
Expand Down Expand Up @@ -187,7 +187,7 @@ export const TryOut = observer(
setRequest(request => {
if (arrayIndex !== undefined) {
const updatedObject = getUpdatedObject(
request.queryParams,
request.cookieParams,
fieldName,
value,
arrayIndex,
Expand All @@ -212,7 +212,7 @@ export const TryOut = observer(
setRequest(request => {
if (arrayIndex !== undefined) {
const updatedObject = getUpdatedObject(
request.queryParams,
request.header,
fieldName,
value,
arrayIndex,
Expand Down
47 changes: 38 additions & 9 deletions src/utils/tryout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,54 @@ const appendPathParamsToPath = (path: string, pathParams: Record<string, string>
return path;
};

const appendQueryParamsToPath = (path: string, queryParams: Record<string, string>): string => {
const entries = Object.entries(queryParams);
let paramsSuffix = '';

/**
* e.g. [
* ["a", "b"],
* ["c", "d"]
* ]
* becomes "a=b&c=d"
*/
const entriesToQueryString = (entries): string => {
let queryString = '';
for (let i = 0; i < entries.length; i++) {
const [key, value] = entries[i];
paramsSuffix +=
paramsSuffix === ''
queryString +=
queryString === ''
? `${key}=${encodeURIComponent(value)}`
: `&${key}=${encodeURIComponent(value)}`;
}
return queryString;
};

const appendQueryParamsToPath = (path: string, queryParams: Record<string, string>): string => {
const entries = Object.entries(queryParams);
const paramsSuffix = entriesToQueryString(entries);
return paramsSuffix === '' ? path : `${path}?${paramsSuffix}`;
};

/**
*
* @returns equivalent params, with dictionary params converted to string params
* e.g. reqParam = {id: 3, severity:5} becomes reqParam=id%3D%3D3%26%26severity%3D%3D5 or reqParam=id==3&&severity==5
*/
const formatQueryParams = params => {
if (isEmpty(params)) return params;
return Object.fromEntries(
Object.entries(params).map(([key, value]) => {
const isObjectParam = typeof value === 'object' && value !== null;
if (!isObjectParam) return [key, value];
return [key, entriesToQueryString(Object.entries(value as object))];
}),
);
};

export const appendParamsToPath = (
path: string,
pathParams: Record<string, string>,
queryParams: Record<string, string>,
): string => {
path = appendPathParamsToPath(path, pathParams);
path = appendQueryParamsToPath(path, queryParams);
path = appendQueryParamsToPath(path, formatQueryParams(queryParams));
return path;
};

Expand Down Expand Up @@ -197,7 +224,7 @@ export const getUpdatedObject = (
};

/**
* Method used for cleaning up an object from fields having empty strings as values
* Method used for cleaning up an object from fields having empty strings or empty objects as values
* as those make no sense in some use cases, such as for request parameters (query, path etc).
* This method esentially deletes fields that have values such as '', ' ', ' ' (i.e. empty string),
* and arrays, rest of the fields are not touched whatsoever.
Expand All @@ -210,7 +237,9 @@ const cleanEmptyFields = (obj: Record<string, any>): Record<string, any> => {
if (entries.length === 0) return obj;

entries.forEach(([key, value]) => {
typeof obj[key] === 'string' && isEmpty(obj[key].replace(/\s/g, ''))
const isEmptyString = typeof obj[key] === 'string' && isEmpty(obj[key].replace(/\s/g, ''));
const isEmptyObject = typeof obj[key] === 'object' && obj[key] !== null && isEmpty(obj[key]);
isEmptyString || isEmptyObject
? delete obj[key]
: Array.isArray(value)
? getCleanArray(value)
Expand Down

0 comments on commit 5f84938

Please sign in to comment.