123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- # -*- coding: utf-8 -*-
- import typing
-
- from apispec import APISpec
- from apispec import Path
- from apispec.ext.marshmallow.swagger import schema2jsonschema
-
- from hapic.context import ContextInterface
- from hapic.context import RouteRepresentation
- from hapic.decorator import DecoratedController
- from hapic.description import ControllerDescription
-
-
- def bottle_generate_operations(
- spec,
- route: RouteRepresentation,
- description: ControllerDescription,
- ):
- method_operations = dict()
-
- # schema based
- if description.input_body:
- schema_class = type(description.input_body.wrapper.processor.schema)
- method_operations.setdefault('parameters', []).append({
- 'in': 'body',
- 'name': 'body',
- 'schema': {
- '$ref': '#/definitions/{}'.format(schema_class.__name__)
- }
- })
-
- if description.output_body:
- schema_class = type(description.output_body.wrapper.processor.schema)
- method_operations.setdefault('responses', {})\
- [int(description.output_body.wrapper.default_http_code)] = {
- 'description': int(description.output_body.wrapper.default_http_code), # nopep8
- 'schema': {
- '$ref': '#/definitions/{}'.format(schema_class.__name__)
- }
- }
-
- if description.output_file:
- method_operations.setdefault('produces', []).extend(
- description.output_file.wrapper.output_types
- )
- method_operations.setdefault('responses', {})\
- [int(description.output_file.wrapper.default_http_code)] = {
- 'description': int(description.output_file.wrapper.default_http_code), # nopep8
- }
-
- if description.errors:
- for error in description.errors:
- schema_class = type(error.wrapper.schema)
- method_operations.setdefault('responses', {})\
- [int(error.wrapper.http_code)] = {
- 'description': int(error.wrapper.http_code),
- 'schema': {
- '$ref': '#/definitions/{}'.format(schema_class.__name__) # nopep8
- }
- }
-
- # jsonschema based
- if description.input_path:
- schema_class = type(description.input_path.wrapper.processor.schema)
- # TODO: look schema2parameters ?
- jsonschema = schema2jsonschema(schema_class, spec=spec)
- for name, schema in jsonschema.get('properties', {}).items():
- method_operations.setdefault('parameters', []).append({
- 'in': 'path',
- 'name': name,
- 'required': name in jsonschema.get('required', []),
- 'type': schema['type']
- })
-
- if description.input_query:
- schema_class = type(description.input_query.wrapper.processor.schema)
- jsonschema = schema2jsonschema(schema_class, spec=spec)
- for name, schema in jsonschema.get('properties', {}).items():
- method_operations.setdefault('parameters', []).append({
- 'in': 'query',
- 'name': name,
- 'required': name in jsonschema.get('required', []),
- 'type': schema['type']
- })
-
- if description.input_files:
- method_operations.setdefault('consumes', []).append('multipart/form-data')
- for field_name, field in description.input_files.wrapper.processor.schema.fields.items():
- method_operations.setdefault('parameters', []).append({
- 'in': 'formData',
- 'name': field_name,
- 'required': field.required,
- 'type': 'file',
- })
-
- operations = {
- route.method.lower(): method_operations,
- }
-
- return operations
-
-
- class DocGenerator(object):
- def get_doc(
- self,
- controllers: typing.List[DecoratedController],
- context: ContextInterface,
- title: str='',
- description: str='',
- ) -> dict:
- spec = APISpec(
- title=title,
- info=dict(description=description),
- version='1.0.0',
- plugins=(
- 'apispec.ext.marshmallow',
- ),
- schema_name_resolver=generate_schema_name,
- )
-
- schemas = []
- # parse schemas
- for controller in controllers:
- description = controller.description
-
- if description.input_body:
- schemas.append(type(
- description.input_body.wrapper.processor.schema
- ))
-
- if description.input_forms:
- schemas.append(type(
- description.input_forms.wrapper.processor.schema
- ))
-
- if description.output_body:
- schemas.append(type(
- description.output_body.wrapper.processor.schema
- ))
-
- if description.errors:
- for error in description.errors:
- schemas.append(type(error.wrapper.schema))
-
- for schema in set(schemas):
- spec.definition(schema.__name__, schema=schema)
-
- # add views
- # with app.test_request_context():
- paths = {}
- for controller in controllers:
- route = context.find_route(controller)
- swagger_path = context.get_swagger_path(route.rule)
-
- operations = bottle_generate_operations(
- spec,
- route,
- controller.description,
- )
-
- # TODO BS 20171114: TMP code waiting refact of doc
- doc_string = controller.reference.get_doc_string()
- if doc_string:
- for method in operations.keys():
- operations[method]['description'] = doc_string
-
- path = Path(path=swagger_path, operations=operations)
-
- if swagger_path in paths:
- paths[swagger_path].update(path)
- else:
- paths[swagger_path] = path
-
- spec.add_path(path)
-
- return spec.to_dict()
-
-
- # TODO BS 20171109: Must take care of already existing definition names
- def generate_schema_name(schema):
- return schema.__name__
|