Skip to content

Commit e9cbee4

Browse files
committed
Refactor view to gateway
1 parent a38befc commit e9cbee4

File tree

11 files changed

+28
-100
lines changed

11 files changed

+28
-100
lines changed

ansible_base/feature_flags/apps.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
from django.apps import AppConfig
2+
from django.db.models.signals import post_migrate
3+
4+
from ansible_base.feature_flags.utils import create_initial_data
25

36

47
class FeatureFlagsConfig(AppConfig):
58
default_auto_field = 'django.db.models.BigAutoField'
69
name = 'ansible_base.feature_flags'
710
label = 'dab_feature_flags'
811
verbose_name = 'Feature Flags'
12+
13+
def ready(self):
14+
try:
15+
create_initial_data()
16+
except Exception:
17+
post_migrate.connect(create_initial_data, sender=self)

ansible_base/feature_flags/migrations/0001_initial.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Generated by Django 4.2.17 on 2025-06-04 12:00
1+
# Generated by Django 4.2.17 on 2025-06-06 16:22
22

33
import ansible_base.feature_flags.models.aap_flag
44
from django.conf import settings
@@ -24,7 +24,7 @@ class Migration(migrations.Migration):
2424
('name', models.CharField(help_text='The name of the feature flag. Must follow the format of FEATURE_<flag-name>_ENABLED.', max_length=64, validators=[ansible_base.feature_flags.models.aap_flag.validate_feature_flag_name])),
2525
('ui_name', models.CharField(help_text='The pretty name to display in the application User Interface', max_length=64)),
2626
('condition', models.CharField(default='boolean', help_text='Used to specify a condition, which if met, will enable the feature flag.', max_length=64)),
27-
('value', models.CharField(default='True', help_text='The value used to evaluate the conditional specified.', max_length=127)),
27+
('value', models.CharField(default='False', help_text='The value used to evaluate the conditional specified.', max_length=127)),
2828
('required', models.BooleanField(default=False, help_text="If multiple conditions are required to be met to enable a feature flag, 'required' can be used to specify the necessary conditionals.")),
2929
('support_level', models.CharField(choices=[('NOT_FOR_USE', 'Not for use'), ('NOT_FOR_PRODUCTION', 'Not for production'), ('READY_FOR_PRODUCTION', 'Ready for production')], help_text='The support criteria for the feature flag. Must be one of (NOT_FOR_USE, NOT_FOR_PRODUCTION, READY_FOR_PRODUCTION).', max_length=25)),
3030
('visibility', models.CharField(choices=[('public', 'public'), ('private', 'private')], help_text='The visibility level of the feature flag. If private, flag is hidden.', max_length=20)),

ansible_base/feature_flags/models/aap_flag.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ def __str__(self):
3535
)
3636
ui_name = models.CharField(max_length=64, null=False, blank=False, help_text=_("The pretty name to display in the application User Interface"))
3737
condition = models.CharField(max_length=64, default="boolean", help_text=_("Used to specify a condition, which if met, will enable the feature flag."))
38-
value = models.CharField(max_length=127, default="True", help_text=_("The value used to evaluate the conditional specified."))
38+
value = models.CharField(
39+
max_length=127,
40+
default="False",
41+
help_text=_("The value used to evaluate the conditional specified."),
42+
)
3943
required = models.BooleanField(
4044
default=False,
4145
help_text=_("If multiple conditions are required to be met to enable a feature flag, 'required' can be used to specify the necessary conditionals."),

ansible_base/feature_flags/serializers.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,11 @@
11
from flags.state import flag_state
2-
from rest_framework import serializers
32

43
from ansible_base.feature_flags.models import AAPFlag
54
from ansible_base.lib.serializers.common import NamedCommonModelSerializer
65

76
from .utils import get_django_flags
87

98

10-
class FeatureFlagSerializer(NamedCommonModelSerializer):
11-
"""Serialize list of feature flags"""
12-
13-
state = serializers.SerializerMethodField()
14-
15-
class Meta:
16-
model = AAPFlag
17-
fields = NamedCommonModelSerializer.Meta.fields + [x.name for x in AAPFlag._meta.concrete_fields] + ['state']
18-
read_only_fields = ["name", "condition", "required", "support_level", "visibility", "toggle_type", "description", "labels", "ui_name", "support_url"]
19-
20-
def get_state(self, instance):
21-
return flag_state(instance.name)
22-
23-
def to_representation(self, instance):
24-
instance.state = True
25-
ret = super().to_representation(instance)
26-
return ret
27-
28-
299
# TODO: Remove once all components are migrated to the new endpont.
3010
class OldFeatureFlagSerializer(NamedCommonModelSerializer):
3111
"""Serialize list of feature flags"""

ansible_base/feature_flags/urls.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
router = AssociationResourceRouter()
1010

1111
router.register(r'feature_flags/states', views.FeatureFlagsStatesView, basename='aap_flags_states')
12-
router.register(r'feature_flags', views.FeatureFlagsView, basename='aap_flags')
13-
1412
# TODO: Remove once all components are migrated to new endpoints.
1513
api_version_urls = [path('feature_flags_state/', views.OldFeatureFlagsStateListView.as_view(), name='feature-flags-state-list'), path('', include(router.urls))]
1614

ansible_base/feature_flags/utils.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ def load_feature_flags():
4242
"""
4343
Loads in all feature flags into the database. Updates them if necessary.
4444
"""
45+
from ansible_base.resource_registry.signals.handlers import no_reverse_sync
46+
4547
feature_flags_model = apps.get_model('dab_feature_flags', 'AAPFlag')
4648
for flag in AAP_FEATURE_FLAGS:
4749
try:
@@ -53,7 +55,8 @@ def load_feature_flags():
5355
flag['value'] = getattr(settings, flag['name'])
5456
feature_flag = feature_flags_model(**flag)
5557
feature_flag.full_clean()
56-
feature_flag.save()
58+
with no_reverse_sync():
59+
feature_flag.save()
5760
except ValidationError as e:
5861
# Ignore this error unless better way to bypass this
5962
if e.messages[0] == 'Aap flag with this Name and Condition already exists.':
@@ -67,6 +70,8 @@ def delete_feature_flags():
6770
"""
6871
If a feature flag has been removed from the platform flags list, delete it from the database.
6972
"""
73+
from ansible_base.resource_registry.signals.handlers import no_reverse_sync
74+
7075
all_flags = apps.get_model('dab_feature_flags', 'AAPFlag').objects.all()
7176
for flag in all_flags:
7277
found = False
@@ -78,4 +83,5 @@ def delete_feature_flags():
7883
continue
7984
if not found:
8085
logger.info(f"Deleting feature flag: {flag.name} as it is no longer available as a platform flag")
81-
flag.delete()
86+
with no_reverse_sync():
87+
flag.delete()

ansible_base/feature_flags/views.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from rest_framework.viewsets import ModelViewSet
66

77
from ansible_base.feature_flags.models import AAPFlag
8-
from ansible_base.feature_flags.serializers import FeatureFlagSerializer, OldFeatureFlagSerializer
8+
from ansible_base.feature_flags.serializers import OldFeatureFlagSerializer
99
from ansible_base.lib.utils.views.ansible_base import AnsibleBaseView
1010
from ansible_base.lib.utils.views.django_app_api import AnsibleBaseDjangoAppApiView
1111
from ansible_base.lib.utils.views.permissions import IsSuperuserOrAuditor, try_add_oauth2_scope_permission
@@ -14,17 +14,6 @@
1414
from .utils import get_django_flags
1515

1616

17-
class FeatureFlagsView(AnsibleBaseDjangoAppApiView, ModelViewSet):
18-
"""
19-
A view class for displaying feature flags
20-
"""
21-
22-
queryset = AAPFlag.objects.order_by('id')
23-
serializer_class = FeatureFlagSerializer
24-
permission_classes = try_add_oauth2_scope_permission([IsSuperuserOrAuditor])
25-
http_method_names = ['get', 'head', 'options']
26-
27-
2817
class FeatureFlagsStatesView(AnsibleBaseDjangoAppApiView, ModelViewSet):
2918
"""
3019
A view class for displaying feature flags states

ansible_base/lib/dynamic_config/settings_logic.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -306,12 +306,7 @@ def get_mergeable_dab_settings(settings: dict) -> dict: # NOSONAR
306306
if "flags" not in installed_apps:
307307
installed_apps.append('flags')
308308

309-
# After all flags are migrated to database flags, remove settings flag source
310-
# Settings flag source is defined for compatibility until migration is complete
311-
dab_data['FLAG_SOURCES'] = (
312-
'flags.sources.SettingsFlagsSource',
313-
'ansible_base.feature_flags.flag_source.AAPFlagSource',
314-
)
309+
dab_data['FLAG_SOURCES'] = ('ansible_base.feature_flags.flag_source.AAPFlagSource',)
315310

316311
found_template_backend = False
317312
template_context_processor = 'django.template.context_processors.request'

ansible_base/rbac/permission_registry.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ def create_managed_roles(self, apps) -> list[tuple[Model, bool]]:
143143
return ret
144144

145145
def call_when_apps_ready(self, apps, app_config) -> None:
146-
from ansible_base.feature_flags.utils import create_initial_data as feature_flag_create_initial_data
147146
from ansible_base.rbac import triggers
148147
from ansible_base.rbac.evaluations import bound_has_obj_perm, bound_singleton_permissions, connect_rbac_methods
149148
from ansible_base.rbac.management import create_dab_permissions
@@ -173,11 +172,6 @@ def call_when_apps_ready(self, apps, app_config) -> None:
173172
sender=app_config,
174173
dispatch_uid="ansible_base.rbac.triggers.post_migration_rbac_setup",
175174
)
176-
if 'ansible_base.feature_flags' in settings.INSTALLED_APPS:
177-
try:
178-
feature_flag_create_initial_data()
179-
except Exception:
180-
post_migrate.connect(feature_flag_create_initial_data, sender=self)
181175

182176
self.user_model.add_to_class('has_obj_perm', bound_has_obj_perm)
183177
self.user_model.add_to_class('singleton_permissions', bound_singleton_permissions)

test_app/tests/feature_flags/models/test_aap_flag.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def test_feature_flags_from_db(aap_flags, feature_flag):
4141
],
4242
)
4343
def test_feature_flag_database_setting_override(feature_flag, value):
44+
AAPFlag.objects.all().delete()
4445
from ansible_base.feature_flags.utils import create_initial_data
4546

4647
setattr(settings, feature_flag, value)

0 commit comments

Comments
 (0)