def dispatch(self, request, method=''): # in case we do something json doesn't like, we always get back valid # json-rpc response response = self.empty_response() try: raw_data = extract_raw_data_request(request) if request.method == 'GET': valid, D = self.validate_get(request, method) if not valid: raise InvalidRequestError( 'The method you are trying to access is ' 'not availble by GET requests') elif not request.method == 'POST': raise RequestPostError else: try: D = json.loads(raw_data) except Exception, e: raise InvalidRequestError(e.message) if type(D) is list: response = [ self.response_dict(request, d, is_batch=True)[0] for d in D ] status = 200 else: response, status = self.response_dict(request, D) if response is None and (not u'id' in D or D[u'id'] is None): # a notification response = '' return response, status
def batch_response_obj(self, request, D): status = 200 try: responses = [self.response_obj(request, d)[0] for d in D] if not responses: raise InvalidRequestError('Empty array') except Error as e: for response in responses: response.pop('result', None) response['error'] = e.json_rpc_format except Exception as e: other_error = OtherError(e) for response in responses: response.pop('result', None) response['error'] = other_error.json_rpc_format for response in responses: if response is None: continue # notification # Exactly one of result or error MUST be specified. It's not # allowed to specify both or none. if 'result' in response: response.pop('error', None) if not responses: response = self.empty_response(version='2.0') response['error'] = InvalidRequestError().json_rpc_format response.pop('result', None) responses = response if not all(responses): return '', 204 # notification return responses, status
def search_movie_id(self, filename): name, year = self.parse_movie_name(filename) if name is None: return None params_list = [{ 'query': name, 'primary_release_year': year }, { 'query': name, 'year': year }] resp_json = None for params in params_list: resp_json = self.search_movie(params) if 'total_results' in resp_json.keys() and \ resp_json['total_results'] > 0: break if 'total_results' not in resp_json.keys(): if 'errors' in resp_json.keys(): raise InvalidRequestError(message=resp_json['errors']) raise InvalidRequestError(message=resp_json.get('status_message')) if resp_json['total_results'] < 1: return None return resp_json['results'][0]['id']
def get_item_content_url(item_id: str, item: dict = None) -> str: item_doc = item or get_item(item_id) if 'folder' in item_doc.keys(): raise InvalidRequestError( message='You cannot get content for a folder') if item_doc['size'] > 50 * 1024 * 1024: raise InvalidRequestError(message='Large file uses shared link') drive = Drive.create_from_id(item_doc['parentReference']['driveId']) return drive_api.content_url(drive.token, item_id)
def dispatch(self, request, method=''): # in case we do something json doesn't like, we always get back valid # json-rpc response response = self.empty_response() raw_data = extract_raw_data_request(request) try: if request.method == 'GET': valid, D = self.validate_get(request, method) if not valid: raise InvalidRequestError( 'The method you are trying to access is ' 'not availble by GET requests') elif not request.method == 'POST': raise RequestPostError else: try: D = json.loads(raw_data) except Exception as e: raise InvalidRequestError(e.message) if type(D) is list: response = [(yield from self.response_dict(request, d, is_batch=True))[0] for d in D] status = 200 else: response, status = (yield from self.response_dict(request, D)) if response is None and (not 'id' in D or D['id'] is None): # a notification response = '' return response, status except Error as e: #got_request_exception.connect(log_exception, current_app._get_current_object()) response['error'] = e.json_rpc_format status = e.status except Exception as e: # exception missed by others #got_request_exception.connect(log_exception, current_app._get_current_object()) other_error = OtherError(e) response['result'] = None response['error'] = other_error.json_rpc_format status = other_error.status # extract id the request json_request_id = self.extract_id_request(raw_data) response['id'] = json_request_id return response, status
def get_item_shared_link(item_id: str, item: dict = None) -> Union[str, None]: item_doc = item or get_item(item_id) if 'folder' in item_doc.keys(): raise InvalidRequestError(message='You cannot get link for a folder') create_link = item_doc.get('create_link') if create_link is not None: expiration_date_time = create_link.get('expirationDateTime') or '' if expiration_date_time < Utils.utc_datetime( timedelta=datetime.timedelta(days=1)): create_link = None if create_link is None: return None drive_id = item_doc['parentReference']['driveId'] base_down_url = get_base_down_url(drive_id, item_id) web_url = create_link['link']['webUrl'] share = web_url[web_url.rfind('/') + 1:] # download.aspx 加上 '/' 再在后面加任意字符串都行,这里加个文件名方便识别 direct_link = base_down_url.replace( '?share=', '/' + item_doc['name'] + '?share=') + share return direct_link
def make_response(self, rv): """Converts the return value from a view function to a real response object that is an instance of :attr:`response_class`. """ status_or_headers = headers = None if isinstance(rv, tuple): rv, status_or_headers, headers = rv + (None, ) * (3 - len(rv)) if rv is None: raise ValueError('View function did not return a response') if isinstance(status_or_headers, (dict, list)): headers, status_or_headers = status_or_headers, None D = json.loads(extract_raw_data_request(request)) if type(D) is list: raise InvalidRequestError( 'JSON-RPC batch with decorator (make_response) not is supported' ) else: response_obj = self.empty_response(version=D['jsonrpc']) response_obj['id'] = D['id'] response_obj['result'] = rv response_obj.pop('error', None) rv = jsonify(response_obj) if status_or_headers is not None: if isinstance(status_or_headers, string_types): rv.status = status_or_headers else: rv.status_code = status_or_headers if headers: rv.headers.extend(headers) return rv
def upload_file(drive_id: str, upload_path: str, file_path: str) -> int: """ :param drive_id: :param upload_path: 上传至此目录下,结尾带‘/’ :param file_path: 本地文件路径 :return: """ upload_path = upload_path.strip().replace('\\', '/') file_path = file_path.strip().replace('\\', '/') if not upload_path.endswith('/'): upload_path = upload_path + '/' if os.path.isfile(file_path) is False: raise InvalidRequestError(message='File not found.') file_size = os.path.getsize(file_path) if file_size <= 0: return -1 _, filename = os.path.split(file_path) uid = str(uuid.uuid4()) upload_info = UploadInfo(uid=uid, drive_id=drive_id, filename=filename, file_path=file_path, upload_path=upload_path, size=file_size, created_date_time=Utils.str_datetime()) mongodb.upload_info.insert_one(upload_info.json()) upload_pool.add_task(uid) return 0
def dispatch(self, request, method=''): # in case we do something json doesn't like, we always get back valid # json-rpc response response = self.empty_response() raw_data = extract_raw_data_request(request) try: if request.method == 'GET': valid, D = self.validate_get(request, method) if not valid: raise InvalidRequestError( 'The method you are trying to access is ' 'not availble by GET requests') elif not request.method == 'POST': raise RequestPostError() else: try: D = json.loads(raw_data) except Exception as e: raise ParseError( getattr(e, 'message', e.args[0] if len(e.args) > 0 else None)) if type(D) is list: return self.batch_response_obj(request, D) response, status = self.response_obj(request, D) if isinstance(response, Response): return response, status if response is None and (not 'id' in D or D['id'] is None): # a notification response = '' return response, status except Error as e: response.pop('result', None) response['error'] = e.json_rpc_format status = e.status except Exception as e: other_error = OtherError(e) response.pop('result', None) response['error'] = other_error.json_rpc_format status = other_error.status # extract id the request if not response.get('id', False): json_request_id = self.extract_id_request(raw_data) response['id'] = json_request_id # If there was an error in detecting the id in the Request object # (e.g. Parse error/Invalid Request), it MUST be Null. if not response and 'error' in response: if response['error']['name'] in ('ParseError', 'InvalidRequestError', 'RequestPostError'): response['id'] = None return response, status
def get_settings(drive_id: str) -> dict: doc = mongodb.drive.find_one({'id': drive_id}, {'_id': 0, 'settings': 1}) if doc is None: raise InvalidRequestError(message='Cannot find drive') settings = doc.get('settings') or {} for k, v in default_settings.items(): settings[k] = settings.get(k) or v return settings
def modify_settings(drive_id: str, name: str, value: Union[str, bool, int]) -> int: if name not in default_settings.keys(): raise InvalidRequestError(message='Wrong settings name') new_value = value if name.endswith('_path'): # 根目录 '/', 其他目录以'/'开头,结尾不带'/' new_value = Utils.path_with_slash(new_value) r = mongodb.drive.update_one({'id': drive_id}, {'$set': { 'settings.' + name: new_value }}) if r.matched_count == 0: return -1 return 0
def get_movie(movie_id: int, append_collection: bool = False) -> dict: for item in mongodb.tmdb_movie.aggregate([ {'$match': {'id': movie_id}}, {'$lookup': { 'from': 'tmdb_person', 'localField': 'directors', 'foreignField': 'id', 'as': 'directors' }}, {'$unset': 'directors._id'}, {'$project': get_movie_projection} ]): if append_collection and item.get('belongs_to_collection') is not None: item['belongs_to_collection'] = get_collection( item['belongs_to_collection']['id']) return item raise InvalidRequestError(message='Invalid movie id')
def upload_folder(drive_id: str, upload_path: str, folder_path: str) -> int: """ 上传文件夹下的所有文件,不包括子文件夹 :param drive_id: :param upload_path: 上传至此目录下,结尾带‘/’ :param folder_path: 上传此目录下的文件,结尾带'/' :return: """ upload_path = upload_path.strip().replace('\\', '/') folder_path = folder_path.strip().replace('\\', '/') if not upload_path.endswith('/'): upload_path = upload_path + '/' if not folder_path.endswith('/'): folder_path = folder_path + '/' if os.path.isdir(folder_path) is False: raise InvalidRequestError(message='Folder not found.') _, folder_name = os.path.split(folder_path[:-1]) for file in sorted(os.listdir(folder_path), key=lambda x: x.lower()): file_path = folder_path + file if os.path.isfile(file_path) is False: continue file_size = os.path.getsize(file_path) if file_size <= 0: continue uid = str(uuid.uuid4()) upload_info = UploadInfo(uid=uid, drive_id=drive_id, filename=file, file_path=file_path, upload_path=upload_path + folder_name + '/', size=file_size, created_date_time=Utils.str_datetime()) mongodb.upload_info.insert_one(upload_info.json()) upload_pool.add_task(uid) return 0
def get_item(item_id: str) -> dict: doc = mongodb.item.find_one({'id': item_id}, {'_id': 0}) if doc is None: raise InvalidRequestError(message='Cannot find item') return doc
def response_obj(self, request, D, version_hint=JSONRPC_VERSION_DEFAULT): version = version_hint response = self.empty_response(version=version) apply_version = { '2.0': self.apply_version_2_0, '1.1': self.apply_version_1_1, '1.0': self.apply_version_1_0 } try: try: # determine if an object is iterable? iter(D) except TypeError as e: raise InvalidRequestError( getattr(e, 'message', e.args[0] if len(e.args) > 0 else None)) # version: validate if 'jsonrpc' in D: if text_type(D['jsonrpc']) not in apply_version: raise InvalidRequestError( 'JSON-RPC version {0} not supported.'.format( D['jsonrpc'])) version = request.jsonrpc_version = response[ 'jsonrpc'] = text_type(D['jsonrpc']) elif 'version' in D: if text_type(D['version']) not in apply_version: raise InvalidRequestError( 'JSON-RPC version {0} not supported.'.format( D['version'])) version = request.jsonrpc_version = response[ 'version'] = text_type(D['version']) else: version = request.jsonrpc_version = JSONRPC_VERSION_DEFAULT # params: An Array or Object, that holds the actual parameter values # for the invocation of the procedure. Can be omitted if empty. if 'params' not in D or not D['params']: D['params'] = [] if 'method' not in D or 'params' not in D: raise InvalidParamsError( 'Request requires str:"method" and list:"params"') if D['method'] not in self.urls: raise MethodNotFoundError('Method not found. Available methods: {0}' \ .format('\n'.join(list(self.urls.keys())))) method = self.urls[text_type(D['method'])] if getattr(method, 'json_validate', False): validate_params(method, D) if 'id' in D and D['id'] is not None: # regular request response['id'] = D['id'] if version in ('1.1', '2.0'): response.pop('error', None) else: # notification return None, 204 R = apply_version[version](method, D['params']) if 'id' not in D or ('id' in D and D['id'] is None): # notification return None, 204 if isinstance(R, Response): if R.status_code == 200: return R, R.status_code if R.status_code == 401: raise InvalidCredentialsError(R.status) raise OtherError(R.status, R.status_code) try: # New in Flask version 0.10. encoder = current_app.json_encoder() except AttributeError: encoder = json.JSONEncoder() # type of `R` should be one of these or... if not sum([isinstance(R, e) for e in \ string_types + integer_types + \ (float, complex, dict, list, tuple, set, frozenset, NoneType, bool)]): try: rs = encoder.default( R) # ...or something this thing supports except TypeError as exc: raise TypeError( 'Return type not supported, for {0!r}'.format(R)) response['result'] = R status = 200 except Error as e: response['error'] = e.json_rpc_format if version in ('1.1', '2.0'): response.pop('result', None) status = e.status except HTTPException as e: other_error = OtherError(e) response['error'] = other_error.json_rpc_format response['error']['code'] = e.code if version in ('1.1', '2.0'): response.pop('result', None) status = e.code except Exception as e: other_error = OtherError(e) response['error'] = other_error.json_rpc_format status = other_error.status if version in ('1.1', '2.0'): response.pop('result', None) # Exactly one of result or error MUST be specified. It's not # allowed to specify both or none. if version in ('1.1', '2.0') and 'result' in response: response.pop('error', None) return response, status
def response_dict(self, request, D, is_batch=False, version_hint='1.0'): version = version_hint response = self.empty_response(version=version) apply_version = { '2.0': lambda f, p: f(**encode_kw(p)) if type(p) is dict else f(*p), '1.1': lambda f, p: f(*encode_arg11(p), **encode_kw(encode_kw11(p))), '1.0': lambda f, p: f(*p) } try: # params: An Array or Object, that holds the actual parameter values # for the invocation of the procedure. Can be omitted if empty. if 'params' not in D or not D['params']: D['params'] = [] if 'method' not in D or 'params' not in D: raise InvalidParamsError( 'Request requires str:"method" and list:"params"') if D['method'] not in self.urls: raise MethodNotFoundError('Method not found. Available methods: {0}' \ .format('\n'.join(list(self.urls.keys())))) if 'jsonrpc' in D: if text_type(D['jsonrpc']) not in apply_version: raise InvalidRequestError( 'JSON-RPC version {0} not supported.'.format( D['jsonrpc'])) version = request.jsonrpc_version = response[ 'jsonrpc'] = text_type(D['jsonrpc']) elif 'version' in D: if text_type(D['version']) not in apply_version: raise InvalidRequestError( 'JSON-RPC version {0} not supported.'.format( D['version'])) version = request.jsonrpc_version = response[ 'version'] = text_type(D['version']) else: request.jsonrpc_version = '1.0' method = self.urls[text_type(D['method'])] if getattr(method, 'json_validate', False): validate_params(method, D) if 'id' in D and D['id'] is not None: # regular request response['id'] = D['id'] if version in ('1.1', '2.0') and 'error' in response: response.pop('error') elif is_batch: # notification, not ok in a batch format, but happened anyway raise InvalidRequestError else: # notification return None, 204 R = apply_version[version](method, D['params']) if asyncio.iscoroutine(R): R = (yield from R) if 'id' not in D or ('id' in D and D['id'] is None): # notification return None, 204 encoder = current_app.json_encoder() # type of `R` should be one of these or... if not sum([isinstance(R, e) for e in \ string_types + integer_types + (dict, list, set, NoneType, bool)]): try: rs = encoder.default( R) # ...or something this thing supports except TypeError as exc: raise TypeError( "Return type not supported, for {0!r}".format(R)) response['result'] = R status = 200 except Error as e: # exception missed by others #got_request_exception.connect(log_exception, current_app._get_current_object()) response['error'] = e.json_rpc_format if version in ('1.1', '2.0') and 'result' in response: response.pop('result') status = e.status except HTTPException as e: # exception missed by others #got_request_exception.connect(log_exception, current_app._get_current_object()) other_error = OtherError(e) response['error'] = other_error.json_rpc_format response['error']['code'] = e.code if version in ('1.1', '2.0') and 'result' in response: response.pop('result') status = e.code except Exception as e: # exception missed by others #got_request_exception.connect(log_exception, current_app._get_current_object()) other_error = OtherError(e) response['error'] = other_error.json_rpc_format status = other_error.status if version in ('1.1', '2.0') and 'result' in response: response.pop('result') # Exactly one of result or error MUST be specified. It's not # allowed to specify both or none. if version in ('1.1', '2.0' ) and 'error' in response and not response['error']: response.pop('error') return response, status
def response_dict(self, request, D, is_batch=False, version_hint='1.0'): version = version_hint response = self.empty_response(version=version) apply_version = { '2.0': lambda f, r, p: f(**encode_kw(p)) if type(p) is dict else f(*p), '1.1': lambda f, r, p: f(*encode_arg11(p), **encode_kw(encode_kw11(p))), '1.0': lambda f, r, p: f(*p) } try: # params: An Array or Object, that holds the actual parameter values # for the invocation of the procedure. Can be omitted if empty. if 'params' not in D or not D['params']: D['params'] = [] if 'method' not in D or 'params' not in D: raise InvalidParamsError( 'Request requires str:"method" and list:"params"') if D['method'] not in self.urls: raise MethodNotFoundError( 'Method not found. Available methods: %s' % ('\n'.join(self.urls.keys()))) if 'jsonrpc' in D: if str(D['jsonrpc']) not in apply_version: raise InvalidRequestError( 'JSON-RPC version %s not supported.' % D['jsonrpc']) version = request.jsonrpc_version = response['jsonrpc'] = str( D['jsonrpc']) elif 'version' in D: if str(D['version']) not in apply_version: raise InvalidRequestError( 'JSON-RPC version %s not supported.' % D['version']) version = request.jsonrpc_version = response['version'] = str( D['version']) else: request.jsonrpc_version = '1.0' method = self.urls[str(D['method'])] if getattr(method, 'json_validate', False): validate_params(method, D) if 'id' in D and D['id'] is not None: # regular request response['id'] = D['id'] if version in ('1.1', '2.0') and 'error' in response: response.pop('error') elif is_batch: # notification, not ok in a batch format, but happened anyway raise InvalidRequestError else: # notification return None, 204 R = apply_version[version](method, request, D['params']) if 'id' not in D or ('id' in D and D['id'] is None): # notification return None, 204 encoder = current_app.json_encoder() if not sum( map( lambda e: isinstance( R, e), # type of `R` should be one of these or... (dict, str, unicode, int, long, list, set, NoneType, bool))): try: rs = encoder.default( R) # ...or something this thing supports except TypeError, exc: raise TypeError("Return type not supported, for %r" % R) response['result'] = R status = 200