diff --git a/docs/pages/dev/how_to/exclude_featureflag_related_module/exclude_featureflag_related_module.md b/docs/pages/dev/how_to/exclude_featureflag_related_module/exclude_featureflag_related_module.md new file mode 100644 index 0000000000..1b88ee2052 --- /dev/null +++ b/docs/pages/dev/how_to/exclude_featureflag_related_module/exclude_featureflag_related_module.md @@ -0,0 +1,15 @@ +# How to exclude a featureflag related to a module + +## 1. Add a new module for which to exclude its related feature flag + +- Go to `/hat/menupermissions/constants.py` +- Add to the dictionnary `FEATUREFLAGES_TO_EXCLUDE` a new item +- The key is the name of the module like `DATA_COLLECTION_FORMS` in capitale letter +- The value is a list of featureflags(code in capitale letter) to be excluded like `["FEATUREFLAG_1","FEATUREFLAG_2"]` +- The whole `FEATUREFLAGES_TO_EXCLUDE` should be like `FEATUREFLAGES_TO_EXCLUDE = { "MODULE_1": ["FEATUREFLAG_1"], "MODULE_2": ["FEATUREFLAG_2", "FEATUREFLAG_3"],}` + +## 2. Add a feature flag to an existing module to be excluded + +- Go to `/hat/menupermissions/constants.py` +- Check the key corresponding to the module +- Add the featureflag code to the value list of the corresponding module(key) \ No newline at end of file diff --git a/hat/assets/js/apps/Iaso/domains/projects/hooks/requests.ts b/hat/assets/js/apps/Iaso/domains/projects/hooks/requests.ts index c4845bcf0d..5a11befb2b 100644 --- a/hat/assets/js/apps/Iaso/domains/projects/hooks/requests.ts +++ b/hat/assets/js/apps/Iaso/domains/projects/hooks/requests.ts @@ -62,7 +62,7 @@ export const useGetFeatureFlags = (): UseQueryResult< // @ts-ignore return useSnackQuery( ['featureflags'], - () => getRequest('/api/featureflags/'), + () => getRequest('/api/featureflags/except_no_activated_modules/'), undefined, { // using this here to avoid multiple identical calls diff --git a/hat/assets/js/cypress/integration/09 - projects/list.spec.js b/hat/assets/js/cypress/integration/09 - projects/list.spec.js index 62611ef773..f0193bc50c 100644 --- a/hat/assets/js/cypress/integration/09 - projects/list.spec.js +++ b/hat/assets/js/cypress/integration/09 - projects/list.spec.js @@ -31,7 +31,7 @@ const goToPage = ( interceptFlag = false; cy.intercept('GET', '/sockjs-node/**'); cy.intercept('GET', '/api/profiles/me/**', fakeUser); - cy.intercept('GET', '/api/featureflags', { + cy.intercept('GET', '/api/featureflags/except_no_activated_modules/', { fixture: 'featureflags/list.json', }).as('getFeatureFlags'); const options = { diff --git a/hat/menupermissions/constants.py b/hat/menupermissions/constants.py index 563bb2011f..31170da595 100644 --- a/hat/menupermissions/constants.py +++ b/hat/menupermissions/constants.py @@ -61,3 +61,15 @@ {"name": "Registry", "codename": "REGISTRY"}, {"name": "Payments", "codename": "PAYMENTS"}, ] + +FEATUREFLAGES_TO_EXCLUDE = { + "PLANNING": ["PLANNING"], + "ENTITIES": [ + "REPORTS", + "ENTITY", + "MOBILE_ENTITY_WARN_WHEN_FOUND", + "MOBILE_ENTITY_LIMITED_SEARCH", + "MOBILE_ENTITY_NO_CREATION", + "WRITE_ON_NFC_CARDS", + ], +} diff --git a/iaso/api/feature_flags.py b/iaso/api/feature_flags.py index d73ea299ff..751867703c 100644 --- a/iaso/api/feature_flags.py +++ b/iaso/api/feature_flags.py @@ -1,7 +1,10 @@ from rest_framework import serializers, permissions - +from rest_framework.decorators import action +from rest_framework.response import Response from iaso.models import FeatureFlag from .common import ModelViewSet, TimestampField +from hat.menupermissions.constants import FEATUREFLAGES_TO_EXCLUDE +from itertools import chain class FeatureFlagsSerializer(serializers.ModelSerializer): @@ -28,7 +31,27 @@ class FeatureFlagViewSet(ModelViewSet): results_key = "featureflags" http_method_names = ["get", "head", "options"] + def get_results_key(self): + return self.results_key + def get_queryset(self): featureflags = FeatureFlag.objects.all() - return featureflags.order_by("name") + + @action(methods=["GET"], detail=False) + def except_no_activated_modules(self, request): + featureflags = self.get_queryset() + + current_account = request.user.iaso_profile.account + account_modules = current_account.modules + + not_activated_modules = list(set(FEATUREFLAGES_TO_EXCLUDE.keys()) - set(account_modules)) + featureflags_to_exclude = list( + chain.from_iterable([FEATUREFLAGES_TO_EXCLUDE[module] for module in not_activated_modules]) + ) + + if featureflags_to_exclude: + featureflags = featureflags.exclude(code__in=featureflags_to_exclude) + + serializer = FeatureFlagsSerializer(featureflags, many=True) + return Response({self.get_results_key(): serializer.data}) diff --git a/iaso/tests/api/test_projects.py b/iaso/tests/api/test_projects.py index e39bac53e2..10b0c68873 100644 --- a/iaso/tests/api/test_projects.py +++ b/iaso/tests/api/test_projects.py @@ -1,5 +1,7 @@ +from itertools import chain import typing +from hat.menupermissions.constants import FEATUREFLAGES_TO_EXCLUDE from iaso import models as m from iaso.test import APITestCase @@ -57,6 +59,21 @@ def test_feature_flags_list_ok(self): self.assertJSONResponse(response, 200) self.assertValidFeatureFlagListData(response.json(), m.FeatureFlag.objects.count()) + def test_feature_flags_list_except_no_activated_modules(self): + """GET /featureflags/except_no_activated_modules happy path: we expect one result""" + self.client.force_authenticate(self.jane) + response = self.client.get( + "/api/featureflags/except_no_activated_modules/", headers={"Content-Type": "application/json"} + ) + + self.assertJSONResponse(response, 200) + excluded_feature_flags = list( + chain.from_iterable([featureflag for featureflag in FEATUREFLAGES_TO_EXCLUDE.values()]) + ) + self.assertValidFeatureFlagListData( + response.json(), m.FeatureFlag.objects.count() - len(excluded_feature_flags) + ) + def test_projects_list_paginated(self): """GET /projects/ paginated happy path"""