-
-
Notifications
You must be signed in to change notification settings - Fork 377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add in i18n support for validator #161
Conversation
Also includes the ability to customize error format
- revert how error was returned to pass tests - lint code
Codecov Report
@@ Coverage Diff @@
## master #161 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 17 17
Lines 366 379 +13
Branches 75 78 +3
=====================================
+ Hits 366 379 +13
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great proposal @willfarrell, this would be really useful. Thanks a million for it!
Anyway there are some details in the current implementation on which I am not completely sold and I would prefer to reuse features from existing middlewares rather than adding more features to the validator one. Please refer to the specific comments for more details.
Aside from that, once we finalise the implementation, we should make sure we updated Docs and Typings.
package.json
Outdated
@@ -13,7 +13,7 @@ | |||
], | |||
"types": "./index.d.ts", | |||
"scripts": { | |||
"test:lint": "eslint --ignore-pattern='node_modules/' --ignore-pattern='coverage/' --ignore-pattern='docs/' .", | |||
"test:lint": "eslint --fix --ignore-pattern='node_modules/' --ignore-pattern='coverage/' --ignore-pattern='docs/' .", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you adding --fix
here?
IMHO, the test command is meant to only check for errors, not fixing them. You should run eslint --fix
either on save in your IDE (automatically), or manually before commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, ment to remove this before commit. Habit from a pattern I use where my setup auto formats and lint on commit. No IDE settings or remembering to run lint before commit. Check out lint-staged
and husky
.
Will be removed in next commit.
package.json
Outdated
@@ -66,7 +66,9 @@ | |||
"dependencies": { | |||
"@types/aws-lambda": "^8.10.1", | |||
"@types/http-errors": "^1.6.1", | |||
"accept-language": "^3.0.18", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Middy already has negotiator
, which should take care of negotiating languages. Do we really need an extra dependency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally didn't see that other module, will refactor to use negotiator
.
src/middlewares/validator.js
Outdated
} | ||
|
||
module.exports = ({ inputSchema, outputSchema, ajvOptions, errorFormat = errors => errors }) => { | ||
const acceptLanguage = require('accept-language') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead re-doing this here, can we delegate somehow the work of processing the languages to the already available (and configurable) HTTP Content Negotiation middleware?
If we do that, we should just assume that the event
object will contain a property called preferredLanguage
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about that, leveraging another middleware to do the parsing. Opted for having it included within due to the allowed languages being different than what the overall API may support. Plus allows for the middleware to be stand alone. I've refactored to leverage negotiator
for now, but further discussion around this should continue.
src/middlewares/validator.js
Outdated
$data: true // required for ajv-keywords | ||
} | ||
|
||
module.exports = ({ inputSchema, outputSchema, ajvOptions, errorFormat = errors => errors }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On one side, I like the idea of the errorFormat
function. On the other, I would prefer to keep this middleware simpler and encourage people to combine it with the usage of the HTTP Error Handler middleware.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been writing a jsonapi
middleware that essentially replaces httpErrorHandler
. At the time of writing it made sense to do the formatting here, there have been changes since that might make more sense to move it to another middleware. I've done some testing, we're good to remove.
Thanks for the great feedback @lmammino. Changes pushed, ready for next round. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will spend a bit of time playing with your implementation and see if we can generalise some parts. This way I should be able to do a more accurate review 😉
src/middlewares/validator.js
Outdated
'sk', | ||
'sv', | ||
'zh' | ||
]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still not sure it's a good approach to list the languages here and not having any opportunity to configure the selection
…e/validator_i18n
…n combination with the content-negotiation middleware
Hey @willfarrell and others. Please check my suggested implementation (a296341) and let me know what you think. I added few comments in the PR to better describe the changes, but here follows the main ones:
If you like this approach, I'll make sure to update the documentation and the typings |
src/middlewares/validator.js
Outdated
]) | ||
if (!results.length) return 'en' | ||
return results[0] | ||
const availableLanguages = Object.keys(ajvLocalize) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This saves us the trouble of having a hardcoded list of languages
'pt_BR': 'pt-BR', | ||
'pt_br': 'pt-BR' | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes Portuguese less ambiguous and it will work with all possible variations.
Honestly, I am not sure if it's a good idea to go down the loophole of dealing with this kind of things. So far, looking at ajv-i18n
, only Portuguese was the outlier, so I thought it was easy enough to support all the possible variations.
src/middlewares/validator.js
Outdated
console.log(chooseLanguage(handler.event)) | ||
const locale = chooseLanguage(handler.event) | ||
ajvLocalize[locale](validateInput.errors) | ||
const language = chooseLanguage(handler.event, options.defaultLanguage) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the default language can now be configured with options.defaultLanguage
const defaults = {v5: true, $data: true, allErrors: true} | ||
const defaults = { | ||
v5: true, | ||
coerceTypes: 'array', // important for query string params |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, isn't this a breaking change? coerceTypes
is false
by default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a code standpoint, yes. From an end-user standpoint, no. Since this middleware is paired with a json schema, having coerceTypes
enabled would make requests more forgiving when a body
is passed in and enable the ability to parse query strings making them more usable. There are no cases where existing code would fail where they once passed. For example; if someone is using the current defaults their schema would have "type":"string"
for all their query string params, in this case, no coercion would happen. coerceTypes
is only applied if the types don't match. I added this option to support the use case when you expect an array from the query string, but only one item was passed in.
I feel including this as our default will be doing a service to younger developers, allowing their APIs to be more forgiving out of the gate. However, I do understand why if you'd like to default to something more strict. I'm good either way due to I'm always passing in custom options anyway. Let me know which way you want to go.
The background behind coerceTypes: 'array'
can be found here: ajv-validator/ajv#158
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example; if someone is using the current defaults their schema would have "type":"string" for all their query string params, in this case, no coercion would happen.
Totally makes sense for me. And thanks for adding this, I see how it will be useful in my code already!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vladgolubev Along these lines, you may also be interested in this PR: ajv-validator/ajv-keywords#64
I just had a new keyword added, transform
, that allows to you to do some sanitization on strings being passed in. We're drafting up next steps for it here: ajv-validator/ajv-keywords#66
@lmammino I will have time Friday or Saturday to review your suggested changes. At first glance, they look good. |
@lmammino Looks good! The last thing we need is to update the README and I think this PR is good to go. Let me know if you'd like me to assist with that. |
@willfarrell well done. I will try to get this one completed and merged tonight 😉 |
Includes: ajv i18n support (Default:
en
)