Skip to content

Commit

Permalink
Adding support for $ref pointers to additionalProperties. (#1402)
Browse files Browse the repository at this point in the history
* Adding support for $ref pointers to additionalProperties.

* Adding a form test for `$ref` support on `additionalProperties`.

* Inlining a definitions retireval from the registry.

* Prioritizing an additionalProperties $ref over set property types.
  • Loading branch information
erunion authored and epicfaace committed Aug 22, 2019
1 parent 642ad56 commit d2112d3
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 6 deletions.
15 changes: 14 additions & 1 deletion src/components/fields/ObjectField.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,24 @@ class ObjectField extends Component {
}

handleAddClick = schema => () => {
const type = schema.additionalProperties.type;
let type = schema.additionalProperties.type;
const newFormData = { ...this.props.formData };

if (schema.additionalProperties.hasOwnProperty("$ref")) {
const { registry = getDefaultRegistry() } = this.props;
const refSchema = retrieveSchema(
{ $ref: schema.additionalProperties["$ref"] },
registry.definitions,
this.props.formData
);

type = refSchema.type;
}

newFormData[
this.getAvailableKey("newKey", newFormData)
] = this.getDefaultValue(type);

this.props.onChange(newFormData);
};

Expand Down
21 changes: 16 additions & 5 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -537,21 +537,32 @@ export function stubExistingAdditionalProperties(
...schema,
properties: { ...schema.properties },
};

Object.keys(formData).forEach(key => {
if (schema.properties.hasOwnProperty(key)) {
// No need to stub, our schema already has the property
return;
}
const additionalProperties = schema.additionalProperties.hasOwnProperty(
"type"
)
? { ...schema.additionalProperties }
: { type: guessType(formData[key]) };

let additionalProperties;
if (schema.additionalProperties.hasOwnProperty("$ref")) {
additionalProperties = retrieveSchema(
{ $ref: schema.additionalProperties["$ref"] },
definitions,
formData
);
} else if (schema.additionalProperties.hasOwnProperty("type")) {
additionalProperties = { ...schema.additionalProperties };
} else {
additionalProperties = { type: guessType(formData[key]) };
}

// The type of our new key should match the additionalProperties value;
schema.properties[key] = additionalProperties;
// Set our additional property flag so we know it was dynamically added
schema.properties[key][ADDITIONAL_PROPERTY_FLAG] = true;
});

return schema;
}

Expand Down
39 changes: 39 additions & 0 deletions test/Form_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,45 @@ describe("Form", () => {
expect(node.querySelector("input[type=text]").value).eql("hello");
});

it("should propagate referenced definition defaults in objects with additionalProperties", () => {
const schema = {
definitions: {
testdef: { type: "string" },
},
type: "object",
additionalProperties: {
$ref: "#/definitions/testdef",
},
};

const { node } = createFormComponent({ schema });

Simulate.click(node.querySelector(".btn-add"));

expect(node.querySelector("input[type=text]").value).eql("newKey");
});

it("should propagate referenced definition defaults in objects with additionalProperties that have a type present", () => {
// Though `additionalProperties` has a `type` present here, it also has a `$ref` so that
// referenced schema should override it.
const schema = {
definitions: {
testdef: { type: "number" },
},
type: "object",
additionalProperties: {
type: "string",
$ref: "#/definitions/testdef",
},
};

const { node } = createFormComponent({ schema });

Simulate.click(node.querySelector(".btn-add"));

expect(node.querySelector("input[type=number]").value).eql("0");
});

it("should recursively handle referenced definitions", () => {
const schema = {
$ref: "#/definitions/node",
Expand Down
59 changes: 59 additions & 0 deletions test/utils_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { expect } from "chai";
import React from "react";

import {
ADDITIONAL_PROPERTY_FLAG,
asNumber,
orderProperties,
dataURItoBlob,
Expand Down Expand Up @@ -1071,6 +1072,64 @@ describe("utils", () => {
expect(retrieveSchema(schema, definitions)).eql(address);
});

it("should 'resolve' and stub out a schema which contains an `additionalProperties` with a definition", () => {
const schema = {
type: "object",
additionalProperties: {
$ref: "#/definitions/components/schemas/address",
},
};

const address = {
type: "object",
properties: {
street_address: { type: "string" },
city: { type: "string" },
state: { type: "string" },
},
required: ["street_address", "city", "state"],
};

const definitions = { components: { schemas: { address } } };
const formData = { newKey: {} };

expect(retrieveSchema(schema, definitions, formData)).eql({
...schema,
properties: {
newKey: {
...address,
[ADDITIONAL_PROPERTY_FLAG]: true,
},
},
});
});

it.only("should 'resolve' and stub out a schema which contains an `additionalProperties` with a type and definition", () => {
const schema = {
type: "string",
additionalProperties: {
$ref: "#/definitions/number",
},
};

const number = {
type: "number",
};

const definitions = { number };
const formData = { newKey: {} };

expect(retrieveSchema(schema, definitions, formData)).eql({
...schema,
properties: {
newKey: {
...number,
[ADDITIONAL_PROPERTY_FLAG]: true,
},
},
});
});

it("should priorize local definitions over foreign ones", () => {
const schema = {
$ref: "#/definitions/address",
Expand Down

0 comments on commit d2112d3

Please sign in to comment.