def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required): ret = [] for urlpattern in urlpatterns: if isinstance(urlpattern, URLResolver): # Set of included URL patterns regex = get_regex_pattern(urlpattern) namespace = urlpattern.namespace app_name = urlpattern.app_name kwargs = urlpattern.default_kwargs # Add in the included patterns, after applying the suffixes patterns = apply_suffix_patterns(urlpattern.url_patterns, suffix_pattern, suffix_required) ret.append( url(regex, include((patterns, app_name), namespace), kwargs)) else: # Regular URL pattern regex = get_regex_pattern(urlpattern).rstrip('$').rstrip( '/') + suffix_pattern view = urlpattern.callback kwargs = urlpattern.default_args name = urlpattern.name # Add in both the existing and the new urlpattern if not suffix_required: ret.append(urlpattern) ret.append(url(regex, view, kwargs, name)) return ret
def test_nested_route(self): router = ExtendedSimpleRouter() ( router.register(r'users', UserViewSet, 'user') .register(r'groups', GroupViewSet, 'users-group', parents_query_lookups=['user']) ) # test user list self.assertEqual(router.urls[0].name, 'user-list') self.assertEqual(get_regex_pattern(router.urls[0]), r'^users/$') # test user detail self.assertEqual(router.urls[1].name, 'user-detail') self.assertEqual(get_regex_pattern(router.urls[1]), r'^users/{0}/$'.format(self.get_lookup_regex('pk'))) # test users group list self.assertEqual(router.urls[2].name, 'users-group-list') self.assertEqual(get_regex_pattern(router.urls[2]), r'^users/{0}/groups/$'.format( self.get_parent_lookup_regex('user') ) ) # test users group detail self.assertEqual(router.urls[3].name, 'users-group-detail') self.assertEqual(get_regex_pattern(router.urls[3]), r'^users/{0}/groups/{1}/$'.format( self.get_parent_lookup_regex('user'), self.get_lookup_regex('pk') ), )
def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required): ret = [] for urlpattern in urlpatterns: if isinstance(urlpattern, URLResolver): # Set of included URL patterns regex = get_regex_pattern(urlpattern) namespace = urlpattern.namespace app_name = urlpattern.app_name kwargs = urlpattern.default_kwargs # Add in the included patterns, after applying the suffixes patterns = apply_suffix_patterns(urlpattern.url_patterns, suffix_pattern, suffix_required) ret.append(url(regex, include((patterns, app_name), namespace), kwargs)) else: # Regular URL pattern regex = get_regex_pattern(urlpattern).rstrip('$').rstrip('/') + suffix_pattern view = urlpattern.callback kwargs = urlpattern.default_args name = urlpattern.name # Add in both the existing and the new urlpattern if not suffix_required: ret.append(urlpattern) ret.append(url(regex, view, kwargs, name)) return ret
def test_one_route(self): router = ExtendedSimpleRouter() router.register(r'users', UserViewSet, 'user') # test user list self.assertEqual(router.urls[0].name, 'user-list') self.assertEqual(get_regex_pattern(router.urls[0]), r'^users/$') # test user detail self.assertEqual(router.urls[1].name, 'user-detail') self.assertEqual(get_regex_pattern(router.urls[1]), r'^users/{0}/$'.format(self.get_lookup_regex('pk')))
def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_route=None): ret = [] for urlpattern in urlpatterns: if isinstance(urlpattern, URLResolver): # Set of included URL patterns regex = get_regex_pattern(urlpattern) namespace = urlpattern.namespace app_name = urlpattern.app_name kwargs = urlpattern.default_kwargs # Add in the included patterns, after applying the suffixes patterns = apply_suffix_patterns(urlpattern.url_patterns, suffix_pattern, suffix_required, suffix_route) # if the original pattern was a RoutePattern we need to preserve it if is_route_pattern(urlpattern): assert path is not None route = str(urlpattern.pattern) new_pattern = path(route, include((patterns, app_name), namespace), kwargs) else: new_pattern = url(regex, include((patterns, app_name), namespace), kwargs) ret.append(new_pattern) else: # Regular URL pattern regex = get_regex_pattern(urlpattern).rstrip('$').rstrip( '/') + suffix_pattern view = urlpattern.callback kwargs = urlpattern.default_args name = urlpattern.name # Add in both the existing and the new urlpattern if not suffix_required: ret.append(urlpattern) # if the original pattern was a RoutePattern we need to preserve it if is_route_pattern(urlpattern): assert path is not None assert suffix_route is not None route = str( urlpattern.pattern).rstrip('$').rstrip('/') + suffix_route new_pattern = path(route, view, kwargs, name) else: new_pattern = url(regex, view, kwargs, name) ret.append(new_pattern) return ret
def get_all_view_names(self, urlpatterns, parent_regex=''): for pattern in urlpatterns: if isinstance(pattern, URLResolver): regex = '' if get_regex_pattern( pattern) == "^" else get_regex_pattern(pattern) self.get_all_view_names(urlpatterns=pattern.url_patterns, parent_regex=parent_regex + regex) elif isinstance(pattern, URLPattern) and self._is_drf_view( pattern) and not self._is_format_endpoint(pattern): api_endpoint = ApiEndpoint(pattern, parent_regex, self.drf_router) self.endpoints.append(api_endpoint)
def get_api_endpoints(self, patterns=None, prefix=''): """ Return a list of all available API endpoints by inspecting the URL conf. """ if patterns is None: patterns = self.patterns api_endpoints = [] for pattern in patterns: path_regex = prefix + get_regex_pattern(pattern) if isinstance(pattern, URLPattern): path = self.get_path_from_regex(path_regex) callback = pattern.callback if self.should_include_endpoint(path, callback): for method in self.get_allowed_methods(callback): endpoint = (path, method, callback) api_endpoints.append(endpoint) elif isinstance(pattern, URLResolver): nested_endpoints = self.get_api_endpoints( patterns=pattern.url_patterns, prefix=path_regex ) api_endpoints.extend(nested_endpoints) api_endpoints = sorted(api_endpoints, key=endpoint_ordering) return api_endpoints
def get_api_endpoints(self, patterns=None, prefix=''): """ Return a list of all available API endpoints by inspecting the URL conf. """ if patterns is None: patterns = self.patterns api_endpoints = [] for pattern in patterns: path_regex = prefix + get_regex_pattern(pattern) if isinstance(pattern, RegexURLPattern): path = self.get_path_from_regex(path_regex) callback = pattern.callback if self.should_include_endpoint(path, callback): for method in self.get_allowed_methods(callback): endpoint = (path, method, callback) api_endpoints.append(endpoint) elif isinstance(pattern, RegexURLResolver): nested_endpoints = self.get_api_endpoints( patterns=pattern.url_patterns, prefix=path_regex) api_endpoints.extend(nested_endpoints) api_endpoints = sorted(api_endpoints, key=endpoint_ordering) return api_endpoints
def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required, suffix_route=None): ret = [] for urlpattern in urlpatterns: if isinstance(urlpattern, URLResolver): # Set of included URL patterns regex = get_regex_pattern(urlpattern) namespace = urlpattern.namespace app_name = urlpattern.app_name kwargs = urlpattern.default_kwargs # Add in the included patterns, after applying the suffixes patterns = apply_suffix_patterns(urlpattern.url_patterns, suffix_pattern, suffix_required, suffix_route) # if the original pattern was a RoutePattern we need to preserve it if is_route_pattern(urlpattern): assert path is not None route = str(urlpattern.pattern) new_pattern = path(route, include((patterns, app_name), namespace), kwargs) else: new_pattern = url(regex, include((patterns, app_name), namespace), kwargs) ret.append(new_pattern) else: # Regular URL pattern regex = get_regex_pattern(urlpattern).rstrip('$').rstrip('/') + suffix_pattern view = urlpattern.callback kwargs = urlpattern.default_args name = urlpattern.name # Add in both the existing and the new urlpattern if not suffix_required: ret.append(urlpattern) # if the original pattern was a RoutePattern we need to preserve it if is_route_pattern(urlpattern): assert path is not None assert suffix_route is not None route = str(urlpattern.pattern).rstrip('$').rstrip('/') + suffix_route new_pattern = path(route, view, kwargs, name) else: new_pattern = url(regex, view, kwargs, name) ret.append(new_pattern) return ret
def test_nested_route_depth_3(self): router = ExtendedSimpleRouter() (router.register(r'users', UserViewSet, 'user').register( r'groups', GroupViewSet, 'users-group', parents_query_lookups=['user']).register(r'permissions', PermissionViewSet, 'users-groups-permission', parents_query_lookups=[ 'group__user', 'group', ])) # test user list self.assertEqual(router.urls[0].name, 'user-list') self.assertEqual(get_regex_pattern(router.urls[0]), r'^users/$') # test user detail self.assertEqual(router.urls[1].name, 'user-detail') self.assertEqual(get_regex_pattern(router.urls[1]), r'^users/{0}/$'.format(self.get_lookup_regex('pk'))) # test users group list self.assertEqual(router.urls[2].name, 'users-group-list') self.assertEqual( get_regex_pattern(router.urls[2]), r'^users/{0}/groups/$'.format( self.get_parent_lookup_regex('user'))) # test users group detail self.assertEqual(router.urls[3].name, 'users-group-detail') self.assertEqual( get_regex_pattern(router.urls[3]), r'^users/{0}/groups/{1}/$'.format( self.get_parent_lookup_regex('user'), self.get_lookup_regex('pk')), ) # test users groups permission list self.assertEqual(router.urls[4].name, 'users-groups-permission-list') self.assertEqual( get_regex_pattern(router.urls[4]), r'^users/{0}/groups/{1}/permissions/$'.format( self.get_parent_lookup_regex('group__user'), self.get_parent_lookup_regex('group'), )) # test users groups permission detail self.assertEqual(router.urls[5].name, 'users-groups-permission-detail') self.assertEqual( get_regex_pattern(router.urls[5]), r'^users/{0}/groups/{1}/permissions/{2}/$'.format( self.get_parent_lookup_regex('group__user'), self.get_parent_lookup_regex('group'), self.get_lookup_regex('pk')), )
def test_should_include_endpoint_excludes_correctly(self): """This is the specific method that should handle the exclusion""" inspector = EndpointEnumerator(self.patterns) # Not pretty. Mimics internals of EndpointEnumerator to put should_include_endpoint under test pairs = [(inspector.get_path_from_regex(get_regex_pattern(pattern)), pattern.callback) for pattern in self.patterns] should_include = [ inspector.should_include_endpoint(*pair) for pair in pairs ] expected = [False, False, True] assert should_include == expected
def test_nested_route_depth_3(self): router = ExtendedSimpleRouter() ( router.register(r'users', UserViewSet, 'user') .register(r'groups', GroupViewSet, 'users-group', parents_query_lookups=['user']) .register(r'permissions', PermissionViewSet, 'users-groups-permission', parents_query_lookups=[ 'group__user', 'group', ] ) ) # test user list self.assertEqual(router.urls[0].name, 'user-list') self.assertEqual(get_regex_pattern(router.urls[0]), r'^users/$') # test user detail self.assertEqual(router.urls[1].name, 'user-detail') self.assertEqual(get_regex_pattern(router.urls[1]), r'^users/{0}/$'.format(self.get_lookup_regex('pk'))) # test users group list self.assertEqual(router.urls[2].name, 'users-group-list') self.assertEqual(get_regex_pattern(router.urls[2]), r'^users/{0}/groups/$'.format( self.get_parent_lookup_regex('user') ) ) # test users group detail self.assertEqual(router.urls[3].name, 'users-group-detail') self.assertEqual(get_regex_pattern(router.urls[3]), r'^users/{0}/groups/{1}/$'.format( self.get_parent_lookup_regex('user'), self.get_lookup_regex('pk') ), ) # test users groups permission list self.assertEqual(router.urls[4].name, 'users-groups-permission-list') self.assertEqual(get_regex_pattern(router.urls[4]), r'^users/{0}/groups/{1}/permissions/$'.format( self.get_parent_lookup_regex('group__user'), self.get_parent_lookup_regex('group'), ) ) # test users groups permission detail self.assertEqual(router.urls[5].name, 'users-groups-permission-detail') self.assertEqual(get_regex_pattern(router.urls[5]), r'^users/{0}/groups/{1}/permissions/{2}/$'.format( self.get_parent_lookup_regex('group__user'), self.get_parent_lookup_regex('group'), self.get_lookup_regex('pk') ), )
def __get_allowed_methods__(self): viewset_methods = [] if self.drf_router: for prefix, viewset, basename in self.drf_router.registry: if self.callback.cls != viewset: continue lookup = self.drf_router.get_lookup_regex(viewset) routes = self.drf_router.get_routes(viewset) for route in routes: # Only actions which actually exist on the viewset will be bound mapping = self.drf_router.get_method_map( viewset, route.mapping) if not mapping: continue # Build the url pattern regex = route.url.format( prefix=prefix, lookup=lookup, trailing_slash=self.drf_router.trailing_slash) if get_regex_pattern(self.pattern) == regex: funcs, viewset_methods = zip( *[(mapping[m], m.upper()) for m in self.callback.cls.http_method_names if m in mapping]) viewset_methods = list(viewset_methods) if len(set(funcs)) == 1: self.docstring = inspect.getdoc( getattr(self.callback.cls, funcs[0])) view_methods = [ force_str(m).upper() for m in self.callback.cls.http_method_names if self.is_method_allowed(self.callback.cls, m) ] return sorted(viewset_methods + view_methods)
def test_urls_limited_by_lookup_value_regex(self): expected = ['^notes/$', '^notes/(?P<uuid>[0-9a-f]{32})/$'] for idx in range(len(expected)): assert expected[idx] == get_regex_pattern(self.urls[idx])
def get_url_pattern_by_regex_pattern(patterns, pattern_string): # todo: test me for pattern in patterns: if get_regex_pattern(pattern) == pattern_string: return pattern
def _is_format_endpoint(self, pattern): """ Exclude endpoints with a "format" parameter """ return '?P<format>' in get_regex_pattern(pattern)
def __get_path__(self, parent_regex): regex = get_regex_pattern(self.pattern) if parent_regex: return "/{0}{1}".format(self.name_parent, simplify_regex(regex)) return simplify_regex(regex)
def test_nested_route_depth_3_custom_regex(self): """ Nested routes with over two level of depth should respect all parents' `lookup_value_regex` attribute. """ router = ExtendedSimpleRouter() ( router.register(r'users', CustomRegexUserViewSet, 'user') .register(r'groups', CustomRegexGroupViewSet, 'users-group', parents_query_lookups=['user']) .register(r'permissions', CustomRegexPermissionViewSet, 'users-groups-permission', parents_query_lookups=[ 'group__user', 'group', ] ) ) # custom regex configuration user_viewset_regex = CustomRegexUserViewSet.lookup_value_regex group_viewset_regex = CustomRegexGroupViewSet.lookup_value_regex perm_viewset_regex = CustomRegexPermissionViewSet.lookup_value_regex # test user list self.assertEqual(router.urls[0].name, 'user-list') self.assertEqual(get_regex_pattern(router.urls[0]), r'^users/$') # test user detail self.assertEqual(router.urls[1].name, 'user-detail') self.assertEqual(get_regex_pattern(router.urls[1]), r'^users/{0}/$'.format( self.get_custom_regex_lookup('pk', user_viewset_regex)) ) # test users group list self.assertEqual(router.urls[2].name, 'users-group-list') self.assertEqual(get_regex_pattern(router.urls[2]), r'^users/{0}/groups/$'.format( self.get_custom_regex_parent_lookup('user', user_viewset_regex) ) ) # test users group detail self.assertEqual(router.urls[3].name, 'users-group-detail') self.assertEqual(get_regex_pattern(router.urls[3]), r'^users/{0}/groups/{1}/$'.format( self.get_custom_regex_parent_lookup('user', user_viewset_regex), self.get_custom_regex_lookup('pk', group_viewset_regex) ), ) # test users groups permission list self.assertEqual(router.urls[4].name, 'users-groups-permission-list') self.assertEqual(get_regex_pattern(router.urls[4]), r'^users/{0}/groups/{1}/permissions/$'.format( self.get_custom_regex_parent_lookup('group__user', user_viewset_regex), self.get_custom_regex_parent_lookup('group', group_viewset_regex), ) ) # test users groups permission detail self.assertEqual(router.urls[5].name, 'users-groups-permission-detail') self.assertEqual(get_regex_pattern(router.urls[5]), r'^users/{0}/groups/{1}/permissions/{2}/$'.format( self.get_custom_regex_parent_lookup('group__user', user_viewset_regex), self.get_custom_regex_parent_lookup('group', group_viewset_regex), self.get_custom_regex_lookup('pk', perm_viewset_regex) ), )
def test_custom_lookup_url_kwarg_route(self): detail_route = kwarged_notes_router.urls[-1] detail_url_pattern = get_regex_pattern(detail_route) assert '^notes/(?P<text>' in detail_url_pattern
def test_custom_lookup_field_route(self): detail_route = notes_router.urls[-1] detail_url_pattern = get_regex_pattern(detail_route) assert '<uuid>' in detail_url_pattern
def test_urls_can_have_trailing_slash_removed(self): expected = ['^notes$', '^notes/(?P<pk>[^/.]+)$'] for idx in range(len(expected)): assert expected[idx] == get_regex_pattern(self.urls[idx])