def test_manually_routing_nested_routes(self): patterns = [ url(r'^test', simple_fbv), url(r'^test/list/', simple_fbv), ] generator = SchemaGenerator(title='Naming Colisions', patterns=patterns) schema = generator.get_schema() expected = coreapi.Document(url='', title='Naming Colisions', content={ 'test': { 'list': { 'list': coreapi.Link(url='/test/list/', action='get') }, 'list_0': coreapi.Link(url='/test', action='get') } }) assert expected == schema
def test_from_router(self): patterns = [ url(r'from-router', include(naming_collisions_router.urls)), ] generator = SchemaGenerator(title='Naming Colisions', patterns=patterns) schema = generator.get_schema() # not important here desc_0 = schema['detail']['detail_export'].description desc_1 = schema['detail_0'].description expected = coreapi.Document( url='', title='Naming Colisions', content={ 'detail': { 'detail_export': coreapi.Link(url='/from-routercollision/detail/export/', action='get', description=desc_0) }, 'detail_0': coreapi.Link(url='/from-routercollision/detail/', action='get', description=desc_1) }) assert schema == expected
def test_anonymous_request(self): client = APIClient() response = client.get('/', HTTP_ACCEPT='application/vnd.coreapi+json') self.assertEqual(response.status_code, 200) expected = coreapi.Document( url='', title='Example API', content={ 'example': { 'list': coreapi.Link(url='/example/', action='get', fields=[ coreapi.Field('page', required=False, location='query'), coreapi.Field('ordering', required=False, location='query') ]), 'retrieve': coreapi.Link(url='/example/{pk}/', action='get', fields=[ coreapi.Field('pk', required=True, location='path') ]) } }) self.assertEqual(response.data, expected)
def test_multiple_nested_routes(self): schema = coreapi.Document( url='', title='Example API', content={ 'animals': { 'dog': { 'vet': { 'list': coreapi.Link(url='/animals/dog/{id}/vet', action='get', fields=[ coreapi.Field( 'id', required=True, location='path', schema=coreschema.String()) ]) }, 'read': coreapi.Link(url='/animals/dog/{id}', action='get', fields=[ coreapi.Field( 'id', required=True, location='path', schema=coreschema.String()) ]) }, 'cat': { 'list': coreapi.Link(url='/animals/cat/', action='get', fields=[ coreapi.Field( 'id', required=True, location='path', schema=coreschema.String()) ]), 'create': coreapi.Link(url='/aniamls/cat', action='post', fields=[]) } } }) section = schema['animals'] flat_links = schema_links(section) assert len(flat_links) is 4 assert 'cat > create' in flat_links assert 'cat > list' in flat_links assert 'dog > read' in flat_links assert 'dog > vet > list' in flat_links
def test_schema_for_regular_views(self): """ Ensure that schema generation works for ViewSet classes with method limitation by Django CBV's http_method_names attribute """ generator = SchemaGenerator(title='Example API', patterns=self.patterns) request = factory.get('/example1/') schema = generator.get_schema(Request(request)) expected = coreapi.Document( url='http://testserver/example1/', title='Example API', content={ 'example1': { 'list': coreapi.Link( url='/example1/', action='get', fields=[ coreapi.Field('page', required=False, location='query', schema=coreschema.Integer(title='Page', description='A page number within the paginated result set.')), coreapi.Field('page_size', required=False, location='query', schema=coreschema.Integer(title='Page size', description='Number of results to return per page.')), coreapi.Field('ordering', required=False, location='query', schema=coreschema.String(title='Ordering', description='Which field to use when ordering the results.')) ] ), 'custom_list_action': coreapi.Link( url='/example1/custom_list_action/', action='get' ), 'custom_list_action_multiple_methods': { 'read': coreapi.Link( url='/example1/custom_list_action_multiple_methods/', action='get', description='Custom description.', ) }, 'documented_custom_action': { 'read': coreapi.Link( url='/example1/documented_custom_action/', action='get', description='A description of the get method on the custom action.', ), }, 'read': coreapi.Link( url='/example1/{id}/', action='get', fields=[ coreapi.Field('id', required=True, location='path', schema=coreschema.String()), coreapi.Field('ordering', required=False, location='query', schema=coreschema.String(title='Ordering', description='Which field to use when ordering the results.')) ] ) } } ) assert schema == expected
def test_anonymous_request(self): client = APIClient() response = client.get('/', HTTP_ACCEPT='application/coreapi+json') assert response.status_code == 200 expected = coreapi.Document( url='http://testserver/', title='Example API', content={ 'example': { 'list': coreapi.Link( url='/example/', action='get', fields=[ coreapi.Field('page', required=False, location='query', schema=coreschema.Integer(title='Page', description='A page number within the paginated result set.')), coreapi.Field('page_size', required=False, location='query', schema=coreschema.Integer(title='Page size', description='Number of results to return per page.')), coreapi.Field('ordering', required=False, location='query', schema=coreschema.String(title='Ordering', description='Which field to use when ordering the results.')) ] ), 'custom_list_action': coreapi.Link( url='/example/custom_list_action/', action='get' ), 'custom_list_action_multiple_methods': { 'read': coreapi.Link( url='/example/custom_list_action_multiple_methods/', action='get', description='Custom description.', ) }, 'documented_custom_action': { 'read': coreapi.Link( url='/example/documented_custom_action/', action='get', description='A description of the get method on the custom action.', ) }, 'read': coreapi.Link( url='/example/{id}/', action='get', fields=[ coreapi.Field('id', required=True, location='path', schema=coreschema.String()), coreapi.Field('ordering', required=False, location='query', schema=coreschema.String(title='Ordering', description='Which field to use when ordering the results.')) ] ) } } ) assert response.data == expected
def test_default_actions_and_single_custom_action(self): schema = coreapi.Document( url='', title='Example API', content={ 'users': { 'create': coreapi.Link( url='/users/', action='post', fields=[] ), 'list': coreapi.Link( url='/users/', action='get', fields=[] ), 'read': coreapi.Link( url='/users/{id}/', action='get', fields=[ coreapi.Field('id', required=True, location='path', schema=coreschema.String()) ] ), 'update': coreapi.Link( url='/users/{id}/', action='patch', fields=[ coreapi.Field('id', required=True, location='path', schema=coreschema.String()) ] ), 'friends': coreapi.Link( url='/users/{id}/friends', action='get', fields=[ coreapi.Field('id', required=True, location='path', schema=coreschema.String()) ] ) } } ) section = schema['users'] flat_links = schema_links(section) assert len(flat_links) is 5 assert 'list' in flat_links assert 'create' in flat_links assert 'read' in flat_links assert 'update' in flat_links assert 'friends' in flat_links
def get_link(self, path, method, view): """ Return a `coreapi.Link` instance for the given endpoint. """ fields = [] getters = [ self.get_path_fields, self.get_serializer_fields, self.get_pagination_fields, self.get_filter_fields, self.get_docs_fields ] for getter in getters: try: fields.extend(getter(path, method, view)) except Exception: pass if fields and any( [field.location in ('form', 'body') for field in fields]): encoding = self.get_encoding(path, method, view) else: encoding = None description = self.get_description(path, method, view) permissions = self.get_permissions_docs(path, method, view) if permissions is not None: description = "{}\nPermissions:\n========\n{}".format( description, permissions) if self.url and path.startswith('/'): path = path[1:] return coreapi.Link(url=urlparse.urljoin(self.url, path), action=method.lower(), encoding=encoding, fields=fields, description=description)
def get_link(self, path, method, base_url): fields = self.get_path_fields(path, method) fields += self.get_serializer_fields(path, method) fields += self.get_pagination_fields(path, method) fields += self.get_filter_fields(path, method) manual_fields = self.get_manual_fields(path, method) fields = self.update_fields(fields, manual_fields) if fields and any([field.location in ('form', 'body') for field in fields]): encoding = self.get_encoding(path, method) else: encoding = None description = self.get_description(path, method) if base_url and path.startswith('/'): path = path[1:] return coreapi.Link( url=urlparse.urljoin(base_url, path), action=method.lower(), encoding=encoding, fields=fields, description=description )
def test_document_with_link_named_data(self): """ Ref #5395: Doc's `document.data` would fail with a Link named "data". As per #4972, use templatetag instead. """ document = coreapi.Document(title='Data Endpoint API', url='https://api.example.org/', content={ 'data': coreapi.Link( url='/data/', action='get', fields=[], description='Return data.') }) factory = APIRequestFactory() request = factory.get('/') renderer = DocumentationRenderer() html = renderer.render(document, accepted_media_type="text/html", renderer_context={"request": request}) assert '<h1>Data Endpoint API</h1>' in html
def get_link(self, path, method, callback): """ Return a `coreapi.Link` instance for the given endpoint. """ view = callback.cls() for attr, val in getattr(callback, 'initkwargs', {}).items(): setattr(view, attr, val) fields = self.get_path_fields(path, method, callback, view) fields += self.get_serializer_fields(path, method, callback, view) fields += self.get_pagination_fields(path, method, callback, view) fields += self.get_filter_fields(path, method, callback, view) if fields and any( [field.location in ('form', 'body') for field in fields]): encoding = self.get_encoding(path, method, callback, view) else: encoding = None if self.url and path.startswith('/'): path = path[1:] return coreapi.Link(url=urlparse.urljoin(self.url, path), action=method.lower(), encoding=encoding, fields=fields)
def test_schema_for_regular_views(self): """ Ensure that AutoField foreign keys are output as Integer. """ generator = SchemaGenerator(title='Example API', patterns=self.patterns) schema = generator.get_schema() expected = coreapi.Document( url='', title='Example API', content={ 'example': { 'create': coreapi.Link( url='/example/', action='post', encoding='application/json', fields=[ coreapi.Field('name', required=True, location='form', schema=coreschema.String(title='Name')), coreapi.Field('target', required=True, location='form', schema=coreschema.Integer(description='Target', title='Target')), ] ) } } ) assert schema == expected
def get_link(self, path, method, view): methods = [ 'get_path_fields', 'get_serializer_fields', 'get_pagination_fields', 'get_filter_fields' ] fields = [] for method_name in methods: try: fields += getattr(self, method_name)(path, method, view) except (AttributeError, AssertionError): # it suppresses any exceptions caused by some custom serializers, methods etc. pass if fields and any( [field.location in ('form', 'body') for field in fields]): encoding = self.get_encoding(path, method, view) else: encoding = None description = self.get_description(path, method, view) if self.url and path.startswith('/'): path = path[1:] return coreapi.Link(url=urljoin(self.url, path), action=method.lower(), encoding=encoding, fields=fields, description=description)
def get_link(self, path, method, base_url): fields = self.get_path_fields(path, method) fields += self.get_serializer_fields(path, method) fields += self.get_pagination_fields(path, method) fields += self.get_filter_fields(path, method) if self._manual_fields is not None: by_name = {f.name: f for f in fields} for f in self._manual_fields: by_name[f.name] = f fields = list(by_name.values()) if fields and any([field.location in ('form', 'body') for field in fields]): encoding = self.get_encoding(path, method) else: encoding = None description = self.get_description(path, method) if base_url and path.startswith('/'): path = path[1:] return coreapi.Link( url=urlparse.urljoin(base_url, path), action=method.lower(), encoding=encoding, fields=fields, description=description )
def get_link(self, path, method, view): """ Return a `coreapi.Link` instance for the given endpoint. """ fields = self.get_path_fields(path, method, view) fields += self.get_serializer_fields(path, method, view) fields += self.get_pagination_fields(path, method, view) fields += self.get_filter_fields(path, method, view) if fields and any([field.location in ('form', 'body') for field in fields]): encoding = self.get_encoding(path, method, view) else: encoding = None description = self.get_description(path, method, view) if self.url and path.startswith('/'): path = path[1:] return coreapi.Link( url=urlparse.urljoin(self.url, path), action=method.lower(), encoding=encoding, fields=fields, description=description )
def get_link(self, path, method, view): """ Parse YAML docs. __doc__ in yaml format, eg: description: the desc of this api. parameters: - name: mobile description: the mobile number type: string required: true location: query - name: promotion description: the activity id type: int required: true location: form """ method_desc = '' fields = self.get_path_fields(path, method, view) func = getattr(view, method.lower(), None) yaml_doc = None if func and func.__doc__: try: method_desc, raw_yaml_doc = func.__doc__.split('---') yaml_doc = yaml.load(raw_yaml_doc) except Exception: yaml_doc = None if yaml_doc and 'parameters' in yaml_doc: for parameter in yaml_doc.get('parameters'): field = coreapi.Field(name=parameter.get('name'), description=parameter.get('description'), required=parameter.get('required', True), type=parameter.get('type', 'string'), location=parameter.get( 'paramType', 'query')) fields.append(field) fields += self.get_serializer_fields(path, method, view) fields += self.get_pagination_fields(path, method, view) fields += self.get_filter_fields(path, method, view) if fields and any([f.location in ('form', 'body') for f in fields]): encoding = self.get_encoding(path, method, view) else: encoding = None if self.url and path.startswith('/'): path = path[1:] return coreapi.Link(url=parse.urljoin(self.url, path), action=method.lower(), encoding=encoding, fields=fields, description=method_desc)
def test_schema_for_regular_views(self): """ Ensure that schema generation with an API that is not at the URL root continues to use correct structure for link keys. """ generator = SchemaGenerator(title='Example API', patterns=self.patterns) schema = generator.get_schema() expected = coreapi.Document( url='', title='Example API', content={ 'example': { 'create': coreapi.Link(url='/api/v1/example/', action='post', fields=[]), 'list': coreapi.Link(url='/api/v1/example/', action='get', fields=[]), 'read': coreapi.Link(url='/api/v1/example/{id}/', action='get', fields=[ coreapi.Field('id', required=True, location='path', schema=coreschema.String()) ]), 'sub': { 'list': coreapi.Link(url='/api/v1/example/{id}/sub/', action='get', fields=[ coreapi.Field( 'id', required=True, location='path', schema=coreschema.String()) ]) } } }) assert schema == expected
def test_shell_code_example_rendering(self): template = loader.get_template('rest_framework/docs/langs/shell.html') context = { 'document': coreapi.Document(url='https://api.example.org/'), 'link_key': 'testcases > list', 'link': coreapi.Link(url='/data/', action='get', fields=[]), } html = template.render(context) assert 'testcases list' in html
def get_link(self, path, method, view): fields = self.get_path_fields(path, method, view) yaml_doc = None func = getattr(view, view.action) if getattr(view, 'action', None) else None if hasattr(view, 'get') and method == 'GET': func = view.get if hasattr(view, 'post') and method == 'POST': func = view.post if hasattr(view, 'put') and method == 'PUT': func = view.put if hasattr(view, 'delete') and method == 'DELETE': func = view.delete if hasattr(view, 'patch') and method == 'PATCH': func = view.patch if func and func.__doc__: try: yaml_doc = yaml.load(func.__doc__) except: yaml_doc = None if yaml_doc and 'desc' in yaml_doc: desc = yaml_doc.get('desc', '') _method_desc = desc params = yaml_doc.get('parameters', []) for i in params: _name = i.get('name') _desc = i.get('desc') _required = i.get('required', True) _type = i.get('type', 'string') _location = i.get('location', 'query') f = coreapi.Field(name=_name, location=_location, required=_required, description=_desc, type=_type) fields.append(f) else: _method_desc = func.__doc__ if func and func.__doc__ else '' fields += self.get_serializer_fields(path, method, view) fields += self.get_pagination_fields(path, method, view) fields += self.get_filter_fields(path, method, view) if fields and any( [field.location in ('form', 'body') for field in fields]): encoding = self.get_encoding(path, method, view) else: encoding = None if self.url and path.startswith('/'): path = path[1:] return coreapi.Link(url=urlparse.urljoin(self.url, path), action=method.lower(), encoding=encoding, fields=fields, description=_method_desc)
def get_link(self, path, method, base_url): if base_url and path.startswith('/'): path = path[1:] return coreapi.Link(url=urlparse.urljoin(base_url, path), action=method.lower(), encoding=self._encoding, fields=self._fields, description=self._description)
def test_anonymous_request(self): client = APIClient() response = client.get('/', HTTP_ACCEPT='application/coreapi+json') self.assertEqual(response.status_code, 200) expected = coreapi.Document( url='', title='Example API', content={ 'example': { 'list': coreapi.Link(url='/example/', action='get', fields=[ coreapi.Field('page', required=False, location='query'), coreapi.Field('page_size', required=False, location='query'), coreapi.Field('ordering', required=False, location='query') ]), 'custom_list_action': coreapi.Link(url='/example/custom_list_action/', action='get'), 'custom_list_action_multiple_methods': { 'read': coreapi.Link( url='/example/custom_list_action_multiple_methods/', action='get') }, 'read': coreapi.Link(url='/example/{id}/', action='get', fields=[ coreapi.Field('id', required=True, location='path') ]) } }) self.assertEqual(response.data, expected)
def test_view(self): schema_generator = SchemaGenerator(title='Test View', patterns=urlpatterns2) schema = schema_generator.get_schema() expected = coreapi.Document(url='', title='Test View', content={ 'example-view': { 'create': coreapi.Link(url='/example-view/', action='post', fields=[]), 'read': coreapi.Link(url='/example-view/', action='get', fields=[]) } }) self.assertEquals(schema, expected)
def generate_notifications_docs(cls, notification_type): links = {} for key, value in cls.get_notifications(notification_type): generator = cls.get_generator(value["cls"]) links[value["path"]] = coreapi.Link( title=key, description=generator.get_notes(), action="GET", url=value["path"]) return links
def test_schema_for_regular_views(self): """ Ensure that schema generation works for APIView classes. """ generator = SchemaGenerator(title='Example API', patterns=self.patterns) schema = generator.get_schema() expected = coreapi.Document( url='', title='Example API', content={ 'example': { 'create': coreapi.Link( url='/example/', action='post', fields=[] ), 'list': coreapi.Link( url='/example/', action='get', fields=[] ), 'read': coreapi.Link( url='/example/{id}/', action='get', fields=[ coreapi.Field('id', required=True, location='path', schema=coreschema.String()) ] ), 'sub': { 'list': coreapi.Link( url='/example/{id}/sub/', action='get', fields=[ coreapi.Field('id', required=True, location='path', schema=coreschema.String()) ] ) } } } ) assert schema == expected
def get_links(self, request=None): links = LinkNode() # Generate (path, method, view) given (path, method, callback). paths = [] view_endpoints = [] for path, method, callback in self.endpoints: view = self.create_view(callback, method, request) path = self.coerce_path(path, method, view) paths.append(path) view_endpoints.append((path, method, view)) # Only generate the path prefix for paths that will be included if not paths: return None prefix = self.determine_path_prefix(paths) for path, method, view in view_endpoints: if not self.has_view_permissions(path, method, view): continue fields = view.schema.get_path_fields(path, method) fields += view.schema.get_serializer_fields(path, method) fields += view.schema.get_pagination_fields(path, method) fields += view.schema.get_filter_fields(path, method) manual_fields = view.schema.get_manual_fields(path, method) fields = view.schema.update_fields(fields, manual_fields) if fields and any( [field.location in ('form', 'body') for field in fields]): encoding = view.schema.get_encoding(path, method) else: encoding = None description = view.schema.get_description(path, method) if description and len(description) > 0: query_fields, description = self.get_docstring_fields( description) fields += query_fields if self.url and path.startswith('/'): path = path[1:] link = coreapi.Link(url=urlparse.urljoin(self.url, path), action=method.lower(), encoding=encoding, fields=fields, description=description) subpath = path[len(prefix):] keys = self.get_keys(subpath, method, view) insert_into(links, keys, link) return links
def test_single_action(self): schema = coreapi.Document(url='', title='Example API', content={ 'users': { 'list': coreapi.Link(url='/users/', action='get', fields=[]) } }) section = schema['users'] flat_links = schema_links(section) assert len(flat_links) is 1 assert 'list' in flat_links
def test_schema_generator_excludes_correctly(self): """Schema should not include excluded views""" generator = SchemaGenerator(title='Exclusions', patterns=self.patterns) schema = generator.get_schema() expected = coreapi.Document(url='', title='Exclusions', content={ 'included-fbv': { 'list': coreapi.Link(url='/included-fbv/', action='get') } }) assert len(schema.data) == 1 assert 'included-fbv' in schema.data assert schema == expected
def test_view_with_manual_schema(self): path = '/example' method = 'get' base_url = None fields = [ coreapi.Field( "first_field", required=True, location="path", schema=coreschema.String() ), coreapi.Field( "second_field", required=True, location="path", schema=coreschema.String() ), coreapi.Field( "third_field", required=True, location="path", schema=coreschema.String() ), ] description = "A test endpoint" class CustomView(APIView): """ ManualSchema takes list of fields for endpoint. - Provides url and action, which are always dynamic """ schema = ManualSchema(fields, description) expected = coreapi.Link( url=path, action=method, fields=fields, description=description ) view = CustomView() link = view.schema.get_link(path, method, base_url) assert link == expected
def get_link(self, path, method, view, base_url): fields = view.schema.get_path_fields(path, method) if fields and any( [field.location in ('form', 'body') for field in fields]): encoding = view.schema.get_encoding(path, method) else: encoding = "multipart/form-data" description = view.schema.get_description(path, method) if base_url and path.startswith('/'): path = path[1:] return coreapi.Link(url=urlparse.urljoin('', path), action=method.lower(), encoding=encoding, fields=fields, description=description)
def get_link(self, path, method, base_url): """ Generate `coreapi.Link` for self.view, path and method. This is the main _public_ access point. Parameters: * path: Route path for view from URLConf. * method: The HTTP request method. * base_url: The project "mount point" as given to SchemaGenerator """ fields = [ *self.get_path_fields(path, method), *self.get_serializer_fields(path, method), *self.get_pagination_fields(path, method), *self.get_filter_fields(path, method) ] manual_fields = self.get_manual_fields(path, method) fields = self.update_fields(fields, manual_fields) http_method_fields = self.methods_fields.get( f"{method.lower()}_fields", []) fields = self.update_fields(fields, http_method_fields) try: url_name = resolve(path).url_name if url_name is not None: url_name = '_'.join(url_name.split('-')[1:]).lower() view_method_fields = tuple( self.methods_fields.get(f"{url_name}_fields", [])) fields = self.update_fields(fields, view_method_fields) except Exception: # pylint: disable=broad-except pass path = path[1:] if base_url and path.startswith('/') else path encoding = self.get_encoding( path, method) if fields and any(f.location in ('form', 'body') for f in fields) else None return coreapi.Link(url=urlparse.urljoin(base_url, path), action=method.lower(), encoding=encoding, fields=fields, description=self.get_description(path, method))