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
|
|
@ -64,6 +64,7 @@
|
||||||
<property name="optional" value="true"/>
|
<property name="optional" value="true"/>
|
||||||
</module>
|
</module>
|
||||||
|
|
||||||
|
<module name="UnusedImports"/>
|
||||||
<module name="OuterTypeFilename"/>
|
<module name="OuterTypeFilename"/>
|
||||||
<module name="IllegalTokenText">
|
<module name="IllegalTokenText">
|
||||||
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ public abstract class EventProvider implements FeatureProvider {
|
||||||
* No-op if the same onEmit is already attached.
|
* 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) {
|
void attach(TriConsumer<EventProvider, ProviderEvent, ProviderEventDetails> onEmit) {
|
||||||
if (this.onEmit != null && this.onEmit != onEmit) {
|
if (this.onEmit != null && this.onEmit != onEmit) {
|
||||||
|
|
|
||||||
|
|
@ -106,11 +106,16 @@ class ProviderRepository {
|
||||||
BiConsumer<FeatureProvider, String> afterError,
|
BiConsumer<FeatureProvider, String> afterError,
|
||||||
boolean waitForInit) {
|
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
|
// provider is set immediately, on this thread
|
||||||
FeatureProvider oldProvider = clientName != null
|
FeatureProvider oldProvider = clientName != null
|
||||||
? this.providers.put(clientName, newProvider)
|
? this.providers.put(clientName, newProvider)
|
||||||
: this.defaultProvider.getAndSet(newProvider);
|
: this.defaultProvider.getAndSet(newProvider);
|
||||||
afterSet.accept(newProvider);
|
|
||||||
if (waitForInit) {
|
if (waitForInit) {
|
||||||
initializeProvider(newProvider, afterInit, afterShutdown, afterError, oldProvider);
|
initializeProvider(newProvider, afterInit, afterShutdown, afterError, oldProvider);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -138,16 +143,21 @@ class ProviderRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shutDownOld(FeatureProvider oldProvider,Consumer<FeatureProvider> afterShutdown) {
|
private void shutDownOld(FeatureProvider oldProvider, Consumer<FeatureProvider> afterShutdown) {
|
||||||
if (oldProvider != null && !isProviderRegistered(oldProvider)) {
|
if (!isProviderRegistered(oldProvider)) {
|
||||||
shutdownProvider(oldProvider);
|
shutdownProvider(oldProvider);
|
||||||
afterShutdown.accept(oldProvider);
|
afterShutdown.accept(oldProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isProviderRegistered(FeatureProvider oldProvider) {
|
/**
|
||||||
return oldProvider != null && (this.providers.containsValue(oldProvider)
|
* Helper to check if provider is already known (registered).
|
||||||
|| this.defaultProvider.get().equals(oldProvider));
|
* @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) {
|
private void shutdownProvider(FeatureProvider provider) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
package dev.openfeature.sdk;
|
package dev.openfeature.sdk;
|
||||||
|
|
||||||
|
import dev.openfeature.sdk.providers.memory.InMemoryProvider;
|
||||||
import dev.openfeature.sdk.testutils.FeatureProviderTestUtils;
|
import dev.openfeature.sdk.testutils.FeatureProviderTestUtils;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
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.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
class OpenFeatureAPITest {
|
class OpenFeatureAPITest {
|
||||||
|
|
||||||
|
|
@ -40,6 +44,25 @@ class OpenFeatureAPITest {
|
||||||
.isEqualTo(DoSomethingProvider.name);
|
.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
|
@Test
|
||||||
void settingDefaultProviderToNullErrors() {
|
void settingDefaultProviderToNullErrors() {
|
||||||
assertThatCode(() -> api.setProvider(null)).isInstanceOf(IllegalArgumentException.class);
|
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.awaitility.Awaitility.await;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.atMostOnce;
|
||||||
import static org.mockito.Mockito.doThrow;
|
import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
import static org.awaitility.Awaitility.await;
|
import static org.awaitility.Awaitility.await;
|
||||||
|
|
||||||
|
// todo check the need of this utility class as we now have setProviderAndWait capability
|
||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class FeatureProviderTestUtils {
|
public class FeatureProviderTestUtils {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue