-
Notifications
You must be signed in to change notification settings - Fork 139
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(parameters): added BaseProvider
class
#1168
Conversation
} | ||
|
||
abstract class BaseProvider implements BaseProviderInterface { | ||
public store: Map<string, ExpirableValue> = new Map; |
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.
Question: Should this be public
or private
?
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'll go with private
first and broaden later if needed.
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.
If I use private
I'm not sure how to test around it. So I went with private
and extended the class in the tests, similar to what done in the Idempotency utility.
If you have any better way to do it that also allows a pure private
let me know.
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 should be good for now. I would expect that the tests cover all privates
variable from the public
methods.
*/ | ||
public async get(name: string, options?: GetOptionsInterface): Promise<undefined | string | Record<string, unknown>> { | ||
const configs = new GetOptions(options); | ||
const key = [ name, configs.transform ].toString(); |
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 couldn't think of a more elegant way of constructing this key.
For context:
- Python uses a Tuple, which are not supported in JS (only stage 2)
- I was initially using objects like
{ name: string, transform: TransformOptions }
since we are using aMap
, but given thatMap.has
andMap.get
use object identity to compare the key it would never return a match - also it's not possible to extend/customize the equality function used by aMap
Set
have the same limitations described above and that apply toMap
- I briefly considered using external modules like
immutable-js
or custom implementations ofSet
, but opted not to in order to avoid introducing a dependency over this otherwise limited usage.
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.
As long as the test cases are rigorous enough, this should be ok. I cannot see how we can run into having a duplicated here, can we?
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.
Agree.
I wasn't worried about duplicates as much as special characters and such.
} | ||
|
||
// TODO: revisit return type once providers are implemented, it might be missing binary when not transformed | ||
return value; |
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.
As mentioned in the comment here, I stayed generic with types because at this stage I'm not yet sure what each provider/AWS SDK (from _get
) will return.
I have opened #1172 to track this work.
this.addToCache(key, values, configs.maxAge); | ||
} | ||
|
||
// TODO: revisit return type once providers are implemented, it might be missing something |
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 have opened #1172 to track this work.
|
||
} | ||
|
||
// TODO: revisit `value` type once we are clearer on the types returned by the various SDKs |
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 have opened #1172 to track this work.
) { | ||
return new TextDecoder('utf-8').decode(fromBase64(value)); | ||
} else { | ||
// TODO: revisit this type once we are clearer on types returned by SDKs |
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 have opened #1172 to track this work.
} | ||
} | ||
|
||
class GetMultipleOptions implements GetMultipleOptionsInterface { |
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.
Should these classes be in separate files?
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.
Given this file's size, I would say so. But it is not critical.
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.
Addressed in f9259eb
const value = this.store.get(key); | ||
if (value) { | ||
if (value.isExpired()) { | ||
this.store.delete(key); |
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.
As far as I can tell, the Python implementation doesn't clear expired items from the cache.
Can you think of any case in which doing that might become an issue?
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.
No. We just trade time for space here. The operation is O(1) so I don't think there is any issue to delete it now.
The problem is more on doing more than one thing in a single method. It is not obvious to me that hasKeyExpiredInCache()
will prune the this.store
. The new maintainer may use this function and didn't know that he's pruned the cache. Thus, I'm incline to remove this.
If we want to prune expired items, I would expect a public/protected method to do this explicitly. But that will cause O(n) and I am not sure if it's useful. Map has O(1) access so we are only saving the memory consumption here, which I expect it to be very small gain.
Thus, I'm incline to remove this and keep the expired item.
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 said. It's open to debate. I don't think that it's bad enough to block this PR. So I would give an approval. You can decide in later commit if you want to remove 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.
Good points, I didn't think about the operation costs and I agree that the memory savings are marginal.
I have removed it.
BaseProvider
class
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.
One comment on prunning expire key
} | ||
|
||
abstract class BaseProvider implements BaseProviderInterface { | ||
public store: Map<string, ExpirableValue> = new Map; |
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'll go with private
first and broaden later if needed.
} | ||
} | ||
|
||
class GetMultipleOptions implements GetMultipleOptionsInterface { |
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.
Given this file's size, I would say so. But it is not critical.
*/ | ||
public async get(name: string, options?: GetOptionsInterface): Promise<undefined | string | Record<string, unknown>> { | ||
const configs = new GetOptions(options); | ||
const key = [ name, configs.transform ].toString(); |
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.
As long as the test cases are rigorous enough, this should be ok. I cannot see how we can run into having a duplicated here, can we?
const value = this.store.get(key); | ||
if (value) { | ||
if (value.isExpired()) { | ||
this.store.delete(key); |
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.
No. We just trade time for space here. The operation is O(1) so I don't think there is any issue to delete it now.
The problem is more on doing more than one thing in a single method. It is not obvious to me that hasKeyExpiredInCache()
will prune the this.store
. The new maintainer may use this function and didn't know that he's pruned the cache. Thus, I'm incline to remove this.
If we want to prune expired items, I would expect a public/protected method to do this explicitly. But that will cause O(n) and I am not sure if it's useful. Map has O(1) access so we are only saving the memory consumption here, which I expect it to be very small gain.
Thus, I'm incline to remove this and keep the expired item.
const value = this.store.get(key); | ||
if (value) { | ||
if (value.isExpired()) { | ||
this.store.delete(key); |
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 said. It's open to debate. I don't think that it's bad enough to block this PR. So I would give an approval. You can decide in later commit if you want to remove this.
128ed70
to
14f8b81
Compare
interface GetMultipleOptionsInterface { | ||
maxAge?: number | ||
forceFetch?: boolean | ||
sdkOptions?: unknown | ||
transform?: string | ||
throwOnTransformError?: boolean | ||
} |
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.
Just to reduce boilerplate:
interface GetMultipleOptionsInterface extends GetOptionsInterface {
throwOnTransformError?: boolean
}
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.
Good point, please feel free to address this in your PR for AppConfigprovider
Description of your changes
This PR introduces a new package (
parameters
) in the npm workspace as well as theBaseProvider
for the new utility and its unit tests.The changes related to the new workspace package include:
pacakges/parameters
folder withpackage.json
The changes related to the
BaseProvider
include the content of thesrc
andtests
directories:BaseProvider
classBaseProvider
Please don't take in account the content of the
README.md
file nor the docstrings (or lack thereof) for the various features, both items will be addressed in their own PRs.How to verify this change
See unit tests added as part of this PR.
Related issues, RFCs
Issue number: #1173
PR status
Is this ready for review?: YES
Is it a breaking change?: NO
Checklist
Breaking change checklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.