Example #1
0
    def url_patterns(self):
        # First, add our "positively" flagged URLs, where when the flag
        # matches the defined state, the view is served for the pattern
        # and not the fallback.
        url_patterns = []
        for pattern in super(FlaggedURLResolver, self).url_patterns:
            # Get the fallback view, if there is one, and remove it from
            # the list of fallback patterns.
            fallback = self.fallback
            if isinstance(self.fallback, (list, tuple)):
                fallback = next(
                    (p.callback for p in self.fallback_patterns
                     if p.pattern.describe() == pattern.pattern.describe()),
                    None,
                )

            flag_decorator = flag_check(self.flag_name,
                                        self.state,
                                        fallback=fallback)

            route_pattern = pattern.pattern

            flagged_pattern = URLPattern(
                route_pattern,
                flag_decorator(pattern.callback),
                pattern.default_args,
                pattern.name,
            )

            url_patterns.append(flagged_pattern)

        # Next, add "negatively" flagged URLs, where the flag does not match
        # the defined state, for any remaining fallback patterns that didn't
        # match other url patterns.
        described_patterns = [p.pattern.describe() for p in url_patterns]
        negative_patterns = (p for p in self.fallback_patterns
                             if p.pattern.describe() not in described_patterns)

        for pattern in negative_patterns:
            flag_decorator = flag_check(self.flag_name, not self.state)

            route_pattern = pattern.pattern

            flagged_pattern = URLPattern(
                route_pattern,
                flag_decorator(pattern.callback),
                pattern.default_args,
                pattern.name,
            )

            url_patterns.append(flagged_pattern)

        return url_patterns
Example #2
0
    def test_decorated_flag_enabled(self):
        def view(request):
            return HttpResponse("ok")

        decorated = flag_check("FLAG_ENABLED", True)(view)
        response = decorated(self.request)
        self.assertContains(response, "ok")
Example #3
0
    def test_pass_if_not_set_no_flag_exists(self):
        def view(request):
            return HttpResponse("ok")

        decorated = flag_check("FLAG_DOES_NOT_EXIST", False)(view)
        response = decorated(self.request)
        self.assertContains(response, "ok")
Example #4
0
def _flagged_path(flag_name, route, view, kwargs=None, name=None,
                  state=True, fallback=None, Pattern=None):
    """ Make a URL depend on the state of a feature flag """
    if callable(view):
        flagged_view = flag_check(flag_name,
                                  state,
                                  fallback=fallback)(view)

        if Pattern:  # pragma: no cover
            route_pattern = Pattern(route, name=name, is_endpoint=True)
        else:  # pragma: no cover
            route_pattern = route

        return URLPattern(route_pattern, flagged_view, kwargs, name)

    elif isinstance(view, (list, tuple)):
        urlconf_module, app_name, namespace = view

        if Pattern:  # pragma: no cover
            route_pattern = Pattern(route, name=name, is_endpoint=True)
        else:  # pragma: no cover
            route_pattern = route

        return FlaggedURLResolver(
            flag_name, route_pattern, urlconf_module, kwargs,
            app_name=app_name, namespace=namespace,
            state=state, fallback=fallback)

    else:
        raise TypeError('view must be a callable')
Example #5
0
    def test_pass_if_not_set_disabled(self):
        def view(request):
            return HttpResponse("ok")

        decorated = flag_check("FLAG_DISABLED", False)(view)
        response = decorated(self.request)
        self.assertContains(response, "ok")
Example #6
0
    def as_view(cls, **initkwargs):
        flag_name = initkwargs.get("flag_name", cls.flag_name)
        state = initkwargs.get("state", cls.state)
        fallback = initkwargs.get("fallback", cls.fallback)
        condition = initkwargs.get("condition", cls.condition)

        if flag_name is None:
            raise ImproperlyConfigured(
                "FlaggedViewMixin requires a 'flag_name' argument.")

        if condition is not None:
            warnings.warn(
                "condition attribute of FlaggedViewMixin is deprecated and "
                "will be removed in a future version of Django-Flags. "
                "Please use the state attribute instead.",
                FutureWarning,
            )
            state = condition

        view = super(FlaggedViewMixin, cls).as_view(**initkwargs)

        decorator = flag_check(
            flag_name,
            state,
            fallback=fallback,
        )

        return decorator(view)
Example #7
0
    def test_fallback_view(self):
        def fallback(request, **kwargs):
            return HttpResponse("fallback")

        decorator = flag_check("FLAG_DISABLED", True, fallback=fallback)
        decorated = decorator(self.view)
        response = decorated(self.request)
        self.assertContains(response, "fallback")
Example #8
0
    def test_fallback_view(self):
        def fallback(request):
            return HttpResponse('fallback')

        decorator = flag_check('FLAG_DISABLED', True, fallback=fallback)
        decorated = decorator(self.view)
        response = decorated(self.request)
        self.assertContains(response, 'fallback')
Example #9
0
    def test_pass_if_not_set_no_flag_exists(self):
        def view(request):
            return HttpResponse('ok')

        decorated = flag_check(self.flag_name, False)(view)
        response = decorated(self.request)
        if isinstance(response.content, str):
            content = response.content
        else:
            content = bytes.decode(response.content)
        self.assertEqual(content, 'ok')
Example #10
0
    def test_view_fallback_same_args(self):
        def view(request, extra_arg, kwarg=None):
            return HttpResponse('ok')  # pragma: no cover

        def fallback(request, extra_arg, kwarg=None):
            return HttpResponse('fallback')

        decorator = flag_check('FLAG_DISABLED', True, fallback=fallback)
        decorated = decorator(view)
        response = decorated(self.request, 'an extra argument', kwarg='foo')
        self.assertContains(response, 'fallback')
