Skip to content

Commit

Permalink
Merge branch 'asaid-0-unicodeRegExp'
Browse files Browse the repository at this point in the history
  • Loading branch information
epoberezkin committed Apr 11, 2021
2 parents dba1aeb + 5cab9bf commit 614454d
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 8 deletions.
10 changes: 10 additions & 0 deletions docs/options.md
Expand Up @@ -36,6 +36,7 @@ const defaultOptions = {
allErrors: false,
verbose: false,
discriminator: false, // *
unicodeRegExp: true // *
$comment: false, // *
formats: {},
keywords: {},
Expand Down Expand Up @@ -167,6 +168,15 @@ Include the reference to the part of the schema (`schema` and `parentSchema`) an

Support [discriminator keyword](./json-schema.md#discriminator) from [OpenAPI specification](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md).

### unicodeRegExp

By default Ajv uses unicode flag "u" with "pattern" and "patternProperties", as per JSON Schema spec. See [RegExp.prototype.unicode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode) .

Option values:

- `true` (default) - use unicode flag "u".
- `false` - do not use flag "u".

### $comment

Log or pass the value of `$comment` keyword to a function.
Expand Down
5 changes: 4 additions & 1 deletion lib/core.ts
Expand Up @@ -97,6 +97,7 @@ export interface CurrentOptions {
allErrors?: boolean
verbose?: boolean
discriminator?: boolean
unicodeRegExp?: boolean
$comment?:
| true
| ((comment: string, schemaPath?: string, rootSchema?: AnySchemaObject) => unknown)
Expand Down Expand Up @@ -212,7 +213,8 @@ type RequiredInstanceOptions = {
| "messages"
| "addUsedSchema"
| "validateSchema"
| "validateFormats"]: NonNullable<Options[K]>
| "validateFormats"
| "unicodeRegExp"]: NonNullable<Options[K]>
} & {code: InstanceCodeOptions}

export type InstanceOptions = Options & RequiredInstanceOptions
Expand All @@ -239,6 +241,7 @@ function requiredOptions(o: Options): RequiredInstanceOptions {
addUsedSchema: o.addUsedSchema ?? true,
validateSchema: o.validateSchema ?? true,
validateFormats: o.validateFormats ?? true,
unicodeRegExp: o.unicodeRegExp ?? true,
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/vocabularies/applicator/additionalProperties.ts
Expand Up @@ -60,7 +60,7 @@ const def: CodeKeywordDefinition & AddedKeywordDefinition = {
definedProp = nil
}
if (patProps.length) {
definedProp = or(definedProp, ...patProps.map((p) => _`${usePattern(gen, p)}.test(${key})`))
definedProp = or(definedProp, ...patProps.map((p) => _`${usePattern(cxt, p)}.test(${key})`))
}
return not(definedProp)
}
Expand Down
2 changes: 1 addition & 1 deletion lib/vocabularies/applicator/patternProperties.ts
Expand Up @@ -50,7 +50,7 @@ const def: CodeKeywordDefinition = {

function validateProperties(pat: string): void {
gen.forIn("key", data, (key) => {
gen.if(_`${usePattern(gen, pat)}.test(${key})`, () => {
gen.if(_`${usePattern(cxt, pat)}.test(${key})`, () => {
cxt.subschema(
{
keyword: "patternProperties",
Expand Down
7 changes: 4 additions & 3 deletions lib/vocabularies/code.ts
Expand Up @@ -90,11 +90,12 @@ export function callValidateCode(
return context !== nil ? _`${func}.call(${context}, ${args})` : _`${func}(${args})`
}

export function usePattern(gen: CodeGen, pattern: string): Name {
export function usePattern({gen, it: {opts}}: KeywordCxt, pattern: string): Name {
const u = opts.unicodeRegExp ? "u" : ""
return gen.scopeValue("pattern", {
key: pattern,
ref: new RegExp(pattern, "u"),
code: _`new RegExp(${pattern}, "u")`,
ref: new RegExp(pattern, u),
code: _`new RegExp(${pattern}, ${u})`,
})
}

Expand Down
6 changes: 4 additions & 2 deletions lib/vocabularies/validation/pattern.ts
Expand Up @@ -17,8 +17,10 @@ const def: CodeKeywordDefinition = {
$data: true,
error,
code(cxt: KeywordCxt) {
const {gen, data, $data, schema, schemaCode} = cxt
const regExp = $data ? _`(new RegExp(${schemaCode}, "u"))` : usePattern(gen, schema) // TODO regexp should be wrapped in try/catch
const {data, $data, schema, schemaCode, it} = cxt
// TODO regexp should be wrapped in try/catchs
const u = it.opts.unicodeRegExp ? "u" : ""
const regExp = $data ? _`(new RegExp(${schemaCode}, ${u}))` : usePattern(cxt, schema)
cxt.fail$data(_`!${regExp}.test(${data})`)
},
}
Expand Down
67 changes: 67 additions & 0 deletions spec/options/unicodeRegExp.spec.ts
@@ -0,0 +1,67 @@
import _Ajv from "../ajv"
import chai from "../chai"
const should = chai.should()

describe("unicodeRegExp option", () => {
const unicodeChar = "\uD83D\uDC4D"
const unicodeSchema = {
type: "string",
pattern: `^[${unicodeChar}]$`,
}

const schemaWithEscape = {
type: "string",
pattern: "^[\\:]$",
}

const patternPropertiesSchema = {
type: "object",
patternProperties: {
"^\\:.*$": {type: "number"},
},
additionalProperties: false,
}

describe("= true (default)", () => {
const ajv = new _Ajv()
it("should fail schema compilation if used invalid (unnecessary) escape sequence for pattern", () => {
should.throw(() => {
ajv.compile(schemaWithEscape)
}, /Invalid escape/)
})

it("should fail schema compilation if used invalid (unnecessary) escape sequence for patternProperties", () => {
should.throw(() => {
ajv.compile(patternPropertiesSchema)
}, /Invalid escape/)
})

it("should validate unicode character", () => {
const validate = ajv.compile(unicodeSchema)
validate(unicodeChar).should.equal(true)
})
})

describe("= false", () => {
const ajv = new _Ajv({unicodeRegExp: false})
it("should pass schema compilation if used unnecessary escape sequence for pattern", () => {
should.not.throw(() => {
const validate = ajv.compile(schemaWithEscape)
validate(":").should.equal(true)
})
})

it("should pass schema compilation if used unnecessary escape sequence for patternProperties", () => {
should.not.throw(() => {
const validate = ajv.compile(patternPropertiesSchema)
validate({":test": 1}).should.equal(true)
validate({test: 1}).should.equal(false)
})
})

it("should not validate unicode character", () => {
const validate = ajv.compile(unicodeSchema)
validate(unicodeChar).should.equal(false)
})
})
})

0 comments on commit 614454d

Please sign in to comment.