def get_user(username: str): query = f'MATCH (a) WHERE a.username = \'{username}\' RETURN a' with neo4j_driver.session() as session: user_in_db = session.run(query) user_data = user_in_db.data()[0]['a'] return UserInDB(**user_data)
async def create_user(username: str, password: str, full_name: Optional[str] = None, disabled: Optional[bool] = None): # Create dictionary of new user attributes attributes = {'username':username, 'full_name':full_name, 'hashed_password':create_password_hash(password), 'joined':str(datetime.utcnow()), 'disabled':disabled} # Write Cypher query and run against the database cypher_search = 'MATCH (user:User) WHERE user.username = $username RETURN user' cypher_create = 'CREATE (user:User {params}) RETURN user' with neo4j_driver.session() as session: # First, run a search of users to determine if username is already in use check_users = session.run(query=cypher_search, parameters={'username':username}) # Return error message if username is already in the database if check_users.data(): raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=f"Operation not permitted, user with username {username} already exists.", headers={"WWW-Authenticate": "Bearer"}) response = session.run(query=cypher_create, parameters={'params':attributes}) user_data = response.data()[0]['user'] user = User(**user_data) return user
async def read_node_id(node_id: int, current_user: User = Depends(get_current_active_user)): ''' **Retrieves data about a node in the graph, based on node ID.** :param **node_id** (str) - node id, used for indexed search :returns: Node response, with node id, labels, and properties. ''' cypher = '''Match (node) WHERE ID(node) = $node_id RETURN ID(node) as id, LABELS(node) as labels, node''' with neo4j_driver.session() as session: result = session.run(query=cypher, parameters={'node_id': node_id}) node_data = result.data()[0] # Check node for type User, and send error message if needed if 'User' in node_data['labels']: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail= f"Operation not permitted, please use User endpoints to retrieve user information.", headers={"WWW-Authenticate": "Bearer"}) # Return Node response return Node(node_id=node_data['id'], labels=node_data['labels'], properties=node_data['node'])
async def update_relationship(relationship_id: int, attributes: dict): cypher = '''MATCH (nodeA)-[relationship]->(nodeB) WHERE ID(relationship) = $rel_id SET relationship += $attributes RETURN nodeA, ID(nodeA), LABELS(nodeA), relationship, ID(relationship), TYPE(relationship), nodeB, ID(nodeB), LABELS(nodeB)''' with neo4j_driver.session() as session: result = session.run(query=cypher, parameters={ 'rel_id': relationship_id, 'attributes': attributes }) relationship_data = result.data() # Organise the data about the nodes in the relationship source_node = Node(node_id=relationship_data['ID(nodeA)'], labels=relationship_data['LABELS(nodeA)'], properties=relationship_data['nodeA']) target_node = Node(node_id=relationship_data['ID(nodeB)'], labels=relationship_data['LABELS(nodeB)'], properties=relationship_data['nodeB']) # Return Relationship response return Relationship( relationship_id=relationship_data['ID(relationship)'], relationship_type=relationship_data['TYPE(relationship)'], properties=relationship_data['PROPERTIES(relationship)'], source_node=source_node, target_node=target_node)
async def update_node(node_id: int, attributes: dict): # Check that property to update is not part of base list for key in attributes: if key in base_properties: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail= f"Operation not permitted, that property field cannot be updated.", headers={"WWW-Authenticate": "Bearer"}) cypher = '''MATCH (node) WHERE ID(node) = $id SET node += $attributes RETURN node, ID(node) as id, LABELS(node) as labels''' with neo4j_driver.session() as session: result = session.run(query=cypher, parameters={ 'id': node_id, 'attributes': attributes }) node_data = result.data()[0] # Return Node response return Node(node_id=node_data['id'], labels=node_data['labels'], properties=node_data['node'])
async def read_nodes(search_node_property: str, node_property_value: str, current_user: User = Depends(get_current_active_user)): ''' Retrieves data about a collection of nodes in the graph, based on node property. :param **node_property** (str) - property to search in nodes :param **node_property_value** (str) - value of property, to select the correct node :returns: Node response, with node id, labels, and properties. Returns only first response. ''' cypher = ( f'Match (node)\n' f'WHERE node.{search_node_property} = \'{node_property_value}\'\n' 'RETURN ID(node) as id, LABELS(node) as labels, node') with neo4j_driver.session() as session: result = session.run(query=cypher) collection_data = result.data() node_list = [] for node in collection_data: # Create node for each result in query node = Node(node_id=node['id'], labels=node['labels'], properties=node['node']) # Append each node result into Nodes list node_list.append(node) # Return Nodes response with collection as list return Nodes(nodes=node_list)
async def read_user(username: str): query = 'MATCH (user) WHERE user.username = $username RETURN user' with neo4j_driver.session() as session: user_in_db = session.run(query=query, parameters={'username':username}) user_data = user_in_db.data()[0]['user'] user_id = user_data['id'] return User(**user_data)
async def create_node(label: str, node_attributes: dict, permission: Optional[str] = None, current_user: User = Depends(get_current_active_user)): # Check that node is not User if label == 'User': raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail= f"Operation not permitted, cannot create a User with this method.", headers={"WWW-Authenticate": "Bearer"}) # Check that node has an acceptable label if label not in node_labels: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=f"Operation not permitted, node label is not accepted.", headers={"WWW-Authenticate": "Bearer"}) # Check that attributes dictionary does not modify base fields for key in node_attributes: if key in base_properties: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail= f"Operation not permitted, you cannot modify those fields with this method.", headers={"WWW-Authenticate": "Bearer"}) cypher = ( f'CREATE (new_node:{label})\n' 'SET new_node.created_by = $created_by\n' 'SET new_node.created_time = $created_time\n' 'SET new_node.permission = $permission\n' 'SET new_node += {attributes}\n' 'RETURN new_node, LABELS(new_node) as labels, ID(new_node) as id') with neo4j_driver.session() as session: result = session.run(query=cypher, parameters={ 'created_by': current_user.username, 'created_time': str(datetime.utcnow()), 'permission': permission, 'attributes': node_attributes }) node_data = result.data()[0] return Node(node_id=node_data['id'], labels=node_data['labels'], properties=node_data['new_node'])
async def reset_password(new_password: str, current_user: User = Depends(get_current_active_user)): # Get current user's username and encrypt new password username = current_user.username new_password_hash = create_password_hash(new_password) # Execute Cypher query to reset the hashed_password attribute cypher_reset_password = ('MATCH (user) WHERE user.username = $username\n' 'SET user.hashed_password = $new_password_hash\n' 'RETURN user') with neo4j_driver.session() as session: updated_user = session.run(query=cypher_reset_password, parameters={'username':username, 'new_password_hash':new_password_hash}) user_data = updated_user.data()[0]['user'] user = User(**user_data) return user
async def delete_node(node_id: int): cypher = '''MATCH (node) WHERE ID(node) = $node_id DELETE node''' with neo4j_driver.session() as session: result = session.run(query=cypher, parameters={'node_id': node_id}) node_data = result.data() # Confirm deletion was completed by empty response if not node_data: return { 'response': f'Node with ID: {node_id} was successfully deleted from the graph.' } else: return node_data
async def update_user(attributes: dict, username: str): # Add check to stop call if password is being changed for k in attributes: if k == 'hashed_password': raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Operation not permitted, cannot update password with this method.", headers={"WWW-Authenticate": "Bearer"}) # Execute Cypher query to reset the hashed_password attribute cypher_update_user = ('MATCH (user: User) WHERE user.username = $user\n' 'SET user += {dict_params}\n' 'RETURN user') with neo4j_driver.session() as session: updated_user = session.run(query=cypher_update_user, parameters={'user':username, 'dict_params':attributes}) user_data = updated_user.data()[0]['user'] user = User(**user_data) return user
async def delete_relationship(relationship_id: int): cypher = '''MATCH (a)-[relationship]->(b) WHERE ID(relationship) = $relationship_id DELETE relationship''' with neo4j_driver.session() as session: result = session.run(query=cypher, parameters={'relationship_id': relationship_id}) relationship_data = result.data() # Confirm deletion was completed by empty response if not relationship_data: return { 'response': f'Relationship with ID: {relationship_id} was successfully deleted from the graph.' } else: return relationship_data
async def cypher_query(cypher_string: str): with neo4j_driver.session() as session: response = session.run(query=cypher_string) query_response = Query(response=response.data()) return query_response
async def create_relationship( source_node_label: str, source_node_property: str, source_node_property_value: str, target_node_label: str, target_node_property: str, target_node_property_value: str, relationship_type: str, relationship_attributes: dict, permission: Optional[str] = None, current_user: User = Depends(get_current_active_user)): # Check that relationship has an acceptable type if relationship_type not in relationship_types: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail= f"Operation not permitted, relationship type is not accepted.", headers={"WWW-Authenticate": "Bearer"}) # Check that attributes dictionary does not modify base fields for key in relationship_attributes: if key in base_properties: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail= f"Operation not permitted, you cannot modify those fields with this method.", headers={"WWW-Authenticate": "Bearer"}) cypher = ( f'MATCH (nodeA:{source_node_label}) WHERE nodeA.{source_node_property} = $nodeA_property\n' f'MATCH (nodeB:{target_node_label}) WHERE nodeB.{target_node_property} = $nodeB_property\n' f'CREATE (nodeA)-[relationship:{relationship_type}]->(nodeB)\n' 'SET relationship.created_by = $created_by\n' 'SET relationship.created_time = $created_time\n' 'SET relationship.permission = $permission\n' 'SET relationship += {relationship_attributes}\n' 'RETURN nodeA, nodeB, LABELS(nodeA), LABELS(nodeB), ID(nodeA), ID(nodeB), ID(relationship), TYPE(relationship), PROPERTIES(relationship)' ) with neo4j_driver.session() as session: result = session.run(query=cypher, parameters={ 'created_by': current_user.username, 'created_time': str(datetime.utcnow()), 'permission': permission, 'nodeA_property': source_node_property_value, 'nodeB_property': target_node_property_value, 'relationship_attributes': relationship_attributes }) relationship_data = result.data()[0] # Organise the data about the nodes in the relationship source_node = Node(node_id=relationship_data['ID(nodeA)'], labels=relationship_data['LABELS(nodeA)'], properties=relationship_data['nodeA']) target_node = Node(node_id=relationship_data['ID(nodeB)'], labels=relationship_data['LABELS(nodeB)'], properties=relationship_data['nodeB']) # Return Relationship response return Relationship( relationship_id=relationship_data['ID(relationship)'], relationship_type=relationship_data['TYPE(relationship)'], properties=relationship_data['PROPERTIES(relationship)'], source_node=source_node, target_node=target_node)