Example #11
0
    def test_view_fallback_same_args(self):
        def view(request, extra_arg, kwarg=None):
            return HttpResponse("ok")  # pragma: no cover

        def fallback(request, extra_arg, kwarg=None):
            return HttpResponse("fallback")

        decorator = flag_check("FLAG_DISABLED", True, fallback=fallback)
        decorated = decorator(view)
        response = decorated(self.request, "an extra argument", kwarg="foo")
        self.assertContains(response, "fallback")
Example #12
0
    def test_fallback_view(self):
        def fallback(request):
            return HttpResponse('fallback')

        decorator = flag_check(self.flag_name, True, fallback=fallback)
        decorated = decorator(self.view)
        response = decorated(self.request)
        if isinstance(response.content, str):
            content = response.content
        else:
            content = bytes.decode(response.content)
        self.assertEqual(content, 'fallback')
Example #13
0
    def test_pass_if_not_set_disabled(self):
        def view(request):
            return HttpResponse('ok')

        Flag.objects.create(key=self.flag_name, enabled_by_default=False)
        decorated = flag_check(self.flag_name, False)(view)
        response = decorated(self.request)
        if isinstance(response.content, str):
            content = response.content
        else:
            content = bytes.decode(response.content)
        self.assertEqual(content, 'ok')
Example #14
0
    def test_view_fallback_different_args(self):
        def view(request, extra_arg, kwarg=None):
            return HttpResponse("ok")  # pragma: no cover

        def fallback(request):
            return HttpResponse("fallback")  # pragma: no cover

        decorator = flag_check("FLAG_DISABLED", True, fallback=fallback)
        with warnings.catch_warnings(record=True) as warning_list:
            decorator(view)

            self.assertTrue(
                any(item.category == RuntimeWarning for item in warning_list))
Example #15
0
    def test_pass_if_not_set_fallback_view(self):
        def fallback(request, **kwargs):
            return HttpResponse("fallback")

        decorator = flag_check(
            "FLAG_ENABLED",
            False,
            fallback=fallback,
        )

        decorated = decorator(self.view)
        response = decorated(self.request)
        self.assertContains(response, "fallback")
Example #16
0
    def test_pass_if_not_set_fallback_view(self):
        def fallback(request):
            return HttpResponse('fallback')

        decorator = flag_check(
            'FLAG_ENABLED',
            False,
            fallback=fallback,
        )

        decorated = decorator(self.view)
        response = decorated(self.request)
        self.assertContains(response, 'fallback')
Example #17
0
    def dispatch(self, request, *args, **kwargs):
        if self.flag_name is None:
            raise ImproperlyConfigured(
                "FlaggedViewMixin requires a 'flag_name' argument.")

        super_dispatch = super(FlaggedViewMixin, self).dispatch

        decorator = flag_check(
            self.flag_name,
            self.condition,
            fallback=self.fallback,
        )

        return decorator(super_dispatch)(request, *args, **kwargs)
Example #18
0
    def test_pass_if_not_set_fallback_view(self):
        Flag.objects.create(key=self.flag_name, enabled_by_default=True)

        def fallback(request):
            return HttpResponse('fallback')

        decorator = flag_check(
            self.flag_name,
            False,
            fallback=fallback,
        )

        decorated = decorator(self.view)
        response = decorated(self.request)
        if isinstance(response.content, str):
            content = response.content
        else:
            content = bytes.decode(response.content)
        self.assertEqual(content, 'fallback')
Example #19
0
def flagged_url(flag_name,
                regex,
                view,
                kwargs=None,
                name=None,
                condition=True,
                fallback=None):
    """ Make a URL depend on the state of a feature flag """

    if callable(view):
        flagged_view = flag_check(flag_name, condition,
                                  fallback=fallback)(view)
        return url(regex, flagged_view, kwargs=kwargs, name=name)

    elif isinstance(view, (list, tuple)):
        # XXX: For right now, we don't support include()
        raise TypeError('Flagged include() is not supported')

    else:
        raise TypeError('view must be a callable')
Example #20
0
 def test_pass_if_not_set_enabled(self):
     decorated = flag_check("FLAG_ENABLED", False)(self.view)
     self.assertRaises(Http404, decorated, self.request)
     self.assertEqual(self.mock_view.call_count, 0)
Example #21
0
 def test_decorated_no_flag_exists(self):
     decorated = flag_check(self.flag_name, True)(self.view)
     self.assertRaises(Http404, decorated, self.request)
     self.assertEqual(self.view.call_count, 0)
Example #22
0
 def test_decorated_flag_disabled(self):
     decorated = flag_check("FLAG_DISABLED", True)(self.view)
     self.assertRaises(Http404, decorated, self.request)
     self.assertEqual(self.mock_view.call_count, 0)
Example #23
0
 def test_decorated_no_flag_exists(self):
     decorated = flag_check("FLAG_DOES_NOT_EXIST", True)(self.view)
     with self.assertRaises(Http404):
         decorated(self.request)
     self.assertEqual(self.mock_view.call_count, 0)
Example #24
0
 def test_pass_if_not_set_enabled(self):
     Flag.objects.create(key=self.flag_name, enabled_by_default=True)
     decorated = flag_check(self.flag_name, False)(self.view)
     self.assertRaises(Http404, decorated, self.request)
     self.assertEqual(self.view.call_count, 0)