Skip to content

Commit

Permalink
fix: fix recursive SchemaObject in typescript 2
Browse files Browse the repository at this point in the history
BREAKING CHANGE: SchemaObject codec was renamed to SchemaObjectCodec
  • Loading branch information
raveclassic committed Nov 5, 2019
1 parent e95c7fd commit 8ab9fc0
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 118 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { assert, property } from 'fast-check';
import { $refArbitrary } from '../../../../../utils/__tests__/ref.spec';
import {
getSerializedObjectType,
getSerializedPropertyType,
getSerializedRecursiveType,
getSerializedRefType,
} from '../../../common/data/serialized-type';
import { pipe } from 'fp-ts/lib/pipeable';
import { serializeSchemaObject } from '../schema-object';
import { SchemaObjectCodec } from '../../../../../schema/2.0/schema-object/schema-object';
import { right } from 'fp-ts/lib/Either';
import { either } from 'fp-ts';
import { reportIfFailed } from '../../../../../utils/io-ts';

describe('SchemaObject serializer', () => {
describe('recursive', () => {
it('level 1', () => {
assert(
property($refArbitrary, ref => {
const schema = SchemaObjectCodec.decode({
type: 'object',
required: ['recursive'],
properties: {
recursive: {
$ref: ref.$ref,
},
},
});
const expected = pipe(
ref,
getSerializedRefType(ref),
getSerializedPropertyType('recursive', true),
getSerializedObjectType(),
getSerializedRecursiveType(ref, true),
);
const serialized = pipe(
schema,
reportIfFailed,
either.chain(schema => serializeSchemaObject(ref, schema)),
);
expect(serialized).toEqual(right(expected));
}),
);
});
it('level 2', () => {
assert(
property($refArbitrary, ref => {
const schema = SchemaObjectCodec.decode({
type: 'object',
required: ['children'],
properties: {
children: {
type: 'object',
required: ['recursive'],
properties: {
recursive: {
$ref: ref.$ref,
},
},
},
},
});
const expected = pipe(
ref,
getSerializedRefType(ref),
getSerializedPropertyType('recursive', true),
getSerializedObjectType(),
getSerializedPropertyType('children', true),
getSerializedObjectType(),
getSerializedRecursiveType(ref, true),
);
const serialized = pipe(
schema,
reportIfFailed,
either.chain(schema => serializeSchemaObject(ref, schema)),
);
expect(serialized).toEqual(right(expected));
}),
);
});
});
});
30 changes: 18 additions & 12 deletions src/language/typescript/2.0/serializers/schema-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@ import { array, either, option, record } from 'fp-ts';
import { traverseArrayEither } from '../../../../utils/either';
import { ReferenceObject } from '../../../../schema/2.0/reference-object';

