feat: add GCP Secret Manager OpenFeature provider#1772
feat: add GCP Secret Manager OpenFeature provider#1772mahpatil wants to merge 1 commit intoopen-feature:mainfrom
Conversation
Adds a new OpenFeature provider for GCP Secret Manager that enables reading feature flags from GCP Secret Manager secrets. Includes the provider implementation with flag caching, value conversion, unit tests, integration tests, and a sample application demonstrating usage.
There was a problem hiding this comment.
Code Review
This pull request introduces a new OpenFeature provider for Google Cloud Secret Manager, enabling feature flags to be managed as GCP secrets. The changes include the core provider implementation with caching and value conversion, along with comprehensive unit and integration tests, and a sample application. Feedback suggests adding an initial entry to the new module's CHANGELOG.md, refining the spotbugs-exclusions.xml to only include relevant exclusions, and improving the testability of FlagCache by injecting a Clock instance instead of relying on Instant.now() and Thread.sleep().
| @@ -0,0 +1 @@ | |||
|
|
|||
There was a problem hiding this comment.
A new module should have an initial entry in its CHANGELOG.md to document its creation and initial version. This helps maintain a clear history of changes from the very beginning.
| # Changelog | |
| ## 0.0.1 | |
| ### ✨ New Features | |
| * Initial release of the GCP Secret Manager OpenFeature provider. |
| <!-- Ignore generated dev.openfeature.flagd.grpc schemas --> | ||
| <Match> | ||
| <Package name="~dev\.openfeature\.flagd\.grpc.*" /> | ||
| </Match> | ||
|
|
||
| <!-- Ignore generated dev.openfeature.flagd.sync schemas --> | ||
| <Match> | ||
| <Package name="~dev\.openfeature\.flagd\.sync.*" /> | ||
| </Match> | ||
|
|
||
| <!-- Allow predictable random for backoff jitter generation --> | ||
| <Match> | ||
| <Class name="dev.openfeature.contrib.providers.flagd.resolver.common.backoff.BackoffService" /> | ||
| <Bug pattern="PREDICTABLE_RANDOM" /> | ||
| </Match> | ||
|
|
||
| <!-- Suppress: EI_EXPOSE_REP2 in ModuleMachine --> | ||
| <Match> | ||
| <Class name="dev.openfeature.contrib.providers.gofeatureflag.wasm.ModuleMachine"/> | ||
| <Bug pattern="EI_EXPOSE_REP2"/> | ||
| </Match> | ||
|
|
||
| <!-- Suppress: SKIPPED_CLASS_TOO_BIG in ModuleMachineFuncGroup_0 --> | ||
| <Match> | ||
| <Class name="dev.openfeature.contrib.providers.gofeatureflag.wasm.ModuleMachineFuncGroup_0"/> | ||
| <Bug pattern="SKIPPED_CLASS_TOO_BIG"/> | ||
| </Match> | ||
|
|
||
| <!-- All bugs in test classes, except for JUnit-specific bugs --> | ||
| <Match> | ||
| <Class name="~.*\.*Test" /> | ||
| <Not> | ||
| <Bug code="IJU" /> | ||
| </Not> | ||
| </Match> |
There was a problem hiding this comment.
This spotbugs-exclusions.xml file is specific to the gcp-secret-manager provider. It should ideally only contain exclusions relevant to this module. The current file includes exclusions for other modules, which indicates a copy-paste error and makes the configuration less maintainable.
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter xmlns="https://github.com/spotbugs/filter/3.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd">
<!-- All bugs in test classes, except for JUnit-specific bugs -->
<Match>
<Class name="~.*\.*Test" />
<Not>
<Bug code="IJU" />
</Not>
</Match>
</FindBugsFilter>
| } | ||
|
|
||
| boolean isExpired() { | ||
| return Instant.now().isAfter(expiresAt); | ||
| } |
There was a problem hiding this comment.
Using Instant.now() directly within CacheEntry makes time-sensitive logic harder to test deterministically. Consider injecting a Clock instance into FlagCache and subsequently into CacheEntry to allow for better testability and control over time.
CacheEntry(String value, Duration ttl, Clock clock) {
this.value = value;
this.expiresAt = Instant.now(clock).plus(ttl);
}
boolean isExpired() {
return Instant.now(clock).isAfter(expiresAt);
}|
|
||
| @Test | ||
| @DisplayName("get() returns empty after TTL expires") | ||
| void getAfterTtlExpiry() throws InterruptedException { |
There was a problem hiding this comment.
Using Thread.sleep() in tests can lead to flaky tests and slow down the test suite. For time-sensitive logic like cache expiry, it's better to use a Clock abstraction (e.g., java.time.Clock) that can be mocked or controlled in tests. This would make tests deterministic and faster.
// Simulate time passing using a mocked Clock if FlagCache is refactored to accept one
Summary
samples/gcp-secret-manager-sample/with setup/teardown scriptsProvider Details
providers/gcp-secret-managerGcpSecretManagerProviderTest plan
FlagCache,FlagValueConverter, andGcpSecretManagerProviderGcpSecretManagerProviderIntegrationTest) requires a real GCP project — setGCP_PROJECT_IDenv var to runsamples/gcp-secret-manager-sample/README.mdto run end-to-end