def _update_paths(resource, domain): """Update the lookup paths for a resource. Re-format the default _id lookup and add additional lookups, if any exist. """ title = domain['item_title'] key = '%s__id' % title path = '/%s/{%sId}' % (resource, title.lower()) updates = {'description': 'The `_id` field of a %s document' % title} add_documentation({'parameters': {key: updates}}) try: lookup = domain['additional_lookup'] except KeyError: pass else: field = lookup['field'] params = [{ 'in': 'path', 'name': field, 'required': False, 'description': '*Instead* of the `_id`, you can also use the ' '`%s` field as an alternative lookup when ' '*retrieving* a document.' % field, 'type': lookup['url'], }] add_documentation({ 'paths': {path: {'get': {'parameters': params}}}, })
def _update_top_level(app): """Update top-level descriptions.""" # Extend documentation description # Markdown files that will be included in the API documentation doc_files = ['Introduction.md', 'Cheatsheet.md', 'Auth.md', 'OAuth.md'] dirname = path.dirname(path.realpath(__file__)) doc_paths = [path.join(dirname, filename) for filename in doc_files] additional_docs = [] for filename in doc_paths: with open(filename) as file: additional_docs.append(file.read().strip()) # Join parts with double newlines (empty line) for markdown formatting docs = app.config['SWAGGER_INFO'] docs['description'] = "\n\n".join( (docs['description'].strip(), *additional_docs)) # Add logo add_documentation({'info': {'x-logo': app.config['SWAGGER_LOGO']}}) # Add servers add_documentation({'servers': app.config['SWAGGER_SERVERS']}) # Required to tell online docs that we don't return xml app.config['XML'] = False
def _update_top_level(app): """Update top-level descriptions.""" # Extend documentation description # Markdown files that will be included in the API documentation doc_files = ['Introduction.md', 'Cheatsheet.md', 'Auth.md', 'OAuth.md'] dirname = path.dirname(path.realpath(__file__)) doc_paths = [path.join(dirname, filename) for filename in doc_files] additional_docs = [] for filename in doc_paths: with open(filename) as file: additional_docs.append(file.read().strip()) # Join parts with double newlines (empty line) for markdown formatting docs = app.config['SWAGGER_INFO'] docs['description'] = "\n\n".join((docs['description'].strip(), *additional_docs)) # Add logo add_documentation({'info': {'x-logo': app.config['SWAGGER_LOGO']}}) # Add servers add_documentation({'servers': app.config['SWAGGER_SERVERS']}) # Required to tell online docs that we don't return xml app.config['XML'] = False
def add_swagger(the_app): from eve_swagger import swagger, add_documentation from .auxiliary import construct_alert_stream_doc logger.info("Generating Swagger documentation") add_documentation(construct_alert_stream_doc(ALERT_STREAM_URL)) the_app.register_blueprint(swagger) the_app.config["SWAGGER_INFO"] = swagger_info
def _add_param_refs(path, method, references): """Helper to add references to query parameters to a path method.""" parameters = [{'$ref': '#/parameters/%s' % ref} for ref in references] add_documentation({ 'paths': {path: {method.lower(): {'parameters': parameters}}}, })
def _add_error_refs(path, method, codes): """Helper to add references to error responses to a path method.""" errors = {str(code): {'$ref': '#/definitions/%s' % code} for code in codes} add_documentation({ 'paths': {path: {method.lower(): {'responses': errors}}}, })
def _add_param_refs(path, method, references): """Helper to add references to query parameters to a path method.""" parameters = [{'$ref': '#/parameters/%s' % ref} for ref in references] add_documentation({ 'paths': { path: { method.lower(): { 'parameters': parameters } } }, })
def _add_error_refs(path, method, codes): """Helper to add references to error responses to a path method.""" errors = {str(code): {'$ref': '#/definitions/%s' % code} for code in codes} add_documentation({ 'paths': { path: { method.lower(): { 'responses': errors } } }, })
def _update_property(prop, key, value): """Helper to update a property.""" add_documentation({ 'definitions': { domain['item_title']: { 'properties': { prop: { key: value } } } } })
def set_methods_documentation(app): for resource, rd in app.config['DOMAIN'].items(): if (rd.get('disable_documentation') or resource.endswith('_versions')): continue methods = rd['resource_methods'] url = '/%s' % rd['url'] for method in methods: parameters = [] if method == "GET": parameters = [{ "name": "where", "in": "query", "type": "string", "description": "filter by value" }, { "name": "sort", "in": "query", "type": "string", "description": "sort by value" }] add_documentation({ 'paths': { url: { method.lower(): { "parameters": parameters, "security": [{ "JWTAuth": [] }] } } } }) methods = rd['item_methods'] item_id = '%sId' % rd['item_title'].lower() url = '/%s/{%s}' % (rd['url'], item_id) for method in methods: add_documentation({ 'paths': { url: { method.lower(): { "security": [{ "JWTAuth": [] }] } } } })
def _update_paths(resource, domain): """Update the lookup paths for a resource. Re-format the default _id lookup and add additional lookups, if any exist. """ title = domain['item_title'] key = '%s__id' % title path = '/%s/{%sId}' % (resource, title.lower()) updates = {'description': 'The `_id` field of a %s document' % title} add_documentation({'parameters': {key: updates}}) try: lookup = domain['additional_lookup'] except KeyError: pass else: field = lookup['field'] params = [{ 'in': 'path', 'name': field, 'required': False, 'description': '*Instead* of the `_id`, you can also use the ' '`%s` field as an alternative lookup when ' '*retrieving* a document.' % field, 'type': lookup['url'], }] add_documentation({ 'paths': { path: { 'get': { 'parameters': params } } }, })
def init(): for resource, rd in app.config['DOMAIN'].items(): if (rd.get('disable_documentation') or resource.endswith('_versions')): continue methods = rd['resource_methods'] # url = '/%s' % rd['url'] url = '/%s' % _clear_regex(rd['url']) for method in methods: add_documentation({ 'paths': { url: { method.lower(): { "security": [{ "JwtAuth": [] }], 'produces': ['application/json'] } } } }) methods = rd['item_methods'] item_id = '%sId' % rd['item_title'].lower() url = '/%s/{%s}' % (rd['url'], item_id) url = '/%s/{%s}' % (_clear_regex(rd['url']), item_id) for method in methods: add_documentation({ 'paths': { url: { method.lower(): { "security": [{ "JwtAuth": [] }], 'produces': ['application/json'] } } } })
def init_app(app): """Create a ReDoc endpoint at /docs.""" # Generate documentation (i.e. swagger/OpenApi) to be used by any UI # will be exposed at /docs/api-docs app.register_blueprint(swagger, url_prefix="/docs") # host the ui (we use redoc) at /docs app.register_blueprint(redoc) register_validator(app, DocValidator) # replace null-type fields with '' # TODO: make this dynamic add_documentation({'definitions': {'User': {'properties': { 'password': {'default': ''}, 'nethz': {'default': ''} }}}}) add_documentation({'securityDefinitions': { 'AMIVauth': { 'type': 'apiKey', 'name': 'Authorization', 'in': 'header', 'description': 'Enter a session token you got with POST to ' '/sessions, or an API key, stored in the server config' } }}) # just an example of how to include code samples add_documentation({'paths': {'/sessions': {'post': { 'x-code-samples': [ { 'lang': 'python', 'source': '\n\n'.join([ 'import requests', ('login = requests.post("http://api.amiv.ethz.ch/sessions",' ' data={"user": "******", "password": "******"})'), "token = login.json()['token']", ('# now use this token to authenticate a request\n' 'response = requests.get(' '"https://api.amiv.ethz.ch/users/myuser", ' 'auth=requests.auth.HTTPBasicAuth(token, ""))')]) } ] }}}})
from flask import current_app as app, jsonify, Blueprint, request, abort from eve.auth import requires_auth from eve.utils import config from eve.methods.get import getitem_internal from eve_swagger import add_documentation from shared.datetime import after_request_cache blueprint = Blueprint('auth', __name__) CORS(blueprint, max_age=timedelta(seconds=config.CACHE_EXPIRES)) resource = 'users' add_documentation({ 'paths': { '/sign': { 'post': { 'summary': 'Retrieves a User token', 'tags': ['User'], } } } }) @blueprint.route('/sign', methods=['POST']) def sign(): data = request.values or request.get_json() if not data: abort(400, description='username required') expected_username = data.get('username') if not expected_username:
def _update_definitions(app): """Update the definitions in the docs. In particular, extend the `parameters` section with definitions for the various query parameters, e.g. filter and embed. Furthermore, add definitions for the error responses to the `definitions` section. """ add_documentation({ # Query parameters 'parameters': { 'auth': { "in": "header", "name": "Authorization", "description": "API token.<br />(read more in " "[Authentication and Authorization]" "(#section/Authentication-and-Authorization))", "required": True, "type": "string" }, 'filter': { "in": "query", "name": "where", "type": "object", "description": "Apply a filter." "<br />[(Cheatsheet)](#section/Cheatsheet)", }, 'max_results': { "in": "query", "name": "max_results", "type": "integer", "minimum": 0, "maximum": app.config['PAGINATION_LIMIT'], "default": app.config['PAGINATION_DEFAULT'], "description": "Limit the number of results per page." "<br />[(Cheatsheet)](#section/Cheatsheet)", }, 'page': { "in": "query", "name": "page", "type": "integer", "minimum": 1, "description": "Specify result page." "<br />[(Cheatsheet)](#section/Cheatsheet)", }, 'sort': { "in": "query", "name": "sort", "type": "object", "description": "Sort results." "<br />[(Cheatsheet)](#section/Cheatsheet)", }, 'embed': { "in": "query", "name": "embedded", "type": "object", "description": "Control embedding of related resources." "<br />[(Cheatsheet)](#section/Cheatsheet)", }, 'project': { "in": "query", "name": "projection", "type": "object", "description": "Show/hide fields in response." "<br />[(Cheatsheet)](#section/Cheatsheet)", }, }, # Error Responses 'definitions': { '404': { 'description': 'No item with the provided `_id` exists', }, '401': { 'description': 'The `Authorization` header is missing or ' 'contains an invalid token', }, '403': { 'description': 'The `Authorization` header contains a valid ' 'token, but you do not have the required ' 'permissions', }, '422': { 'description': 'Validation of the document body failed', }, '428': { 'description': "The `If-Match` header is missing", }, '412': { 'description': "The `If-Match` header does not match the " "current `_etag`", } } })
app.register_blueprint(swagger) app.on_insert_formulas += on_insert_formulas_callback # Update Swagger doc add_documentation( swagger, { 'paths': { '/rendered-formulas/{renderedFormulaId}': { 'get': { 'parameters': [{ 'in': 'path', 'name': 'renderedFormulaId', 'required': True, 'description': 'The id of the svg media', 'type': 'string', }], 'responses': { '200': { 'description': 'Formula svg fetched successfully', 'type': 'media', } } } } } }) if __name__ == '__main__': app.run(host='0.0.0.0', port=service_port)
import re from flask import current_app as app, Blueprint from eve_swagger import add_documentation blueprint = Blueprint('swagger', __name__) add_documentation({ 'securityDefinitions': { "JwtAuth": { "type": "apiKey", "name": "Authorization", "in": "header" } } }) def _clear_regex(_str): return '/'.join(map(lambda x: re.sub('regex(.*):', '', x), _str.split('/'))) @blueprint.before_app_first_request def init(): for resource, rd in app.config['DOMAIN'].items(): if (rd.get('disable_documentation') or resource.endswith('_versions')): continue methods = rd['resource_methods'] # url = '/%s' % rd['url'] url = '/%s' % _clear_regex(rd['url'])
add_documentation({ 'paths': { '/tokens': {'post': { 'summary': 'JWT Authentication', 'parameters': [{ 'in': 'body', 'name': 'credentials', 'required': True, 'schema': { 'type': 'object', 'properties': { 'email': {'type': 'string', 'required': True}, 'password': {'type': 'string', 'required': True} } } }], 'responses': { '201': {'description': 'JWT token with user info encoded in payload'}, '404': {'description': 'User not found'}, '422': {'description': 'Invalid parameters'} }, 'tags': ['Tokens'] }}, '/users/{userId}/categories': { 'get': { 'summary': 'Retrieves one or more of user\'s categories', 'parameters': [ {'$ref': '#/parameters/User__id'} ], 'responses': { '200': { 'description': 'An array of user\'s categories', 'schema': { 'type': 'array', 'items': { '$ref': '#/definitions/category' } } } }, 'tags': ['User categories'], 'security': [{'Authorization': []}] } }, '/users/{userId}/alarms': { 'get': { 'summary': 'Retrieves one or more of user\'s alarms', 'parameters': [ {'$ref': '#/parameters/User__id'} ], 'responses': { '200': { 'description': 'An array of user\'s alarms', 'schema': { 'type': 'array', 'items': { '$ref': '#/definitions/Alarm' } } } }, 'tags': ['User alarms'], 'security': [{'Authorization': []}] } }, '/categories/{categoryId}/events': { 'get': { 'summary': 'Retrieves one or more of category\'s events', 'parameters': [ {'$ref': '#/parameters/category__id'} ], 'responses': { '200': { 'description': 'An array of category\'s events', 'schema': { 'type': 'array', 'items': { '$ref': '#/definitions/Event' } } } }, 'tags': ['Category events'], 'security': [{'Authorization': []}] } } } })
from eve_swagger import add_documentation blueprint = Blueprint('forgot_password', __name__) CORS(blueprint, max_age=timedelta(seconds=config.CACHE_EXPIRES)) mail = Mail() resource = 'users' template = """Hi %s ini password baru kamu ya %s""" add_documentation({ 'paths': { '/forgot_password': { 'post': { 'summary': 'Reset password and send to email', 'tags': ['User'], } } } }) @blueprint.route('/forgot_password', methods=['POST']) def forgot_password(): mail.init_app(app) data = request.values or request.get_json() if not data: abort(422, description='username or email required') expected_username = data.get('username')
response.set_data(attestation_file) app = Eve() CORS(app) # vNSF hooks. app.on_pre_POST_vnsfs += VnsfHooks.onboard_vnsf app.on_fetched_item_vnsfs += VnsfHooks.send_minimal_vnsf_data app.on_fetched_item_attestation += VnsfHooks.send_vnsf_attestation app.on_delete_item_vnsfs += VnsfHooks.delete_vnsf app.on_post_GET_attestation += send_attestation # Network Services hooks. app.on_pre_POST_nss += NsHooks.onboard_ns app.on_delete_item_nss += NsHooks.delete_ns app.register_blueprint(swagger) app.config['SWAGGER_INFO'] = store_docs.swagger_info add_documentation({'paths': store_docs.paths}) if __name__ == '__main__': log.setup_logging(config_file='src/logging.yaml') logger = logging.getLogger(__name__) # use '0.0.0.0' to ensure your REST API is reachable from all your # network (and not only your computer). app.run(host='0.0.0.0', port=cfg.BACKENDAPI_PORT, debug=True)
"schemes": ["https"], } # If dev environment, use HTTP for swagger if environment == "dev": SWAGGER_CONFIG["schemes"] = ["http"] swagger = get_swagger_blueprint() # Update documentation: Fix invalid warnings add_documentation(swagger, { "components": { "parameters": { "Item_id": { "schema": { "type": "string" } } } } }) add_documentation(swagger, { "components": { "parameters": { "Equipment_id": { "schema": { "type": "string" } } } }
# http event hooks app.on_post_POST_states += post_POST_states_trigger_take_snapshot app.on_post_POST_labrequests += post_POST_labrequests_add_expiry_time app.on_pre_DELETE_reservations += pre_DELETE_reservations_validation app.on_pre_DELETE_labs += pre_DELETE_labs_validation eve_swagger.add_documentation({ 'paths': { "/labrequests/{labrequestId}/keepalive": { "patch": { "summary": "Sets expireAt 1 minute from now", "responses": { "200": { "description": "Accepted" } }, "parameters": [{ "$ref": "#/parameters/Labrequest__id" }], "tags": ["Labrequest"] } } } }) eve_swagger.add_documentation({ 'paths': { "/states/{stateId}/revert": { "patch": { "summary": "Reverts lab to this state", "responses": { "200": {
def _update_property(prop, key, value): """Helper to update a property.""" add_documentation({'definitions': { domain['item_title']: {'properties': {prop: {key: value}}} }})
'description': 'New API for accesing pictograms and material', 'termsOfService': 'TODO', 'contact': { 'name': 'Arasaac Team', 'url': 'https://github.com/arasaac' }, 'license': { 'name': 'CC by SA', 'url': 'https://github.com/arasaac', } } # optional. Will use flask.request.host if missing. # app.config['SWAGGER_HOST'] = 'myhost.com' # optional. Add/Update elements in the documentation at run-time without deleting subtrees. ''' add_documentation({'paths': {'/images': {'get': {'parameters': [ { 'in': 'query', 'name': 'foobar', 'required': False, 'description': 'special query parameter', 'type': 'string' }] }}}}) ''' if __name__ == '__main__': app.run(host='0.0.0.0', debug=True)
'schemes': [], } SWAGGER_EXT = { 'securityDefinitions': { 'JWTAuth': { 'type': 'apiKey', 'in': 'header', 'name': 'Authorization' } }, 'security': [{ 'JWTAuth': [] }], } add_documentation(SWAGGER_EXT) def init_documentation(app): app.config['SWAGGER_INFO'] = SWAGGER_INFO def set_methods_documentation(app): for resource, rd in app.config['DOMAIN'].items(): if (rd.get('disable_documentation') or resource.endswith('_versions')): continue methods = rd['resource_methods'] url = '/%s' % rd['url'] for method in methods: parameters = [] if method == "GET":
from eve.utils import config from werkzeug.exceptions import NotFound from eve_swagger import add_documentation blueprint = Blueprint('import', __name__) CORS(blueprint, max_age=timedelta(seconds=config.CACHE_EXPIRES)) def allowed_key(allowed_key, item): return dict(item) add_documentation({ 'paths': {'/import/branches': {'put': { 'summary': 'Replaces a branches document', 'tags': ['Import'], "security": [{"JwtAuth": []}], }}} }) @blueprint.route('/import/branches', methods=['PUT']) def _import_branches(): data = request.json items = data[config.ITEMS] def branch_convert(item): allowed_key = ('name', 'address') item = filter(lambda v: v[0] in allowed_key, item.items()) item = dict(item) item = filter(lambda v: v[1] is not None, item.items())
'name': 'BSD', 'url': 'https://github.com/pyeve/eve-swagger/blob/master/LICENSE', }, 'schemes': ['http', 'https'], } # optional. Will use flask.request.host if missing. app.config['SWAGGER_HOST'] = HOST # optional. Add/Update elements in the documentation at run-time without deleting subtrees. add_documentation({ 'paths': { '/status': { 'get': { 'parameters': [{ 'in': 'query', 'name': 'foobar', 'required': False, 'description': 'special query parameter', 'type': 'string' }] } } } }) http_server = HTTPServer(WSGIContainer(app)) http_server.listen(DEFAULT_PORT, address=HOST) print "running at server: {}:{}".format(HOST, DEFAULT_PORT) IOLoop.instance().start()
return data class_attendances_tutors = [] for v in attendances_tutors: if v['attendance'] == class_attendance[config.ID_FIELD]: class_attendances_tutors.append(v) data = {config.ITEMS: class_attendances_tutors} return data add_documentation({ 'paths': {'/schedules': {'get': { 'summary': 'Retrieves a Schedule document', 'tags': ['Classe'], "security": [{"JwtAuth": []}], }}} }) def exclude_current_user_attendance(class_): if not class_.get('last_attendances'): return True if not class_['last_attendances'].get(config.ITEMS): return True items = class_['last_attendances'][config.ITEMS] if len(items): for v2 in items:
return False return True add_documentation({ 'paths': { '/students/attendances': { 'get': { 'summary': 'Retrieves one or more student attendance', "responses": { "200": { "description": "Student document fetched successfully", "schema": { "$ref": "#/definitions/User" } } }, 'tags': ['User'], "security": [{ "JwtAuth": [] }], } } } }) @blueprint.route('/students/attendances', methods=['GET']) @requires_auth('/students/attendances') def get_students():