def custom_validation(cls, data, outv_id=None, inv_id=None, outv_label=None, inv_label=None): """ Provides validation to confirm that 1) A coreVertex is only ever owned by one team/coreVertex at a time 2) A coreVertex can only be owned by a coreVertex whose Template has the canHaveChildren property set to True """ existing_edge_q = f"g.V().has('{inv_label}', 'id', '{inv_id}')" + \ f".inE('owns')" existing_edge = client.submit(existing_edge_q).all().result() if existing_edge: raise CustomValidationFailedException( "CoreVertex can only be owned by a single parent at a time!") # Checking for the template's canHaveChildren property if the # parent is a coreVertex if outv_label == "coreVertex": parent_template_query = \ f"g.V().has('{outv_label}', 'id', '{outv_id}')" + \ f".out('{CoreVertexInheritsFromTemplate.LABEL}')" parent_template = Template.vertex_to_instance( client.submit(parent_template_query).all().result()[0]) if parent_template.canHaveChildren != "True": raise CustomValidationFailedException( "CoreVertex can only be owned by a CoreVertex that has" " it's template's canHaveChildren property set to `true`!") return data
def get_team_owner(self, team_id): """ Returns the Account instance that owns the given team """ query = f"g.V().hasLabel('{core.Team.LABEL}').has('id', '{team_id}')" + \ f".in('{AccountOwnsTeam.LABEL}')" owner = client.submit(query).all().result() return Account.vertex_to_instance(owner[0])
def all_team_templates(cls, team_id): """ Return all templates under the given team """ query = f"g.V().has('{Team.LABEL}', 'id', '{team_id}')" + \ f".out('{cls.LABEL}')" results = client.submit(query).all().result() return [Template.vertex_to_instance(i) for i in results]
def get_templates_with_details(cls, team_id): """ Returns the all Template instance owned by the given team with the required details; - id, name, topicsCount -- SERIALIZED """ query = f"g.V().has('{Team.LABEL}', 'id', '{team_id}')" + \ f".out('{TeamOwnsTemplate.LABEL}').hasLabel('{Template.LABEL}')" + \ f".project('id', 'name', 'topicsCount', 'properties')" + \ f".by(values('id')).by(values('name'))" + \ f".by(inE('{CoreVertexInheritsFromTemplate.LABEL}').outV()" + \ f".hasLabel('{CoreVertex.LABEL}').count())" + \ f".by(outE('{TemplateHasProperty.LABEL}').inV().fold())" result = client.submit(query).all().result() templates = [] for template in result: properties = [ TemplateProperty.vertex_to_instance(i) for i in template["properties"] ] template["properties"] = [] for prop in properties: template["properties"].append({ "id": prop.id, "name": prop.name, "fieldType": prop.fieldType, "propertyOptions": prop.propertyOptions, "index": prop.index }) templates.append(template) return templates
def get_user_permissions(self, user_id): """ Returns all roles assigned to the given user for this CoreVertex as a dictionary of {direct_role: <edge>, indirect_roles: [edges...]} """ # This will return all of the permissions the user has for all of the # vertices in this vertex's path to the root query = f"g.V().has('{self.LABEL}', 'id', '{self.id}')" + \ f".until(__.hasLabel('{Team.LABEL}'))" + \ f".repeat(__.in('{CoreVertexOwnership.LABEL}')).path()" + \ f".unfold().inE('{auth.UserAssignedToCoreVertex.LABEL}')" + \ f".as('e').outV().has('{auth.User.LABEL}', 'id', '{user_id}')" + \ f".select('e')" results = client.submit(query).all().result() roles = {"indirect_roles": [], "direct_role": None} for edge in results: if edge["inV"] == self.id: roles["direct_role"] = auth.UserAssignedToCoreVertex \ .edge_to_instance(edge).role else: roles["indirect_roles"].append( auth.UserAssignedToCoreVertex.edge_to_instance(edge).role) return roles
def get_template(cls, vertex_type, vertex_id, template_id): """ Returns the template owned by the given: "Team" if the vertex_type is a `team`, or "CoreVertex's Root Level Team" if the vertex_type is a `coreVertex` """ base_template_query = f".out('{TeamOwnsTemplate.LABEL}')" + \ f".has('id', '{template_id}')" if vertex_type == "team": # If this node is the team (root), we can just check if it has an # outgoing edge to this template query = f"g.V().has('{Team.LABEL}', 'id', '{vertex_id}')" + \ base_template_query else: # Otherwise, we have to find the Team (Root) for this CoreVertex, # and then check if that team has the template query = f"g.V().has('{CoreVertex.LABEL}', 'id', '{vertex_id}')" + \ f".repeat(__.in('{CoreVertexOwnership.LABEL}'))" + \ f".until(__.hasLabel('{Team.LABEL}'))" + \ base_template_query template = client.submit(query).all().result() if not template: return None return Template.vertex_to_instance(template[0])
def get_template_with_properties(cls, template_id, parent_team_id=None): """ Returns the template and the template properties belonging to it in a single query """ query = "g.V()" # Prepending a team query if the team id arg is provided if parent_team_id: query += f".has('{Team.LABEL}', 'id', " + \ f"'{parent_team_id}').out('{TeamOwnsTemplate.LABEL}')" query += f".has('{Template.LABEL}', 'id', '{template_id}')" + \ f".fold().project('template', 'properties').by(unfold())" + \ f".by(unfold().out('{TemplateHasProperty.LABEL}').fold())" try: # This raises a 597 error if there's nothing found res = client.submit(query).all().result() except: return None template = Template.vertex_to_instance(res[0]["template"]) template.properties = [ TemplateProperty.vertex_to_instance(i) for i in res[0]["properties"] ] return template
def list_messages(node_id, start=0, end=10, date_filter="gt", filter_date=None): """ Returns all messages sent against the given node since the given `since` datetime object NOTE: Serialized """ query = f"g.V().has('id', '{node_id}')" + \ f".out('{NodeHasMessage.LABEL}').order().by('sent_at')" if date_filter and filter_date: query += f".has('sent_at', {date_filter}('{filter_date.isoformat()}'))" query += f".project('id', 'text', 'sent_at', 'author')" + \ f".by(values('id'))" + \ f".by(values('text'))" +\ f".by(values('sent_at'))" + \ f".by(inE('{UserSentMessage.LABEL}').outV())" result = client.submit(query).all().result() if result: result = result[start:end] messages = [] for item in result: msg = Message(id=item["id"], text=item["text"], sent_at=item["sent_at"]) msg.author = auth.User.vertex_to_instance(item["author"]) messages.append(msg) return messages return result
def get_teams(cls, account_id): """ Returns all teams owned by this account id """ query = f"g.V().hasLabel('{Account.LABEL}')" + \ f".has('id', '{account_id}').out('{cls.LABEL}')" results = client.submit(query).all().result() return [core.Team.vertex_to_instance(i) for i in results]
def get_favorite_nodes(cls, user_id, parent_id=None): """ Returns all of the favorite nodes for the given user that he has access to """ if parent_id: query = f"g.V().has('id', '{parent_id}')" + \ f".emit()" + \ f".until(out('{CoreVertexOwnership.LABEL}').count().is(0))" + \ f".repeat(out('{CoreVertexOwnership.LABEL}'))" else: query = f"g.V().hasLabel('{CoreVertex.LABEL}')" query += f".as('cv').in('{cls.LABEL}').has('id', '{user_id}')" + \ f".select('cv')" result = client.submit(query).all().result() nodes = [] for node in result: if node["label"] == "coreVertex": nodes.append(CoreVertex.vertex_to_instance(node)) else: nodes.append(Team.vertex_to_instance(node)) return nodes
def get_account_admins(cls, account_id): """ Returns all Users that are admins of the given account """ query = f"g.V().has('{Account.LABEL}', 'id', '{account_id}')" + \ f".in('{cls.LABEL}')" result = client.submit(query).all().result() return [User.vertex_to_instance(i) for i in result]
def get_all_template_inheritors(cls, template_id): """ Returns all vertices that inherit from this template id """ query = f"g.V().has('{Template.LABEL}', 'id', '{template_id}')" + \ f".in('{cls.LABEL}')" res = client.submit(query).all().result() return [CoreVertex.vertex_to_instance(i) for i in res]
def get_template_properties(cls, template_id): """ Returns all TemplateProperties belonging to the given template """ query = f"g.V().has('{Template.LABEL}', 'id', '{template_id}')" + \ f".out('{cls.LABEL}')" res = client.submit(query).all().result() return [TemplateProperty.vertex_to_instance(i) for i in res]
def get_users(self): """ Returns all users who "hold" this account through the UserHoldsAccount edge """ query = f"g.V().has('{self.LABEL}', 'id', '{self.id}')" + \ f".in('{UserHoldsAccount.LABEL}').hasLabel('{User.LABEL}')" r = client.submit(query).all().result() return [User.vertex_to_instance(i) for i in r]
def get_last_checked_time(user_id, node_id): """ Returns the time that this user last checked messages """ query = f"g.V().has('{auth.User.LABEL}', 'id', '{user_id}')" + \ f".outE('{UserLastCheckedMessage.LABEL}').as('e')" + \ f".inV().has('id', '{node_id}').select('e').order()" + \ f".by('time', decr)" result = client.submit(query).all().result() if result: last_read = UserLastCheckedMessage.edge_to_instance(result[0]) return last_read return None
def update_properties_index(cls, property_ids): """ Receives a list of property IDs, and updates all of their index fields with their index in the given list """ query = f"g.V().hasLabel('{cls.LABEL}').choose(id())" for index, prop_id in enumerate(property_ids): query += f".option('{prop_id}', property('index', {index}))" result = client.submit(query).all().result() return [cls.vertex_to_instance(i) for i in result]
def get_root(cls, core_vertex_id): """ Returns the team vertex at the base of this core-vertex ownership tree [UNTESTED] """ query = \ f"g.V().has('{CoreVertex.LABEL}', 'id', '{core_vertex_id}')" + \ f".until(hasLabel('{Team.LABEL}')).repeat(out('{cls.LABEL}'))" team = client.submit(query).all().result()[0] return Team.vertex_to_instance(team)
def get_user_permissions(self, user_id): """ Returns the roles assigned to the given user for this Team """ query = f"g.V().has('{self.LABEL}', 'id', '{self.id}')" + \ f".inE('{auth.UserAssignedToCoreVertex.LABEL}').as('e')" + \ f".outV().has('{auth.User.LABEL}', 'id', '{user_id}')" + \ f".select('e')" result = client.submit(query).all().result() return auth.UserAssignedToCoreVertex.edge_to_instance(result[0]).role \ if result else None
def update(cls, validated_data={}, vertex_id=None): """ Updates the template through the base `update` method, as well as added functionality for adding (DROPPING + RECREATING) the properties """ template_properties = validated_data.pop("properties", []) template = super().update(validated_data=validated_data, vertex_id=vertex_id) # Dropping all template properties and recreating them if template_properties: drop_query = f"g.V().has('{cls.LABEL}', 'id', '{vertex_id}')" + \ f".out('{TemplateHasProperty.LABEL}').drop()" client.submit(drop_query) # Recreating the provided template_properties create_query = "g.V()" + \ f".has('{Template.LABEL}', 'id', '{template.id}').as('t')" for prop in template_properties: # Vertex Create + partition key query create_query += f".addV('{TemplateProperty.LABEL}')" + \ f".property('{DATABASE_SETTINGS['partition_key']}', " + \ f"'{TemplateProperty.LABEL}')" # A property call for each field for field, field_type in TemplateProperty.properties.items(): create_query += f".property('{field}', '{prop[field]}')" # The edge linking the template to the property create_query += f".addE('{TemplateHasProperty.LABEL}')" + \ ".from('t')" # Selecting all created properties at the end of the query create_query += f".outV().out('{TemplateHasProperty.LABEL}')" res = client.submit(create_query).all().result() template.properties = [ TemplateProperty.vertex_to_instance(i) for i in res ] return template
def bulk_update_template_data(cls, nodes): """ Bulk updates the template data property for each node in the array - format for nodes must be: { nodeId: templateDataString } """ query = f"g.V().hasLabel('{cls.LABEL}').choose(id())" for node in nodes: query += f".option('{node['id']}', " + \ f"property('templateData', '{node['templateData']}'))" result = client.submit(query).all().result() return result
def get_children_tree(parent_id, user_id): """ Returns the children of the given parent node in a tree view list format as [ {'name': '...', 'children': [...]} ] with their direct sub-children - Also returns an isFavorite value for each child """ query = f"g.V().has('id', '{parent_id}').out('owns')" + \ f".hasLabel('{CoreVertex.LABEL}').as('children')" + \ f".select('children').by(project('topChild', 'template', 'sub_children')" + \ f".by()" + \ f".by(outE('{CoreVertexInheritsFromTemplate.LABEL}').inV())" + \ f".by(outE('{CoreVertexOwnership.LABEL}').inV()" + \ f".hasLabel('{CoreVertex.LABEL}').as('subchild')" + \ f".outE('{CoreVertexInheritsFromTemplate.LABEL}').inV()" + \ f".as('subchildTemplate').select('subchild')" + \ f".map(outE('{CoreVertexOwnership.LABEL}').inV()" + \ f".hasLabel('{CoreVertex.LABEL}').count()).as('childCount')" + \ f".select('subchild', 'subchildTemplate', 'childCount').fold()))" tree = [] favorite_nodes = UserFavoriteNode.get_favorite_nodes( user_id, parent_id=parent_id) favorite_nodes = [i.id for i in favorite_nodes] # Raises an exception if there are NO direct children try: result = client.submit(query).all().result() except: return tree for child_data in result: child = CoreVertex.vertex_to_instance(child_data["topChild"]) child.template = CoreVertex.vertex_to_instance( child_data["template"]) child.isFavorite = child.id in favorite_nodes sub_children = [] for sub_child in child_data["sub_children"]: cv = CoreVertex.vertex_to_instance(sub_child["subchild"]) cv.isFavorite = cv.id in favorite_nodes cv.template = Template.vertex_to_instance( sub_child["subchildTemplate"]) if sub_child["childCount"] > 0: cv.children = [] sub_children.append(cv) if sub_children: child.children = sub_children tree.append(child) return tree
def get_teams_with_detail(cls, account_id, user_id): """ Returns all teams owned by the given account id with its own properties as well as the following details: - templatesCount, - member (array) - topicsCount -- Returns only the teams the user has access to -- SERIALIZED """ query = f"g.V().has('{auth.Account.LABEL}', 'id', '{account_id}')" + \ f".out('{auth.AccountOwnsTeam.LABEL}').as('team')" + \ f".inE('{auth.UserAssignedToCoreVertex.LABEL}').outV()" + \ f".as('member').select('team')" + \ f".project('templatesCount', 'name', 'id', 'member', 'topicsCount')" + \ f".by(outE('{TeamOwnsTemplate.LABEL}').inV()" + \ f".hasLabel('{Template.LABEL}').count())" + \ f".by(values('name'))" + \ f".by(values('id'))" + \ f".by(select('member'))" + \ f".by(outE('{TeamOwnsTemplate.LABEL}').inV()" + \ f".hasLabel('{Template.LABEL}')" + \ f".inE('{CoreVertexInheritsFromTemplate.LABEL}').count())" result = client.submit(query).all().result() teams = {} for team in result: if team["id"] not in teams: teams[team["id"]] = { "id": team["id"], "name": team["name"], "templatesCount": team["templatesCount"], "topicsCount": team["topicsCount"], "members": [] } member = { "id": team["member"]["id"], "email": team["member"]["properties"]["email"][0]["value"], "avatarLink": "" # [TODO] } if member not in teams[team["id"]]["members"]: teams[team["id"]]["members"].append(member) # Filtering to only teams that this user is a member of teams = list(teams.values()) teams = [ team for team in teams if user_id in [member["id"] for member in team["members"]] ] return teams
def get_held_accounts(cls, user_id, initialize_models=False): """ Returns all accounts "held by" (edge) this user or the accounts that the user has access to at least one of the teams in If the `initialize_models` arg is True, the vertexes are converted into Account models before being returned; otherwise they're returned as IDs """ user_accounts_q = f"g.V().has('{cls.LABEL}', 'id', '{user_id}')" + \ f".as('u').out('{UserHoldsAccount.LABEL}')" + \ f".hasLabel('{Account.LABEL}')" + \ f".store('accounts').select('u')" + \ f".out('{UserAssignedToCoreVertex.LABEL}')" + \ f".hasLabel('team').in('{AccountOwnsTeam.LABEL}')" + \ f".store('accounts').cap('accounts')" + \ f".unfold().dedup()" user_accounts = client.submit(user_accounts_q).all().result() if initialize_models: account_ids = ','.join([f"'{i['id']}'" for i in user_accounts]) admins_query = f"g.V().has('id', within({account_ids}))" + \ f".inE('{UserIsAccountAdmin.LABEL}')" + \ f".project('inV', 'outV')" + \ f".by(inV()).by(outV())" admins = client.submit(admins_query).all().result() accounts = [] for account in user_accounts: account = Account.vertex_to_instance(account) account.admins = list( map(lambda x: User.vertex_to_instance(x["outV"]), filter(lambda x: x["inV"]["id"] == account.id, admins))) accounts.append(account) return accounts else: return [i["id"] for i in user_accounts]
def get_user_assigned_role(cls, core_vertex_id, user_id, inv_label=None): """ Returns the role (UserAssignedToCoreVertex instance) assigned to the user for the given core-vertex """ INV_LABEL = inv_label or cls.INV_LABEL query = f"g.V().hasLabel('{INV_LABEL}')" + \ f".has('id', '{core_vertex_id}').inE('{cls.LABEL}').as('e')" + \ f".outV().has('id', '{user_id}').select('e')" result = client.submit(query).all().result() if result: result = result[0] edge = UserAssignedToCoreVertex.edge_to_instance(result) return edge return None
def get_children(cls, parent_id, parent_type, template_id=None): """ Returns all DIRECT children coreVertices under the given parent """ query = f"g.V().has('{parent_type}', 'id', '{parent_id}')" + \ f".out('{cls.LABEL}').hasLabel('{CoreVertex.LABEL}')" if template_id: query += f".as('cv')" + \ f".out('{CoreVertexInheritsFromTemplate.LABEL}')" + \ f".has('id', '{template_id}').select('cv')" result = client.submit(query).all().result() return [CoreVertex.vertex_to_instance(i) for i in result]
def test_user_registration_with_duplicate_email(self): """ Tests the user registration endpoint to confirm that a user can't be created with an existing email """ # Creating the base user that will be tested against result = client.submit(self.create_query).all().result() assert result, ValueError("Test Error - Could not " "create user manually!") # Changing the username to confirm the email duplication is checked self.user_details["username"] = "******" r = self.client.post(self.url, json=self.user_details) self.assertEqual(r.status_code, 400) self.assertEqual(r.json["error"], "User already exists!")
def custom_validation(cls, data, outv_id=None, inv_id=None, outv_label=None, inv_label=None): """ Provides custom validation to confirm that: 1) A Core Vertex only inherits from one template at a time and 2) Core Vertex only inherits froom a template owned by the vertex's team [TODO] """ # Verification for [1] existing_template_query = \ f"g.V().has('{cls.OUTV_LABEL}', 'id', '{outv_id}')" + \ f".out('{cls.LABEL}')" existing_template = client.submit( existing_template_query).all().result() if existing_template: raise CustomValidationFailedException( "A CoreVertex can only inherit from a single Template " "at a time") # Verification for [2] team_template_query = \ f"g.V().has('{CoreVertex.LABEL}', 'id', '{outv_id}')" + \ f".until(hasLabel('{Team.LABEL}'))" + \ f".repeat(__.in('{CoreVertexOwnership.LABEL}')).emit()" + \ f".out('{TeamOwnsTemplate.LABEL}')" + \ f".has('{Template.LABEL}', 'id', '{inv_id}')" template_exists = client.submit(team_template_query).all().result() if not template_exists: raise CustomValidationFailedException( "The provided template either doesn't exist, or is not owned" "by the same team as the CoreVertex") return data
def get_account_with_admins(cls, account_id): """ Returns the account details along with the admins of this account """ query = f"g.V().has('{cls.LABEL}', 'id', '{account_id}')" + \ f".fold().project('account', 'admins')" + \ f".by(unfold())" + \ f".by(unfold().inE('{UserIsAccountAdmin.LABEL}')" + \ f".outV().fold())" result = client.submit(query).all().result() if not result: return [] result = result[0] account = Account.vertex_to_instance(result["account"]) account.admins = [User.vertex_to_instance(i) for i in result["admins"]] return account
def custom_validation(cls, data, outv_id=None, inv_id=None, outv_label=None, inv_label=None): """ Provides validation to confirm that: 1) A template is only ever owned by one team at a time """ existing_edge_q = f"g.V().has('{cls.INV_LABEL}', 'id', '{inv_id}')" + \ f".inE('owns')" existing_edge = client.submit(existing_edge_q).all().result() if existing_edge: raise CustomValidationFailedException( "Template can only be owned by a single template at a time!") return data
def put(self, vertex=None, vertex_id=None, **kwargs): """ Changes the parent of the given core vertex after removing existing parent edge """ data = json.loads(request.data) if "newParent" not in data: return jsonify_response({ "status": "New parent not specified" }, 400) # Moving all of the direct children to the existing parent IF # the node has been made a child of one of it's older children existing_data_query = f"g.V().has('id', '{vertex.id}')" + \ f".project('existingParent', 'newParent', 'directChildren')" + \ f".by(inE('{CoreVertexOwnership.LABEL}').outV())" + \ f".by(until(has('id', '{data['newParent']}').or().loops().is(30))" + \ f".repeat(out('{CoreVertexOwnership.LABEL}')).fold())" + \ f".by(outE('{CoreVertexOwnership.LABEL}').inV().fold())" existing_data = client.submit(existing_data_query).all().result()[0] old_parent_id = existing_data["existingParent"]["id"] existing_direct_children = [CoreVertex.vertex_to_instance(i) for i in existing_data["directChildren"]] new_parent_is_child = len(existing_data["newParent"]) >= 1 print("Existing Data", existing_data) # Updating first level children to have an edge to the old parent # if the new parent is a sub-child of the moved node if new_parent_is_child: print("NEW PARENT IS CHILD") children_ids = ",".join( [f"'{i.id}'" for i in existing_direct_children]) children_relocate_query = f"g.V().has('id', within({children_ids}))" + \ f".inE('{CoreVertexOwnership.LABEL}').drop()" client.submit(children_relocate_query).all().result() children_relocate_query = f"g.V().has('id', '{old_parent_id}')" for index, child in enumerate(existing_direct_children): children_relocate_query += f".addE('{CoreVertexOwnership.LABEL}')" + \ f".to(g.V().has('id', '{child.id}'))" children_relocate_query += ".outV()" client.submit(children_relocate_query).all().result() # Removing the existing parent edge, and adding the new edge query = f"g.V().has('id', '{vertex.id}').as('node')" + \ f".inE('{CoreVertexOwnership.LABEL}').as('existingEdge')" + \ f".inV().addE('{CoreVertexOwnership.LABEL}')" + \ f".from(g.V().has('id', '{data['newParent']}'))" + \ f".select('existingEdge').drop()" res = client.submit(query).all().result() return jsonify_response({ "status": "Success" }, 200)