def test_only_explicitly_declared_fields_are_translated(self, recwarn): class UserSchema(Schema): _id = fields.Int() class Meta: title = 'User' fields = ( '_id', 'email', ) res = swagger.schema2jsonschema(UserSchema) assert res['type'] == 'object' props = res['properties'] assert '_id' in props assert 'email' not in props warning = recwarn.pop() expected_msg = 'Only explicitly-declared fields will be included in the Schema Object.' assert expected_msg in str(warning.message) assert issubclass(warning.category, UserWarning)
def test_schema(self, spec): def pet_view(): return '...' spec.add_path( path='/pet', view=pet_view, operations={'get': { 'responses': { 200: { 'schema': PetSchema } } }}) p = spec._paths['/pet'] assert 'get' in p op = p['get'] assert 'responses' in op assert op['responses'][200]['schema'] == swagger.schema2jsonschema( PetSchema)
def test_integration_with_docstring_introspection(self, spec): class HelloHandler(RequestHandler): def get(self): """Get a greeting endpoint. --- description: get a greeting responses: 200: description: a pet to be returned schema: tests.schemas.PetSchema """ self.write("hello") urlspec = (r'/hello', HelloHandler) spec.add_path(urlspec=urlspec) get_op = spec._paths['/hello']['get'] assert get_op['description'] == 'get a greeting' response = get_op['responses'][200] assert response['description'] == 'a pet to be returned' assert response['schema'] == swagger.schema2jsonschema(PetSchema)
def test_schema2jsonschema_override_name_ma2(self): class ExampleSchema(Schema): _id = fields.Int(load_from='id', dump_to='id') _dt = fields.Int(load_from='lf_no_match', dump_to='dt') _lf = fields.Int(load_from='lf') _global = fields.Int(load_from='global', dump_to='global') class Meta: exclude = ('_global', ) res = swagger.schema2jsonschema(ExampleSchema) assert res['type'] == 'object' props = res['properties'] # `_id` renamed to `id` assert '_id' not in props and props['id']['type'] == 'integer' # `load_from` and `dump_to` do not match, `dump_to` is used assert 'lf_no_match' not in props assert props['dt']['type'] == 'integer' # `load_from` and no `dump_to`, `load_from` is used assert props['lf']['type'] == 'integer' # `_global` excluded correctly assert '_global' not in props and 'global' not in props
def test_dump_only_load_only_fields(self, openapi_version): spec = APISpec(title='Pets', version='0.1', plugins=['apispec.ext.marshmallow'], openapi_version=openapi_version) class UserSchema(Schema): _id = fields.Str(dump_only=True) name = fields.Str() password = fields.Str(load_only=True) res = swagger.schema2jsonschema(UserSchema(), spec) props = res['properties'] assert 'name' in props # dump_only field appears with readOnly attribute assert '_id' in props assert 'readOnly' in props['_id'] # load_only field appears (writeOnly attribute does not exist) assert 'password' in props if openapi_version == '2.0.0': assert 'writeOnly' not in props['password'] else: assert 'writeOnly' in props['password']
def convert_schemas(d, definitions=None): """ Convert Marshmallow schemas to dict definitions """ if Schema is None: raise RuntimeError('Please install marshmallow and apispec') if definitions is None: definitions = {} definitions.update(d.get('definitions', {})) new = {'definitions': definitions} for k, v in d.items(): if isinstance(v, dict): v = convert_schemas(v, definitions) if isinstance(v, (list, tuple)): new_v = [] for item in v: if isinstance(item, dict): new_v.append(convert_schemas(item, definitions)) else: new_v.append(item) v = new_v if inspect.isclass(v) and issubclass(v, Schema): definitions[v.__name__] = schema2jsonschema(v) ref = { "$ref": "#/definitions/{0}".format(v.__name__) } if k == 'parameters': new[k] = schema2parameters(v) new[k][0]['schema'] = ref else: new[k] = ref else: new[k] = v return new
def test_schema_array_in_docstring_uses_ref_if_available_v3(self, spec_3): def pet_view(): """Not much to see here. --- get: parameters: - in: body content: application/json: schema: type: array items: tests.schemas.PetSchema responses: 200: content: application/json: schema: type: array items: tests.schemas.PetSchema """ return '...' spec_3.add_path(path='/pet', view=pet_view) p = spec_3._paths['/pet'] assert 'get' in p op = p['get'] resolved_schema = { 'type': 'array', 'items': swagger.schema2jsonschema(PetSchema), } request_schema = op['parameters'][0]['content']['application/json'][ 'schema'] assert request_schema == resolved_schema response_schema = op['responses'][200]['content']['application/json'][ 'schema'] assert response_schema == resolved_schema
def test_schema_global_state_untouched_2json(self): assert RunSchema._declared_fields['sample']._Nested__schema is None data = swagger.schema2jsonschema(RunSchema) json.dumps(data) assert RunSchema._declared_fields['sample']._Nested__schema is None
def test_responses(self, schemas, path): response = path['get']['responses']['default'] assert response['description'] == 'a band' assert response['schema'] == swagger.schema2jsonschema( schemas.BandSchema)
def bottle_generate_operations( spec, bottle_route: bottle.Route, 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': str(description.output_body.wrapper.default_http_code), # nopep8 'schema': { '$ref': '#/definitions/{}'.format(schema_class.__name__) } } if description.output_file: method_operations.setdefault('produce', []).append( description.output_file.wrapper.output_type ) method_operations.setdefault('responses', {})\ [int(description.output_file.wrapper.default_http_code)] = { 'description': str(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': str(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('consume', []).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 = { bottle_route.method.lower(): method_operations, } return operations
def specs_view(self): specs = { 'swagger': '2.0', 'info': { 'description': self.__doc__, 'version': self.version, 'title': self.name, }, 'basePath': self.url_prefix, 'tags': [], 'paths': {}, 'definitions': {}, 'host': request.host, 'schemes': [request.scheme], } for resource in self.resources: if resource.Schema: specs['definitions'][resource.meta.name] = schema2jsonschema(resource.Schema) specs['tags'].append({ 'name': resource.meta.name, 'description': resource.__doc__ or resource.__class__.__doc__, }) defaults = { 'consumes': ['application/json'], 'produces': ['application/json'], 'security': [{'api_key': []}], 'tags': [resource.meta.name], 'responses': {200: "OK"} } for endpoint, (url_, name_, params_) in resource.meta.endpoints.values(): specs['paths'][ "%s%s" % (resource.meta.url, url_flask_to_swagger(url_))] = path = {} path['get'] = dict( summary=endpoint.__doc__, description=endpoint.__doc__, **defaults) if hasattr(endpoint, 'specs'): path['get'].update(endpoint.specs) specs['paths'][resource.meta.url] = path = {} for method in ('get', 'post'): if method.upper() not in resource.methods or not hasattr(resource, 'post'): continue view = getattr(resource, method) path[method] = dict(summary=view.__doc__, description=view.__doc__, **defaults) if method == 'post': path[method]['parameters'] = [{ 'in': 'body', 'name': 'body', 'description': 'resource body', 'required': True, 'schema': { '$ref': '#/definitions/%s' % resource.meta.name } }] if resource.meta.specs: path[method].update(resource.meta.specs) if resource.meta.url_detail: url_detail = url_flask_to_swagger(resource.meta.url_detail) path = specs['paths'][url_detail] = {} for method in ('get', 'put', 'delete'): if method.upper() not in resource.methods or not hasattr(resource, 'post'): continue view = getattr(resource, method) path[method] = dict( summary=view.__doc__, description=view.__doc__, parameters=[{ 'name': resource.meta.name, 'in': 'path', 'description': 'ID of resource', 'type': 'string', 'required': True }], **defaults) if method == 'put': path[method]['parameters'].append({ 'in': 'body', 'name': 'body', 'description': 'resource body', 'required': True, 'schema': { '$ref': '#/definitions/%s' % resource.meta.name } }) if resource.meta.specs: path[method].update(resource.meta.specs) if isinstance(self.specs, dict): specs.update(self.specs) response = jsonify(specs) response.headers.add_header('Access-Control-Allow-Headers', 'Content-Type, Authorization') response.headers.add_header('Access-Control-Allow-Origin', '*') response.headers.add_header('Access-Control-Allow-Methods', 'GET,POST,DELETE,PUT') return response
def test_field2property_nested_ref(self, spec): category = fields.Nested(CategorySchema) assert swagger.field2property(category) == swagger.schema2jsonschema(CategorySchema) cat_with_ref = fields.Nested(CategorySchema, ref='Category') assert swagger.field2property(cat_with_ref) == {'$ref': 'Category'}
def test_no_required_fields(self): class BandSchema(Schema): drummer = fields.Str() bassist = fields.Str() res = swagger.schema2jsonschema(BandSchema) assert 'required' not in res
def test_required_fields(self): class BandSchema(Schema): drummer = fields.Str(required=True) bassist = fields.Str() res = swagger.schema2jsonschema(BandSchema) assert res['required'] == ['drummer']
def test_invalid_schema(self): with pytest.raises(ValueError): swagger.schema2jsonschema(None)
def test_schema2jsonschema_with_nested_fields(self): res = swagger.schema2jsonschema(PetSchema, use_refs=False) props = res['properties'] assert props['category']['items'] == swagger.schema2jsonschema( CategorySchema)
def test_responses(self, schemas, path): response = path['get']['responses']['default'] assert response['description'] == 'a band' assert response['schema'] == swagger.schema2jsonschema(schemas.BandSchema)