diff --git a/matweb/oas/remove_metadata.yml b/matweb/oas/remove_metadata.yml new file mode 100644 index 0000000..ad5a7da --- /dev/null +++ b/matweb/oas/remove_metadata.yml @@ -0,0 +1,41 @@ +--- +tags: + - "Metadata removal cleaning in one request (automated clients)" +summary: 'Upload a single file which will be cleaned from metadata and returned directly' +requestBody: + description: "The file that will be cleaned from metadata and the cleaned file is returned directly" + required: true + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary +responses: + '200': + description: "The cleaned file" + content: + "*/*": + schema: + type: string + format: binary + 400: + description: "Invalid input" + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 415: + description: "Unsupported file type" + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 500: + description: "Unable to clean the file" + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' \ No newline at end of file diff --git a/matweb/rest_api.py b/matweb/rest_api.py index 2909cf6..50b7c37 100644 --- a/matweb/rest_api.py +++ b/matweb/rest_api.py @@ -84,6 +84,38 @@ class APIDownload(Resource): return send_from_directory(current_app.config['UPLOAD_FOLDER'], filepath, as_attachment=True) +class APIClean(Resource): + @swag_from('./oas/remove_metadata.yml') + def post(self): + if 'file' not in request.files: + abort(400, message='No file part') + + uploaded_file = request.files['file'] + if not uploaded_file.filename: + abort(400, message='No selected `file`') + try: + filename, filepath = utils.save_file(uploaded_file, current_app.config['UPLOAD_FOLDER']) + except ValueError: + abort(400, message='Invalid Filename') + + parser, mime = utils.get_file_parser(filepath) + + if parser is None: + abort(415, message='The type %s is not supported' % mime) + + if parser.remove_all() is not True: + abort(500, message='Unable to clean %s' % mime) + + _, _, _, output_filename = utils.cleanup(parser, filepath, current_app.config['UPLOAD_FOLDER']) + + @after_this_request + def remove_file(response): + os.remove(os.path.join(current_app.config['UPLOAD_FOLDER'], output_filename)) + return response + + return send_from_directory(current_app.config['UPLOAD_FOLDER'], output_filename, as_attachment=True) + + class APIBulkDownloadCreator(Resource): schema = { 'download_list': { @@ -168,6 +200,10 @@ api.add_resource( APIDownload, '/download///' ) +api.add_resource( + APIClean, + '/remove_metadata' +) api.add_resource( APIBulkDownloadCreator, '/download/bulk' diff --git a/test/test_api.py b/test/test_api.py index 6961197..6d48a35 100644 --- a/test/test_api.py +++ b/test/test_api.py @@ -1,13 +1,15 @@ -import unittest -import tempfile import json import os import shutil +import tempfile +import unittest import zipfile -from six import BytesIO +import io from unittest.mock import patch + from openapi_spec_validator import validate_spec +from six import BytesIO import main @@ -433,5 +435,60 @@ class Mat2APITestCase(unittest.TestCase): spec = self.app.get('apispec_1.json').get_json() validate_spec(spec) + def test_remove_metadata(self): + r = self.app.post( + '/api/remove_metadata', + data=dict( + file=(io.BytesIO(b""), 'test.txt'), + ), + follow_redirects=False + ) + self.assertEqual(r.status_code, 200) + self.assertEqual(r.headers['Content-Disposition'], 'attachment; filename=test.cleaned.txt') + self.assertEqual(r.headers['Content-Type'], 'text/plain; charset=utf-8') + self.assertEqual(r.data, b'') + + def test_remove_metdata_validation(self): + r = self.app.post( + '/api/remove_metadata', + data=dict( + fileNotExisting=(io.BytesIO(b""), 'test.random'), + ), + follow_redirects=False + ) + self.assertEqual(r.get_json()['message'], 'No file part') + self.assertEqual(r.status_code, 400) + + r = self.app.post( + '/api/remove_metadata', + data=dict( + file=(io.BytesIO(b""), ''), + ), + follow_redirects=False + ) + self.assertEqual(r.get_json()['message'], 'No selected `file`') + self.assertEqual(r.status_code, 400) + + r = self.app.post( + '/api/remove_metadata', + data=dict( + file=(io.BytesIO(b""), '../../'), + ), + follow_redirects=False + ) + self.assertEqual(r.get_json()['message'], 'Invalid Filename') + self.assertEqual(r.status_code, 400) + + r = self.app.post( + '/api/remove_metadata', + data=dict( + file=(io.BytesIO(b""), 'test.random'), + ), + follow_redirects=False + ) + self.assertEqual(r.get_json()['message'], 'The type None is not supported') + self.assertEqual(r.status_code, 415) + + if __name__ == '__main__': unittest.main()