Skip to content

Commit

Permalink
Merge branch 'master' of github.com:anymaniax/orval
Browse files Browse the repository at this point in the history
  • Loading branch information
anymaniax committed Apr 11, 2023
2 parents 03a058f + f5b1ec9 commit 6f9ad9c
Show file tree
Hide file tree
Showing 19 changed files with 349 additions and 5 deletions.
1 change: 1 addition & 0 deletions packages/core/src/getters/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const getArray = ({
isEnum: false,
type: 'array',
isRef: false,
hasReadonlyProps: resolvedObject.hasReadonlyProps,
};
} else {
throw new Error('All arrays must have an `items` key define');
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/getters/body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export const getBody = ({
const schemas = filteredBodyTypes.flatMap(({ schemas }) => schemas);

const definition = filteredBodyTypes.map(({ value }) => value).join(' | ');
const hasReadonlyProps = filteredBodyTypes.some((x) => x.hasReadonlyProps);
const nonReadonlyDefinition =
hasReadonlyProps && definition ? `NonReadonly<${definition}>` : definition;

const implementation =
generalJSTypesWithArray.includes(definition.toLowerCase()) ||
Expand All @@ -51,7 +54,7 @@ export const getBody = ({

return {
originalSchema: requestBody,
definition,
definition: nonReadonlyDefinition,
implementation,
imports,
schemas,
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/getters/combine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type CombinedData = {
isRef: boolean[];
isEnum: boolean[];
types: string[];
hasReadonlyProps: boolean;
};

type Separator = 'allOf' | 'anyOf' | 'oneOf';
Expand Down Expand Up @@ -92,6 +93,7 @@ export const combineSchemas = ({
acc.types.push(resolvedValue.type);
acc.isRef.push(resolvedValue.isRef);
acc.originalSchema.push(resolvedValue.originalSchema);
acc.hasReadonlyProps ||= resolvedValue.hasReadonlyProps;

return acc;
},
Expand All @@ -103,6 +105,7 @@ export const combineSchemas = ({
isRef: [],
types: [],
originalSchema: [],
hasReadonlyProps: false,
} as CombinedData,
);

Expand Down Expand Up @@ -133,6 +136,7 @@ export const combineSchemas = ({
isEnum: false,
type: 'object' as SchemaType,
isRef: false,
hasReadonlyProps: resolvedData.hasReadonlyProps,
};
}

Expand All @@ -147,6 +151,10 @@ export const combineSchemas = ({
isEnum: false,
type: 'object' as SchemaType,
isRef: false,
hasReadonlyProps:
resolvedData?.hasReadonlyProps ||
resolvedValue?.hasReadonlyProps ||
false,
};
};

Expand Down
8 changes: 7 additions & 1 deletion packages/core/src/getters/object.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ReferenceObject, SchemaObject } from 'openapi3-ts';
import { resolveObject, resolveValue } from '../resolvers';
import { resolveObject, resolveRef, resolveValue } from '../resolvers';
import { ContextSpecs, ScalarValue, SchemaType } from '../types';
import { isBoolean, isReference, jsDoc, pascal } from '../utils';
import { combineSchemas } from './combine';
Expand Down Expand Up @@ -31,6 +31,7 @@ export const getObject = ({
isEnum: false,
type: 'object',
isRef: true,
hasReadonlyProps: item.readOnly || false,
};
}

Expand Down Expand Up @@ -102,6 +103,7 @@ export const getObject = ({

const doc = jsDoc(schema as SchemaObject, true);

acc.hasReadonlyProps ||= isReadOnly || false;
acc.imports.push(...resolvedValue.imports);
acc.value += `\n ${doc ? `${doc} ` : ''}${
isReadOnly ? 'readonly ' : ''
Expand Down Expand Up @@ -137,6 +139,7 @@ export const getObject = ({
type: 'object' as SchemaType,
isRef: false,
schema: {},
hasReadonlyProps: false,
} as ScalarValue,
);
}
Expand All @@ -150,6 +153,7 @@ export const getObject = ({
isEnum: false,
type: 'object',
isRef: false,
hasReadonlyProps: item.readOnly || false,
};
}
const resolvedValue = resolveValue({
Expand All @@ -164,6 +168,7 @@ export const getObject = ({
isEnum: false,
type: 'object',
isRef: false,
hasReadonlyProps: resolvedValue.hasReadonlyProps,
};
}

Expand All @@ -175,5 +180,6 @@ export const getObject = ({
isEnum: false,
type: 'object',
isRef: false,
hasReadonlyProps: item.readOnly || false,
};
};
17 changes: 16 additions & 1 deletion packages/core/src/getters/res-req-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const getResReqTypes = (
type: 'unknown',
isEnum: false,
isRef: true,
hasReadonlyProps: false,
originalSchema: mediaType?.schema,
key,
contentType,
Expand Down Expand Up @@ -113,6 +114,7 @@ export const getResReqTypes = (
schemas: [],
type: 'unknown',
isEnum: false,
hasReadonlyProps: false,
formData,
formUrlEncoded,
isRef: true,
Expand Down Expand Up @@ -146,8 +148,19 @@ export const getResReqTypes = (
const isFormUrlEncoded =
formUrlEncodedContentTypes.includes(contentType);

const imports = [
...resolvedValue.imports,
...(resolvedValue.hasReadonlyProps
? [{ name: 'NonReadonly' }]
: []),
];

if ((!isFormData && !isFormUrlEncoded) || !propName) {
return { ...resolvedValue, contentType };
return {
...resolvedValue,
imports,
contentType,
};
}

const formData = isFormData
Expand All @@ -169,6 +182,7 @@ export const getResReqTypes = (

return {
...resolvedValue,
imports,
formData,
formUrlEncoded,
contentType,
Expand All @@ -190,6 +204,7 @@ export const getResReqTypes = (
isEnum: false,
key,
isRef: false,
hasReadonlyProps: false,
contentType: 'application/json',
},
] as ResReqTypesValue[];
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/getters/scalar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const getScalar = ({
schemas: [],
imports: [],
isRef: false,
hasReadonlyProps: item.readOnly || false,
};
}

Expand All @@ -54,6 +55,7 @@ export const getScalar = ({
schemas: [],
imports: [],
isRef: false,
hasReadonlyProps: item.readOnly || false,
};

case 'array': {
Expand Down Expand Up @@ -100,6 +102,7 @@ export const getScalar = ({
imports: [],
schemas: [],
isRef: false,
hasReadonlyProps: item.readOnly || false,
};
}

Expand All @@ -111,6 +114,7 @@ export const getScalar = ({
imports: [],
schemas: [],
isRef: false,
hasReadonlyProps: item.readOnly || false,
};

case 'object':
Expand All @@ -130,6 +134,7 @@ export const getScalar = ({
imports: [],
schemas: [],
isRef: false,
hasReadonlyProps: item.readOnly || false,
};
}

Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/resolvers/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const resolveObject = ({
type: 'object',
originalSchema: resolvedValue.originalSchema,
isRef: resolvedValue.isRef,
hasReadonlyProps: resolvedValue.hasReadonlyProps,
};
}

Expand All @@ -68,6 +69,7 @@ export const resolveObject = ({
type: 'enum',
originalSchema: resolvedValue.originalSchema,
isRef: resolvedValue.isRef,
hasReadonlyProps: resolvedValue.hasReadonlyProps,
};
}

Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/resolvers/value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getScalar } from '../getters';
import { ContextSpecs, ResolverValue, SchemaType } from '../types';
import { isReference } from '../utils';
import { resolveRef } from './ref';
import { resolveObject } from './object';

export const resolveValue = ({
schema,
Expand All @@ -19,6 +20,8 @@ export const resolveValue = ({
context,
);

const resolvedObject = resolveObject({ schema: schemaObject, context });

const { name, specKey, schemaName } = imports[0];

const importSpecKey =
Expand All @@ -32,6 +35,7 @@ export const resolveValue = ({
schemas: [],
isEnum: !!schemaObject?.enum,
originalSchema: schemaObject,
hasReadonlyProps: resolvedObject.hasReadonlyProps,
isRef: true,
};
}
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,7 @@ export const SchemaType = {
export type ScalarValue = {
value: string;
isEnum: boolean;
hasReadonlyProps: boolean;
type: SchemaType;
imports: GeneratorImport[];
schemas: GeneratorSchema[];
Expand All @@ -699,6 +700,7 @@ export type ResReqTypesValue = ScalarValue & {
formData?: string;
formUrlEncoded?: string;
isRef?: boolean;
hasReadonlyProps?: boolean;
key: string;
contentType: string;
originalSchema?: SchemaObject;
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/writers/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './schemas';
export * from './types';
export * from './single-mode';
export * from './split-mode';
export * from './split-tags-mode';
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/writers/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from 'fs-extra';
import { generateImports } from '../generators';
import { GeneratorSchema } from '../types';
import { camel, upath } from '../utils';
import { getOrvalGeneratedTypes } from './types';

const getSchema = ({
schema: { imports, model },
Expand Down Expand Up @@ -125,10 +126,12 @@ export const writeSchemas = async ({
.match(/export \* from(.*)('|")/g)
?.map((s) => s + ';') ?? []) as string[];

const fileContent = [...currentFileExports, ...importStatements]
const exports = [...currentFileExports, ...importStatements]
.sort()
.join('\n');

const fileContent = `${header}\n${exports}\n${getOrvalGeneratedTypes()}`;

await fs.writeFile(schemaFilePath, fileContent);
} catch (e) {
throw `Oups... 🍻. An Error occurred while writing schema index file ${schemaFilePath} => ${e}`;
Expand Down
27 changes: 27 additions & 0 deletions packages/core/src/writers/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export const getOrvalGeneratedTypes = () => `
// https://stackoverflow.com/questions/49579094/typescript-conditional-types-filter-out-readonly-properties-pick-only-requir/49579497#49579497
type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends <
T,
>() => T extends Y ? 1 : 2
? A
: B;
type WritableKeys<T> = {
[P in keyof T]-?: IfEquals<
{ [Q in P]: T[P] },
{ -readonly [Q in P]: T[P] },
P
>;
}[keyof T];
type UnionToIntersection<U> =
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never;
type DistributeReadOnlyOverUnions<T> = T extends any ? NonReadonly<T> : never;
type Writable<T> = Pick<T, WritableKeys<T>>;
export type NonReadonly<T> = [T] extends [UnionToIntersection<T>] ? {
[P in keyof Writable<T>]: T[P] extends object
? NonReadonly<NonNullable<T[P]>>
: T[P];
} : DistributeReadOnlyOverUnions<T>;
`;
25 changes: 25 additions & 0 deletions samples/react-query/basic/petstore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
put:
summary: Update a pet
operationId: updatePets
tags:
- pets
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
responses:
'200':
description: Created Pet
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/pets/{petId}:
get:
summary: Info for a specific pet
Expand Down Expand Up @@ -168,6 +192,7 @@ components:
properties:
petsRequested:
type: integer
readOnly: true
type:
type: string
enum:
Expand Down
Loading

0 comments on commit 6f9ad9c

Please sign in to comment.