Skip to content

Commit

Permalink
fix(objectschema)!: omit id from schema returned by without + only
Browse files Browse the repository at this point in the history
the methods without and only return new schema objects with the same id
that was found on the original. In terms of schema reuse, this creates
two different schemas with the same id which is technically invalid.
Additionally, it was not possible to set the id property on an object
schema that had properties defined. This problem originates from the
setattribute function which will always set the attribute on the
schema properties if they are defined. In the case of the object schema,
this is almost always the case. This changes the id function on the
object schema to always generate a new schema with the id set on the
object schema rather than its properties

BREAKING CHANGE: ObjectSchema.id() will always set the id on the root object
BREAKING CHANGE: ObjectSchema.without() will omit id from the return schema
BREAKING CHANGE: ObjectSchema.only() will omit id from the return schema
  • Loading branch information
esatterwhite committed Oct 3, 2022
1 parent f62a218 commit 16a1bd4
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v12.14.0
v14.19.0
33 changes: 29 additions & 4 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,12 @@ it can be handy to arbitrary modify the schema injecting a fragment</p>
<dt><a href="#ObjectSchema">ObjectSchema([options])</a> ⇒ <code><a href="#StringSchema">StringSchema</a></code></dt>
<dd><p>Represents a ObjectSchema.</p>
</dd>
<dt><a href="#id">id(id)</a></dt>
<dd><p>It defines a URI for the schema, and the base URI that other URI references within the schema are resolved against.
Calling <code>id</code> on an ObjectSchema will alway set the id on the root of the object rather than in its &quot;properties&quot;, which
differs from other schema types.</p>
<p><a href="https://tools.ietf.org/html/draft-handrews-json-schema-01#section-8.2">reference</a></p>
</dd>
<dt><a href="#additionalProperties">additionalProperties(value)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>This keyword determines how child instances validate for objects, and does not directly validate the immediate instance itself.
Validation with &quot;additionalProperties&quot; applies only to the child values of instance names that do not match any names in &quot;properties&quot;,
Expand Down Expand Up @@ -276,10 +282,12 @@ Note the property name that the schema is testing will always be a string.</p>
<p><a href="https://tools.ietf.org/id/draft-handrews-json-schema-validation-01.html#rfc.section.6.5.4">reference</a></p>
</dd>
<dt><a href="#only">only(properties)</a> ⇒ <code><a href="#ObjectSchema">ObjectSchema</a></code></dt>
<dd><p>Returns an object schema with only a subset of keys provided</p>
<dd><p>Returns an object schema with only a subset of keys provided. If called on an ObjectSchema with an
<code>$id</code>, it will be removed and the return value will be considered a new schema.</p>
</dd>
<dt><a href="#without">without(properties)</a> ⇒ <code><a href="#ObjectSchema">ObjectSchema</a></code></dt>
<dd><p>Returns an object schema without a subset of keys provided</p>
<dd><p>Returns an object schema without a subset of keys provided. If called on an ObjectSchema with an
<code>$id</code>, it will be removed and the return value will be considered a new schema.</p>
</dd>
<dt><a href="#definition">definition(name, props)</a> ⇒ <code>FluentSchema</code></dt>
<dd><p>The &quot;definitions&quot; keywords provides a standardized location for schema authors to inline re-usable JSON Schemas into a more general schema.
Expand Down Expand Up @@ -951,6 +959,21 @@ Represents a ObjectSchema.
| [options.schema] | [<code>StringSchema</code>](#StringSchema) | | Default schema |
| [options.generateIds] | [<code>boolean</code>](#boolean) | <code>false</code> | generate the id automatically e.g. #properties.foo |

<a name="id"></a>

## id(id)
It defines a URI for the schema, and the base URI that other URI references within the schema are resolved against.
Calling `id` on an ObjectSchema will alway set the id on the root of the object rather than in its "properties", which
differs from other schema types.

[reference](https://tools.ietf.org/html/draft-handrews-json-schema-01#section-8.2)

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| id | [<code>string</code>](#string) | an #id |

<a name="additionalProperties"></a>

## additionalProperties(value) ⇒ <code>FluentSchema</code>
Expand Down Expand Up @@ -1086,7 +1109,8 @@ The value of "properties" MUST be an object. Each value of this object MUST be a
<a name="only"></a>

## only(properties) ⇒ [<code>ObjectSchema</code>](#ObjectSchema)
Returns an object schema with only a subset of keys provided
Returns an object schema with only a subset of keys provided. If called on an ObjectSchema with an
`$id`, it will be removed and the return value will be considered a new schema.

**Kind**: global function

Expand All @@ -1097,7 +1121,8 @@ Returns an object schema with only a subset of keys provided
<a name="without"></a>

## without(properties) ⇒ [<code>ObjectSchema</code>](#ObjectSchema)
Returns an object schema without a subset of keys provided
Returns an object schema without a subset of keys provided. If called on an ObjectSchema with an
`$id`, it will be removed and the return value will be considered a new schema.

**Kind**: global function

Expand Down
25 changes: 21 additions & 4 deletions src/ObjectSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,21 @@ const ObjectSchema = ({ schema = initialState, ...options } = {}) => {
return {
...BaseSchema({ ...options, schema }),

/**
* It defines a URI for the schema, and the base URI that other URI references within the schema are resolved against.
* Calling `id` on an ObjectSchema will alway set the id on the root of the object rather than in its "properties", which
* differs from other schema types.
*
* {@link https://tools.ietf.org/html/draft-handrews-json-schema-01#section-8.2|reference}
* @param {string} id - an #id
**/
id: id => {
if (!id)
throw new FluentSchemaError(
`id should not be an empty fragment <#> or an empty string <> (e.g. #myId)`
)
return options.factory({ schema: { ...schema, $id: id }, ...options })
},
/**
* This keyword determines how child instances validate for objects, and does not directly validate the immediate instance itself.
* Validation with "additionalProperties" applies only to the child values of instance names that do not match any names in "properties",
Expand Down Expand Up @@ -335,15 +350,16 @@ const ObjectSchema = ({ schema = initialState, ...options } = {}) => {
},

/**
* Returns an object schema with only a subset of keys provided
* Returns an object schema with only a subset of keys provided. If called on an ObjectSchema with an
* `$id`, it will be removed and the return value will be considered a new schema.
*
* @param properties a list of properties you want to keep
* @returns {ObjectSchema}
*/
only: properties => {
return ObjectSchema({
schema: {
...schema,
...omit(schema, ['$id', 'properties']),
properties: schema.properties.filter(({ name }) => properties.includes(name)),
required: schema.required.filter(p => properties.includes(p))
},
Expand All @@ -352,15 +368,16 @@ const ObjectSchema = ({ schema = initialState, ...options } = {}) => {
},

/**
* Returns an object schema without a subset of keys provided
* Returns an object schema without a subset of keys provided. If called on an ObjectSchema with an
* `$id`, it will be removed and the return value will be considered a new schema.
*
* @param properties a list of properties you dont want to keep
* @returns {ObjectSchema}
*/
without: properties => {
return ObjectSchema({
schema: {
...schema,
...omit(schema, ['$id', 'properties']),
properties: schema.properties.filter(({ name }) => !properties.includes(name)),
required: schema.required.filter(p => !properties.includes(p))
},
Expand Down
20 changes: 13 additions & 7 deletions src/ObjectSchema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ describe('ObjectSchema', () => {
type: 'object'
})
})
it('invalid', () => {
expect(() => {
ObjectSchema().id('')
}).toThrowError(
new S.FluentSchemaError(
'id should not be an empty fragment <#> or an empty string <> (e.g. #myId)'
)
)
})
})
})

Expand Down Expand Up @@ -213,14 +222,15 @@ describe('ObjectSchema', () => {
describe('id', () => {
it('valid', () => {
const id = 'myId'
const prop = 'prop'
expect(
ObjectSchema()
.prop('prop')
.id(id)
.valueOf().properties[prop]
.valueOf()
).toEqual({
$id: id
$id: id,
properties: {'prop': {}},
type: 'object'
})
})

Expand Down Expand Up @@ -915,7 +925,6 @@ describe('ObjectSchema', () => {

expect(only.valueOf()).toEqual({
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'base',
title: 'base',
properties: {
foo: {
Expand All @@ -939,7 +948,6 @@ describe('ObjectSchema', () => {

expect(only.valueOf()).toEqual({
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'base',
title: 'base',
properties: {
foo: {
Expand Down Expand Up @@ -974,7 +982,6 @@ describe('ObjectSchema', () => {

expect(without.valueOf()).toEqual({
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'base',
title: 'base',
properties: {
bar: {
Expand All @@ -1001,7 +1008,6 @@ describe('ObjectSchema', () => {

expect(without.valueOf()).toEqual({
$schema: 'http://json-schema.org/draft-07/schema#',
$id: 'base',
title: 'base',
properties: {
baz: {
Expand Down

0 comments on commit 16a1bd4

Please sign in to comment.