def parameters(): parameters = OrderedDict() for (resource_name, rd) in app.config['DOMAIN'].items(): if (resource_name.endswith('_versions') or rd.get('disable_documentation')): continue title = rd['item_title'] lookup_field = rd['item_lookup_field'] if lookup_field not in rd['schema']: rd['schema'][lookup_field] = {'type': 'objectid'} eve_type = rd['schema'][lookup_field]['type'] descr = rd['schema'][lookup_field].get('description') or '' example = rd['schema'][lookup_field].get('example') or '' if 'data_relation' in rd['schema'][lookup_field]: # the lookup field is a copy of another field dr = rd['schema'][lookup_field]['data_relation'] # resource definition of the data relation source source_rd = app.config['DOMAIN'][dr['resource']] # schema of the data relation source field source_def = source_rd['schema'][dr['field']] # key in #/definitions/... source_def_name = source_rd['item_title'] + '_' + dr['field'] # copy description if necessary descr = descr or source_def.get('description') descr = descr + ' (links to {0})'.format(source_def_name) p = OrderedDict() p['in'] = 'path' p['name'] = title.lower() + 'Id' p['required'] = True p['description'] = descr p['example'] = example p['type'] = eve_type if eve_type == 'objectid': p['type'] = 'string' p['format'] = 'objectid' elif eve_type == 'datetime': p['type'] = 'string' p['format'] = 'date-time' elif eve_type == 'float': p['type'] = 'number' p['format'] = 'float' parameters[title + '_' + lookup_field] = p return parameters
def delete_response(rd): return OrderedDict([('summary', 'Deletes all %s' % rd['resource_title']), ('responses', { '204': { 'description': 'operation has been successful' } }), ('tags', [rd['item_title']])])
def get_parameters(rd): return OrderedDict([ ('in', 'body'), ('name', rd['item_title']), ('required', True), ('schema', get_ref_schema(rd)), ])
def index(): def node(parent, key, value): if value: parent[key] = value root = OrderedDict() root['swagger'] = '2.0' node(root, 'info', info()) node(root, 'host', host()) node(root, 'basePath', base_path()) node(root, 'schemes', schemes()) node(root, 'consumes', consumes()) node(root, 'produces', produces()) node(root, 'paths', paths()) node(root, 'definitions', definitions()) node(root, 'parameters', parameters()) node(root, 'responses', responses()) node(root, 'securityDefinitions', security_definitions()) node(root, 'security', security()) node(root, 'tags', tags()) node(root, 'externalDocs', external_docs()) _nested_update(root, swagger.additional_documentation) return jsonify(root)
def paths(): def _clear_regex(_str): return '/'.join( map(lambda x: re.sub('regex(.*):', '', x), _str.split('/'))) paths = OrderedDict() for resource, rd in app.config['DOMAIN'].items(): if (rd.get('disable_documentation') or resource.endswith('_versions')): continue rd['url'] = _clear_regex(rd['url']) rd['resource_title'] = _clear_regex(rd['resource_title']) methods = rd['resource_methods'] if methods: url = '/%s' % rd['url'] paths[url] = _resource(resource, rd, methods) methods = rd['item_methods'] if methods: item_id = '%sId' % rd['item_title'].lower() url = '/%s/{%s}' % (rd['url'], item_id) paths[url] = _item(resource, rd, methods) return paths
def header_parameters(): r = OrderedDict() r['in'] = 'header' r['name'] = 'If-Match' r['description'] = 'Current value of the _etag field' r['required'] = app.config['IF_MATCH'] and app.config['ENFORCE_IF_MATCH'] r['type'] = 'string' return r
def definitions(): definitions = OrderedDict() dr_sources = {} for rd in app.config['DOMAIN'].values(): dr_sources.update(_get_dr_sources(rd['schema'])) for rd in app.config['DOMAIN'].values(): if rd.get('disable_documentation'): continue title = rd['item_title'] definitions[title] = _object(rd, dr_sources) if 'description' in rd: definitions[title]['description'] = rd['description'] # add data_relation source fields to #/definitions/ definitions.update(dr_sources) return definitions
def definitions(): definitions = OrderedDict() dr_sources = {} for rd in app.config["DOMAIN"].values(): dr_sources.update(_get_dr_sources(rd["schema"])) for rd in app.config["DOMAIN"].values(): if rd.get("disable_documentation"): continue title = rd["item_title"] definitions[title] = _object(rd, dr_sources) if "description" in rd: definitions[title]["description"] = rd["description"] # add data_relation source fields to #/definitions/ definitions.update(dr_sources) return definitions
def header_parameters(): r = OrderedDict() r['in'] = 'header' r['name'] = 'If-Match' r['description'] = 'Current value of the _etag field' r['required'] = True r['type'] = 'string' return r
def post_response(rd): return OrderedDict([('summary', 'Stores one or more %s' % rd['resource_title']), ('parameters', [get_parameters(rd)]), ('responses', { '201': { 'description': 'operation has been successful' } }), ('tags', [rd['item_title']])])
def getitem_response(rd): title = rd['item_title'] return OrderedDict([('summary', 'Retrieves a %s document' % title), ('responses', { '200': { 'description': '%s document fetched successfully' % title, 'schema': get_ref_schema(rd) }, }), ('parameters', [id_parameter(rd)]), ('tags', [rd['item_title']])])
def get_response(rd): title = rd['resource_title'] return OrderedDict([('summary', 'Retrieves one or more %s' % title), ('responses', { '200': { 'description': 'An array of %s' % title, 'schema': { 'type': 'array', 'items': get_ref_schema(rd) } } }), ('tags', [rd['item_title']])])
def deleteitem_response(rd): title = rd['item_title'] return OrderedDict([('summary', 'Deletes a %s document' % title), ('responses', { '204': { 'description': '%s document deleted successfully' % title } }), ('parameters', [id_parameter(rd), header_parameters()]), ('tags', [rd['item_title']])])
def put_response(rd): title = rd['item_title'] return OrderedDict([ ('summary', 'Replaces a %s document' % title), ('responses', { '200': { 'description': '%s document replaced successfully' % title } }), ('parameters', [id_parameter(rd), get_parameters(rd), header_parameters()]), ('tags', [rd['item_title']]) ])
def _resource(resource, rd, methods): item = OrderedDict() describe_hooks = app.config.get('ENABLE_HOOK_DESCRIPTION', False) if 'GET' in methods: item['get'] = get_response(rd) if 'POST' in methods: item['post'] = post_response(rd) if 'DELETE' in methods: item['delete'] = delete_response(rd) if describe_hooks: for m in methods: hook_desc = _hook_descriptions(resource, m) if hook_desc != '': item[m.lower()]['description'] = '**Hooks**:' + hook_desc return item
def paths(): paths = OrderedDict() for resource, rd in app.config['DOMAIN'].items(): if (rd.get('disable_documentation') or resource.endswith('_versions')): continue methods = rd['resource_methods'] if methods: url = '/%s' % rd['url'] paths[url] = _resource(resource, rd, methods) methods = rd['item_methods'] if methods: item_id = '%sId' % rd['item_title'].lower() url = '/%s/{%s}' % (rd['url'], item_id) paths[url] = _item(resource, rd, methods) return paths
def info(): validate_info() cfg = app.config[eve_swagger.INFO] def node(parent, cfg, key): value = cfg.get(key) if value: parent[key] = cfg[key] info = OrderedDict() node(info, cfg, 'title') node(info, cfg, 'description') node(info, cfg, 'termsOfService') node(info, cfg, 'contact') node(info, cfg, 'license') node(info, cfg, 'version') return info
def _item(resource, rd, methods): item = OrderedDict() describe_hooks = app.config.get('ENABLE_HOOK_DESCRIPTION', False) if 'GET' in methods: item['get'] = getitem_response(rd) if 'PUT' in methods: item['put'] = put_response(rd) if 'PATCH' in methods: item['patch'] = patch_response(rd) if 'DELETE' in methods: item['delete'] = deleteitem_response(rd) if describe_hooks: for m in methods: hook_desc = _hook_descriptions(resource, m, item=True) if hook_desc != '': item[m.lower()]['description'] = '**Hooks**:' + hook_desc return item
def _object(rd, dr_sources): props = {} required = [] for field, rules in rd['schema'].items(): if rules.get('required') is True: required.append(field) def_name = rd['item_title'] + '_' + field props[field] = _field_props(rules, dr_sources, def_name) if def_name in dr_sources: # the current field is a source of a data_relation # replace None in dr_sources with the field properties dr_sources[def_name] = OrderedDict(props[field]) props[field] = {'$ref': '#/definitions/{0}'.format(def_name)} if 'data_relation' in rules: # the current field is a copy of another field dr = rules['data_relation'] if dr['resource'] not in app.config['DOMAIN']: # source of data_relation does not exist continue title = app.config['DOMAIN'][dr['resource']]['item_title'] source_def_name = title + '_' + dr['field'] props[field] = { '$ref': '#/definitions/{0}'.format(source_def_name) } field_def = {} field_def['type'] = 'object' field_def['properties'] = props if len(required): field_def['required'] = required return field_def
""" import re from collections import Mapping from flask import Blueprint, jsonify, make_response, request, \ current_app as app from functools import wraps from eve_swagger import OrderedDict from .definitions import definitions from .objects import info, host, base_path, schemes, consumes, produces, \ parameters, responses, security_definitions, security, tags, \ external_docs from .paths import paths swagger = Blueprint('eve_swagger', __name__) swagger.additional_documentation = OrderedDict() def add_documentation(doc): _nested_update(swagger.additional_documentation, doc) def _modify_response(f): @wraps(f) def decorated(*args, **kwargs): if request.method == 'OPTIONS': resp = app.make_default_options_response() else: resp = make_response(f(*args, **kwargs)) # CORS
def parameters(): parameters = OrderedDict() for (resource_name, rd) in app.config['DOMAIN'].items(): if (resource_name.endswith('_versions') or rd.get('disable_documentation')): continue title = rd['item_title'] lookup_field = rd['item_lookup_field'] if lookup_field not in rd['schema']: rd['schema'][lookup_field] = {'type': 'objectid'} eve_type = rd['schema'][lookup_field]['type'] descr = rd['schema'][lookup_field].get('description') or '' example = rd['schema'][lookup_field].get('example') or '' if 'data_relation' in rd['schema'][lookup_field]: # the lookup field is a copy of another field dr = rd['schema'][lookup_field]['data_relation'] # resource definition of the data relation source source_rd = app.config['DOMAIN'][dr['resource']] # schema of the data relation source field source_def = source_rd['schema'][dr['field']] # key in #/definitions/... source_def_name = source_rd['item_title'] + '_' + dr['field'] # copy description if necessary descr = descr or source_def.get('description') descr = descr + ' (links to {0})'.format(source_def_name) p = OrderedDict() p['in'] = 'path' p['name'] = title.lower() + 'Id' p['required'] = True p['description'] = descr p['example'] = example p['type'] = eve_type if eve_type == 'objectid': p['type'] = 'string' p['format'] = 'objectid' elif eve_type == 'datetime': p['type'] = 'string' p['format'] = 'date-time' elif eve_type == 'float': p['type'] = 'number' p['format'] = 'float' parameters[title + '_' + lookup_field] = p # Let's add the technical eve parameters : projection(field selection), # embedded(relationship) and where (sort) if rd['projection']: p = OrderedDict() p['in'] = 'query' p['name'] = 'projection' p['required'] = False p['description'] = 'Allow to select fields :\n' \ ' by addition : {"lastname": 1, "born": 1}=> only '\ 'these two fields\n' \ ' or by subtraction {"born": 0} => wihtout ' \ 'the "born" field' p['example'] = '{"lastname": 1, "born": 1} or {"born": 0}' p['type'] = 'string' parameters[p['name']] = p if rd['embedding']: p = OrderedDict() p['in'] = 'query' p['name'] = 'embedded' p['required'] = False p['description'] = 'Allow to embed the other side of a relationship' \ ' into the current payload :\n' \ ' format {"author":1, "phones":0} => embed the ' \ 'author but don\'t embed the phone\n' \ 'see http://docs.python-eve.org/en/latest' \ 'features.html#projections' p['example'] = '{"author":1,"cosignees":1}' p['type'] = 'string' parameters[p['name']] = p if rd['sorting']: p = OrderedDict() p['in'] = 'query' p['name'] = 'where' p['required'] = False p['description'] = 'see https://eve-sqlalchemy.readthedocs.io/en/' \ 'latest/tutorial.html#sqlalchemy-expressions' p['example'] = '{"firstname":"in(\"(\'John\',\'Fred\'\"))" ' \ 'or {"lastname":"like(\"Smi%\")"}' p['type'] = 'string' parameters[p['name']] = p if rd['allowed_filters']: p = OrderedDict() p['in'] = 'query' p['name'] = 'sort' p['required'] = False p['description'] = 'see https://eve-sqlalchemy.readthedocs.io/en' \ '/latest/tutorial.html#sqlalchemy-sorting' p['example'] = '[("lastname", -1, "nullslast")] ' \ 'or lastname,-created_at' p['type'] = 'string' parameters[p['name']] = p return parameters