Skip to content

Commit

Permalink
Merge pull request #146 from mankdev/nea-support
Browse files Browse the repository at this point in the history
Support nonEmptyArray for arrays with minItems > 0 for OpenAPI 3.0
  • Loading branch information
kokovtsev authored Jul 13, 2021
2 parents 64b2b83 + 5038b65 commit 77c4361
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 19 deletions.
3 changes: 2 additions & 1 deletion src/language/typescript/2.0/serializers/items-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import { Ref } from '../../../../utils/ref';
import { pipe } from 'fp-ts/lib/pipeable';
import { either } from 'fp-ts';
import { Either, right } from 'fp-ts/lib/Either';
import { none } from 'fp-ts/lib/Option';

export const serializeItemsObject = (from: Ref, itemsObject: ItemsObject): Either<Error, SerializedType> => {
switch (itemsObject.type) {
case 'array': {
return pipe(serializeItemsObject(from, itemsObject.items), either.map(getSerializedArrayType()));
return pipe(serializeItemsObject(from, itemsObject.items), either.map(getSerializedArrayType(none)));
}
case 'string': {
return getSerializedStringType(from, itemsObject.format);
Expand Down
3 changes: 2 additions & 1 deletion src/language/typescript/2.0/serializers/parameter-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { fromSerializedType, SerializedParameter } from '../../common/data/seria
import { pipe } from 'fp-ts/lib/pipeable';
import { either, option } from 'fp-ts';
import { constFalse } from 'fp-ts/lib/function';
import { none } from 'fp-ts/lib/Option';

export const serializeParameterObject = (
from: Ref,
Expand Down Expand Up @@ -45,7 +46,7 @@ export const serializeParameterObject = (
case 'array': {
return pipe(
serializeItemsObject(from, parameterObject.items),
either.map(getSerializedArrayType()),
either.map(getSerializedArrayType(none)),
either.map(toSerializedParameter),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,21 @@ describe('SchemaObject', () => {
format: constant(none),
deprecated: constant(none),
nullable: constant(none),
minItems: constant(none),
maxItems: constant(none),
}),
format: constant(none),
deprecated: constant(none),
nullable: constant(none),
minItems: constant(none),
maxItems: constant(none),
});
assert(
property($refArbitrary, schema, string(), (from, schema, name) => {
const expected = pipe(
schema.items,
serializeSchemaObject(from, name),
either.map(getSerializedArrayType(name)),
either.map(getSerializedArrayType(none, name)),
);
const serialized = pipe(schema, serializeSchemaObject(from, name));
expect(serialized).toEqual(expected);
Expand All @@ -98,7 +102,11 @@ describe('SchemaObject', () => {
$ref: $refArbitrary.$ref,
},
});
const expected = pipe($refArbitrary, getSerializedRefType(from), getSerializedArrayType(name));
const expected = pipe(
$refArbitrary,
getSerializedRefType(from),
getSerializedArrayType(none, name),
);
expect(pipe(schema, reportIfFailed, either.chain(serializeSchemaObject(from, name)))).toEqual(
right(expected),
);
Expand All @@ -125,7 +133,7 @@ describe('SchemaObject', () => {
const expected = pipe(
ref,
getSerializedRefType(ref),
getSerializedArrayType(undefined),
getSerializedArrayType(none, undefined),
getSerializedOptionPropertyType('children', true),
getSerializedObjectType(undefined),
getSerializedRecursiveType(ref, true),
Expand Down
7 changes: 4 additions & 3 deletions src/language/typescript/3.0/serializers/schema-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,14 @@ const serializeSchemaObjectWithRecursion = (from: Ref, shouldTrackRecursion: boo
const serialized = ReferenceObjectCodec.is(items)
? pipe(
fromString(items.$ref),
mapLeft(() => new Error(`Unable to serialize SchemaObjeft array items ref "${items.$ref}"`)),
mapLeft(() => new Error(`Unable to serialize SchemaObject array items ref "${items.$ref}"`)),
either.map(getSerializedRefType(from)),
)
: pipe(items, serializeSchemaObjectWithRecursion(from, false, undefined));

return pipe(
serialized,
either.map(getSerializedArrayType(name)),
either.map(getSerializedArrayType(schemaObject.minItems, name)),
either.map(getSerializedNullableType(isNullable)),
);
}
Expand All @@ -109,7 +110,7 @@ const serializeSchemaObjectWithRecursion = (from: Ref, shouldTrackRecursion: boo
mapLeft(
() =>
new Error(
`Unablew to serialize SchemaObject additionalProperties ref "${additionalProperties.$ref}"`,
`Unable to serialize SchemaObject additionalProperties ref "${additionalProperties.$ref}"`,
),
),
either.map(getSerializedRefType(from)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { ReferenceObject, ReferenceObjectCodec } from '../../../../schema/asynca
import { traverseNEAEither } from '../../../../utils/either';
import { constFalse } from 'fp-ts/lib/function';
import { sequenceEither } from '@devexperts/utils/dist/adt/either.utils';
import { Option } from 'fp-ts/lib/Option';
import { none, Option } from 'fp-ts/lib/Option';

export const serializeSchemaObject = (
from: Ref,
Expand Down Expand Up @@ -192,5 +192,5 @@ const serializeArray = (
const serialized = ReferenceObjectCodec.is(items)
? pipe(fromString(items.$ref), either.map(getSerializedRefType(from)))
: serializeSchemaObjectWithRecursion(from, items, false);
return pipe(serialized, either.map(getSerializedArrayType(name)));
return pipe(serialized, either.map(getSerializedArrayType(none, name)));
};
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('SerializedType', () => {
it('getSerializedArrayType', () => {
assert(
property(serializedTypeArbitrary, name, (s, name) => {
expect(pipe(s, getSerializedArrayType(name))).toEqual(
expect(pipe(s, getSerializedArrayType(none, name))).toEqual(
serializedType(
`Array<${s.type}>`,
`array(${s.io}${when(name !== undefined, `, '${name}'`)})`,
Expand All @@ -46,6 +46,24 @@ describe('SerializedType', () => {
}),
);
});
it('getSerializedArrayType with minItems not 0', () => {
assert(
property(serializedTypeArbitrary, name, (s, name) => {
expect(pipe(s, getSerializedArrayType(some(1), name))).toEqual(
serializedType(
`NonEmptyArray<${s.type}>`,
`nonEmptyArray(${s.io}${when(name !== undefined, `, '${name}'`)})`,
[
...s.dependencies,
serializedDependency('nonEmptyArray', 'io-ts-types/lib/nonEmptyArray'),
serializedDependency('NonEmptyArray', 'fp-ts/lib/NonEmptyArray'),
],
s.refs,
),
);
}),
);
});
it('getSerializedPropertyType', () => {
assert(
property(string(), serializedTypeArbitrary, boolean(), (name, s, isRequired) => {
Expand Down
32 changes: 25 additions & 7 deletions src/language/typescript/common/data/serialized-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { head, NonEmptyArray } from 'fp-ts/lib/NonEmptyArray';
import { JSONPrimitive } from '../../../../utils/io-ts';
import { pipe } from 'fp-ts/lib/pipeable';
import { nonEmptyArray, option } from 'fp-ts';
import { none, Option, some } from 'fp-ts/lib/Option';
import { none, Option, some, exists } from 'fp-ts/lib/Option';
import { utilsRef } from '../bundled/utils';
import { Either } from 'fp-ts/lib/Either';
import { combineEither } from '@devexperts/utils/dist/adt/either.utils';
Expand Down Expand Up @@ -123,12 +123,30 @@ export const SERIALIZED_NULL_TYPE = serializedType('null', 'nullType', [serializ
export const getSerializedNullableType = (isNullable: boolean) => (type: SerializedType): SerializedType =>
isNullable ? getSerializedUnionType([type, SERIALIZED_NULL_TYPE]) : type;

export const getSerializedArrayType = (name?: string) => (serialized: SerializedType): SerializedType =>
serializedType(
`Array<${serialized.type}>`,
`array(${serialized.io}${when(name !== undefined, `, '${name}'`)})`,
[...serialized.dependencies, serializedDependency('array', 'io-ts')],
serialized.refs,
export const getSerializedArrayType = (minItems: Option<number>, name?: string) => (
serialized: SerializedType,
): SerializedType =>
pipe(
minItems,
exists(minItems => minItems > 0),
isNonEmpty =>
isNonEmpty
? serializedType(
`NonEmptyArray<${serialized.type}>`,
`nonEmptyArray(${serialized.io}${when(name !== undefined, `, '${name}'`)})`,
[
...serialized.dependencies,
serializedDependency('nonEmptyArray', 'io-ts-types/lib/nonEmptyArray'),
serializedDependency('NonEmptyArray', 'fp-ts/lib/NonEmptyArray'),
],
serialized.refs,
)
: serializedType(
`Array<${serialized.type}>`,
`array(${serialized.io}${when(name !== undefined, `, '${name}'`)})`,
[...serialized.dependencies, serializedDependency('array', 'io-ts')],
serialized.refs,
),
);

export const getSerializedRefType = (from: Ref) => (to: Ref): SerializedType => {
Expand Down
6 changes: 5 additions & 1 deletion src/schema/3.0/schema-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@ import { array, boolean, intersection, literal, record, recursion, string, type,
import { ReferenceObject, ReferenceObjectCodec } from './reference-object';
import { Option } from 'fp-ts/lib/Option';
import { optionFromNullable } from 'io-ts-types/lib/optionFromNullable';
import { Codec, JSONPrimitive, JSONPrimitiveCodec } from '../../utils/io-ts';
import { Codec, JSONPrimitive, JSONPrimitiveCodec, numberOption } from '../../utils/io-ts';
import { NonEmptyArray } from 'fp-ts/lib/NonEmptyArray';
import { nonEmptyArray } from 'io-ts-types/lib/nonEmptyArray';

export interface BaseSchemaObject {
readonly format: Option<string>;
readonly deprecated: Option<boolean>;
readonly nullable: Option<boolean>;
readonly maxItems: Option<number>;
readonly minItems: Option<number>;
}

const BaseSchemaObjectCodec: Codec<BaseSchemaObject> = type({
format: optionFromNullable(string),
deprecated: optionFromNullable(boolean),
nullable: optionFromNullable(boolean),
maxItems: numberOption,
minItems: numberOption,
});

export interface EnumSchemaObject extends BaseSchemaObject {
Expand Down
25 changes: 25 additions & 0 deletions test/specs/3.0/demo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,31 @@ components:
properties:
self:
$ref: '#/components/schemas/37sds afd,asd.324sfa as2_+='
TestNonEmpty:
type: array
minItems: 1
items:
type: object
properties:
foo:
type: string
bar:
type: number
TestArrayWithMinItems0:
type: array
minItems: 0
items:
type: object
properties:
foo:
type: string
bar:
type: number
TestNonEmptyRef:
type: array
minItems: 1
items:
$ref: "#/components/schemas/TestAllOf"
TestNullable:
type: array
nullable: true
Expand Down

0 comments on commit 77c4361

Please sign in to comment.