def delete_all(self, request): try: # patch __log self.__log = self._GRest__log (primary, _) = validate_models(self) if all([ENABLE_DELETE_ALL == "True", primary.model]): # user wants to delete all items (including relations) results = db.cypher_query("MATCH (n:{0}) DETACH DELETE n".format( primary.model.__name__)) if results[0] == []: return serialize(dict(result="OK")) else: raise HTTPException(msg.DELETE_FAILED, 500) else: raise HTTPException(msg.FEATURE_IS_DISABLED, 403) except (DoesNotExist, AttributeError) as e: self.__log.exception(e) raise HTTPException(msg.ITEM_DOES_NOT_EXIST, 404) except UniqueProperty as e: self.__log.exception(e) raise HTTPException(msg.NON_UNIQUE_PROPERTY, 409) except RequiredProperty as e: self.__log.exception(e) raise HTTPException(msg.REQUIRE_PROPERTY_MISSING, 500)
def patch(self, request, primary_id): try: # patch __log self.__log = self._GRest__log (primary, _) = validate_models(self, primary_id) primary_selected_item = None if primary.id is not None: primary_selected_item = primary.model.nodes.get_or_none( **{primary.selection_field: primary.id}) if primary_selected_item: new_item = primary.model() # parse input data (validate or not!) json_data = validate_input(new_item.validation_rules, request) updated_item = None with db.transaction: primary_selected_item.__dict__.update(json_data) updated_item = primary_selected_item.save() updated_item.refresh() if updated_item: return serialize(dict(result="OK")) else: raise HTTPException(msg.UPDATE_FAILED, 500) else: raise HTTPException(msg.ITEM_DOES_NOT_EXIST, 404) except DoesNotExist as e: self.__log.exception(e) raise HTTPException("The requested item or relation does not exist.", 404)
def index(self: FlaskView, request: Request) -> "grest.GRestResponse": try: # patch __log self.__log = self._GRest__log __validation_rules__ = { "skip": fields.Int(required=False, validate=lambda skip: skip >= 0, missing=0), "limit": fields.Int(required=False, validate=lambda lim: lim >= 1 and lim <= 100, missing=QUERY_LIMIT), "order_by": fields.Str(required=False, missing="?") } (primary, _) = validate_models(self) query_data = validate_input( __validation_rules__, request, location="query") skip = query_data.get("skip") limit = query_data.get("skip") + query_data.get("limit") order_by = escape(query_data.get("order_by")) if order_by: if order_by.startswith("-"): # select property for descending ordering order_by_prop = order_by[1:] else: # select property for ascending ordering order_by_prop = order_by primary_model_props = primary.model.defined_properties().keys() if all([order_by_prop not in primary_model_props, order_by_prop != "?"]): raise HTTPException(msg.INVALID_ORDER_PROPERTY, 404) total_items = len(primary.model.nodes) if total_items <= 0: raise HTTPException(msg.NO_ITEM_EXISTS.format( model=primary.model_name), 404) if skip > total_items: raise HTTPException(msg.VALIDATION_FAILED, 422) items = primary.model.nodes.order_by(order_by)[skip:limit] if items: return serialize({pluralize(primary.model_name): [item.to_dict() for item in items]}) else: raise HTTPException(msg.NO_ITEM_EXISTS.format( model=primary.model_name), 404) except DoesNotExist as e: self.__log.exception(str(e)) raise HTTPException(msg.ITEM_DOES_NOT_EXIST, 404)
def get(self: FlaskView, primary_id: str, secondary_model_name: Optional[str] = None, secondary_id: Optional[str] = None) -> "grest.GRestResponse": try: # patch __log self.__log = self._GRest__log (primary, secondary) = validate_models(self, primary_id, secondary_model_name, secondary_id) primary_selected_item = primary.model.nodes.get_or_none( **{primary.selection_field: primary.id}) if all([primary_selected_item, secondary.model, secondary.id]): # user selected a nested model with 2 keys # (from the primary and secondary models) # /users/user_id/roles/role_id -> selected role of this user # /categories/cat_id/tags/tag_id -> selected tag of this category # In this example, the p variable of type Post # is the secondary_item # (u:User)-[:POSTED]-(p:Post) secondary_item = primary_selected_item.get_all( secondary.model_name, secondary.selection_field, secondary.id, retrieve_relations=True) return serialize( {singularize(secondary.model_name): secondary_item}) elif all([primary_selected_item, secondary.model]): # user selected a nested model with primary key # (from the primary and the secondary models) # /users/user_1/roles -> all roles for this user relationships = primary_selected_item.get_all( secondary.model_name, retrieve_relations=True) return serialize({pluralize(secondary.model_name): relationships}) else: # user selected a single item (from the primary model) if primary_selected_item: return serialize( {primary.model_name: primary_selected_item.to_dict()}) else: raise HTTPException( msg.MODEL_DOES_NOT_EXIST.format(model=primary.model_name), 404) except (DoesNotExist, AttributeError) as e: self.__log.exception(str(e)) raise HTTPException(msg.ITEM_DOES_NOT_EXIST, 404)
def validate_input(validation_rules, request): # parse input data (validate or not!) # noinspection PyBroadException try: query_data = parser.parse(validation_rules, request) except: raise HTTPException(msg.VALIDATION_FAILED, 422) if not query_data: # if a non-existent property is present or misspelled, # the json_data property is empty! raise HTTPException(msg.INVALID_PROPERTIES, 409) return query_data
def validate_models(self, primary_id=None, secondary_model_name=None, secondary_id=None): class Primary: id = None model = None model_name = None selection_field = None class Secondary: id = None model = None model_name = None selection_field = None if primary_id: Primary.id = escape(unquote(primary_id)) Primary.model = self.__model__.get("primary") Primary.model_name = Primary.model.__name__.lower() Primary.selection_field = self.__selection_field__.get("primary") if secondary_model_name: Secondary.model_name = escape(unquote(secondary_model_name)) if secondary_id: Secondary.id = escape(unquote(secondary_id)) Secondary.model = Secondary.selection_field = None if "secondary" in self.__model__: Secondary.model = self.__model__.get( "secondary").get(Secondary.model_name) if "secondary" in self.__selection_field__: Secondary.selection_fields = self.__selection_field__.get( "secondary") Secondary.selection_field = Secondary.selection_fields.get( Secondary.model_name) if all([Secondary.model_name is not None, Secondary.model is None]): raise HTTPException(msg.RELATION_DOES_NOT_EXIST, 404) return (Primary, Secondary)
def put(self, request, primary_id, secondary_model_name=None, secondary_id=None): try: # patch __log self.__log = self._GRest__log (primary, secondary) = validate_models(self, primary_id, secondary_model_name, secondary_id) primary_selected_item = None if primary.id is not None: primary_selected_item = primary.model.nodes.get_or_none( **{primary.selection_field: primary.id}) secondary_selected_item = None if secondary.id is not None: secondary_selected_item = secondary.model.nodes.get_or_none( **{secondary.selection_field: secondary.id}) if all([ primary_selected_item, secondary_selected_item, secondary.model, secondary.id ]): # user either wants to update a relation or # has provided invalid information if hasattr(primary_selected_item, secondary.model_name): # Get relation between primary and secondary objects relation = getattr(primary_selected_item, secondary.model_name) # If there is a relation model between the two, # validate requests based on that relation_model = relation.definition["model"] json_data = {} if relation_model is not None: # TODO: find a way to validate relationships json_data = request.get_json(silent=True) with db.transaction: # remove all relationships for each_relation in relation.all(): relation.disconnect(each_relation) if not json_data: related_item = relation.connect( secondary_selected_item) else: related_item = relation.connect( secondary_selected_item, json_data) if related_item: return serialize(dict(result="OK")) else: raise HTTPException(msg.RELATION_DOES_NOT_EXIST, 404) elif all([ primary_selected_item is not None, secondary.model is None, secondary.id is None ]): # a single item is going to be updated(/replaced) with the # provided JSON data # parse input data (validate or not!) json_data = validate_input(primary.model().validation_rules, request) # delete old node and its relations primary_selected_item.delete() with db.transaction: new_item = primary.model(**json_data).save() new_item.refresh() return serialize({ primary.selection_field: getattr(new_item, primary.selection_field) }) else: raise HTTPException(msg.BAD_REQUEST, 400) except (DoesNotExist, AttributeError) as e: self.__log.exception(e.message) raise HTTPException(msg.ITEM_DOES_NOT_EXIST, 404) except UniqueProperty as e: self.__log.exception(e.message) raise HTTPException(msg.NON_UNIQUE_PROPERTIY, 409) except RequiredProperty as e: self.__log.exception(e.message) raise HTTPException(msg.REQUIRE_PROPERTY_MISSING, 500)
def delete(self, request, primary_id, secondary_model_name=None, secondary_id=None): try: # patch __log self.__log = self._GRest__log (primary, secondary) = validate_models(self, primary_id, secondary_model_name, secondary_id) primary_selected_item = None if primary.id is not None: primary_selected_item = primary.model.nodes.get_or_none( **{primary.selection_field: primary.id}) secondary_selected_item = None if secondary.id is not None: secondary_selected_item = secondary.model.nodes.get_or_none( **{secondary.selection_field: secondary.id}) if all([primary_selected_item, secondary_selected_item, secondary.model, secondary.id]): # user either wants to delete a relation or # has provided invalid information if hasattr(primary_selected_item, secondary.model_name): relation_exists = primary_selected_item.relation_exists( secondary.model_name, secondary_selected_item) if not relation_exists: # There is an no relation raise HTTPException(msg.RELATION_DOES_NOT_EXIST, 404) else: # Get relation between primary and secondary objects relation = getattr( primary_selected_item, secondary.model_name) with db.transaction: # remove all relationships for each_relation in relation.all(): relation.disconnect(each_relation) if secondary_selected_item not in relation.all(): return serialize(dict(result="OK")) else: raise HTTPException(msg.DELETE_FAILED, 500) elif all([primary_selected_item is not None, secondary.model is None, secondary.id is None]): with db.transaction: if primary_selected_item.delete(): return serialize(dict(result="OK")) else: raise HTTPException(msg.DELETE_FAILED, 500) else: raise HTTPException(msg.RELATION_DOES_NOT_EXIST, 404) except (DoesNotExist, AttributeError) as e: self.__log.exception(e) raise HTTPException(msg.ITEM_DOES_NOT_EXIST, 404) except UniqueProperty as e: self.__log.exception(e) raise HTTPException(msg.NON_UNIQUE_PROPERTY, 409) except RequiredProperty as e: self.__log.exception(e) raise HTTPException(msg.REQUIRE_PROPERTY_MISSING, 500)
def post(self: FlaskView, primary_id: Optional[str] = None, secondary_model_name: Optional[str] = None, secondary_id: Optional[str] = None) -> "grest.GRestResponse": try: # patch __log self.__log = self._GRest__log (primary, secondary) = validate_models(self, primary_id, secondary_model_name, secondary_id) primary_selected_item = None if primary.id is not None: primary_selected_item = primary.model.nodes.get_or_none( **{primary.selection_field: primary.id}) secondary_selected_item = None if secondary.id is not None: secondary_selected_item = secondary.model.nodes.get_or_none( **{secondary.selection_field: secondary.id}) if all([ primary_selected_item, secondary_selected_item, secondary.model, secondary.id ]): # user either wants to update a relation or # has provided invalid information if hasattr(primary_selected_item, secondary.model_name): relation_exists = primary_selected_item.relation_exists( # type: ignore secondary.model_name, secondary_selected_item) if relation_exists: # There is an existing relation raise HTTPException(msg.RELATION_EXISTS, 409) else: # Get relation between primary and secondary objects relation = getattr(primary_selected_item, secondary.model_name) # If there is a relation model between the two, # validate requests based on that relation_model = relation.definition["model"] json_data: Optional[Any] = None if relation_model is not None: # TODO: find a way to validate relationships json_data = request.get_json(silent=True) with db.transaction: if not json_data: related_item = relation.connect( secondary_selected_item) else: related_item = relation.connect( secondary_selected_item, json_data) if related_item: return serialize(dict(result="OK")) else: raise HTTPException(msg.RELATION_DOES_NOT_EXIST, 404) elif all([ primary_selected_item is None, secondary.model is None, secondary.id is None ]): new_item = primary.model() # parse input data (validate or not!) json_data = validate_input(new_item.validation_rules, request) # user wants to add a new item (from the primary model) new_item = primary.model.nodes.get_or_none(**json_data) if not new_item: with db.transaction: new_item = primary.model(**json_data).save() new_item.refresh() return serialize({ primary.selection_field: getattr(new_item, primary.selection_field) }) else: raise HTTPException( msg.ITEM_EXISTS.format(item=primary.model_name), 409) else: raise HTTPException(msg.BAD_REQUEST, 400) except (DoesNotExist, AttributeError) as e: self.__log.exception(str(e)) raise HTTPException(msg.ITEM_DOES_NOT_EXIST, 404) except UniqueProperty as e: self.__log.exception(str(e)) raise HTTPException(msg.NON_UNIQUE_PROPERTY, 409) except RequiredProperty as e: self.__log.exception(str(e)) raise HTTPException(msg.REQUIRE_PROPERTY_MISSING, 500) return None