Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/v17'
Browse files Browse the repository at this point in the history
  • Loading branch information
Marsup committed Aug 28, 2024
2 parents ed25e95 + 3cb73d6 commit 239ec33
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 12 deletions.
1 change: 1 addition & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -3087,6 +3087,7 @@ Requires the string value to be a valid [RFC 3986](http://tools.ietf.org/html/rf
- `relativeOnly` - Restrict only relative URIs. Defaults to `false`.
- `allowQuerySquareBrackets` - Allows unencoded square brackets inside the query string. This is **NOT** RFC 3986 compliant but query strings like `abc[]=123&abc[]=456` are very common these days. Defaults to `false`.
- `domain` - Validate the domain component using the options specified in [`string.domain()`](#stringdomainoptions).
- `encodeUri` - When `convert` is true, if the validation fails, attempts to encode the URI using [`encodeURI`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI) before validating it again. This allows to provide, for example, unicode URIs, and have it encoded for you. Defaults to `false`.

```js
// Accept git or git http/https
Expand Down
8 changes: 4 additions & 4 deletions lib/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,14 @@ exports.template = function (value, messages, code, state, prefs) {

exports.label = function (flags, state, prefs, messages) {

if (flags.label) {
return flags.label;
}

if (!prefs.errors.label) {
return '';
}

if (flags.label) {
return flags.label;
}

let path = state.path;
if (prefs.errors.label === 'key' &&
state.path.length > 1) {
Expand Down
6 changes: 6 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,12 @@ declare namespace Joi {
* Validate the domain component using the options specified in `string.domain()`.
*/
domain?: DomainOptions;
/**
* Encode URI before validation.
*
* @default false
*/
encodeUri?: boolean;
}

interface DataUriOptions {
Expand Down
9 changes: 6 additions & 3 deletions lib/types/alternatives.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,10 +327,13 @@ internals.errors = function (failures, { error, state }) {
const [type, code] = report.code.split('.');
if (code !== 'base') {
complex.push({ type: schema.type, report });
continue;
}

valids.add(type);
else if (report.code === 'object.base') {
valids.add(report.local.type);
}
else {
valids.add(type);
}
}

// All errors are base types or valids
Expand Down
13 changes: 11 additions & 2 deletions lib/types/string.js
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ module.exports = Any.extend({
uri: {
method(options = {}) {

Common.assertOptions(options, ['allowRelative', 'allowQuerySquareBrackets', 'domain', 'relativeOnly', 'scheme']);
Common.assertOptions(options, ['allowRelative', 'allowQuerySquareBrackets', 'domain', 'relativeOnly', 'scheme', 'encodeUri']);

if (options.domain) {
Common.assertOptions(options.domain, ['allowFullyQualified', 'allowUnicode', 'maxDomainSegments', 'minDomainSegments', 'tlds']);
Expand All @@ -661,7 +661,16 @@ module.exports = Any.extend({
return helpers.error('string.uri');
}

const match = regex.exec(value);
let match = regex.exec(value);

if (!match && helpers.prefs.convert && options.encodeUri) {
const encoded = encodeURI(value);
match = regex.exec(encoded);
if (match) {
value = encoded;
}
}

if (match) {
const matched = match[1] || match[2];
if (domain &&
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "joi",
"description": "Object schema validation",
"version": "17.12.0",
"version": "17.13.3",
"repository": "git://github.com/hapijs/joi",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand All @@ -15,8 +15,8 @@
"validation"
],
"dependencies": {
"@hapi/hoek": "^11.0.2",
"@hapi/tlds": "^1.0.2",
"@hapi/hoek": "^11.0.4",
"@hapi/tlds": "^1.0.6",
"@hapi/topo": "^6.0.2",
"@hapi/address": "^5.1.1",
"@hapi/pinpoint": "^2.0.1",
Expand Down
15 changes: 15 additions & 0 deletions test/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,21 @@ describe('errors', () => {
expect(schema.validate({ x: { y: { a: [1] } } }, { errors: { label: 'key' } }).error).to.be.an.error('"[0]" must be a string');
});

it('removes labels when label is false', () => {

const schema = Joi.object({
x: Joi.object({
y: Joi.object({
z: Joi.valid('z').label('z'),
a: Joi.array().items(Joi.string().label('item'))
})
})
}).options({ errors: { label: false } });

expect(schema.validate({ x: { y: { z: 'o' } } }).error).to.be.an.error('must be [z]');
expect(schema.validate({ x: { y: { a: [1] } } }).error).to.be.an.error('must be a string');
});

describe('annotate()', () => {

it('annotates error', () => {
Expand Down
12 changes: 12 additions & 0 deletions test/types/alternatives.js
Original file line number Diff line number Diff line change
Expand Up @@ -1983,6 +1983,18 @@ describe('alternatives', () => {
[{ p: 'a' }, false, 'oops']
]);
});

it('validates alternatives with the correct type', () => {

const schema = Joi.alternatives([
Joi.boolean(),
Joi.function()
]);

Helper.validate(schema, [
['wrong', false, '"value" must be one of [boolean, function]']
]);
});
});

describe('when()', () => {
Expand Down
59 changes: 59 additions & 0 deletions test/types/string.js
Original file line number Diff line number Diff line change
Expand Up @@ -9043,6 +9043,65 @@ describe('string', () => {
]);
});

it('validates uri with accented characters with encoding', () => {

const schema = Joi.string().uri({ encodeUri: true });

Helper.validate(schema, { convert: true }, [
['https://linkedin.com/in/aïssa/', true, 'https://linkedin.com/in/a%C3%AFssa/'],
['https://linkedin.com/in/a%C3%AFssa/', true, 'https://linkedin.com/in/a%C3%AFssa/'],
['/#.domain.com/', false, {
message: '"value" must be a valid uri',
path: [],
type: 'string.uri',
context: { label: 'value', value: '/#.domain.com/' }
}]
]);
});

it('validates relative uri with accented characters with encoding', () => {

const schema = Joi.string().uri({ encodeUri: true, allowRelative: true });

Helper.validate(schema, { convert: true }, [
['/in/aïssa/', true, '/in/a%C3%AFssa/'],
['/in/a%C3%AFssa/', true, '/in/a%C3%AFssa/']
]);
});

it('validates uri with encodeUri and scheme', () => {

const schema = Joi.string().uri({ encodeUri: true, scheme: 'https' });

Helper.validate(schema, { convert: true }, [
['https://linkedin.com/in/aïssa/', true, 'https://linkedin.com/in/a%C3%AFssa/'],
['http://linkedin.com/in/aïssa/', false, {
message: '"value" must be a valid uri with a scheme matching the https pattern',
path: [],
type: 'string.uriCustomScheme',
context: {
scheme: 'https',
value: 'http://linkedin.com/in/aïssa/',
label: 'value'
}
}]
]);
});

it('validates uri with accented characters without encoding', () => {

const schema = Joi.string().uri({ encodeUri: true });

Helper.validate(schema, { convert: false }, [
['https://linkedin.com/in/aïssa/', false, {
message: '"value" must be a valid uri',
path: [],
type: 'string.uri',
context: { value: 'https://linkedin.com/in/aïssa/', label: 'value' }
}]
]);
});

it('errors on unknown options', () => {

expect(() => Joi.string().uri({ foo: 'bar', baz: 'qux' })).to.throw('Options contain unknown keys: foo,baz');
Expand Down

0 comments on commit 239ec33

Please sign in to comment.