Skip to content

Commit 6f1989b

Browse files
authored
feat: add alOff support
closes #11
1 parent 89e79dd commit 6f1989b

File tree

4 files changed

+78
-44
lines changed

4 files changed

+78
-44
lines changed

src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import { head, last } from 'fp-ts/lib/Array';
1111
import { TFileReader } from './fileReader';
1212
import { Right } from 'fp-ts/lib/Either';
1313
import { ValidationError } from 'io-ts';
14-
import { serialize } from './language/typescript';
1514
import * as del from 'del';
15+
import { serialize } from './language/typescript';
1616

1717
const log = console.log.bind(console, '[SWAGGER-CODEGEN-TS]:');
1818

src/language/typescript.ts

+53-33
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
TAllOfSchemaObject,
23
TBodyParameterObject,
34
TDefinitionsObject,
45
TNonArrayItemsObject,
@@ -8,6 +9,7 @@ import {
89
TPathParameterObject,
910
TPathsObject,
1011
TQueryParameterObject,
12+
TReferenceSchemaObject,
1113
TResponseObject,
1214
TResponsesObject,
1315
TSchemaObject,
@@ -32,7 +34,7 @@ import { intercalate } from 'fp-ts/lib/Foldable2v';
3234
import { collect, lookup } from 'fp-ts/lib/Record';
3335
import { identity } from 'fp-ts/lib/function';
3436

35-
const EMPTY_DEPENDENCIES: TDepdendency[] = [];
37+
const EMPTY_DEPENDENCIES: TDependency[] = [];
3638
const EMPTY_REFS: string[] = [];
3739
const SUCCESSFUL_CODES = ['200', 'default'];
3840

@@ -41,17 +43,17 @@ const concatIf = <A>(condition: boolean, as: A[], a: A[]): A[] => concatIfL(cond
4143
const unless = (condition: boolean, a: string): string => (condition ? '' : a);
4244
const when = (condition: boolean, a: string): string => (condition ? a : '');
4345

44-
type TDepdendency = {
46+
type TDependency = {
4547
name: string;
4648
path: string;
4749
};
4850
type TSerializedType = {
4951
type: string;
5052
io: string;
51-
dependencies: TDepdendency[];
53+
dependencies: TDependency[];
5254
refs: string[];
5355
};
54-
const serializedType = (type: string, io: string, dependencies: TDepdendency[], refs: string[]): TSerializedType => ({
56+
const serializedType = (type: string, io: string, dependencies: TDependency[], refs: string[]): TSerializedType => ({
5557
type,
5658
io,
5759
dependencies,
@@ -65,7 +67,7 @@ const serializedParameter = (
6567
type: string,
6668
io: string,
6769
isRequired: boolean,
68-
dependencies: TDepdendency[],
70+
dependencies: TDependency[],
6971
refs: string[],
7072
): TSerializedParameter => ({
7173
type,
@@ -82,7 +84,7 @@ const serializedPathParameter = (
8284
type: string,
8385
io: string,
8486
isRequired: boolean,
85-
dependencies: TDepdendency[],
87+
dependencies: TDependency[],
8688
refs: string[],
8789
): TSerializedPathParameter => ({
8890
name,
@@ -92,15 +94,15 @@ const serializedPathParameter = (
9294
dependencies,
9395
refs,
9496
});
95-
const dependency = (name: string, path: string): TDepdendency => ({
97+
const dependency = (name: string, path: string): TDependency => ({
9698
name,
9799
path,
98100
});
99101
const dependencyOption = dependency('Option', 'fp-ts/lib/Option');
100102
const dependencyCreateOptionFromNullable = dependency('createOptionFromNullable', 'io-ts-types');
101-
const OPTION_DEPENDENCIES: TDepdendency[] = [dependencyOption, dependencyCreateOptionFromNullable];
103+
const OPTION_DEPENDENCIES: TDependency[] = [dependencyOption, dependencyCreateOptionFromNullable];
102104

103-
const monoidDependencies = getArrayMonoid<TDepdendency>();
105+
const monoidDependencies = getArrayMonoid<TDependency>();
104106
const monoidRefs = getArrayMonoid<string>();
105107
const monoidSerializedType = getRecordMonoid<TSerializedType>({
106108
type: monoidString,
@@ -210,43 +212,61 @@ const serializePath = (url: string, item: TPathItemObject, rootName: string, cwd
210212
const get = item.get.map(operation => serializeOperationObject(url, 'GET', operation, rootName, cwd));
211213
const put = item.put.map(operation => serializeOperationObject(url, 'PUT', operation, rootName, cwd));
212214
const post = item.post.map(operation => serializeOperationObject(url, 'POST', operation, rootName, cwd));
213-
const remove = item['delete'].map(operation => serializeOperationObject(url, 'DELETE', operation, rootName, cwd));
215+
const remove = item.delete.map(operation => serializeOperationObject(url, 'DELETE', operation, rootName, cwd));
214216
const options = item.options.map(operation => serializeOperationObject(url, 'OPTIONS', operation, rootName, cwd));
215217
const head = item.head.map(operation => serializeOperationObject(url, 'HEAD', operation, rootName, cwd));
216218
const patch = item.patch.map(operation => serializeOperationObject(url, 'PATCH', operation, rootName, cwd));
217219
const operations = catOptions([get, put, post, remove, options, head, patch]);
218220
return foldSerialized(operations);
219221
};
220222

223+
const is$ref = (a: TReferenceSchemaObject | TAllOfSchemaObject): a is TReferenceSchemaObject =>
224+
Object.prototype.hasOwnProperty.bind(a)('$ref');
225+
221226
const serializeSchemaObject = (schema: TSchemaObject, rootName: string, cwd: string): TSerializedType => {
222227
switch (schema.type) {
223228
case undefined: {
224-
const $ref = schema.$ref;
225-
const parts = fromNullable($ref.match(/^((.+)\/(.+)\.(.+))?#\/(.+)\/(.+)$/));
226-
227-
const defBlock = parts.mapNullable(parts => parts[5]);
228-
const refFileName = parts.mapNullable(parts => parts[3]);
229-
const safeType = parts.mapNullable(parts => parts[6]);
230-
231-
if (safeType.isNone() || defBlock.isNone()) {
232-
throw new Error(`Invalid $ref: ${$ref}`);
229+
if (is$ref(schema)) {
230+
const $ref = schema.$ref;
231+
const parts = fromNullable($ref.match(/^((.+)\/(.+)\.(.+))?#\/(.+)\/(.+)$/));
232+
233+
const defBlock = parts.mapNullable(parts => parts[5]);
234+
const refFileName = parts.mapNullable(parts => parts[3]);
235+
const safeType = parts.mapNullable(parts => parts[6]);
236+
237+
if (safeType.isNone() || defBlock.isNone()) {
238+
throw new Error(`Invalid $ref: ${$ref}`);
239+
}
240+
241+
const type = safeType.value;
242+
243+
const io = getIOName(type);
244+
const isRecursive = rootName === type || rootName === io;
245+
const definitionFilePath = refFileName.isSome()
246+
? getRelativeOutRefPath(cwd, defBlock.value, refFileName.value, type)
247+
: getRelativeRefPath(cwd, defBlock.value, type);
248+
249+
return serializedType(
250+
type,
251+
io,
252+
isRecursive
253+
? EMPTY_DEPENDENCIES
254+
: [dependency(type, definitionFilePath), dependency(io, definitionFilePath)],
255+
[type],
256+
);
233257
}
234258

235-
const type = safeType.value;
236-
237-
const io = getIOName(type);
238-
const isRecursive = rootName === type || rootName === io;
239-
const definitionFilePath = refFileName.isSome()
240-
? getRelativeOutRefPath(cwd, defBlock.value, refFileName.value, type)
241-
: getRelativeRefPath(cwd, defBlock.value, type);
259+
const results = schema.allOf.map(item => serializeSchemaObject(item, rootName, cwd));
260+
const types = results.map(item => item.type);
261+
const ios = results.map(item => item.io);
262+
const dependencies = fold(monoidDependencies)(results.map(item => item.dependencies));
263+
const refs = fold(monoidRefs)(results.map(item => item.refs));
242264

243265
return serializedType(
244-
type,
245-
io,
246-
isRecursive
247-
? EMPTY_DEPENDENCIES
248-
: [dependency(type, definitionFilePath), dependency(io, definitionFilePath)],
249-
[type],
266+
intercalate(monoidString, array)(' & ', types),
267+
`intersection([${intercalate(monoidString, array)(', ', ios)}])`,
268+
[dependency('intersection', 'io-ts'), ...dependencies],
269+
refs,
250270
);
251271
}
252272
case 'string': {
@@ -603,7 +623,7 @@ const getIOName = (name: string): string => `${name}IO`;
603623
const getOperationName = (operation: TOperationObject, httpMethod: string) =>
604624
operation.operationId.getOrElse(httpMethod);
605625

606-
const serializeDependencies = (dependencies: TDepdendency[]): string =>
626+
const serializeDependencies = (dependencies: TDependency[]): string =>
607627
collect(groupBy(dependencies, dependency => dependency.path), (key, dependencies) => {
608628
const names = uniqString(dependencies.toArray().map(dependency => dependency.name));
609629
return `import { ${names.join(',')} } from '${dependencies.head.path}';`;

src/swagger.ts

+23-9
Original file line numberDiff line numberDiff line change
@@ -120,25 +120,24 @@ export const BooleanPropertySchemaObject: t.Tagged<'type', TBooleanPropertySchem
120120
type: t.literal('boolean'),
121121
});
122122

123+
export type TAllOfSchemaObject = TBaseSchemaObjectProps & {
124+
allOf: TSchemaObject[];
125+
description: Option<string>;
126+
type: undefined;
127+
};
123128
export type TReferenceSchemaObject = TReferenceObject &
124129
TBaseSchemaObjectProps & {
125130
type: undefined;
126131
};
127-
export const ReferenceSchemaObject = t.intersection([
128-
ReferenceObject,
129-
t.type({
130-
...BaseSchemaObjectProps,
131-
type: t.literal(undefined as any),
132-
}),
133-
]);
132+
export type TReferenceOrAllOfSchemeObject = TReferenceSchemaObject | TAllOfSchemaObject;
134133

135134
export type TArraySchemaObject = TBaseSchemaObjectProps & {
136135
type: 'array';
137136
items: TSchemaObject;
138137
};
139138

140139
export type TSchemaObject =
141-
| TReferenceSchemaObject
140+
| TReferenceOrAllOfSchemeObject
142141
| TObjectSchemaObject
143142
| TStringPropertySchemaObject
144143
| TNumberPropertySchemaObject
@@ -160,9 +159,24 @@ export const SchemaObject: t.Type<TSchemaObject, mixed> = t.recursion<TSchemaObj
160159
properties: createOptionFromNullable(t.dictionary(t.string, SchemaObject)),
161160
additionalProperties: createOptionFromNullable(SchemaObject),
162161
});
162+
const ReferenceOrAllOfSchemaObject: t.Tagged<'type', TReferenceOrAllOfSchemeObject, mixed> = t.union([
163+
t.intersection([
164+
ReferenceObject,
165+
t.type({
166+
...BaseSchemaObjectProps,
167+
type: t.literal(undefined as any),
168+
}),
169+
]),
170+
t.type({
171+
...BaseSchemaObjectProps,
172+
description: stringOption,
173+
type: t.literal(undefined as any),
174+
allOf: t.array(SchemaObject),
175+
}),
176+
]);
163177

164178
return t.taggedUnion('type', [
165-
ReferenceSchemaObject,
179+
ReferenceOrAllOfSchemaObject,
166180
ArraySchemaObject,
167181
ObjectSchemaObject,
168182
StringPropertySchemaObject,

test/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ generate({
1818
});
1919

2020
generate({
21-
pathsToSpec: [path.resolve(self, './specs/yaml/swagger.yml'), path.resolve(self, './specs/yaml/common.yml')],
21+
pathsToSpec: [path.resolve(self, './specs/yaml/common.yml'), path.resolve(self, './specs/yaml/swagger.yml')],
2222
out: path.resolve(self, './out/yaml'),
2323
serialize,
2424
fileReader: fromYaml,

0 commit comments

Comments
 (0)