Prevent errors when using string filters with empty values (#11836)

* Prevent errors when using string filters with empty values

* Add tests

* String schema validation forced to false for null values
This commit is contained in:
ian
2022-03-22 02:03:42 +08:00
committed by GitHub
parent 9b8d72ec5c
commit d0aa61e94a
2 changed files with 143 additions and 18 deletions

View File

@@ -65,6 +65,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _ncontains contain match`, () => {
const mockFieldFilter = { field: { _ncontains: 'field' } } as FieldFilter;
const mockSchema = Joi.object({
@@ -75,6 +76,16 @@ describe(`generateJoi`, () => {
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _ncontains with null value`, () => {
const mockFieldFilter = { field: { _ncontains: null } } as FieldFilter;
const mockSchema = Joi.object({
field: Joi.any().equal(true),
})
.unknown()
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _contains contain match`, () => {
const mockFieldFilter = { field: { _contains: 'field' } } as FieldFilter;
const mockSchema = Joi.object({
@@ -84,28 +95,43 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _contains with null value`, () => {
const mockFieldFilter = { field: { _contains: null } } as FieldFilter;
const mockSchema = Joi.object({
field: Joi.any().equal(true),
})
.unknown()
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns a value if the substring is included in the value`, () => {
expect(() => {
Joi.assert('testfield', (Joi.string() as StringSchema).contains('field'));
}).toEqual(expect.any(Function));
});
it(`returns a value if the substring is not contained in the value`, () => {
expect(() => {
Joi.assert('testfield', (Joi.string() as StringSchema).contains('field'));
}).toEqual(expect.any(Function));
});
it(`returns an error if the substring is included in the value`, () => {
expect(() => {
Joi.assert('field', (Joi.string() as StringSchema).ncontains('field'));
// eslint-disable-next-line no-useless-escape
}).toThrowError(`\"value\" can't contain [field]`);
});
it(`returns an error if the substring is not contained in the value`, () => {
expect(() => {
Joi.assert('test', (Joi.string() as StringSchema).contains('field'));
// eslint-disable-next-line no-useless-escape
}).toThrowError(`\"value\" must contain [field`);
});
it(`returns the correct schema for a _starts_with match`, () => {
const mockFieldFilter = { field: { _starts_with: 'field' } } as FieldFilter;
const mockSchema = Joi.object({
@@ -117,6 +143,17 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for a _starts_with with null value`, () => {
const mockFieldFilter = { field: { _starts_with: null } } as FieldFilter;
const mockSchema = Joi.object({
field: Joi.any().equal(true),
})
.unknown()
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for a _nstarts_with with match`, () => {
const mockFieldFilter = { field: { _nstarts_with: 'field' } } as FieldFilter;
const mockSchema = Joi.object({
@@ -129,6 +166,17 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for a _nstarts_with with null value`, () => {
const mockFieldFilter = { field: { _nstarts_with: null } } as FieldFilter;
const mockSchema = Joi.object({
field: Joi.any().equal(true),
})
.unknown()
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an ends_with match`, () => {
const mockFieldFilter = { field: { _ends_with: 'field' } } as FieldFilter;
const mockSchema = Joi.object({
@@ -141,6 +189,16 @@ describe(`generateJoi`, () => {
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an ends_with with null value`, () => {
const mockFieldFilter = { field: { _ends_with: null } } as FieldFilter;
const mockSchema = Joi.object({
field: Joi.any().equal(true),
})
.unknown()
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for a doesnt _nends_with match`, () => {
const mockFieldFilter = { field: { _nends_with: 'field' } } as FieldFilter;
const mockSchema = Joi.object({
@@ -153,6 +211,17 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for a doesnt _nends_with with null value`, () => {
const mockFieldFilter = { field: { _nends_with: null } } as FieldFilter;
const mockSchema = Joi.object({
field: Joi.any().equal(true),
})
.unknown()
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _in match`, () => {
const mockFieldFilter = { field: { _in: 'field' } } as FieldFilter;
const mockSchema = Joi.object({
@@ -172,6 +241,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _gt number match`, () => {
const mockFieldFilter = { field: { _gt: 1 } } as FieldFilter;
const mockSchema = Joi.object({
@@ -181,6 +251,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _gt date match`, () => {
const mockFieldFilter = { field: { _gt: date } } as FieldFilter;
const mockSchema = Joi.object({
@@ -200,6 +271,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _gte date match`, () => {
const mockFieldFilter = { field: { _gte: date } } as FieldFilter;
const mockSchema = Joi.object({
@@ -219,6 +291,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _lt date match`, () => {
const mockFieldFilter = { field: { _lt: date } } as FieldFilter;
const mockSchema = Joi.object({
@@ -228,6 +301,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _lte number match`, () => {
const mockFieldFilter = { field: { _lte: 1 } } as FieldFilter;
const mockSchema = Joi.object({
@@ -237,6 +311,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _lte date match`, () => {
const mockFieldFilter = { field: { _lte: date } } as FieldFilter;
const mockSchema = Joi.object({
@@ -246,6 +321,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _null match`, () => {
const mockFieldFilter = { field: { _null: null } } as FieldFilter;
const mockSchema = Joi.object({
@@ -255,6 +331,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _nnull match`, () => {
const mockFieldFilter = { field: { _nnull: null } } as FieldFilter;
const mockSchema = Joi.object({
@@ -264,6 +341,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _empty match`, () => {
const mockFieldFilter = { field: { _empty: '' } } as FieldFilter;
const mockSchema = Joi.object({
@@ -273,6 +351,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _nempty match`, () => {
const mockFieldFilter = { field: { _nempty: '' } } as FieldFilter;
const mockSchema = Joi.object({
@@ -282,6 +361,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _between number match`, () => {
const mockFieldFilter = { field: { _between: [1, 3] } } as FieldFilter;
const mockSchema = Joi.object({
@@ -291,6 +371,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _between date match`, () => {
const mockFieldFilter = { field: { _between: [date, compareDate] } } as FieldFilter;
const mockSchema = Joi.object({
@@ -300,6 +381,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _nbetween number match`, () => {
const mockFieldFilter = { field: { _nbetween: [1, 3] } } as FieldFilter;
const mockSchema = Joi.object({
@@ -309,6 +391,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _nbetween date match`, () => {
const mockFieldFilter = { field: { _nbetween: [date, compareDate] } } as FieldFilter;
const mockSchema = Joi.object({
@@ -318,6 +401,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _submitted match`, () => {
const mockFieldFilter = { field: { _submitted: '' } } as FieldFilter;
const mockSchema = Joi.object({
@@ -327,6 +411,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _regex match when wrapped`, () => {
const mockFieldFilter = { field: { _regex: '/.*field$/' } } as FieldFilter;
const mockSchema = Joi.object({
@@ -336,6 +421,7 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _regex match when unwrapped`, () => {
const mockFieldFilter = { field: { _regex: '.*field$' } } as FieldFilter;
const mockSchema = Joi.object({
@@ -345,4 +431,14 @@ describe(`generateJoi`, () => {
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
it(`returns the correct schema for an _regex match with null value`, () => {
const mockFieldFilter = { field: { _regex: null } } as FieldFilter;
const mockSchema = Joi.object({
field: Joi.any().equal(true),
})
.unknown()
.describe();
expect(generateJoi(mockFieldFilter).describe()).toStrictEqual(mockSchema);
});
});

View File

@@ -113,37 +113,61 @@ export function generateJoi(filter: FieldFilter, options?: JoiOptions): AnySchem
}
if (operator === '_contains') {
schema[key] = getStringSchema().contains(compareValue);
if (compareValue === null || compareValue === undefined) {
schema[key] = Joi.any().equal(true);
} else {
schema[key] = getStringSchema().contains(compareValue);
}
}
if (operator === '_ncontains') {
schema[key] = getStringSchema().ncontains(compareValue);
if (compareValue === null || compareValue === undefined) {
schema[key] = Joi.any().equal(true);
} else {
schema[key] = getStringSchema().ncontains(compareValue);
}
}
if (operator === '_starts_with') {
schema[key] = getStringSchema().pattern(new RegExp(`^${escapeRegExp(compareValue as string)}.*`), {
name: 'starts_with',
});
if (compareValue === null || compareValue === undefined) {
schema[key] = Joi.any().equal(true);
} else {
schema[key] = getStringSchema().pattern(new RegExp(`^${escapeRegExp(compareValue)}.*`), {
name: 'starts_with',
});
}
}
if (operator === '_nstarts_with') {
schema[key] = getStringSchema().pattern(new RegExp(`^${escapeRegExp(compareValue as string)}.*`), {
name: 'starts_with',
invert: true,
});
if (compareValue === null || compareValue === undefined) {
schema[key] = Joi.any().equal(true);
} else {
schema[key] = getStringSchema().pattern(new RegExp(`^${escapeRegExp(compareValue)}.*`), {
name: 'starts_with',
invert: true,
});
}
}
if (operator === '_ends_with') {
schema[key] = getStringSchema().pattern(new RegExp(`.*${escapeRegExp(compareValue as string)}$`), {
name: 'ends_with',
});
if (compareValue === null || compareValue === undefined) {
schema[key] = Joi.any().equal(true);
} else {
schema[key] = getStringSchema().pattern(new RegExp(`.*${escapeRegExp(compareValue)}$`), {
name: 'ends_with',
});
}
}
if (operator === '_nends_with') {
schema[key] = getStringSchema().pattern(new RegExp(`.*${escapeRegExp(compareValue as string)}$`), {
name: 'ends_with',
invert: true,
});
if (compareValue === null || compareValue === undefined) {
schema[key] = Joi.any().equal(true);
} else {
schema[key] = getStringSchema().pattern(new RegExp(`.*${escapeRegExp(compareValue)}$`), {
name: 'ends_with',
invert: true,
});
}
}
if (operator === '_in') {
@@ -219,8 +243,13 @@ export function generateJoi(filter: FieldFilter, options?: JoiOptions): AnySchem
}
if (operator === '_regex') {
const wrapped = compareValue.startsWith('/') && compareValue.endsWith('/');
schema[key] = getStringSchema().regex(new RegExp(wrapped ? compareValue.slice(1, -1) : compareValue));
if (compareValue === null || compareValue === undefined) {
schema[key] = Joi.any().equal(true);
} else {
const wrapped =
typeof compareValue === 'string' ? compareValue.startsWith('/') && compareValue.endsWith('/') : false;
schema[key] = getStringSchema().regex(new RegExp(wrapped ? compareValue.slice(1, -1) : compareValue));
}
}
}