def pagination(self): """Return all page parameters as a dict. :return dict: a dict of pagination information To allow multiples strategies, all parameters starting with `page` will be included. e.g:: { "number": '25', "size": '150', } Example with number strategy:: >>> query_string = {'page[number]': '25', 'page[size]': '10'} >>> parsed_query.pagination {'number': '25', 'size': '10'} """ # check values type result = self._get_key_values('page') for key, value in result.items(): if key not in ('number', 'size'): raise BadRequest( {'parameter': 'page'}, "{} is not a valid parameter of pagination".format(key)) try: int(value) except ValueError: raise BadRequest({'parameter': 'page[{}]'.format(key)}, "Parse error") return result
def delete(self, *args, **kwargs): """Delete relationship(s) """ json_data = request.get_json() relationship_field, related_type_, related_id_field = self._get_relationship_data() if 'data' not in json_data: raise BadRequest('/data', 'You must provide data with a "data" route node') if isinstance(json_data['data'], dict): if 'type' not in json_data['data']: raise BadRequest('/data/type', 'Missing type in "data" node') if 'id' not in json_data['data']: raise BadRequest('/data/id', 'Missing id in "data" node') if json_data['data']['type'] != related_type_: raise InvalidType('/data/type', 'The type field does not match the resource type') if isinstance(json_data['data'], list): for obj in json_data['data']: if 'type' not in obj: raise BadRequest('/data/type', 'Missing type in "data" node') if 'id' not in obj: raise BadRequest('/data/id', 'Missing id in "data" node') if obj['type'] != related_type_: raise InvalidType('/data/type', 'The type provided does not match the resource type') obj_, updated = self.data_layer.delete_relationship(json_data, relationship_field, related_id_field, **kwargs) qs = QSManager(request.args, self.schema) schema = compute_schema(self.schema, dict(), qs, qs.include) status_code = 200 if updated is True else 204 return schema.dump(obj_), status_code
def patch(self, *args, **kwargs): """Update a relationship""" json_data = request.get_json() or {} relationship_field, model_relationship_field, related_type_, related_id_field = self._get_relationship_data( ) if 'data' not in json_data: raise BadRequest('You must provide data with a "data" route node', source={'pointer': '/data'}) if isinstance(json_data['data'], dict): if 'type' not in json_data['data']: raise BadRequest('Missing type in "data" node', source={'pointer': '/data/type'}) if 'id' not in json_data['data']: raise BadRequest('Missing id in "data" node', source={'pointer': '/data/id'}) if json_data['data']['type'] != related_type_: raise InvalidType( 'The type field does not match the resource type', source={'pointer': '/data/type'}) if isinstance(json_data['data'], list): for obj in json_data['data']: if 'type' not in obj: raise BadRequest('Missing type in "data" node', source={'pointer': '/data/type'}) if 'id' not in obj: raise BadRequest('Missing id in "data" node', source={'pointer': '/data/id'}) if obj['type'] != related_type_: raise InvalidType( 'The type provided does not match the resource type', source={'pointer': '/data/type'}) self.before_patch(args, kwargs, json_data=json_data) obj_, updated = self._data_layer.update_relationship( json_data, model_relationship_field, related_id_field, kwargs) qs = QSManager(request.args, self.schema) includes = qs.include if relationship_field not in qs.include: includes.append(relationship_field) schema = compute_schema(self.schema, dict(), qs, includes) if updated is False: return '', 204 result = schema.dump(obj_).data if result.get('links', {}).get('self') is not None: result['links']['self'] = request.path self.after_patch(result) return result, 200
def patch(self, *args, **kwargs): """Update an object""" json_data = request.get_json() or {} qs = QSManager(request.args, self.schema) schema_kwargs = getattr(self, 'patch_schema_kwargs', dict()) schema_kwargs.update({'partial': True}) self.before_marshmallow(args, kwargs) schema = compute_schema(self.schema, schema_kwargs, qs, qs.include) try: data, errors = schema.load(json_data) except IncorrectTypeError as e: errors = e.messages for error in errors['errors']: error['status'] = '409' error['title'] = "Incorrect type" return errors, 409 except ValidationError as e: errors = e.messages for message in errors['errors']: message['status'] = '422' message['title'] = "Validation error" return errors, 422 if errors: for error in errors['errors']: error['status'] = "422" error['title'] = "Validation error" return errors, 422 if 'id' not in json_data['data']: raise BadRequest('Missing id in "data" node', source={'pointer': '/data/id'}) if json_data['data']['id'] != str(kwargs[self.data_layer.get('url_field', 'id')]): raise BadRequest('Value of id does not match the resource identifier in url', source={'pointer': '/data/id'}) self.before_patch(args, kwargs, data=data) obj = self.update_object(data, qs, kwargs) result = schema.dump(obj).data self.after_patch(result) return result
def delete(self, *args, **kwargs): """Delete relationship(s)""" json_data = request.get_json() or {} relationship_field, model_relationship_field, related_type_, related_id_field = self._get_relationship_data( ) if 'data' not in json_data: raise BadRequest('You must provide data with a "data" route node', source={'pointer': '/data'}) if isinstance(json_data['data'], dict): if 'type' not in json_data['data']: raise BadRequest('Missing type in "data" node', source={'pointer': '/data/type'}) if 'id' not in json_data['data']: raise BadRequest('Missing id in "data" node', source={'pointer': '/data/id'}) if json_data['data']['type'] != related_type_: raise InvalidType( 'The type field does not match the resource type', source={'pointer': '/data/type'}) if isinstance(json_data['data'], list): for obj in json_data['data']: if 'type' not in obj: raise BadRequest('Missing type in "data" node', source={'pointer': '/data/type'}) if 'id' not in obj: raise BadRequest('Missing id in "data" node', source={'pointer': '/data/id'}) if obj['type'] != related_type_: raise InvalidType( 'The type provided does not match the resource type', source={'pointer': '/data/type'}) self.before_delete(args, kwargs, json_data=json_data) obj_, updated = self._data_layer.delete_relationship( json_data, model_relationship_field, related_id_field, kwargs) status_code = 200 result = {'meta': {'message': 'Relationship successfully updated'}} if updated is False: result = '' status_code = 204 final_result = self.after_delete(result, status_code) return final_result
def delete(self, *args, **kwargs): """Delete relationship(s) """ self.before_delete(args, kwargs) json_data = request.get_json() relationship_field, model_relationship_field, related_type_, related_id_field = self._get_relationship_data( ) if 'data' not in json_data: raise BadRequest('/data', 'You must provide data with a "data" route node') if isinstance(json_data['data'], dict): if 'type' not in json_data['data']: raise BadRequest('/data/type', 'Missing type in "data" node') if 'id' not in json_data['data']: raise BadRequest('/data/id', 'Missing id in "data" node') if json_data['data']['type'] != related_type_: raise InvalidType( '/data/type', 'The type field does not match the resource type') if isinstance(json_data['data'], list): for obj in json_data['data']: if 'type' not in obj: raise BadRequest('/data/type', 'Missing type in "data" node') if 'id' not in obj: raise BadRequest('/data/id', 'Missing id in "data" node') if obj['type'] != related_type_: raise InvalidType( '/data/type', 'The type provided does not match the resource type') obj_, updated = self._data_layer.delete_relationship( json_data, model_relationship_field, related_id_field, kwargs) qs = QSManager(request.args, self.schema) includes = qs.include if relationship_field not in qs.include: includes.append(relationship_field) schema = compute_schema(self.schema, dict(), qs, includes) status_code = 200 if updated is True else 204 result = schema.dump(obj_).data if result.get('links', {}).get('self') is not None: result['links']['self'] = request.path self.after_delete(result) return result, status_code
def _get_key_values(self, name): """Return a dict containing key / values items for a given key, used for items like filters, page, etc. :param str name: name of the querystring parameter :return dict: a dict of key / values items """ results = {} for key, value in self.qs.items(): try: if not key.startswith(name): continue key_start = key.index('[') + 1 key_end = key.index(']') item_key = key[key_start:key_end] if ',' in value: item_value = value.split(',') else: item_value = value results.update({item_key: item_value}) except Exception: raise BadRequest("Parse error", source={'parameter': key}) return results
def before_create_object(self, data, view_kwargs): """Prior to creating a new item, check all is OK and insert the user_id of the current user into the request, so that the foreign key for user_id is correctly s0tored with created item. Also stores the category_id of the request URL into the data storage used to set the foreign key of the item pointing to the category. """ # pylint: disable=no-self-use # POST /api/v1/categories/<int:category_id>/items category_id = view_kwargs.get('category_id') if ('name' not in data and 'description' not in data): raise BadRequest('Must include id (of category), name and ' 'description fields') try: category = Category.query.filter_by(id=category_id).one() except NoResultFound: raise ObjectNotFound({'parameter': 'category_id'}, "Category with id: {} not found".format( data['category_id'])) # set the foreign key to the logged in user data['user_id'] = g.current_user.id # set the foreign key for category data['category_id'] = category.id
def patch(self, *args, **kwargs): """Update an object """ json_data = request.get_json() qs = QSManager(request.args, self.schema) schema_kwargs = getattr(self.opts, 'schema_patch_kwargs', dict()) schema_kwargs.update({'partial': True}) schema = compute_schema(self.schema, schema_kwargs, qs, qs.include) try: data, errors = schema.load(json_data) except IncorrectTypeError as e: errors = e.messages for error in errors['errors']: error['status'] = '409' error['title'] = "Incorrect type" return errors, 409 except ValidationError as e: errors = e.messages for message in errors['errors']: message['status'] = '422' message['title'] = "Validation error" return errors, 422 if errors: for error in errors['errors']: error['status'] = "422" error['title'] = "Validation error" return errors, 422 if 'id' not in json_data['data']: raise BadRequest('/data/id', 'Missing id in "data" node') if json_data['data']['id'] != str(kwargs[getattr(self.data_layer, 'url_field', 'id')]): raise BadRequest('/data/id', 'Value of id does not match the resource identifier in url') obj = self.data_layer.get_object(**kwargs) updated = self.data_layer.update_object(obj, data, **kwargs) result = schema.dump(obj) status_code = 200 if updated is True else 204 return result.data, status_code
def pagination(self): """Return all page parameters as a dict. :return dict: a dict of pagination information To allow multiples strategies, all parameters starting with `page` will be included. e.g:: { "number": '25', "size": '150', } Example with number strategy:: >>> query_string = {'page[number]': '25', 'page[size]': '10'} >>> parsed_query.pagination {'number': '25', 'size': '10'} """ # check values type result = self._get_key_values('page') for key, value in result.items(): if key not in ('number', 'size'): raise BadRequest( "{} is not a valid parameter of pagination".format(key), source={'parameter': 'page'}) try: int(value) except ValueError: raise BadRequest("Parse error", source={'parameter': 'page[{}]'.format(key)}) if current_app.config.get( 'ALLOW_DISABLE_PAGINATION', True) is False and int( result.get('size', 1)) == 0: raise BadRequest("You are not allowed to disable pagination", source={'parameter': 'page[size]'}) if current_app.config.get( 'MAX_PAGE_SIZE') is not None and 'size' in result: if int(result['size']) > current_app.config['MAX_PAGE_SIZE']: raise BadRequest("Maximum page size is {}".format( current_app.config['MAX_PAGE_SIZE']), source={'parameter': 'page[size]'}) return result
def before_create_object(self, data, unused_view_kwargs): """Prior to creating a new user, check all is OK""" # POST /users if ('email' not in data or 'password' not in data or 'first_name' not in data or 'last_name' not in data): raise BadRequest('Must include email, password, first_name and' 'last_name fields') user = self.session.query(User).filter_by(email=data['email']).first() if user: if user.blocked: raise JsonApiException(detail='Account has been blocked.' 'Contact the site administrator.', title='Permission denied', status='403') else: raise BadRequest('Email {} already registered'.format( data['email']))
def before_create_object(self, data, unused_view_kwargs): """Prior to creating a new category, check all is OK and insert the user_id of the current user into the request, so that the foreign key for user_id is correctly stored with created category """ # pylint: disable=no-self-use # POST /api/v1/categories if 'name' not in data: raise BadRequest('Must include name field') # set the foreign key to the logged in user data['user_id'] = g.current_user.id