From 13a3ab00761a7aa56780fd5d066e618da102e8c5 Mon Sep 17 00:00:00 2001 From: mg Date: Tue, 25 Apr 2023 20:52:45 +0200 Subject: [PATCH] add swagger (#1) Co-authored-by: Michael Grote Reviewed-on: https://git.mgrote.net/mg/python-api-server/pulls/1 --- README.md | 111 +++++++++------------------------------------ app.py | 88 +++++++++++++++++++++++++++++++++++ docker-compose.yml | 18 -------- requirements.txt | 1 + 4 files changed, 111 insertions(+), 107 deletions(-) delete mode 100644 docker-compose.yml diff --git a/README.md b/README.md index 72f439b..3bd9894 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ a small flask-application for storing and downloading stuff like small binaries - ## Variables - ``MAX_CONTENT_LENGTH``: maximal Filesize in MB; defaults to 5MB @@ -11,94 +10,28 @@ a small flask-application for storing and downloading stuff like small binaries ## Example Docker-Compose -see [docker-compose.yml](./docker-compose.yml) +```yaml +version: '3' +services: + python-api-server: + container_name: httpd-api + image: quotengrote/python-api-server:v2 + ports: + - "5040:5000" + volumes: + - uploads:/uploads + environment: + # FLASK_DEBUG: 1 # for debugging + # FLASK_APP: app # for debugging + MAX_CONTENT_LENGTH: 10 + UPLOAD_DIRECTORY: /uploads + AUTH_TOKEN: myuploadtoken + +volumes: + uploads: + +``` ## API-Endpoints -### /list - -#### input - -```bash -curl -H "token: myuploadtoken" http://docker10.host.lan:5040/list | jq -``` - -#### output - -```bash -{ - "files": [ - { - "last_modified": "2023-04-13 11:43:51", - "name": "file1", - "size": 1034 - }, - { - "last_modified": "2023-04-13 11:53:59", - "name": "file2", - "size": 5 - }, - { - "last_modified": "2023-04-13 12:41:18", - "name": "file3", - "size": 3478 - } - ] -} -``` - -### /upload - -If a existing file has the same name as the newly uploaded file, it will be overwritten. - -#### input - -```bash -curl -X POST -H "token: myuploadtoken" -F "file=@tests/file" http://docker10.host.lan:5040/upload | jq -``` - -#### output - -```bash -{ - "success": "File 'file' successfully uploaded" -} -``` - -### /download - -#### input - -```bash -wget http://docker10.host.lan:5040/download/file -``` - -### /delete - -#### input - -```bash -curl -X DELETE -H "token: myuploadtoken" http://docker10.host.lan:5040/delete/file | jq -``` - -#### output - -```bash -{ - "success": "File 'file' successfully deleted" -} -``` - -### /health - -#### input - -```bash -curl http://docker10.host.lan:5040/health -``` - -#### output - -```bash -OK -``` +- see [Flasgger](https://github.com/flasgger/flasgger): ``http://:5040/apidocs/`` diff --git a/app.py b/app.py index cb1cf9d..dd49463 100644 --- a/app.py +++ b/app.py @@ -3,8 +3,10 @@ import re import uuid from flask import Flask, request, jsonify, send_from_directory import datetime +from flasgger import Swagger, swag_from app = Flask(__name__) +swagger = Swagger(app) app.config['UPLOAD_DIRECTORY'] = os.environ.get('UPLOAD_DIRECTORY', '/uploads') app.config['MAX_CONTENT_LENGTH'] = int(os.environ.get('MAX_CONTENT_LENGTH', '5')) * 1024 * 1024 # in MB @@ -17,10 +19,41 @@ def is_valid_filename(filename): @app.route('/health', methods=['GET']) def health_check(): + """ + Endpoint for health check. + --- + responses: + 200: + description: OK + """ return 'OK' @app.route('/upload', methods=['POST']) def upload_file(): + """ + Endpoint for uploading files. + Filename can only contain alphanumeric characters, hyphens, underscores, and periods. + If the filename is the same as an existing file, this file will be overwritten. + --- + parameters: + - name: file + in: formData + type: file + required: true + description: The file to upload. + - name: token + in: header + type: string + required: true + description: Authentication token. + responses: + 200: + description: File uploaded successfully. + 400: + description: Bad request. + 401: + description: Unauthorized. + """ if 'file' not in request.files: return jsonify({'error': 'No file part in the request'}), 400 @@ -43,6 +76,21 @@ def upload_file(): @app.route('/download/', methods=['GET']) def download_file(filename): + """ + Endpoint for downloading files. + --- + parameters: + - name: filename + in: path + type: string + required: true + description: The name of the file to download. + responses: + 200: + description: File downloaded successfully. + 404: + description: File not found. + """ try: return send_from_directory(app.config['UPLOAD_DIRECTORY'], filename) except FileNotFoundError: @@ -50,6 +98,28 @@ def download_file(filename): @app.route('/delete/', methods=['DELETE']) def delete_file(filename): + """ + Endpoint for deleting files. + --- + parameters: + - name: filename + in: path + type: string + required: true + description: The name of the file to delete. + - name: token + in: header + type: string + required: true + description: Authentication token. + responses: + 200: + description: File deleted successfully. + 401: + description: Unauthorized. + 404: + description: File not found. + """ if 'token' not in request.headers: return jsonify({'error': 'No token supplied'}), 401 @@ -63,8 +133,26 @@ def delete_file(filename): os.remove(file_path) return jsonify({'success': 'File \'{}\' successfully deleted'.format(filename)}) + @app.route('/list', methods=['GET']) def list_files(): + """ + Endpoint for listing files. + --- + parameters: + - name: token + in: header + type: string + required: true + description: Authentication token. + responses: + 200: + description: Listed files successfully. + 400: + description: Bad request. + 401: + description: Unauthorized. + """ if 'token' not in request.headers: return jsonify({'error': 'No token supplied'}), 401 diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 52b1267..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: '3' -services: - python-api-server: - container_name: httpd-api - image: quotengrote/python-api-server:latest - ports: - - "5040:5000" - volumes: - - uploads:/uploads - environment: - # FLASK_DEBUG: 1 # for debugging - # FLASK_APP: app # for debugging - MAX_CONTENT_LENGTH: 10 - UPLOAD_DIRECTORY: /uploads - AUTH_TOKEN: myuploadtoken - -volumes: - uploads: diff --git a/requirements.txt b/requirements.txt index c23a02a..302e5d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ Flask==2.0.1 Flask-Cors==3.0.10 gunicorn requests +flasgger