diff --git a/README.md b/README.md index e520e7e..433a4b9 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,9 @@ TODO: architecture ## API -TODO: Swagger file +Base API contract is stored in the [doc](/doc/contract.json) folder. +You can see the UI in http://:/ and the live documentation in +http://:/swagger.json tho. ## Run @@ -31,6 +33,12 @@ $ FLASK_APP=src/app flask run $ python src/app.py ``` +```bash +$ curl -X PUT http://:/search \ + -H 'content-type: application/json' \ + -d @'base.template.json' +``` + ## Deploy The deployment is automated by the CI/CD pipeline but you can always run it in diff --git a/base.template.json b/base.template.json new file mode 100644 index 0000000..4f9fc5b --- /dev/null +++ b/base.template.json @@ -0,0 +1,7 @@ +{ + "query": { + "word": "fit", + "strict": true + }, + "source": "https://www.virtusize.jp/" +} diff --git a/complex.template.json b/complex.template.json new file mode 100644 index 0000000..5366978 --- /dev/null +++ b/complex.template.json @@ -0,0 +1,8 @@ +{ + "query": { + "word": "fit", + "strict": false + }, + "source": "https://www.virtusize.jp/" +} + diff --git a/doc/contract.json b/doc/contract.json new file mode 100644 index 0000000..a257563 --- /dev/null +++ b/doc/contract.json @@ -0,0 +1,122 @@ +{ + "swagger": "2.0", + "basePath": "/", + "paths": { + "/health": { + "get": { + "responses": { + "200": { + "description": "Success" + } + }, + "operationId": "get_health", + "tags": [ + "" + ] + } + }, + "/search": { + "put": { + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/Word" + } + } + }, + "operationId": "put_search", + "parameters": [ + { + "name": "payload", + "required": true, + "in": "body", + "schema": { + "$ref": "#/definitions/Search" + } + }, + { + "name": "X-Fields", + "in": "header", + "type": "string", + "format": "mask", + "description": "An optional fields mask" + } + ], + "tags": [ + "" + ] + } + } + }, + "info": { + "title": "Word search engine", + "version": "1.0", + "description": "The service will allow a user to search an specific word in an URL" + }, + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "tags": [ + { + "name": "", + "description": "Simple word search engine" + } + ], + "definitions": { + "Search": { + "properties": { + "query": { + "$ref": "#/definitions/Query" + }, + "source": { + "type": "string" + } + }, + "type": "object" + }, + "Query": { + "properties": { + "word": { + "type": "string" + }, + "strict": { + "type": "boolean", + "default": true + } + }, + "type": "object" + }, + "Word": { + "properties": { + "__this": { + "type": "string" + }, + "query": { + "$ref": "#/definitions/Query" + }, + "source": { + "type": "string" + }, + "found": { + "type": "boolean" + }, + "count": { + "type": "integer" + } + }, + "type": "object" + } + }, + "responses": { + "ParseError": { + "description": "When a mask can't be parsed" + }, + "MaskError": { + "description": "When any error occurs on mask" + } + } +} diff --git a/environment.yml b/environment.yml index 6ca3407..d836682 100644 --- a/environment.yml +++ b/environment.yml @@ -4,3 +4,4 @@ dependencies: - flask - pip: - flask-restplus + - prometheus-client diff --git a/src/api/controller.py b/src/api/controller.py index be06edc..a2ee963 100644 --- a/src/api/controller.py +++ b/src/api/controller.py @@ -3,11 +3,28 @@ from flask_restplus import abort from flask import request +from api.schema import response_schema +from api.schema import request_schema +from api.schema import query_schema + ns = Namespace('', description='Simple word search engine') +ns.add_model('Word', response_schema) +ns.add_model('Query', query_schema) +ns.add_model('Search', request_schema) + @ns.route("/health") class Health(Resource): def get(self, **kwargs): return {"alive": True} + + +@ns.route("/search") +class Search(Resource): + @ns.doc(body=request_schema) + @ns.expect(request_schema) + @ns.marshal_with(response_schema, skip_none=True, code=200) + def put(self, **kwargs): + return {"strict": True} diff --git a/src/api/model.py b/src/api/model.py new file mode 100644 index 0000000..5d83974 --- /dev/null +++ b/src/api/model.py @@ -0,0 +1,4 @@ +class Search: + def __init__(self, **kwargs): + for key, value in kwargs.items(): + setattr(self, key, value) diff --git a/src/api/schema.py b/src/api/schema.py new file mode 100644 index 0000000..8e77262 --- /dev/null +++ b/src/api/schema.py @@ -0,0 +1,21 @@ +from flask_restplus import fields +from flask_restplus import Model + + +query_schema = Model('Query', { + 'word': fields.String, + 'strict': fields.Boolean(default=True), +}) + +response_schema = Model('Word', { + '__this': fields.Url(absolute=True), + 'query': fields.Nested(query_schema), + 'source': fields.String, + 'found': fields.Boolean, + 'count': fields.Integer, +}) + +request_schema = Model('Search', { + 'query': fields.Nested(query_schema), + 'source': fields.String, +}) diff --git a/src/api/utils/__init__.py b/src/api/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/requirements.txt b/src/requirements.txt index 99e5f4d..901a44d 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -3,3 +3,4 @@ get==1.0.3 httplib2==0.11.3 pytest==4.2.0 flask-restplus==0.12.1 +prometheus-client==0.6.0