Skip to content
This repository has been archived by the owner on Sep 29, 2023. It is now read-only.

Commit

Permalink
fix: can sanitize items in arrays that can be null, close #53
Browse files Browse the repository at this point in the history
  • Loading branch information
bahmutov committed May 11, 2018
1 parent 7abe296 commit 2c81a7d
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 53 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@ When asserting an object against a schema a custom error is thrown. It is an ins
* `schemaName` is the title of the schema, like `Person`
* `schemaVersion` the version like `1.0.0` of the schema violated, if known.

## Debugging

To see log messages from this module, run with `DEBUG=schema-tools`

## Testing

Uses [ava-ts](https://github.com/andywer/ava-ts#readme) to run Ava test runner directly against TypeScript test files. Use `npm t` to build and test everything in the `test` folder.
Expand Down
48 changes: 24 additions & 24 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 21 additions & 3 deletions src/sanitize.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { PlainObject, JsonSchema, SchemaCollection } from './objects'
import { assertSchema, getObjectSchema } from './api'
import debugApi from 'debug'
import { clone } from 'ramda'
import { assertSchema, getObjectSchema } from './api'
import { FormatDefaults } from './formats'
import { JsonSchema, PlainObject, SchemaCollection } from './objects'

const debug = debugApi('schema-tools')

export const isDynamicFormat = (formatDefaults: FormatDefaults | undefined) => (
format: string,
Expand All @@ -12,7 +15,10 @@ const isString = s => typeof s === 'string'
const canPropertyBeString = type =>
type === 'string' || (Array.isArray(type) && type.includes('string'))

const isArrayType = prop => prop.type === 'array' && prop.items
const canPropertyBeArray = type =>
type === 'array' || (Array.isArray(type) && type.includes('array'))

const isArrayType = prop => canPropertyBeArray(prop.type) && prop.items

const isStringArray = prop =>
isArrayType(prop) && canPropertyBeString(prop.items.type)
Expand Down Expand Up @@ -45,16 +51,26 @@ export const sanitizeBySchema = (
}

const prop = props[key]
debug('looking at property %j', prop)

if (key in object && Array.isArray(object[key])) {
debug('%s is present as an array', key)

if (isStringArray(prop)) {
debug('%s is a string array', key)
// go through the items in the array and if the format is dynamic
// set default values
const list: string[] = result[key] as string[]

if (prop.items && prop.items.format) {
const itemFormat = prop.items.format
debug('items format %s', itemFormat)

if (formatDefaults && isDynamic(itemFormat)) {
debug(
'format %s is dynamic, need to replace with default value',
itemFormat,
)
const defaultValue = formatDefaults[itemFormat]
for (let k = 0; k < list.length; k += 1) {
list[k] = defaultValue as string
Expand All @@ -63,6 +79,8 @@ export const sanitizeBySchema = (
}
}
} else if (isArrayType(prop) && hasPropertiesArray(prop)) {
debug('property %s is array-like and has properties', key)

const list: PlainObject[] = object[key] as PlainObject[]
const propSchema: JsonSchema = prop.items as JsonSchema
result[key] = list.map(sanitizeBySchema(propSchema, formatDefaults))
Expand Down
179 changes: 153 additions & 26 deletions test/sanitize-test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import test from 'ava'
import { sanitize, sanitizeBySchema } from '../src/sanitize'
import { schemas, exampleFormats } from './example-schemas'
import stringify from 'json-stable-stringify'
import { JsonSchema } from '../src/objects'
import { getDefaults } from '../src/formats'
import { JsonSchema } from '../src/objects'
import { sanitize, sanitizeBySchema } from '../src/sanitize'
import { exampleFormats, schemas } from './example-schemas'

const schemaName = 'person'
const schemaVersion = '1.0.0'
Expand Down Expand Up @@ -33,33 +33,160 @@ test('sanitize with default values', t => {
})
})

const schema: JsonSchema = {
title: 'TestSchema',
type: 'object',
properties: {
createdAt: {
type: 'string',
format: 'date-time',
},
name: {
type: 'string',
},
hook: {
type: 'string',
format: 'hookId',
},
ids: {
type: 'array',
items: {
test('sanitize empty object using schema', t => {
const schema: JsonSchema = {
title: 'TestSchema',
type: 'object',
properties: {
createdAt: {
type: 'string',
format: 'date-time',
},
name: {
type: 'string',
},
hook: {
type: 'string',
format: 'uuid',
format: 'hookId',
},
ids: {
type: 'array',
items: {
type: 'string',
format: 'uuid',
},
},
},
},
}
}

test('sanitize empty object using schema', t => {
const o = {}
const result = sanitizeBySchema(schema)(o)
const result = sanitizeBySchema(schema, formatDefaults)(o)
t.deepEqual(result, {})
})

test('sanitize string array', t => {
t.plan(1)
const schema: JsonSchema = {
title: 'TestSchema',
type: 'object',
properties: {
names: {
type: 'array',
items: {
type: 'string',
format: 'name',
},
},
},
}

const o = {
names: ['Joe', 'Mary'],
}
const result = sanitizeBySchema(schema, formatDefaults)(o)
t.deepEqual(
result,
{ names: ['Buddy', 'Buddy'] },
'both names were replaced with default value for the format',
)
})

test('sanitize array', t => {
const schema: JsonSchema = {
title: 'TestSchema',
type: 'object',
properties: {
names: {
type: 'array',
items: {
// requires "title" in order to be considered a schema
title: 'Name',
type: 'object',
properties: {
name: {
type: 'string',
format: 'name',
},
},
},
},
},
}

const o = {
names: [
{
name: 'Joe',
},
{
name: 'Mary',
},
],
}
const result = sanitizeBySchema(schema, formatDefaults)(o)
t.deepEqual(
result,
{
names: [
{
name: 'Buddy',
},
{
name: 'Buddy',
},
],
},
'name in each object is sanitized',
)
})

test('sanitize array that can be null', t => {
const schema: JsonSchema = {
title: 'TestSchema',
type: 'object',
properties: {
names: {
// notice that names can be "null"
// https://github.com/cypress-io/schema-tools/issues/53
type: ['array', 'null'],
items: {
// requires "title" in order to be considered a schema
title: 'Name',
type: 'object',
properties: {
name: {
type: 'string',
format: 'name',
},
},
},
},
},
}

const o = {
names: [
{
name: 'Joe',
},
{
name: 'Mary',
},
],
}
const result = sanitizeBySchema(schema, formatDefaults)(o)
t.deepEqual(
result,
{
names: [
{
name: 'Buddy',
},
{
name: 'Buddy',
},
],
},
'name in each object is sanitized',
)
})

0 comments on commit 2c81a7d

Please sign in to comment.