Skip to content
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

Merged
merged 10 commits into from
Nov 21, 2022
Merged

Conversation

dreamorosi
Copy link
Contributor

@dreamorosi dreamorosi commented Nov 16, 2022

Description of your changes

This PR introduces a new package (parameters) in the npm workspace as well as the BaseProvider for the new utility and its unit tests.

The changes related to the new workspace package include:

  • New pacakges/parameters folder with package.json
  • TSConfig files
  • Eslint file
  • Jest test harness, and jestconfig file
  • README file

The changes related to the BaseProvider include the content of the src and tests directories:

  • New BaseProvider class
  • New types and exceptions to support the class
  • Unit tests for BaseProvider

Note
I have left a few comments/questions that I would appreciate reviewers to engage with during the review

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

  • My changes meet the tenets criteria
  • I have performed a self-review of my own code
  • I have commented my code where necessary, particularly in areas that should be flagged with a TODO, or hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have made corresponding changes to the examples
  • My changes generate no new warnings
  • The code coverage hasn't decreased
  • I have added tests that prove my change is effective and works
  • New and existing unit tests pass locally and in Github Actions
  • Any dependent changes have been merged and published in downstream module
  • The PR title follows the conventional commit semantics

Breaking change checklist

  • I have documented the migration process
  • I have added, implemented necessary warnings (if it can live side by side)

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@pull-request-size pull-request-size bot added the size/L PRs between 100-499 LOC label Nov 16, 2022
@dreamorosi dreamorosi added feature PRs that introduce new features or minor changes and removed feature labels Nov 16, 2022
@dreamorosi dreamorosi self-assigned this Nov 16, 2022
@pull-request-size pull-request-size bot added size/XL PRs between 500-999 LOC, often PRs that grown with feedback and removed size/L PRs between 100-499 LOC labels Nov 17, 2022
}

abstract class BaseProvider implements BaseProviderInterface {
public store: Map<string, ExpirableValue> = new Map;
Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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();
Copy link
Contributor Author

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 a Map, but given that Map.has and Map.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 a Map
  • Set have the same limitations described above and that apply to Map
  • I briefly considered using external modules like immutable-js or custom implementations of Set, but opted not to in order to avoid introducing a dependency over this otherwise limited usage.

Copy link
Contributor

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?

Copy link
Contributor Author

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;
Copy link
Contributor Author

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
Copy link
Contributor Author

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
Copy link
Contributor Author

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
Copy link
Contributor Author

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 {
Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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);
Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor

@ijemmy ijemmy Nov 18, 2022

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.

Copy link
Contributor Author

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.

@dreamorosi dreamorosi linked an issue Nov 17, 2022 that may be closed by this pull request
4 tasks
@dreamorosi dreamorosi changed the title feat(parameters): added BaseProvider class feat(parameters): added BaseProvider class Nov 17, 2022
@dreamorosi dreamorosi marked this pull request as ready for review November 17, 2022 17:43
@dreamorosi dreamorosi added parameters This item relates to the Parameters Utility and removed feature labels Nov 17, 2022
ijemmy
ijemmy previously approved these changes Nov 18, 2022
Copy link
Contributor

@ijemmy ijemmy left a 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;
Copy link
Contributor

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.

packages/parameters/src/BaseProvider.ts Show resolved Hide resolved
}
}

class GetMultipleOptions implements GetMultipleOptionsInterface {
Copy link
Contributor

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();
Copy link
Contributor

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);
Copy link
Contributor

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);
Copy link
Contributor

@ijemmy ijemmy Nov 18, 2022

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.

@pull-request-size pull-request-size bot added size/XXL PRs with 1K+ LOC, largely documentation related and removed size/XL PRs between 500-999 LOC, often PRs that grown with feedback labels Nov 19, 2022
@pull-request-size pull-request-size bot added size/XL PRs between 500-999 LOC, often PRs that grown with feedback and removed size/XXL PRs with 1K+ LOC, largely documentation related labels Nov 19, 2022
@dreamorosi dreamorosi merged commit d717a26 into main Nov 21, 2022
@dreamorosi dreamorosi deleted the feat/parameters/base branch November 21, 2022 10:07
Comment on lines +10 to +16
interface GetMultipleOptionsInterface {
maxAge?: number
forceFetch?: boolean
sdkOptions?: unknown
transform?: string
throwOnTransformError?: boolean
}
Copy link
Contributor

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
}

Copy link
Contributor Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature PRs that introduce new features or minor changes parameters This item relates to the Parameters Utility size/XL PRs between 500-999 LOC, often PRs that grown with feedback
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature request: implement BaseProvider
3 participants