export const serializeSchemaObject = (from: Ref, schema: SchemaObject): Either<Error, SerializedType> => {
export const serializeSchemaObject = (from: Ref, schema: SchemaObject): Either<Error, SerializedType> =>
serializeSchemaObjectWithRecursion(from, schema, true);

const serializeSchemaObjectWithRecursion = (
from: Ref,
schema: SchemaObject,
shouldTrackRecursion: boolean,
): Either<Error, SerializedType> => {
// check non-typed schemas first
if (ReferenceObject.is(schema)) {
return pipe(
Expand Down Expand Up @@ -88,7 +95,7 @@ export const serializeSchemaObject = (from: Ref, schema: SchemaObject): Either<E
}
case 'array': {
return pipe(
serializeSchemaObject(from, schema.items),
serializeSchemaObjectWithRecursion(from, schema.items, false),
either.map(result =>
serializedType(
`Array<${result.type}>`,
Expand All @@ -102,7 +109,13 @@ export const serializeSchemaObject = (from: Ref, schema: SchemaObject): Either<E
case 'object': {
const additionalProperties = pipe(
schema.additionalProperties,
option.map(additionalProperties => serializeAdditionalProperties(from, additionalProperties)),
option.map(additionalProperties =>
pipe(
serializeSchemaObjectWithRecursion(from, additionalProperties, false),
either.map(getSerializedDictionaryType()),
either.map(getSerializedRecursiveType(from, shouldTrackRecursion)),
),
),
);
const properties = () =>
pipe(
Expand All @@ -117,14 +130,14 @@ export const serializeSchemaObject = (from: Ref, schema: SchemaObject): Either<E
option.getOrElse(constFalse),
);
return pipe(
serializeSchemaObject(from, value),
serializeSchemaObjectWithRecursion(from, value, false),
either.map(getSerializedPropertyType(name, isRequired)),
);
}),
sequenceEither,
either.map(s => intercalateSerializedTypes(serializedType(';', ',', [], []), s)),
either.map(getSerializedObjectType()),
either.map(getSerializedRecursiveType(from, true)),
either.map(getSerializedRecursiveType(from, shouldTrackRecursion)),
),
),
);
Expand All @@ -151,10 +164,3 @@ const serializeEnum = (enumValue: Array<string | number | boolean>): SerializedT
[],
);
};

const serializeAdditionalProperties = (from: Ref, properties: SchemaObject): Either<Error, SerializedType> =>
pipe(
serializeSchemaObject(from, properties),
either.map(getSerializedDictionaryType()),
either.map(getSerializedRecursiveType(from, true)),
);
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,14 @@ import {
getSerializedRecursiveType,
getSerializedRefType,
} from '../../../common/data/serialized-type';
import { Either, right } from 'fp-ts/lib/Either';
import { right } from 'fp-ts/lib/Either';
import { assert, constant, property, record, string } from 'fast-check';
import { $refArbitrary } from '../../../../../utils/__tests__/ref.spec';
import { pipe } from 'fp-ts/lib/pipeable';
import { either } from 'fp-ts';
import { SchemaObjectCodec } from '../../../../../schema/3.0/schema-object';
import { none } from 'fp-ts/lib/Option';

const chainEither = <A, EB, B>(f: (a: A) => Either<EB, B>) => <EA>(fa: Either<EA, A>): Either<EA | EB, B> =>
pipe(
fa,
either.chain<EA | EB, A, B>(f),
);
import { reportIfFailed } from '../../../../../utils/io-ts';

describe('SchemaObject', () => {
describe('serializeSchemaObject', () => {
Expand Down Expand Up @@ -67,107 +62,109 @@ describe('SchemaObject', () => {
expect(
pipe(
schema,
chainEither(serializeSchemaObject(from, name)),
reportIfFailed,
either.chain(serializeSchemaObject(from, name)),
),
).toEqual(right(expected));
}),
);
});
});
describe('recursive', () => {
describe('local', () => {
it('object with array of items of self type', () => {
assert(
property($refArbitrary, ref => {
const schema = SchemaObjectCodec.decode({
type: 'object',
required: ['children'],
properties: {
children: {
type: 'array',
items: {
$ref: ref.$ref, // references self
},
it('object with array of items of self type', () => {
assert(
property($refArbitrary, ref => {
const schema = SchemaObjectCodec.decode({
type: 'object',
required: ['children'],
properties: {
children: {
type: 'array',
items: {
$ref: ref.$ref, // references self
},
},
});
const expected = pipe(
ref,
getSerializedRefType(ref),
getSerializedArrayType(undefined),
getSerializedPropertyType('children', true),
getSerializedObjectType(undefined),
getSerializedRecursiveType(ref, true),
);
const serialized = pipe(
schema,
chainEither(serializeSchemaObject(ref)),
);
},
});
const expected = pipe(
ref,
getSerializedRefType(ref),
getSerializedArrayType(undefined),
getSerializedPropertyType('children', true),
getSerializedObjectType(undefined),
getSerializedRecursiveType(ref, true),
);
const serialized = pipe(
schema,
reportIfFailed,
either.chain(serializeSchemaObject(ref)),
);

expect(serialized).toEqual(right(expected));
}),
);
});
it('object with array of items of object type with one of properties of self type', () => {
assert(
property($refArbitrary, ref => {
const schema = SchemaObjectCodec.decode({
type: 'object',
required: ['children'],
properties: {
children: {
type: 'object',
properties: {
self: {
$ref: ref.$ref, // references self
},
expect(serialized).toEqual(right(expected));
}),
);
});
it('object with array of items of object type with one of properties of self type', () => {
assert(
property($refArbitrary, ref => {
const schema = SchemaObjectCodec.decode({
type: 'object',
required: ['children'],
properties: {
children: {
type: 'object',
properties: {
self: {
$ref: ref.$ref, // references self
},
required: ['self'],
},
required: ['self'],
},
});
const serialized = pipe(
schema,
chainEither(serializeSchemaObject(ref)),
);
const expected = pipe(
ref,
getSerializedRefType(ref),
getSerializedPropertyType('self', true),
getSerializedObjectType(undefined),
getSerializedPropertyType('children', true),
getSerializedObjectType(undefined),
getSerializedRecursiveType(ref, true),
);
expect(serialized).toEqual(right(expected));
}),
);
});
it('object with additionalProperties of self type', () => {
assert(
property($refArbitrary, ref => {
const schema = SchemaObjectCodec.decode({
type: 'object',
additionalProperties: {
$ref: ref.$ref, // references self
},
});
const serialized = pipe(
schema,
chainEither(serializeSchemaObject(ref)),
);
},
});
const serialized = pipe(
schema,
reportIfFailed,
either.chain(serializeSchemaObject(ref)),
);
const expected = pipe(
ref,
getSerializedRefType(ref),
getSerializedPropertyType('self', true),
getSerializedObjectType(undefined),
getSerializedPropertyType('children', true),
getSerializedObjectType(undefined),
getSerializedRecursiveType(ref, true),
);
expect(serialized).toEqual(right(expected));
}),
);
});
it('object with additionalProperties of self type', () => {
assert(
property($refArbitrary, ref => {
const schema = SchemaObjectCodec.decode({
type: 'object',
additionalProperties: {
$ref: ref.$ref, // references self
},
});
const serialized = pipe(
schema,
reportIfFailed,
either.chain(serializeSchemaObject(ref)),
);

const expected = pipe(
ref,
getSerializedRefType(ref),
getSerializedDictionaryType(undefined),
getSerializedRecursiveType(ref, true),
);
const expected = pipe(
ref,
getSerializedRefType(ref),
getSerializedDictionaryType(undefined),
getSerializedRecursiveType(ref, true),
);

expect(serialized).toEqual(right(expected));
}),
);
});
expect(serialized).toEqual(right(expected));
}),
);
});
});
});
Expand Down
4 changes: 2 additions & 2 deletions src/schema/2.0/definitions-object.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Dictionary } from '../../utils/types';
import { SchemaObject } from './schema-object/schema-object';
import { SchemaObject, SchemaObjectCodec } from './schema-object/schema-object';
import { dictionary } from '../../utils/io-ts';

export interface DefinitionsObject extends Dictionary<SchemaObject> {}

export const DefinitionsObject = dictionary(SchemaObject, 'DefinitionsObject');
export const DefinitionsObject = dictionary(SchemaObjectCodec, 'DefinitionsObject');
4 changes: 2 additions & 2 deletions src/schema/2.0/parameter-object.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { boolean, literal, string, type, union } from 'io-ts';
import { Codec } from '../../utils/io-ts';
import { SchemaObject } from './schema-object/schema-object';
import { SchemaObject, SchemaObjectCodec } from './schema-object/schema-object';
import { Option } from 'fp-ts/lib/Option';
import { ItemsObject, ItemsObjectCodec } from './items-object';
import { optionFromNullable } from 'io-ts-types/lib/optionFromNullable';
Expand Down Expand Up @@ -43,7 +43,7 @@ const BodyParameterObjectCodec: Codec<BodyParameterObject> = type(
{
...BaseParameterObjectProps,
in: literal('body'),
schema: SchemaObject,
schema: SchemaObjectCodec,
},
'BodyParameterObject',
);
Expand Down
Loading

0 comments on commit 8ab9fc0

Please sign in to comment.