Skip to content

Commit

Permalink
feat(msw): split polymorphic mocks into its own functions (#1365)
Browse files Browse the repository at this point in the history
* feat(msw): split polymorphic mocks into its own functions

* fix: formatting

* fix: remove extra comma

* fix: whitespace

* fix: split objects that combine with oneOf, rename functions

* fix: issue with duplicate functions in multi resposes

* fix: import with correct specKey

* fix: check rootKey

* fix: remove region

---------

Co-authored-by: Alfred Jonsson <alfred.jonsson@decerno.se>
  • Loading branch information
AffeJonsson and Alfred Jonsson committed May 19, 2024
1 parent 7026beb commit ddf7964
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 11 deletions.
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 @@ -133,6 +137,7 @@ export const getMockObject = ({
context,
imports,
existingReferencedProperties,
splitMockImplementations,
});

imports.push(...resolvedValue.imports);
Expand Down Expand Up @@ -192,6 +197,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 @@ -265,6 +268,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
? `${addedSplitMockImplementations.join('\n\n')}\n\n`
: '';

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 isHandlerOverridden = isReturnHttpResponse && !isTextPlain;
const infoParam = isHandlerOverridden ? 'info' : '';
Expand Down Expand Up @@ -163,6 +174,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 @@ -175,6 +188,7 @@ export const generateMSW = (
response.imports,
response.types.success,
response.contentTypes,
splitMockImplementations,
);

const mockImplementations = [baseDefinition.implementation.function];
Expand All @@ -200,6 +214,7 @@ export const generateMSW = (
response.imports,
[statusResponse],
[statusResponse.contentType],
splitMockImplementations,
);
mockImplementations.push(definition.implementation.function);
handlerImplementations.push(definition.implementation.handler);
Expand Down
6 changes: 6 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,6 +134,7 @@ export const getResponsesMockDefinition = ({
transformer?: (value: unknown, definition: string) => string;
context: ContextSpecs;
mockOptions?: GlobalMockOptions;
splitMockImplementations: string[];
}) => {
return responses.reduce(
(
Expand Down Expand Up @@ -189,6 +191,7 @@ export const getResponsesMockDefinition = ({
}
: context,
existingReferencedProperties: [],
splitMockImplementations,
allowOverride: true,
});

Expand Down Expand Up @@ -218,6 +221,7 @@ export const getMockDefinition = ({
transformer,
context,
mockOptions,
splitMockImplementations,
}: {
operationId: string;
tags: string[];
Expand All @@ -228,6 +232,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 +249,7 @@ export const getMockDefinition = ({
transformer,
context,
mockOptions,
splitMockImplementations,
});

return {
Expand Down

0 comments on commit ddf7964

Please sign in to comment.