-
Notifications
You must be signed in to change notification settings - Fork 446
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: configuration validation #1778
Conversation
|
Looking good. What's the thinking behind this pattern?: class ServiceClass implements MyService {
constructor (components, init) {
// use init
}
}
export function myService (init: MyServiceInit = {}): (components: MyComponents) => MyService {
const validatedConfig = object({
// config def
}).validateSync(init)
return (components) => {
return new ServiceClass(components, validatedConfig)
}
} The thing that looks wonky to me is that the class doesn't validate it's args, it's done in the factory instead, so the class has to trust whatever it's given. The config schema is also created for each object instantiation instead of having one definition that all instances can use - does it retain state or something? Why not do something like: const configSchema = object({
// config def
})
class ServiceClass implements MyService {
constructor (components, init) {
init = configSchema.validate(init)
// use init
}
}
export function myService (init: MyServiceInit = {}): (components: MyComponents) => MyService {
return (components) => {
return new ServiceClass(components, init)
}
} |
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.
See the comment above..
If we continue to use factory method pattern with a 1-to-1 mapping of service to method then this way doesn't offer any benefits but also there isn't any drawback of the service object trusting the parameters it was passed (since only one method would pass those parameters in).
Yup's schema is immutable, meaning that modifications to the schema will result in new schema instances rather than altering the original schema. So I can see the drawback of creating one for each object instantiated.
I decided to refactor this given that we can also remove the need for re-setting the defaults in the constructor of the classes since yup returns an object with defined values for each field. |
@achingbrain would you be able to give this a review please? |
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 don't see any major errors but I have a few questions:
- Are the changes in the
*.spec.*
files fixes to incorrect configs set during testing, that were found because of these changes? If so, that's great. - What is the bundle size increase with this?
- Are there any hot-paths that we need to look out for?
- things where instantiation happens frequently and config validation is re-run (and not needed)? If there are, I missed them.
this.keepAlive = init.keepAlive ?? true | ||
this.gateway = init.gateway | ||
|
||
const validIPRegex = /^(?:(?:^|\.)(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])){4}$/ |
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.
would love to see a link to the tests around the regex similar to https://www.regextester.com/104038 or https://regex101.com/r/aL7tV3/1 or similar
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.
That link should suffice :) Normally I would agree with you but since it's for validation as opposed to a regex used in a function, the validation is a test in and of itself imo
Co-authored-by: Russell Dempsey <[email protected]>
That's correct.
Gzipped and minified yup is 12kB which is negligible
This would only instantiate one validation object per class that's already instantiated, which again is insignificant, any optimizations in that regard would be peripheral to this. |
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 was not ready. I know it's taken a long time but other issues have been higher priority. Please wait for review before merging.
maxIncomingPendingConnections: 1000, | ||
maxConnections: 1000, |
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 value for these was fine before, why do these now need to be set?
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.
See my comment #1778
this.keepAlive = init.keepAlive ?? true | ||
this.gateway = init.gateway | ||
|
||
const validIPRegex = /^(?:(?:^|\.)(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])){4}$/ |
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.
Can we use a function for validation instead of a regex? Then reuse something like @chainsafe/is-ip
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.
what's the benefit of using a external dependency and parsing the string over a regular expression?
@@ -235,7 +235,9 @@ describe('libp2p.connections', () => { | |||
}, | |||
connectionManager: { | |||
minConnections, | |||
maxConnections: 1 | |||
maxConnections: 1, | |||
inboundConnectionThreshold: 1, |
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 do we need to specify the extra config now? This seems to happen in a few places here.
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.
Because the intention is that we should not be able to have more inbound connections than maxConnections
as that would result in them getting pruned anyways, discussing with @wemeetagain he raised a use case where this may be applicable though, where a node may want to reach maxConnections as fast as possible and so it may accept a greater amount of inbound connections in a short space of time.
@@ -24,7 +24,9 @@ export default { | |||
const peerId = await createEd25519PeerId() | |||
const libp2p = await createLibp2p({ | |||
connectionManager: { | |||
inboundConnectionThreshold: Infinity, | |||
inboundConnectionThreshold: 1000, |
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 take it yup doesn't accept Infinity
as a number?
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.
Correct
@@ -108,14 +109,23 @@ class DefaultAutoNATService implements Startable { | |||
private started: boolean | |||
|
|||
constructor (components: AutoNATComponents, init: AutoNATServiceInit) { | |||
const validatedConfig = object({ |
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.
Please have config definitions outside of the class so we don't create new validators on every object instantiation:
const configValidator = object({
protocolPrefix: string().default(PROTOCOL_PREFIX),
// ...etc
})
class DefaultAutoNATService implements Startable {
// ...etc
constructor (components: AutoNATComponents, init: AutoNATServiceInit) {
const config = configValidator.validateSync(init)
// ...etc
}
}
Co-authored-by: Russell Dempsey <[email protected]>
Reverted and re-opened as #2133 |
This reverts commit e9099d4.
Closes #1573
Closes #1757
Closes #1903
I decided to go with yup for a few reasons: