def test_deletable(self): FlagState.objects.create( name="DBFLAG", condition="boolean", value="True", ) self.assertFalse(deletable(get_flags().get("MYFLAG"))) self.assertFalse(deletable(get_flags().get("EMPTYFLAG"))) self.assertTrue(deletable(get_flags().get("DBFLAG")))
def test_ignore_errors(self): # Without ignore_errors with self.assertRaises(Exception): get_flags( sources=["flags.tests.test_sources.ExceptionalFlagsSource"]) # With ignore_errors flags = get_flags( sources=["flags.tests.test_sources.ExceptionalFlagsSource"], ignore_errors=True, ) self.assertEqual(flags, {})
def edit_condition(request, name, condition_pk=None): flag = get_flags().get(name) try: condition = FlagState.objects.get(pk=condition_pk) except FlagState.DoesNotExist: condition = None if request.method == 'POST': form = FlagStateForm(request.POST, initial={'name': name}, instance=condition) if form.is_valid(): form.save() if flag_enabled('WAGTAILFLAGS_ADMIN_BIG_LIST'): return redirect('{flags_list}#{flag_name}'.format( flags_list=resolve_url('wagtailflags:list'), flag_name=form.instance.name)) else: return redirect('wagtailflags:flag_index', name=form.instance.name) else: form = FlagStateForm(initial={'name': name}, instance=condition) context = { 'flag': flag, 'form': form, 'condition_str': str(condition), 'condition_pk': condition_pk, } return render(request, 'wagtailflags/flags/edit_condition.html', context)
def flag_index(request, name): flag = get_flags().get(name) # If there's a database boolean condition, fetch it and treat it as a # on/off switch if 'enable' in request.GET or 'disable' in request.GET: db_boolean_condition = next( (c for c in flag.conditions if c.condition == 'boolean' and getattr(c, 'obj', None) is not None), None) if db_boolean_condition is None: boolean_condition_obj = FlagState.objects.create( name=name, condition='boolean', value='True') else: boolean_condition_obj = db_boolean_condition.obj if 'enable' in request.GET and not bool_enabled(flag): boolean_condition_obj.value = True elif 'disable' in request.GET and bool_enabled(flag): boolean_condition_obj.value = False boolean_condition_obj.save() if flag_enabled('WAGTAILFLAGS_ADMIN_BIG_LIST'): return redirect('{flags_list}#{flag_name}'.format( flags_list=resolve_url('wagtailflags:list'), flag_name=name)) else: return redirect('wagtailflags:flag_index', name=name) context = { 'flag': flag, } return render(request, 'wagtailflags/flags/flag_index.html', context)
def edit_condition(request, name, condition_pk=None): flag = get_flags().get(name) if not flag: raise Http404 try: condition = FlagState.objects.get(pk=condition_pk) except FlagState.DoesNotExist: condition = None if request.method == "POST": form = FlagStateForm( request.POST, initial={"name": name}, instance=condition ) if form.is_valid(): form.save() return redirect("wagtailflags:flag_index", name=name) else: form = FlagStateForm(initial={"name": name}, instance=condition) context = { "flag": flag, "form": form, "condition_str": str(condition), "condition_pk": condition_pk, } return render(request, "wagtailflags/flags/edit_condition.html", context)
def flags(request): q_flag = request.GET.get('flag', False) list_flags = get_flags(sources=None, ignore_errors=False) if not q_flag: # We return the list of all flags. data = [] for (flag, fobject) in list_flags.items(): data.append({ 'name': flag }) return JsonResponse(data, safe=False) else: # We return the enabled settings for the set flag. if q_flag in list_flags: data = [{'name': q_flag, "conditions": []}] for condition in list_flags[q_flag].conditions: data[0]['conditions'].append({ 'required': condition.__dict__['required'], 'type': condition.__dict__['condition'], 'value': condition.__dict__['value'] }) return JsonResponse(data, safe=False) else: return JsonResponse([{'error': 'The flag does not exist.'}], safe=False)
def test_state_str_required_non_required_bool_false(self): flag = get_flags().get("MYFLAG") self.assertEqual( "MYFLAG is <b>enabled</b> when <i>all</i> required conditions " "and <i>any</i> non-required condition is met.", state_str(flag), )
def flag_conditions_check(app_configs, **kwargs): from flags.sources import get_flags errors = [] flags = get_flags(ignore_errors=True) for name, flag in flags.items(): for condition in flag.conditions: if condition.fn is None: errors.append( Warning( (f"Flag {name} has non-existent condition " f"'{condition.condition}'."), hint=(f"Register '{condition.condition}' as a " "Django-Flags condition."), id="flags.E001", )) elif condition.fn.validate is not None: try: condition.fn.validate(condition.value) except ValidationError as e: errors.append( Warning( (f"Flag {name}'s '{condition.condition}' " "condition has an invalid value."), hint=e.message, id="flags.E002", )) return errors
def _set_flag_state( flag_name, state, create_boolean_condition=True, request=None ): """ A private function to set a boolean condition to the desired state """ flags = get_flags(request=request) flag = flags.get(flag_name) if flag is None: raise KeyError(f"No flag with name {flag_name} exists") db_boolean_condition = next( ( c for c in flag.conditions if c.condition == "boolean" and getattr(c, "obj", None) is not None ), None, ) if db_boolean_condition is not None: # We already have a boolean condition boolean_condition_obj = db_boolean_condition.obj elif db_boolean_condition is None and create_boolean_condition: # We can create a boolean condition and we need to. boolean_condition_obj = FlagState.objects.create( name=flag_name, condition="boolean", value="True" ) else: raise ValueError(f"Flag {flag_name} does not have a boolean condition") boolean_condition_obj.value = state boolean_condition_obj.save()
def test_state_str_required_no_non_required_no_bool(self): flag = get_flags().get("MYFLAG") self.assertEqual( "MYFLAG is <b>enabled</b> when <i>all</i> required conditions " "are met.", state_str(flag), )
def test_state_str_required_no_non_required_bool_false(self): flag = get_flags().get("MYFLAG") self.assertEqual( "MYFLAG is <b>disabled</b> for all requests, " "even when <i>all</i> required conditions are met.", state_str(flag), )
def render(self, path): request = RequestFactory().get(path) request.flag_conditions = get_flags() template = Template( "{% load hmda_banners %}" "{% hmda_outage_banner request %}" ) return template.render(Context({"request": request}))
def __init__(self, *args, **kwargs): super(FlagStateForm, self).__init__(*args, **kwargs) self.fields["name"].choices = [(f, f) for f in sorted(get_flags().keys())] self.fields["condition"].choices = [(c, c) for c in sorted(get_conditions())]
def render(self, path): request = RequestFactory().get(path) request.flag_conditions = get_flags() template = Template( '{% load hmda_banners %}' '{% hmda_outage_banner request %}' ) return template.render(Context({'request': request}))
def render(self, path): request = RequestFactory().get(path) request.flag_conditions = get_flags() template = Template( '{% load complaint_banners %}' '{% complaint_maintenance_banner request %}' ) return template.render(Context({'request': request}))
def _get_flag_state(flag_name, **kwargs): """ A private function that performs the actual state checking """ flags = get_flags(request=kwargs.get("request")) flag = flags.get(flag_name) if flag is not None: return flag.check_state(**kwargs) return None
def test_get_flags(self): flags = get_flags(sources=[ "flags.sources.SettingsFlagsSource", "flags.tests.test_sources.TestFlagsSource", ]) self.assertIn("OTHER_FLAG", flags) self.assertIn("SOURCED_FLAG", flags) self.assertEqual(len(flags["OTHER_FLAG"].conditions), 0) self.assertEqual(len(flags["SOURCED_FLAG"].conditions), 1)
def generate_stats(self, request, response): flags = get_flags(request=request) self.record_stats({ "request": request, "flags": sorted(flags.values(), key=lambda x: x.name), })
def test_get_flags(self): flags = get_flags(sources=[ 'flags.sources.SettingsFlagsSource', 'flags.tests.test_sources.TestFlagsSource', ]) self.assertIn('OTHER_FLAG', flags) self.assertIn('SOURCED_FLAG', flags) self.assertEqual(len(flags['OTHER_FLAG'].conditions), 0) self.assertEqual(len(flags['SOURCED_FLAG'].conditions), 1)
def test_get_flags_ensure_combined_conditions_work(self): FlagState.objects.create(name='MY_FLAG', condition='boolean', value='True') flags = get_flags(sources=[ 'flags.sources.SettingsFlagsSource', 'flags.sources.DatabaseFlagsSource', ]) self.assertIn('MY_FLAG', flags) my_flag = flags['MY_FLAG'] self.assertTrue(my_flag.check_state())
def test_get_flags_ensure_combined_conditions_work(self): FlagState.objects.create(name="MY_FLAG", condition="boolean", value="True") flags = get_flags(sources=[ "flags.sources.SettingsFlagsSource", "flags.sources.DatabaseFlagsSource", ]) self.assertIn("MY_FLAG", flags) my_flag = flags["MY_FLAG"] self.assertTrue(my_flag.check_state())
def generate_stats(self, request, response): if hasattr(request, FlagConditionsMiddleware.request_attribute): flags = getattr(request, FlagConditionsMiddleware.request_attribute) else: flags = get_flags() self.record_stats({ "request": request, "flags": sorted(flags.values(), key=lambda x: x.name), })
def flag_state(flag_name, **kwargs): """ Return the value for the flag by passing kwargs to its conditions """ flags = None # If the request is given as a kwargs, and the FlagConditionsMiddleware is # enabled, use the flag conditions attached to the request. if 'request' in kwargs: flags = getattr(kwargs['request'], FlagConditionsMiddleware.request_attribute, None) if flags is None: flags = get_flags() flag = flags.get(flag_name) if flag is not None: return flag.check_state(**kwargs) return None
def delete_condition(request, name, condition_pk): flag = get_flags().get(name) if not flag: raise Http404 condition = get_object_or_404(FlagState, pk=condition_pk) if request.method == "POST": condition.delete() return redirect("wagtailflags:flag_index", name=name) context = { "flag": flag, "condition_str": str(condition), "condition_pk": condition.pk, } return render(request, "wagtailflags/flags/delete_condition.html", context)
def delete_flag(request, name): """ Delete a database flag. """ flag = get_flags().get(name) if not flag: raise Http404 if not deletable(flag): return HttpResponseForbidden(name) if request.method == "POST": FlagState.objects.filter(name=name).delete() return redirect("wagtailflags:list") context = { "flag": flag, } return render(request, "wagtailflags/flags/delete_flag.html", context)
def delete_condition(request, name, condition_pk): flag = get_flags().get(name) condition = get_object_or_404(FlagState, pk=condition_pk) if request.method == 'POST': condition.delete() if flag_enabled('WAGTAILFLAGS_ADMIN_BIG_LIST'): return redirect('{flags_list}#{flag_name}'.format( flags_list=resolve_url('wagtailflags:list'), flag_name=name)) else: return redirect('wagtailflags:flag_index', name=name) context = { 'flag': flag, 'condition_str': str(condition), 'condition_pk': condition.pk, } return render(request, 'wagtailflags/flags/delete_condition.html', context)
def test_uses_cached_flags_from_request(self): request = HttpRequest() # The initial call looks up flag conditions from the database source. with self.assertNumQueries(1): get_flags(request=request) # Subsequent calls with a request object don't need to redo the lookup # because they have the cached flags. with self.assertNumQueries(0): get_flags(request=request) # But subsequent calls without a request object still redo the lookup. with self.assertNumQueries(1): get_flags()
def flag_conditions_check(app_configs, **kwargs): from flags.sources import get_flags error_str = 'Flag {flag} has non-existent condition "{condition}"' error_hint = 'Register "{condition}" as a Django-Flags condition.' errors = [] flags = get_flags(ignore_errors=True) for name, flag in flags.items(): for condition in flag.conditions: if condition.fn is None: errors.append( Warning( error_str.format(flag=name, condition=condition.condition), hint=error_hint.format(condition=condition.condition), id='flags.E001', )) return errors
def _flag_state(flag_name, **kwargs): """ This is a private function that performs the actual state checking """ flags = None # If the request is given as a kwargs, and the FlagConditionsMiddleware is # enabled, use the flag conditions attached to the request. if 'request' in kwargs: flags = getattr( kwargs['request'], FlagConditionsMiddleware.request_attribute, None ) if flags is None: flags = get_flags() flag = flags.get(flag_name) if flag is not None: return flag.check_state(**kwargs) return None
def flag_index(request, name): flag = get_flags().get(name) if not flag: raise Http404 # If there's a database boolean condition, fetch it and treat it as a # on/off switch if "enable" in request.GET or "disable" in request.GET: db_boolean_condition = next( ( c for c in flag.conditions if c.condition == "boolean" and getattr(c, "obj", None) is not None ), None, ) if db_boolean_condition is None: boolean_condition_obj = FlagState.objects.create( name=name, condition="boolean", value="True" ) else: boolean_condition_obj = db_boolean_condition.obj if "enable" in request.GET and not bool_enabled(flag): boolean_condition_obj.value = True flag_enabled.send(sender=flag_index, flag_name=flag.name) elif "disable" in request.GET and bool_enabled(flag): boolean_condition_obj.value = False flag_disabled.send(sender=flag_index, flag_name=flag.name) boolean_condition_obj.save() return redirect("wagtailflags:flag_index", name=name) context = { "flag": flag, } return render(request, "wagtailflags/flags/flag_index.html", context)
def test_disablable_enabled_required(self): flag = get_flags().get("MYFLAG") self.assertFalse(disablable(flag))
def test_disablable_disabled_not_required(self): flag = get_flags().get("MYFLAG") self.assertTrue(disablable(flag))