summaryrefslogtreecommitdiff
path: root/matweb/rest_api.py
diff options
context:
space:
mode:
authorjfriedli2020-04-23 10:39:35 -0700
committerjfriedli2020-04-23 10:39:35 -0700
commite1bac8b6a7fd857f38b7bcb678398c82baaa8fd5 (patch)
treefa87e526289e455f2f17b86973d08eb6850e721f /matweb/rest_api.py
parentd14988fa3fa97f549fb8eaf601cb2c687cdce143 (diff)
Refactoring
Diffstat (limited to 'matweb/rest_api.py')
-rw-r--r--matweb/rest_api.py139
1 files changed, 139 insertions, 0 deletions
diff --git a/matweb/rest_api.py b/matweb/rest_api.py
new file mode 100644
index 0000000..60d834f
--- /dev/null
+++ b/matweb/rest_api.py
@@ -0,0 +1,139 @@
1import os
2import base64
3import io
4import binascii
5import zipfile
6from uuid import uuid4
7
8from flask import after_this_request, send_from_directory
9from flask_restful import Resource, reqparse, abort, request
10from cerberus import Validator
11from werkzeug.datastructures import FileStorage
12from urllib.parse import urljoin
13
14from matweb import file_removal_scheduler, utils
15
16
17class APIUpload(Resource):
18
19 def __init__(self, **kwargs):
20 self.upload_folder = kwargs['upload_folder']
21
22 def post(self):
23 utils.check_upload_folder(self.upload_folder)
24 req_parser = reqparse.RequestParser()
25 req_parser.add_argument('file_name', type=str, required=True, help='Post parameter is not specified: file_name')
26 req_parser.add_argument('file', type=str, required=True, help='Post parameter is not specified: file')
27
28 args = req_parser.parse_args()
29 try:
30 file_data = base64.b64decode(args['file'])
31 except binascii.Error as err:
32 abort(400, message='Failed decoding file: ' + str(err))
33
34 file = FileStorage(stream=io.BytesIO(file_data), filename=args['file_name'])
35 filename, filepath = utils.save_file(file, self.upload_folder)
36 parser, mime = utils.get_file_parser(filepath)
37
38 if parser is None:
39 abort(415, message='The type %s is not supported' % mime)
40
41 meta = parser.get_meta()
42 if not parser.remove_all():
43 abort(500, message='Unable to clean %s' % mime)
44
45 key, meta_after, output_filename = utils.cleanup(parser, filepath, self.upload_folder)
46 return utils.return_file_created_response(
47 output_filename,
48 mime,
49 key,
50 meta,
51 meta_after,
52 urljoin(request.host_url, '%s/%s/%s/%s' % ('api', 'download', key, output_filename))
53 )
54
55
56class APIDownload(Resource):
57
58 def __init__(self, **kwargs):
59 self.upload_folder = kwargs['upload_folder']
60
61 def get(self, key: str, filename: str):
62 complete_path, filepath = utils.is_valid_api_download_file(filename, key, self.upload_folder)
63 # Make sure the file is NOT deleted on HEAD requests
64 if request.method == 'GET':
65 file_removal_scheduler.run_file_removal_job(self.upload_folder)
66
67 @after_this_request
68 def remove_file(response):
69 if os.path.exists(complete_path):
70 os.remove(complete_path)
71 return response
72
73 return send_from_directory(self.upload_folder, filepath, as_attachment=True)
74
75
76class APIBulkDownloadCreator(Resource):
77
78 def __init__(self, **kwargs):
79 self.upload_folder = kwargs['upload_folder']
80
81 schema = {
82 'download_list': {
83 'type': 'list',
84 'minlength': 2,
85 'maxlength': int(os.environ.get('MAT2_MAX_FILES_BULK_DOWNLOAD', 10)),
86 'schema': {
87 'type': 'dict',
88 'schema': {
89 'key': {'type': 'string', 'required': True},
90 'file_name': {'type': 'string', 'required': True}
91 }
92 }
93 }
94 }
95 v = Validator(schema)
96
97 def post(self):
98 utils.check_upload_folder(self.upload_folder)
99 data = request.json
100 if not self.v.validate(data):
101 abort(400, message=self.v.errors)
102 # prevent the zip file from being overwritten
103 zip_filename = 'files.' + str(uuid4()) + '.zip'
104 zip_path = os.path.join(self.upload_folder, zip_filename)
105 cleaned_files_zip = zipfile.ZipFile(zip_path, 'w')
106 with cleaned_files_zip:
107 for file_candidate in data['download_list']:
108 complete_path, file_path = utils.is_valid_api_download_file(
109 file_candidate['file_name'],
110 file_candidate['key'],
111 self.upload_folder
112 )
113 try:
114 cleaned_files_zip.write(complete_path)
115 os.remove(complete_path)
116 except ValueError:
117 abort(400, message='Creating the archive failed')
118
119 try:
120 cleaned_files_zip.testzip()
121 except ValueError as e:
122 abort(400, message=str(e))
123
124 parser, mime = utils.get_file_parser(zip_path)
125 if not parser.remove_all():
126 abort(500, message='Unable to clean %s' % mime)
127 key, meta_after, output_filename = utils.cleanup(parser, zip_path, self.upload_folder)
128 return {
129 'output_filename': output_filename,
130 'mime': mime,
131 'key': key,
132 'meta_after': meta_after,
133 'download_link': urljoin(request.host_url, '%s/%s/%s/%s' % ('api', 'download', key, output_filename))
134 }, 201
135
136
137class APISupportedExtensions(Resource):
138 def get(self):
139 return utils.get_supported_extensions()