fix: tolerate duplicate provider registrations (#725)
* fix provider mulitple regiration issue Signed-off-by: Kavindu Dodanduwa <kavindudodanduwa@gmail.com> * fix lint Signed-off-by: Kavindu Dodanduwa <kavindudodanduwa@gmail.com> * fix tests Signed-off-by: Kavindu Dodanduwa <kavindudodanduwa@gmail.com> * improve test and add check for unused imports Signed-off-by: Kavindu Dodanduwa <kavindudodanduwa@gmail.com> --------- Signed-off-by: Kavindu Dodanduwa <kavindudodanduwa@gmail.com>
This commit is contained in:
parent
07ea4c02cb
commit
3319e55870
|
|
@ -63,7 +63,8 @@
|
|||
default="checkstyle-xpath-suppressions.xml" />
|
||||
<property name="optional" value="true"/>
|
||||
</module>
|
||||
|
||||
|
||||
<module name="UnusedImports"/>
|
||||
<module name="OuterTypeFilename"/>
|
||||
<module name="IllegalTokenText">
|
||||
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ public abstract class EventProvider implements FeatureProvider {
|
|||
* "Attach" this EventProvider to an SDK, which allows events to propagate from this provider.
|
||||
* No-op if the same onEmit is already attached.
|
||||
*
|
||||
* @param onEmit the function to run when a provider emits events.
|
||||
* @param onEmit the function to run when a provider emits events.
|
||||
* @throws IllegalStateException if attempted to bind a new emitter for already bound provider
|
||||
*
|
||||
*/
|
||||
void attach(TriConsumer<EventProvider, ProviderEvent, ProviderEventDetails> onEmit) {
|
||||
if (this.onEmit != null && this.onEmit != onEmit) {
|
||||
|
|
|
|||
|
|
@ -106,11 +106,16 @@ class ProviderRepository {
|
|||
BiConsumer<FeatureProvider, String> afterError,
|
||||
boolean waitForInit) {
|
||||
|
||||
if (!isProviderRegistered(newProvider)) {
|
||||
// only run afterSet if new provider is not already attached
|
||||
afterSet.accept(newProvider);
|
||||
}
|
||||
|
||||
// provider is set immediately, on this thread
|
||||
FeatureProvider oldProvider = clientName != null
|
||||
? this.providers.put(clientName, newProvider)
|
||||
: this.defaultProvider.getAndSet(newProvider);
|
||||
afterSet.accept(newProvider);
|
||||
? this.providers.put(clientName, newProvider)
|
||||
: this.defaultProvider.getAndSet(newProvider);
|
||||
|
||||
if (waitForInit) {
|
||||
initializeProvider(newProvider, afterInit, afterShutdown, afterError, oldProvider);
|
||||
} else {
|
||||
|
|
@ -138,16 +143,21 @@ class ProviderRepository {
|
|||
}
|
||||
}
|
||||
|
||||
private void shutDownOld(FeatureProvider oldProvider,Consumer<FeatureProvider> afterShutdown) {
|
||||
if (oldProvider != null && !isProviderRegistered(oldProvider)) {
|
||||
private void shutDownOld(FeatureProvider oldProvider, Consumer<FeatureProvider> afterShutdown) {
|
||||
if (!isProviderRegistered(oldProvider)) {
|
||||
shutdownProvider(oldProvider);
|
||||
afterShutdown.accept(oldProvider);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isProviderRegistered(FeatureProvider oldProvider) {
|
||||
return oldProvider != null && (this.providers.containsValue(oldProvider)
|
||||
|| this.defaultProvider.get().equals(oldProvider));
|
||||
/**
|
||||
* Helper to check if provider is already known (registered).
|
||||
* @param provider provider to check for registration
|
||||
* @return boolean true if already registered, false otherwise
|
||||
*/
|
||||
private boolean isProviderRegistered(FeatureProvider provider) {
|
||||
return provider != null
|
||||
&& (this.providers.containsValue(provider) || this.defaultProvider.get().equals(provider));
|
||||
}
|
||||
|
||||
private void shutdownProvider(FeatureProvider provider) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
package dev.openfeature.sdk;
|
||||
|
||||
import dev.openfeature.sdk.providers.memory.InMemoryProvider;
|
||||
import dev.openfeature.sdk.testutils.FeatureProviderTestUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class OpenFeatureAPITest {
|
||||
|
||||
|
|
@ -40,6 +44,25 @@ class OpenFeatureAPITest {
|
|||
.isEqualTo(DoSomethingProvider.name);
|
||||
}
|
||||
|
||||
@Test
|
||||
void providerToMultipleNames() {
|
||||
FeatureProvider inMemAsEventingProvider = new InMemoryProvider(Collections.EMPTY_MAP);
|
||||
FeatureProvider noOpAsNonEventingProvider = new NoOpProvider();
|
||||
|
||||
// register same provider for multiple names & as default provider
|
||||
OpenFeatureAPI.getInstance().setProviderAndWait(inMemAsEventingProvider);
|
||||
OpenFeatureAPI.getInstance().setProviderAndWait("clientA", inMemAsEventingProvider);
|
||||
OpenFeatureAPI.getInstance().setProviderAndWait("clientB", inMemAsEventingProvider);
|
||||
OpenFeatureAPI.getInstance().setProviderAndWait("clientC", noOpAsNonEventingProvider);
|
||||
OpenFeatureAPI.getInstance().setProviderAndWait("clientD", noOpAsNonEventingProvider);
|
||||
|
||||
assertEquals(inMemAsEventingProvider, OpenFeatureAPI.getInstance().getProvider());
|
||||
assertEquals(inMemAsEventingProvider, OpenFeatureAPI.getInstance().getProvider("clientA"));
|
||||
assertEquals(inMemAsEventingProvider, OpenFeatureAPI.getInstance().getProvider("clientB"));
|
||||
assertEquals(noOpAsNonEventingProvider, OpenFeatureAPI.getInstance().getProvider("clientC"));
|
||||
assertEquals(noOpAsNonEventingProvider, OpenFeatureAPI.getInstance().getProvider("clientD"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void settingDefaultProviderToNullErrors() {
|
||||
assertThatCode(() -> api.setProvider(null)).isInstanceOf(IllegalArgumentException.class);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.assertThatCode;
|
|||
import static org.awaitility.Awaitility.await;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.atMostOnce;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import lombok.experimental.UtilityClass;
|
|||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
|
||||
// todo check the need of this utility class as we now have setProviderAndWait capability
|
||||
@UtilityClass
|
||||
public class FeatureProviderTestUtils {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue