Skip to content

Commit

Permalink
fix: combiners inside of arrays (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
P0lip authored Sep 9, 2019
1 parent 80612b3 commit f9e11b6
Show file tree
Hide file tree
Showing 14 changed files with 335 additions and 45 deletions.
28 changes: 28 additions & 0 deletions src/__fixtures__/array-of-allofs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"title": "Test",
"type": "object",
"properties": {
"array-all-objects": {
"type": "array",
"items": {
"allOf": [
{
"properties": {
"foo": {
"type": "string"
}
}
},
{
"properties": {
"bar": {
"type": "string"
}
}
}
],
"type": "object"
}
}
}
}
22 changes: 10 additions & 12 deletions src/components/JsonSchemaViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import * as React from 'react';
import SchemaWorker, { WebWorker } from 'web-worker:../workers/schema.ts';

import { JSONSchema4 } from 'json-schema';
import { GoToRefHandler, RowRenderer, SchemaTreeListNode } from '../types';
import { isCombiner } from '../utils/isCombiner';
import { GoToRefHandler, IArrayNode, IBaseNode, RowRenderer, SchemaKind, SchemaTreeListNode } from '../types';
import { getArraySubtype } from '../utils/getArraySubtype';
import { isSchemaViewerEmpty } from '../utils/isSchemaViewerEmpty';
import { isCombinerNode } from '../utils/nodes';
import { renderSchema } from '../utils/renderSchema';
import { ComputeSchemaMessageData, isRenderedSchemaMessage } from '../workers/messages';
import { SchemaTree } from './SchemaTree';
Expand Down Expand Up @@ -111,17 +112,14 @@ export class JsonSchemaViewerComponent extends React.PureComponent<IJsonSchemaVi
if (i === 0) break;
i--;

if (
!hasAllOf &&
this.props.mergeAllOf !== false &&
node.metadata &&
isCombiner(node.metadata) &&
node.metadata.combiner === 'allOf'
) {
hasAllOf = true;
}

nodes.push(node);

if (hasAllOf || this.props.mergeAllOf === false || !node.metadata) continue;

hasAllOf =
((node.metadata as IBaseNode).type === SchemaKind.Array &&
getArraySubtype(node.metadata as IArrayNode) === 'allOf') ||
(node.metadata && isCombinerNode(node.metadata) && node.metadata.combiner === 'allOf');
}

needsFullRendering = hasAllOf || this.props.maxRows <= nodes.length;
Expand Down
5 changes: 2 additions & 3 deletions src/components/shared/Property.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { size as _size } from 'lodash-es';
import * as React from 'react';
import { GoToRefHandler, IArrayNode, IObjectNode, SchemaKind, SchemaNodeWithMeta } from '../../types';
import { inferType } from '../../utils/inferType';
import { isCombiner } from '../../utils/isCombiner';
import { isRef } from '../../utils/isRef';
import { isCombinerNode, isRefNode } from '../../utils/nodes';
import { Types } from './Types';

