Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(msw): split polymorphic mocks into its own functions #1365

Merged
merged 11 commits into from
May 19, 2024
12 changes: 7 additions & 5 deletions packages/mock/src/faker/getters/combine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {
MockOptions,
} from '@orval/core';
import omit from 'lodash.omit';
import { MockDefinition, MockSchemaObject } from '../../types';
import { resolveMockValue } from '../resolvers';
import { MockSchemaObject } from '../../types';

export const combineSchemasMock = ({
item,
Expand All @@ -18,6 +18,7 @@ export const combineSchemasMock = ({
context,
imports,
existingReferencedProperties,
splitMockImplementations,
}: {
item: MockSchemaObject;
separator: 'allOf' | 'oneOf' | 'anyOf';
Expand All @@ -33,7 +34,8 @@ export const combineSchemasMock = ({
// This is used to prevent recursion when combining schemas
// When an element is added to the array, it means on this iteration, we've already seen this property
existingReferencedProperties: string[];
}) => {
splitMockImplementations: string[];
}): MockDefinition => {
let combineImports: GeneratorImport[] = [];
let includedProperties: string[] = (combine?.includedProperties ?? []).slice(
0,
Expand All @@ -56,6 +58,7 @@ export const combineSchemasMock = ({
context,
imports,
existingReferencedProperties,
splitMockImplementations,
})
: undefined;

Expand Down Expand Up @@ -93,6 +96,7 @@ export const combineSchemasMock = ({
context,
imports,
existingReferencedProperties,
splitMockImplementations,
});

combineImports.push(...resolvedValue.imports);
Expand All @@ -109,9 +113,7 @@ export const combineSchemasMock = ({
}

if (itemResolvedValue?.value && separator !== 'oneOf' && isLastElement) {
currentValue = `${currentValue}${
itemResolvedValue?.value ? `,${itemResolvedValue.value}` : ''
}`;
currentValue = `${currentValue ? `${currentValue},` : ''}${itemResolvedValue.value}`;
}

const isObjectBounds =
Expand Down
6 changes: 6 additions & 0 deletions packages/mock/src/faker/getters/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const getMockObject = ({
context,
imports,
existingReferencedProperties,
splitMockImplementations,
allowOverride = false,
}: {
item: MockSchemaObject;
Expand All @@ -38,6 +39,7 @@ export const getMockObject = ({
// This is used to prevent recursion when combining schemas
// When an element is added to the array, it means on this iteration, we've already seen this property
existingReferencedProperties: string[];
splitMockImplementations: string[];
// This is used to add the overrideResponse to the object
allowOverride?: boolean;
}): MockDefinition => {
Expand All @@ -54,6 +56,7 @@ export const getMockObject = ({
context,
imports,
existingReferencedProperties,
splitMockImplementations,
});
}

Expand All @@ -69,6 +72,7 @@ export const getMockObject = ({
context,
imports,
existingReferencedProperties,
splitMockImplementations,
});
}

Expand Down Expand Up @@ -116,6 +120,7 @@ export const getMockObject = ({
context,
imports,
existingReferencedProperties,
splitMockImplementations,
});

imports.push(...resolvedValue.imports);
Expand Down Expand Up @@ -175,6 +180,7 @@ export const getMockObject = ({
context,
imports,
existingReferencedProperties,
splitMockImplementations,
});

return {
Expand Down
4 changes: 4 additions & 0 deletions packages/mock/src/faker/getters/scalar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const getMockScalar = ({
combine,
context,
existingReferencedProperties,
splitMockImplementations,
allowOverride = false,
}: {
item: MockSchemaObject;
Expand All @@ -41,6 +42,7 @@ export const getMockScalar = ({
// This is used to prevent recursion when combining schemas
// When an element is added to the array, it means on this iteration, we've already seen this property
existingReferencedProperties: string[];
splitMockImplementations: string[];
// This is used to add the overrideResponse to the object
allowOverride?: boolean;
}): MockDefinition => {
Expand Down Expand Up @@ -157,6 +159,7 @@ export const getMockScalar = ({
context,
imports,
existingReferencedProperties,
splitMockImplementations,
});

if (enums) {
Expand Down Expand Up @@ -259,6 +262,7 @@ export const getMockScalar = ({
context,
imports,
existingReferencedProperties,
splitMockImplementations,
allowOverride,
});
}
Expand Down
42 changes: 40 additions & 2 deletions packages/mock/src/faker/resolvers/value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import {
GeneratorImport,
getRefInfo,
isReference,
isRootKey,
MockOptions,
pascal,
} from '@orval/core';
import get from 'lodash.get';
import { SchemaObject } from 'openapi3-ts/oas30';
import { getMockScalar } from '../getters/scalar';
import { MockDefinition, MockSchemaObject } from '../../types';
import { overrideVarName } from '../getters';
import { getMockScalar } from '../getters/scalar';

const isRegex = (key: string) => key[0] === '/' && key[key.length - 1] === '/';

Expand Down Expand Up @@ -56,6 +59,7 @@ export const resolveMockValue = ({
context,
imports,
existingReferencedProperties,
splitMockImplementations,
allowOverride,
}: {
schema: MockSchemaObject;
Expand All @@ -71,6 +75,7 @@ export const resolveMockValue = ({
// This is used to prevent recursion when combining schemas
// When an element is added to the array, it means on this iteration, we've already seen this property
existingReferencedProperties: string[];
splitMockImplementations: string[];
allowOverride?: boolean;
}): MockDefinition & { type?: string } => {
if (isReference(schema)) {
Expand All @@ -80,7 +85,7 @@ export const resolveMockValue = ({
refPaths,
} = getRefInfo(schema.$ref, context);

const schemaRef = get(context.specs[specKey], refPaths);
const schemaRef = get(context.specs[specKey], refPaths as string[]);

const newSchema = {
...schemaRef,
Expand All @@ -101,8 +106,40 @@ export const resolveMockValue = ({
},
imports,
existingReferencedProperties,
splitMockImplementations,
allowOverride,
});
if (
scalar.value &&
newSchema.type === 'object' &&
combine?.separator === 'oneOf'
) {
const funcName = `get${pascal(operationId)}Response${pascal(newSchema.name)}Mock`;
if (
!splitMockImplementations?.some((f) =>
f.includes(`export const ${funcName}`),
)
) {
const discriminatedProperty = newSchema.discriminator?.propertyName;

let type = `Partial<${newSchema.name}>`;
if (discriminatedProperty) {
type = `Omit<${type}, '${discriminatedProperty}'>`;
}

const args = `${overrideVarName}: ${type} = {}`;
const value = newSchema.oneOf
? `faker.helpers.arrayElement([${scalar.value}])`
: scalar.value;
const func = `export const ${funcName} = (${args}): ${newSchema.name} => ({...${value}, ...${overrideVarName}});`;
splitMockImplementations?.push(func);
}
scalar.value = `{...${funcName}()}`;
scalar.imports.push({
name: newSchema.name,
specKey: isRootKey(specKey, context.target) ? undefined : specKey,
});
}

return {
...scalar,
Expand All @@ -119,6 +156,7 @@ export const resolveMockValue = ({
context,
imports,
existingReferencedProperties,
splitMockImplementations,
allowOverride,
});

Expand Down
23 changes: 19 additions & 4 deletions packages/mock/src/msw/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ClientMockGeneratorBuilder,
generateDependencyImports,
GenerateMockImports,
GeneratorDependency,
Expand All @@ -9,11 +10,10 @@ import {
isObject,
pascal,
ResReqTypesValue,
ClientMockGeneratorBuilder,
} from '@orval/core';
import { getDelay } from '../delay';
import { getRouteMSW, overrideVarName } from '../faker/getters';
import { getMockDefinition, getMockOptionsDataOverride } from './mocks';
import { getDelay } from '../delay';

const getMSWDependencies = (locale?: string): GeneratorDependency[] => [
{
Expand Down Expand Up @@ -59,7 +59,9 @@ const generateDefinition = (
responseImports: GeneratorImport[],
responses: ResReqTypesValue[],
contentTypes: string[],
splitMockImplementations: string[],
) => {
const oldSplitMockImplementations = [...splitMockImplementations];
const { definitions, definition, imports } = getMockDefinition({
operationId,
tags,
Expand All @@ -69,6 +71,7 @@ const generateDefinition = (
override,
context,
mockOptions: !isFunction(mock) ? mock : undefined,
splitMockImplementations,
});

const mockData = getMockOptionsDataOverride(operationId, override);
Expand All @@ -90,10 +93,18 @@ const generateDefinition = (
const getResponseMockFunctionName = `${getResponseMockFunctionNameBase}${pascal(name)}`;
const handlerName = `${handlerNameBase}${pascal(name)}`;

const mockImplementation = isReturnHttpResponse
? `export const ${getResponseMockFunctionName} = (${isResponseOverridable ? `overrideResponse: Partial< ${returnType} > = {}` : ''})${mockData ? '' : `: ${returnType}`} => (${value})\n\n`
const addedSplitMockImplementations = splitMockImplementations.slice(
oldSplitMockImplementations.length,
);
splitMockImplementations.push(...addedSplitMockImplementations);
const mockImplementations = addedSplitMockImplementations.length
? `//#region ${getResponseMockFunctionName} sub functions\n${addedSplitMockImplementations.join('\n\n')}\n//#endregion\n\n`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to include annotate, I think another way to express them using variable names would be appropriate. Or i think you can just delete it if it's not necessary.

: '';

const mockImplementation = isReturnHttpResponse
? `${mockImplementations}export const ${getResponseMockFunctionName} = (${isResponseOverridable ? `overrideResponse: Partial< ${returnType} > = {}` : ''})${mockData ? '' : `: ${returnType}`} => (${value})\n\n`
: mockImplementations;

const delay = getDelay(override, !isFunction(mock) ? mock : undefined);
const handlerImplementation = `
export const ${handlerName} = (${isReturnHttpResponse && !isTextPlain ? `overrideResponse?: ${returnType}` : ''}) => {
Expand Down Expand Up @@ -159,6 +170,8 @@ export const generateMSW = (
const handlerName = `get${pascal(operationId)}MockHandler`;
const getResponseMockFunctionName = `get${pascal(operationId)}ResponseMock`;

const splitMockImplementations: string[] = [];

const baseDefinition = generateDefinition(
'',
route,
Expand All @@ -171,6 +184,7 @@ export const generateMSW = (
response.imports,
response.types.success,
response.contentTypes,
splitMockImplementations,
);

const mockImplementations = [baseDefinition.implementation.function];
Expand All @@ -196,6 +210,7 @@ export const generateMSW = (
response.imports,
[statusResponse],
[statusResponse.contentType],
splitMockImplementations,
);
mockImplementations.push(definition.implementation.function);
handlerImplementations.push(definition.implementation.handler);
Expand Down
7 changes: 7 additions & 0 deletions packages/mock/src/msw/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export const getResponsesMockDefinition = ({
transformer,
context,
mockOptions,
splitMockImplementations,
}: {
operationId: string;
tags: string[];
Expand All @@ -133,7 +134,9 @@ export const getResponsesMockDefinition = ({
transformer?: (value: unknown, definition: string) => string;
context: ContextSpecs;
mockOptions?: GlobalMockOptions;
splitMockImplementations: string[];
}) => {
// console.log(responses);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like I forgot to turn this off.

return responses.reduce(
(
acc,
Expand Down Expand Up @@ -189,6 +192,7 @@ export const getResponsesMockDefinition = ({
}
: context,
existingReferencedProperties: [],
splitMockImplementations,
allowOverride: true,
});

Expand Down Expand Up @@ -218,6 +222,7 @@ export const getMockDefinition = ({
transformer,
context,
mockOptions,
splitMockImplementations,
}: {
operationId: string;
tags: string[];
Expand All @@ -228,6 +233,7 @@ export const getMockDefinition = ({
transformer?: (value: unknown, definition: string) => string;
context: ContextSpecs;
mockOptions?: GlobalMockOptions;
splitMockImplementations: string[];
}) => {
const mockOptionsWithoutFunc = getMockWithoutFunc(
context.specs[context.specKey],
Expand All @@ -244,6 +250,7 @@ export const getMockDefinition = ({
transformer,
context,
mockOptions,
splitMockImplementations,
});

return {
Expand Down