From a84ccb44cc183d3b7afb0354aa77317663d6528a Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 30 Nov 2020 10:40:18 +0100 Subject: [PATCH 1/3] [enh] Allow file type in actionmaps --- moulinette/interfaces/api.py | 53 ++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/moulinette/interfaces/api.py b/moulinette/interfaces/api.py index 3a61939b..f53dec63 100644 --- a/moulinette/interfaces/api.py +++ b/moulinette/interfaces/api.py @@ -5,14 +5,17 @@ import errno import logging import argparse from json import dumps as json_encode +from tempfile import mkdtemp from gevent import sleep from gevent.queue import Queue from geventwebsocket import WebSocketError -from bottle import request, response, Bottle, HTTPResponse +from bottle import request, response, Bottle, HTTPResponse, FileUpload from bottle import abort +from shutil import rmtree + from moulinette import msignals, m18n, env from moulinette.actionsmap import ActionsMap from moulinette.core import MoulinetteError, MoulinetteValidationError @@ -29,6 +32,8 @@ logger = log.getLogger("moulinette.interface.api") # API helpers ---------------------------------------------------------- +# We define a global variable to manage in a dirty way the upload... +UPLOAD_DIR = None CSRF_TYPES = set( ["text/plain", "application/x-www-form-urlencoded", "multipart/form-data"] @@ -111,6 +116,7 @@ class _HTTPArgumentParser(object): self._positional = [] # list(arg_name) self._optional = {} # dict({arg_name: option_strings}) + self._upload_dir = None def set_defaults(self, **kwargs): return self._parser.set_defaults(**kwargs) @@ -145,9 +151,9 @@ class _HTTPArgumentParser(object): # Append newly created action if len(action.option_strings) == 0: - self._positional.append(action.dest) + self._positional.append(action) else: - self._optional[action.dest] = action.option_strings + self._optional[action.dest] = action return action @@ -155,11 +161,24 @@ class _HTTPArgumentParser(object): arg_strings = [] # Append an argument to the current one - def append(arg_strings, value, option_string=None): - if isinstance(value, bool): + def append(arg_strings, value, action): + option_string = None + if len(action.option_strings) > 0: + option_string = action.option_strings[0] + + if isinstance(value, bool) or isinstance(action.const, bool): # Append the option string only + if option_string is not None and value != 0: + arg_strings.append(option_string) + elif isinstance(value, FileUpload) and (isinstance(action.type, argparse.FileType) or action.type == open): + # Upload the file in a temp directory + global UPLOAD_DIR + if UPLOAD_DIR is None: + UPLOAD_DIR = mkdtemp(prefix='moulinette_upload_') + value.save(UPLOAD_DIR) if option_string is not None: arg_strings.append(option_string) + arg_strings.append(UPLOAD_DIR + '/' + value.filename) elif isinstance(value, str): if option_string is not None: arg_strings.append(option_string) @@ -192,14 +211,14 @@ class _HTTPArgumentParser(object): return arg_strings # Iterate over positional arguments - for dest in self._positional: - if dest in args: - arg_strings = append(arg_strings, args[dest]) + for action in self._positional: + if action.dest in args: + arg_strings = append(arg_strings, args[action.dest], action) # Iterate over optional arguments - for dest, opt in self._optional.items(): + for dest, action in self._optional.items(): if dest in args: - arg_strings = append(arg_strings, args[dest], opt[0]) + arg_strings = append(arg_strings, args[dest], action) return self._parser.parse_args(arg_strings, namespace) @@ -319,8 +338,12 @@ class _ActionsMapPlugin(object): # Format boolean params for a in args: params[a] = True + # Append other request params - for k, v in request.params.decode().dict.items(): + req_params = request.params.decode().dict.items() + # TODO test special chars in filename + req_params += request.files.dict.items() + for k, v in req_params: v = _format(v) if k not in params.keys(): params[k] = v @@ -495,6 +518,14 @@ class _ActionsMapPlugin(object): else: return format_for_response(ret) finally: + + # Clean upload directory + # FIXME do that in a better way + global UPLOAD_DIR + if UPLOAD_DIR is not None: + rmtree(UPLOAD_DIR, True) + UPLOAD_DIR = None + # Close opened WebSocket by putting StopIteration in the queue try: queue = self.log_queues[request.get_cookie("session.id")] From 36cac914d664934ba1bad670daa7dab8f801d4dd Mon Sep 17 00:00:00 2001 From: ljf Date: Sat, 8 May 2021 18:01:54 +0200 Subject: [PATCH 2/3] [enh] tox recommendation --- moulinette/interfaces/api.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/moulinette/interfaces/api.py b/moulinette/interfaces/api.py index f53dec63..bd8d903a 100644 --- a/moulinette/interfaces/api.py +++ b/moulinette/interfaces/api.py @@ -170,15 +170,17 @@ class _HTTPArgumentParser(object): # Append the option string only if option_string is not None and value != 0: arg_strings.append(option_string) - elif isinstance(value, FileUpload) and (isinstance(action.type, argparse.FileType) or action.type == open): + elif isinstance(value, FileUpload) and ( + isinstance(action.type, argparse.FileType) or action.type == open + ): # Upload the file in a temp directory global UPLOAD_DIR if UPLOAD_DIR is None: - UPLOAD_DIR = mkdtemp(prefix='moulinette_upload_') + UPLOAD_DIR = mkdtemp(prefix="moulinette_upload_") value.save(UPLOAD_DIR) if option_string is not None: arg_strings.append(option_string) - arg_strings.append(UPLOAD_DIR + '/' + value.filename) + arg_strings.append(UPLOAD_DIR + "/" + value.filename) elif isinstance(value, str): if option_string is not None: arg_strings.append(option_string) From 29292583fa7c28831ac776acd7de77c239a21d59 Mon Sep 17 00:00:00 2001 From: ljf Date: Fri, 30 Jul 2021 18:35:19 +0200 Subject: [PATCH 3/3] [fix] 500 error --- moulinette/interfaces/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/moulinette/interfaces/api.py b/moulinette/interfaces/api.py index bd8d903a..f6c6141a 100644 --- a/moulinette/interfaces/api.py +++ b/moulinette/interfaces/api.py @@ -342,9 +342,9 @@ class _ActionsMapPlugin(object): params[a] = True # Append other request params - req_params = request.params.decode().dict.items() + req_params = list(request.params.decode().dict.items()) # TODO test special chars in filename - req_params += request.files.dict.items() + req_params+=list(request.files.dict.items()) for k, v in req_params: v = _format(v) if k not in params.keys():