export interface IProperty {
Expand All @@ -12,7 +11,7 @@ export interface IProperty {
}

export const Property: React.FunctionComponent<IProperty> = ({ node, onGoToRef }) => {
const type = isRef(node) ? '$ref' : isCombiner(node) ? node.combiner : node.type;
const type = isRefNode(node) ? '$ref' : isCombinerNode(node) ? node.combiner : node.type;
const subtype =
type === SchemaKind.Array && (node as IArrayNode).items !== undefined
? inferType((node as IArrayNode).items!)
Expand Down
229 changes: 229 additions & 0 deletions src/utils/__tests__/__snapshots__/renderSchema.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,234 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renderSchema util should match array-of-allofs.json 1`] = `
Array [
Object {
"canHaveChildren": true,
"id": "random-id",
"level": 0,
"metadata": Object {
"additionalProperties": undefined,
"annotations": Object {
"title": "Test",
},
"enum": undefined,
"id": "random-id",
"path": Array [],
"patternProperties": undefined,
"properties": Object {
"array-all-objects": Object {
"items": Object {
"allOf": Array [
Object {
"properties": Object {
"foo": Object {
"type": "string",
},
},
"type": "object",
},
Object {
"properties": Object {
"bar": Object {
"type": "string",
},
},
"type": "object",
},
],
"type": "object",
},
"type": "array",
},
},
"type": "object",
"validations": Object {},
},
"name": "",
},
Object {
"canHaveChildren": true,
"id": "random-id",
"level": 1,
"metadata": Object {
"additionalItems": undefined,
"annotations": Object {},
"enum": undefined,
"id": "random-id",
"items": Object {
"allOf": Array [
Object {
"properties": Object {
"foo": Object {
"type": "string",
},
},
"type": "object",
},
Object {
"properties": Object {
"bar": Object {
"type": "string",
},
},
"type": "object",
},
],
"type": "object",
},
"name": "array-all-objects",
"path": Array [
"properties",
"array-all-objects",
],
"required": false,
"subtype": "allOf",
"type": "array",
"validations": Object {},
},
"name": "",
},
Object {
"canHaveChildren": true,
"id": "random-id",
"level": 2,
"metadata": Object {
"annotations": Object {},
"combiner": "allOf",
"id": "random-id",
"name": "array-all-objects",
"path": Array [
"properties",
"array-all-objects",
"items",
],
"properties": Array [
Object {
"properties": Object {
"foo": Object {
"type": "string",
},
},
"type": "object",
},
Object {
"properties": Object {
"bar": Object {
"type": "string",
},
},
"type": "object",
},
],
"required": false,
"type": "object",
},
"name": "",
},
Object {
"canHaveChildren": true,
"id": "random-id",
"level": 3,
"metadata": Object {
"additionalProperties": undefined,
"annotations": Object {},
"enum": undefined,
"id": "random-id",
"path": Array [
"properties",
"array-all-objects",
"items",
"allOf",
0,
],
"patternProperties": undefined,
"properties": Object {
"foo": Object {
"type": "string",
},
},
"type": "object",
"validations": Object {},
},
"name": "",
},
Object {
"id": "random-id",
"level": 4,
"metadata": Object {
"annotations": Object {},
"enum": undefined,
"id": "random-id",
"name": "foo",
"path": Array [
"properties",
"array-all-objects",
"items",
"allOf",
0,
"properties",
"foo",
],
"required": false,
"type": "string",
"validations": Object {},
},
"name": "",
},
Object {
"canHaveChildren": true,
"id": "random-id",
"level": 3,
"metadata": Object {
"additionalProperties": undefined,
"annotations": Object {},
"divider": "and",
"enum": undefined,
"id": "random-id",
"path": Array [
"properties",
"array-all-objects",
"items",
"allOf",
1,
],
"patternProperties": undefined,
"properties": Object {
"bar": Object {
"type": "string",
},
},
"type": "object",
"validations": Object {},
},
"name": "",
},
Object {
"id": "random-id",
"level": 4,
"metadata": Object {
"annotations": Object {},
"enum": undefined,
"id": "random-id",
"name": "bar",
"path": Array [
"properties",
"array-all-objects",
"items",
"allOf",
1,
"properties",
"bar",
],
"required": false,
"type": "string",
"validations": Object {},
},
"name": "",
},
]
`;

exports[`renderSchema util should match array-of-objects.json 1`] = `
Array [
Object {
Expand Down
10 changes: 5 additions & 5 deletions src/utils/__tests__/isCombiner.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { isCombiner } from '../isCombiner';
import { isCombinerNode } from '../nodes';

describe('isCombiner function', () => {
describe('isCombinerNode function', () => {
test('should return false if object without combiner is given', () => {
expect(isCombiner({} as any)).toBe(false);
expect(isCombiner({ properties: [] } as any)).toBe(false);
expect(isCombinerNode({} as any)).toBe(false);
expect(isCombinerNode({ properties: [] } as any)).toBe(false);
});

test.each(['allOf', 'anyOf', 'oneOf'])('should return true if object with %s is given', combiner => {
expect(isCombiner({ combiner } as any)).toBe(true);
expect(isCombinerNode({ combiner } as any)).toBe(true);
});
});
1 change: 1 addition & 0 deletions src/utils/__tests__/renderSchema.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('renderSchema util', () => {
['combiner-schema.json', ''],
['array-of-objects.json', ''],
['array-of-refs.json', ''],
['array-of-allofs.json', ''],
['tickets.schema.json', ''],
])('should match %s', (schema, dereferenced) => {
expect(
Expand Down
21 changes: 21 additions & 0 deletions src/utils/getArraySubtype.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { JSONSchema4TypeName } from 'json-schema';
import { IArrayNode, JSONSchema4CombinerName } from '../types';
import { getCombiner } from './getCombiner';
import { inferType } from './inferType';

export function getArraySubtype(
node: IArrayNode,
): JSONSchema4TypeName | JSONSchema4TypeName[] | JSONSchema4CombinerName | string | undefined {
if (!node.items || Array.isArray(node.items)) return;
if ('$ref' in node.items) {
return `$ref( ${node.items.$ref} )`;
}

const combiner = getCombiner(node.items);

if (combiner !== undefined) {
return combiner;
}

return inferType(node.items);
}
8 changes: 8 additions & 0 deletions src/utils/getCombiner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { JSONSchema4 } from 'json-schema';
import { JSONSchema4CombinerName } from '../types';

export const getCombiner = (node: JSONSchema4): JSONSchema4CombinerName | void => {
if ('allOf' in node) return 'allOf';
if ('anyOf' in node) return 'anyOf';
if ('oneOf' in node) return 'oneOf';
};
6 changes: 4 additions & 2 deletions src/utils/isCombiner.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ICombinerNode, SchemaNode } from '../types';
import { JSONSchema4CombinerName } from '../types';

export const isCombiner = (node: SchemaNode): node is ICombinerNode => 'combiner' in node;
const combinerTypes = ['allOf', 'oneOf', 'anyOf'];

export const isCombiner = (type: string): type is JSONSchema4CombinerName => combinerTypes.includes(type);
3 changes: 0 additions & 3 deletions src/utils/isRef.ts

This file was deleted.

Loading

0 comments on commit f9e11b6

Please sign in to comment.