Skip to content
Snippets Groups Projects
Commit 47caffb5 authored by OZGCloud's avatar OZGCloud
Browse files

Merge pull request 'OZG-7000-feature-toggles' (#62) from OZG-7000-feature-toggles into master

parents 5585d2d5 a31866d9
No related branches found
No related tags found
No related merge requests found
Showing
with 271 additions and 26 deletions
......@@ -111,6 +111,18 @@ spec:
value: {{ .Values.zufiManager.address }}
- name: grpc_client_zufi-manager_negotiationType
value: {{ (.Values.zufiManager).grpcClientNegotiationType | default "TLS" }}
{{- if ((.Values.ozgcloud).feature).postfach }}
- name: ozgcloud_feature_postfach
value: {{ .Values.ozgcloud.feature.postfach | quote }}
{{- end }}
{{- if ((.Values.ozgcloud).feature).benutzerRollen }}
- name: ozgcloud_feature_benutzerRollen
value: {{ .Values.ozgcloud.feature.benutzerRollen | quote }}
{{- end }}
{{- if ((.Values.ozgcloud).feature).organisationsEinheiten }}
- name: ozgcloud_feature_organisationsEinheiten
value: {{ .Values.ozgcloud.feature.organisationsEinheiten | quote }}
{{- end }}
envFrom:
{{- if (.Values.database).useExternal }}
- secretRef:
......
package de.ozgcloud.admin.common;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
@Configuration
@ConfigurationProperties(prefix = "ozgcloud.feature")
public class FeatureToggleProperties {
private boolean postfach;
private boolean benutzerRollen;
private boolean organisationsEinheiten;
}
......@@ -34,4 +34,5 @@ public class Environment {
private String authServer;
private String clientId;
private String realm;
private Features features;
}
......@@ -30,6 +30,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import de.ozgcloud.admin.RootController;
import de.ozgcloud.admin.common.FeatureToggleProperties;
import lombok.RequiredArgsConstructor;
@RestController("ozgCloudEnvironmentController")
......@@ -43,6 +44,9 @@ public class EnvironmentController {
private final OAuth2Properties oAuthProperties;
private final FeatureToggleProperties featureToggleProperties;
private final FeaturesMapper featuresMapper;
@GetMapping
public Environment getEnvironment() {
return Environment.builder()
......@@ -51,6 +55,7 @@ public class EnvironmentController {
.authServer(oAuthProperties.getAuthServerUrl())
.realm(oAuthProperties.getRealm())
.clientId(oAuthProperties.getResource())
.features(featuresMapper.fromFeatureToggleProperties(featureToggleProperties))
.build();
}
}
package de.ozgcloud.admin.environment;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class Features {
private final boolean postfach;
private final boolean benutzerRollen;
}
package de.ozgcloud.admin.environment;
import org.mapstruct.Mapper;
import org.mapstruct.NullValueCheckStrategy;
import de.ozgcloud.admin.common.FeatureToggleProperties;
@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
interface FeaturesMapper {
Features fromFeatureToggleProperties(FeatureToggleProperties featureToggleProperties);
}
......@@ -30,14 +30,20 @@ import org.springframework.hateoas.server.RepresentationModelProcessor;
import org.springframework.stereotype.Component;
import de.ozgcloud.admin.Root;
import de.ozgcloud.admin.common.FeatureToggleProperties;
import lombok.RequiredArgsConstructor;
@Component
@RequiredArgsConstructor
class OrganisationsEinheitRootProcessor implements RepresentationModelProcessor<EntityModel<Root>> {
static final String REL_ORGANISATIONS_EINHEITEN = "organisationsEinheiten";
private final FeatureToggleProperties featureToggleProperties;
@Override
public EntityModel<Root> process(EntityModel<Root> model) {
return model.add(linkTo(methodOn(OrganisationsEinheitController.class).getAll()).withRel(REL_ORGANISATIONS_EINHEITEN));
return model.addIf(featureToggleProperties.isOrganisationsEinheiten(),
() -> linkTo(methodOn(OrganisationsEinheitController.class).getAll()).withRel(REL_ORGANISATIONS_EINHEITEN));
}
}
......@@ -4,3 +4,7 @@ ozgcloud:
sync:
organisationseinheiten:
cron: "* */5 * * * *"
feature:
postfach: true
benutzerRollen: true
organisationsEinheiten: true
\ No newline at end of file
......@@ -9,3 +9,9 @@ server:
management:
server:
port: 8081
ozgcloud:
feature:
postfach: true
benutzer-rollen: true
organisations-einheiten: true
\ No newline at end of file
......@@ -17,6 +17,10 @@ ozgcloud:
api:
user: administrationApiUser
password: administrationApiUser
feature:
postfach: true
benutzer-rollen: true
organisations-einheiten: true
grpc:
client:
......
#
# Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
# Ministerpräsidenten des Landes Schleswig-Holstein
# Staatskanzlei
# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
#
# Lizenziert unter der EUPL, Version 1.2 oder - sobald
# diese von der Europäischen Kommission genehmigt wurden -
# Folgeversionen der EUPL ("Lizenz");
# Sie dürfen dieses Werk ausschließlich gemäß
# dieser Lizenz nutzen.
# Eine Kopie der Lizenz finden Sie hier:
#
# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
#
# Sofern nicht durch anwendbare Rechtsvorschriften
# gefordert oder in schriftlicher Form vereinbart, wird
# die unter der Lizenz verbreitete Software "so wie sie
# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
# ausdrücklich oder stillschweigend - verbreitet.
# Die sprachspezifischen Genehmigungen und Beschränkungen
# unter der Lizenz sind dem Lizenztext zu entnehmen.
#
suite: test feature properties
release:
name: administration
namespace: sh-helm-test
templates:
- templates/deployment.yaml
set:
ozgcloud:
bundesland: sh
bezeichner: helm
sso:
serverUrl: https://sso.company.local
imagePullSecret: image-pull-secret
tests:
- it: should not contain feature properties if values are missing
asserts:
- notContains:
path: spec.template.spec.containers[0].env
content:
name: ozgcloud_feature_postfach
any: true
- notContains:
path: spec.template.spec.containers[0].env
content:
name: ozgcloud_feature_benutzerRollen
any: true
- notContains:
path: spec.template.spec.containers[0].env
content:
name: ozgcloud_feature_organisationsEinheiten
any: true
- it: should set (optional) feature properties
set:
ozgcloud:
feature:
postfach: true
benutzerRollen: true
organisationsEinheiten: true
asserts:
- contains:
path: spec.template.spec.containers[0].env
content:
name: ozgcloud_feature_postfach
value: "true"
- contains:
path: spec.template.spec.containers[0].env
content:
name: ozgcloud_feature_benutzerRollen
value: "true"
- contains:
path: spec.template.spec.containers[0].env
content:
name: ozgcloud_feature_organisationsEinheiten
value: "true"
\ No newline at end of file
......@@ -40,6 +40,7 @@ import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import de.ozgcloud.admin.RootController;
import de.ozgcloud.admin.common.FeatureToggleProperties;
import lombok.SneakyThrows;
class EnvironmentControllerTest {
......@@ -52,6 +53,10 @@ class EnvironmentControllerTest {
private ProductionProperties environmentProperties;
@Mock
private OAuth2Properties oAuth2Properties;
@Mock
private FeatureToggleProperties featureToggleProperties;
@Mock
private FeaturesMapper featuresMapper;
private MockMvc mockMvc;
......@@ -72,19 +77,6 @@ class EnvironmentControllerTest {
response.andExpect(status().isOk());
}
@Nested
class TestBody {
@Test
@SneakyThrows
void shouldCallIsProduction() {
when(environmentProperties.isProduction()).thenReturn(true);
doRequest();
verify(environmentProperties).isProduction();
}
@ParameterizedTest
@ValueSource(booleans = { true, false })
@SneakyThrows
......@@ -95,7 +87,6 @@ class EnvironmentControllerTest {
response.andExpect(jsonPath("$.production").value(production));
}
}
@Test
@SneakyThrows
......@@ -138,6 +129,35 @@ class EnvironmentControllerTest {
response.andExpect(jsonPath("$.clientId").value(clientId));
}
@Test
void shouldCallFeaturesMapper() {
doRequest();
verify(featuresMapper).fromFeatureToggleProperties(featureToggleProperties);
}
@SneakyThrows
@ParameterizedTest
@ValueSource(booleans = { true, false })
void shouldContainPostfach(boolean postfach) {
when(featuresMapper.fromFeatureToggleProperties(featureToggleProperties)).thenReturn(Features.builder().postfach(postfach).build());
var response = doRequest();
response.andExpect(jsonPath("$.features.postfach").value(postfach));
}
@SneakyThrows
@ParameterizedTest
@ValueSource(booleans = { true, false })
void shouldContainBenutzerRollen(boolean benutzerRollen) {
when(featuresMapper.fromFeatureToggleProperties(featureToggleProperties)).thenReturn(Features.builder().benutzerRollen(benutzerRollen).build());
var response = doRequest();
response.andExpect(jsonPath("$.features.benutzerRollen").value(benutzerRollen));
}
@SneakyThrows
private ResultActions doRequest() {
return mockMvc.perform(get(EnvironmentController.PATH));
......
package de.ozgcloud.admin.environment;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mapstruct.factory.Mappers;
import de.ozgcloud.admin.common.FeatureToggleProperties;
class FeaturesMapperTest {
private FeaturesMapper mapper = Mappers.getMapper(FeaturesMapper.class);
@Nested
class TestFromFeatureToggleProperties {
private final FeatureToggleProperties props = new FeatureToggleProperties();
@ParameterizedTest
@ValueSource(booleans = {true, false})
void shouldMapPostfach(boolean postfach) {
props.setPostfach(postfach);
var features = mapper.fromFeatureToggleProperties(props);
assertThat(features.isPostfach()).isEqualTo(postfach);
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
void shouldMapBenutzerRollen(boolean benutzerRollen) {
props.setBenutzerRollen(benutzerRollen);
var features = mapper.fromFeatureToggleProperties(props);
assertThat(features.isBenutzerRollen()).isEqualTo(benutzerRollen);
}
}
}
......@@ -24,19 +24,24 @@
package de.ozgcloud.admin.organisationseinheit;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import de.ozgcloud.admin.RootTestFactory;
import de.ozgcloud.admin.common.FeatureToggleProperties;
class OrganisationsEinheitRootProcessorTest {
@InjectMocks
private OrganisationsEinheitRootProcessor organisationsEinheitRootProcessor;
@Mock
private FeatureToggleProperties featureToggleProperties;
@Nested
class TestProcess {
......@@ -45,14 +50,27 @@ class OrganisationsEinheitRootProcessorTest {
class OrganisationsEinheitenLinkRelation {
@Test
void shouldExists() {
void shouldExistsIfFeatureEnabled() {
givenFeatureIsEnabled();
var model = organisationsEinheitRootProcessor.process(EntityModel.of(RootTestFactory.create()));
assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_ORGANISATIONS_EINHEITEN)).isNotEmpty();
}
@Test
void shouldNotExistIfFeatureDisabled() {
givenFeatureIsDisabled();
var model = organisationsEinheitRootProcessor.process(EntityModel.of(RootTestFactory.create()));
assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_ORGANISATIONS_EINHEITEN)).isEmpty();
}
@Test
void shouldHaveHref() {
givenFeatureIsEnabled();
var model = organisationsEinheitRootProcessor.process(EntityModel.of(RootTestFactory.create()));
assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_ORGANISATIONS_EINHEITEN))
......@@ -60,6 +78,14 @@ class OrganisationsEinheitRootProcessorTest {
.extracting(Link::getHref)
.isEqualTo(OrganisationsEinheitController.PATH);
}
private void givenFeatureIsEnabled() {
when(featureToggleProperties.isOrganisationsEinheiten()).thenReturn(true);
}
private void givenFeatureIsDisabled() {
when(featureToggleProperties.isOrganisationsEinheiten()).thenReturn(false);
}
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment