Skip to content

Commit

Permalink
FACT-1431 Launch Darkly (#1531)
Browse files Browse the repository at this point in the history
* Suppression date update:

* Added infra and pipeline set up

* Bumping chart version/ fixing aliases

* Added main launch darkly code

* Added unit tests

* Added int test

* load secrets after test for int

* Added debug

* Switched around test package for LD test

* clean up

* Moved to smoke tests

* Update int props

* Removed not needed func test

---------

Co-authored-by: hmcts-jenkins-j-to-z <61242337+hmcts-jenkins-j-to-z[bot]@users.noreply.github.com>
  • Loading branch information
joshblackmoor and hmcts-jenkins-j-to-z[bot] committed Jan 12, 2024
1 parent 309a01c commit 5185f4f
Show file tree
Hide file tree
Showing 17 changed files with 255 additions and 35 deletions.
8 changes: 6 additions & 2 deletions Jenkinsfile_CNP
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ GradleBuilder builder = new GradleBuilder(this, product)
def nonPrSecrets = [
'reform-scan-${env}': [
secret('notification-staging-queue-send-shared-access-key', 'NOTIFICATION_QUEUE_ACCESS_KEY_WRITE'),
secret('test-s2s-secret', 'TEST_S2S_SECRET')
secret('test-s2s-secret', 'TEST_S2S_SECRET'),
secret('launch-darkly-sdk-key', 'LAUNCH_DARKLY_SDK_KEY'),
secret('launch-darkly-offline-mode', 'LAUNCH_DARKLY_OFFLINE_MODE')
]
]

def prSecrets = [
'reform-scan-${env}': [
secret('test-s2s-secret', 'TEST_S2S_SECRET')
secret('test-s2s-secret', 'TEST_S2S_SECRET'),
secret('launch-darkly-sdk-key', 'LAUNCH_DARKLY_SDK_KEY'),
secret('launch-darkly-offline-mode', 'LAUNCH_DARKLY_OFFLINE_MODE')
]
]

Expand Down
4 changes: 3 additions & 1 deletion Jenkinsfile_nightly
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ def component = "notification-service"
def secrets = [
'reform-scan-${env}': [
secret('fortify-on-demand-username', 'FORTIFY_USER_NAME'),
secret('fortify-on-demand-password', 'FORTIFY_PASSWORD')
secret('fortify-on-demand-password', 'FORTIFY_PASSWORD'),
secret('launch-darkly-sdk-key', 'LAUNCH_DARKLY_SDK_KEY'),
secret('launch-darkly-offline-mode', 'LAUNCH_DARKLY_OFFLINE_MODE')
]
]

Expand Down
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ dependencies {

implementation group: 'org.apache.qpid', name: 'qpid-jms-client', version: '1.11.0'

implementation group: 'com.launchdarkly', name: 'launchdarkly-java-server-sdk', version: '6.3.0'

testImplementation libraries.junit5
testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test', {
exclude group: 'junit', module: 'junit'
Expand All @@ -257,6 +259,7 @@ dependencies {
integrationTestImplementation group: 'org.testcontainers', name: 'junit-jupiter', version: '1.19.3'

smokeTestImplementation sourceSets.main.runtimeClasspath
smokeTestImplementation sourceSets.test.runtimeClasspath
smokeTestImplementation libraries.junit5
smokeTestImplementation group: 'org.assertj', name: 'assertj-core', version: '3.24.2'
smokeTestImplementation group: 'com.typesafe', name: 'config', version: '1.4.3'
Expand Down
2 changes: 1 addition & 1 deletion charts/reform-scan-notification-service/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: v2
description: A Helm chart for Reform Scan Notification Service
name: reform-scan-notification-service
home: https://github.com/hmcts/reform-scan-notification-service
version: 2.0.9
version: 2.0.10
maintainers:
- name: HMCTS BSP Team
email: bspteam@hmcts.net
Expand Down
4 changes: 4 additions & 0 deletions charts/reform-scan-notification-service/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ java:
alias: QUEUE_READ_ACCESS_KEY
- name: app-insights-connection-string
alias: app-insights-connection-string
- name: launch-darkly-sdk-key
alias: LAUNCH_DARKLY_SDK_KEY
- name: launch-darkly-offline-mode
alias: LAUNCH_DARKLY_OFFLINE_MODE
environment:
DB_CONN_OPTIONS: ?sslmode=require
FLYWAY_SKIP_MIGRATIONS: true
Expand Down
11 changes: 11 additions & 0 deletions infrastructure/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,14 @@ resource "azurerm_key_vault_secret" "test_s2s_secret" {
}

# endregion

# Create secrets for Launch darkly - values manually populated
data "azurerm_key_vault_secret" "launch_darkly_sdk_key" {
name = "launch-darkly-sdk-key"
key_vault_id = data.azurerm_key_vault.reform_scan_key_vault.id
}

data "azurerm_key_vault_secret" "launch_darkly_offline_mode" {
name = "launch-darkly-offline-mode"
key_vault_id = data.azurerm_key_vault.reform_scan_key_vault.id
}
1 change: 0 additions & 1 deletion src/integrationTest/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,3 @@ scheduling.task.pending-notifications.send-delay-in-minute=60
scheduling.task.notifications-consume.enabled=false
scheduling.task.notifications-consume.check.delay=1000000
idam.s2s-auth.url=false

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package uk.gov.hmcts.reform.notificationservice.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import uk.gov.hmcts.reform.notificationservice.launchdarkly.LaunchDarklyClient;

import static org.springframework.http.ResponseEntity.ok;

@RestController
public class FeatureFlagController {
private final LaunchDarklyClient featureToggleService;

public FeatureFlagController(LaunchDarklyClient featureToggleService) {
this.featureToggleService = featureToggleService;
}

@GetMapping("/feature-flags/{flag}")
public ResponseEntity<String> flagStatus(@PathVariable String flag) {
boolean isEnabled = featureToggleService.isFeatureEnabled(flag);
return ok(flag + " : " + isEnabled);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package uk.gov.hmcts.reform.notificationservice.launchdarkly;

public final class Flags {
private Flags() {

}

public static final String REFORM_SCAN_NOTIFICATION_SERVICE_TEST = "reform-scan-notification-service-test";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package uk.gov.hmcts.reform.notificationservice.launchdarkly;


import com.launchdarkly.sdk.LDUser;
import com.launchdarkly.sdk.server.interfaces.DataSourceStatusProvider;
import com.launchdarkly.sdk.server.interfaces.LDClientInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class LaunchDarklyClient {
public static final LDUser REFORM_SCAN_NOTIFICATION_SERVICE_USER = new LDUser.Builder("reform-scan-notification"
+ "-service")
.anonymous(true)
.build();

private final LDClientInterface internalClient;

@Autowired
public LaunchDarklyClient(
LaunchDarklyClientFactory launchDarklyClientFactory,
@Value("${launchdarkly.sdk-key:YYYYY}") String sdkKey,
@Value("${launchdarkly.offline-mode:false}") Boolean offlineMode
) {
this.internalClient = launchDarklyClientFactory.create(sdkKey, offlineMode);
}

public boolean isFeatureEnabled(String feature) {
return internalClient.boolVariation(feature, LaunchDarklyClient.REFORM_SCAN_NOTIFICATION_SERVICE_USER,
false);
}

public boolean isFeatureEnabled(String feature, LDUser user) {
return internalClient.boolVariation(feature, user, false);
}

public DataSourceStatusProvider.Status getDataSourceStatus() {
return internalClient.getDataSourceStatusProvider().getStatus();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package uk.gov.hmcts.reform.notificationservice.launchdarkly;

import com.launchdarkly.sdk.server.LDClient;
import com.launchdarkly.sdk.server.LDConfig;
import com.launchdarkly.sdk.server.interfaces.LDClientInterface;
import org.springframework.stereotype.Service;

@Service
public class LaunchDarklyClientFactory {
public LDClientInterface create(String sdkKey, boolean offlineMode) {
LDConfig config = new LDConfig.Builder()
.offline(offlineMode)
.build();
return new LDClient(sdkKey, config);
}
}
5 changes: 5 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,8 @@ jms:
enabled: ${JMS_ENABLED:false}

# end of clients region

launchdarkly:
sdk-key: ${LAUNCH_DARKLY_SDK_KEY:XXXXX}
offline-mode: ${LAUNCH_DARKLY_OFFLINE_MODE:false}

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package uk.gov.hmcts.reform.notificationservice;

import com.launchdarkly.sdk.server.interfaces.DataSourceStatusProvider;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import uk.gov.hmcts.reform.notificationservice.launchdarkly.LaunchDarklyClient;
import uk.gov.hmcts.reform.notificationservice.launchdarkly.LaunchDarklyClientFactory;

import static org.assertj.core.api.Assertions.assertThat;

@TestPropertySource("classpath:application.conf")
@ExtendWith(SpringExtension.class)
class LaunchDarklySmokeTest {

@MockBean
private LaunchDarklyClient ldClient;
@MockBean
private LaunchDarklyClientFactory ldFactory;

@Value("${sdk-key:YYYYY}")
private String sdkKey;

@Value("${offline-mode:false}")
private Boolean offlineMode;

@BeforeEach
void setUp() {
ldFactory = new LaunchDarklyClientFactory();
ldClient = new LaunchDarklyClient(ldFactory, sdkKey, offlineMode);
}

@Test
void checkLaunchDarklyStatus() {
DataSourceStatusProvider.Status ldStatus = ldClient.getDataSourceStatus();
assertThat(ldStatus.getState()).isEqualTo(DataSourceStatusProvider.State.VALID);
}
}

This file was deleted.

2 changes: 2 additions & 0 deletions src/smokeTest/resources/application.conf
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
test-url = "http://localhost:8583"
test-url = ${?TEST_URL}
offline-mode=${LAUNCH_DARKLY_OFFLINE_MODE}
sdk-key=${LAUNCH_DARKLY_SDK_KEY}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package uk.gov.hmcts.reform.notificationservice.launchdarkly;

import com.launchdarkly.sdk.server.interfaces.LDClientInterface;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;

import static org.assertj.core.api.Assertions.assertThat;

class LaunchDarklyClientFactoryTest {
private LaunchDarklyClientFactory factory;

@BeforeEach
void setUp() {
factory = new LaunchDarklyClientFactory();
}

@Test
void testCreate() throws IOException {
try (LDClientInterface client = factory.create("test key", true)) {
assertThat(client).isNotNull();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package uk.gov.hmcts.reform.notificationservice.launchdarkly;

import com.launchdarkly.sdk.LDUser;
import com.launchdarkly.sdk.server.interfaces.LDClientInterface;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class LaunchDarklyClientTest {
private static final String SDK_KEY = "fake-key";
private static final String FAKE_FEATURE = "fake-feature";

@Mock
private LaunchDarklyClientFactory launchDarklyClientFactory;

@Mock
private LDClientInterface ldClient;

@Mock
private LDUser ldUser;

private LaunchDarklyClient launchDarklyClient;

@BeforeEach
void setUp() {
when(launchDarklyClientFactory.create(eq(SDK_KEY), anyBoolean())).thenReturn(ldClient);
launchDarklyClient = new LaunchDarklyClient(launchDarklyClientFactory, SDK_KEY, true);
}

@Test
void testFeatureEnabled() {
when(ldClient.boolVariation(eq(FAKE_FEATURE), any(LDUser.class), anyBoolean())).thenReturn(true);
assertTrue(launchDarklyClient.isFeatureEnabled(FAKE_FEATURE, ldUser));
}

@Test
void testFeatureDisabled() {
when(ldClient.boolVariation(eq(FAKE_FEATURE), any(LDUser.class), anyBoolean())).thenReturn(false);
assertFalse(launchDarklyClient.isFeatureEnabled(FAKE_FEATURE, ldUser));
}

@Test
void testFeatureEnabledWithoutUser() {
when(ldClient.boolVariation(eq(FAKE_FEATURE), any(LDUser.class), anyBoolean())).thenReturn(true);
assertTrue(launchDarklyClient.isFeatureEnabled(FAKE_FEATURE));
}

@Test
void testFeatureDisabledWithoutUser() {
when(ldClient.boolVariation(eq(FAKE_FEATURE), any(LDUser.class), anyBoolean())).thenReturn(false);
assertFalse(launchDarklyClient.isFeatureEnabled(FAKE_FEATURE));
}
}

0 comments on commit 5185f4f

Please sign in to comment.