Skip to content

Commit

Permalink
feat: Support encoding uri (#3027)
Browse files Browse the repository at this point in the history
* feat: Add encodeUri in UriOptions interface

* feat: Add encodeUri in options list for uri method

* feat: Encode uri with true flags in uri validation

* feat: Add test for uri encoding

* fix: Integrate lint suggestions for style consistency

* fix: Pre-encode value before other cases

* feat: Add custom error message for uriEncoding

* feat: Add case for uriEncoding convert false

* docs: Add description for encodeUri
  • Loading branch information
HYOSITIVE authored Apr 23, 2024
1 parent 554a437 commit 9af6f1f
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 1 deletion.
1 change: 1 addition & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -3085,6 +3085,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` - Encodes the uri with non-alphabetical characters. Defaults to `false`.

```js
// Accept git or git http/https
Expand Down
6 changes: 6 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,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
11 changes: 10 additions & 1 deletion lib/types/string.js
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,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 @@ -665,6 +665,10 @@ module.exports = Any.extend({
return helpers.error('string.uri');
}

if (helpers.prefs.convert && options.encodeUri) {
value = encodeURI(value);
}

const match = regex.exec(value);
if (match) {
const matched = match[1] || match[2];
Expand All @@ -686,6 +690,10 @@ module.exports = Any.extend({
return helpers.error('string.uriCustomScheme', { scheme, value });
}

if (options.encodeUri) {
return helpers.error('string.uriEncoding');
}

return helpers.error('string.uri');
}
}
Expand Down Expand Up @@ -736,6 +744,7 @@ module.exports = Any.extend({
'string.uri': '{{#label}} must be a valid uri',
'string.uriCustomScheme': '{{#label}} must be a valid uri with a scheme matching the {{#scheme}} pattern',
'string.uriRelativeOnly': '{{#label}} must be a valid relative uri',
'string.uriEncoding': '{{#label}} must contain only valid characters or "convert" must be allowed',
'string.uppercase': '{{#label}} must only contain uppercase characters'
}
});
Expand Down
23 changes: 23 additions & 0 deletions test/types/string.js
Original file line number Diff line number Diff line change
Expand Up @@ -9012,6 +9012,29 @@ 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/']
]);
});

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 contain only valid characters or "convert" must be allowed',
path: [],
type: 'string.uriEncoding',
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 9af6f1f

Please sign in to comment.