def update_fields(db_object, values): """ Creates a query that updates `db_object` fields with the given values. Args: db_object (DbObject): The DB object being updated. values (dict): Maps Fields to new values. All Fields must be legit fields in `db_object`. Return: (query_string, query_parameters) """ type_name = db_object.type_name() id_param = "%sId" % type_name values_str = " ".join("%s: $%s" % (field.graphql_name, field.graphql_name) for field, _ in values.items()) params = { field.graphql_name: (value, field) for field, value in values.items() } params[id_param] = (db_object.uid, Entity.uid) query_str = """mutation update%sPyApi%s{update%s( where: {id: $%s} data: {%s}) {%s}} """ % ( utils.title_case(type_name), format_param_declaration(params), type_name, id_param, values_str, results_query_part(type(db_object))) return query_str, {name: value for name, (value, _) in params.items()}
def bulk_delete(db_objects, use_where_clause): """ Generates a query that bulk-deletes the given `db_objects` from the DB. Args: db_objects (list): A list of DB objects of the same type. use_where_clause (bool): If the object IDs should be passed to the mutation in a `where` clause or directly as a mutation value. """ type_name = db_objects[0].type_name() if use_where_clause: query_str = "mutation delete%ssPyApi{delete%ss(where: {%sIds: [%s]}){id}}" else: query_str = "mutation delete%ssPyApi{delete%ss(%sIds: [%s]){id}}" query_str = query_str % ( utils.title_case(type_name), utils.title_case(type_name), utils.camel_case(type_name), ", ".join('"%s"' % db_object.uid for db_object in db_objects)) return query_str, {}
def update_relationship(a, b, relationship, update): """ Updates the relationship in DB object `a` to connect or disconnect DB object `b`. Args: a (DbObject): The object being updated. b (DbObject): Object on the other side of the relationship. relationship (Relationship): The relationship from `a` to `b`. update (str): The type of update. Must be either `connect` or `disconnect`. Return: (query_string, query_parameters) """ to_one_disconnect = update == "disconnect" and \ relationship.relationship_type == Relationship.Type.ToOne a_uid_param = utils.camel_case(type(a).type_name()) + "Id" if not to_one_disconnect: b_uid_param = utils.camel_case(type(b).type_name()) + "Id" param_declr = "($%s: ID!, $%s: ID!)" % (a_uid_param, b_uid_param) b_query = "{id: $%s}" % b_uid_param else: param_declr = "($%s: ID!)" % a_uid_param b_query = "true" query_str = """mutation %s%sAnd%sPyApi%s{update%s( where: {id: $%s} data: {%s: {%s: %s}}) {id}} """ % ( utils.title_case(update), type(a).type_name(), type(b).type_name(), param_declr, utils.title_case(type(a).type_name()), a_uid_param, relationship.graphql_name, update, b_query) if to_one_disconnect: params = {a_uid_param: a.uid} else: params = {a_uid_param: a.uid, b_uid_param: b.uid} return query_str, params
def update(self, **kwargs): """ Updates this DB object with new values. Values should be passed as key-value arguments with field names as keys: >>> db_object.update(name="New name", title="A title") Kwargs: Key-value arguments defining which fields should be updated for which values. Keys must be field names in this DB object's type. Raise: InvalidAttributeError: if there exists a key in `kwargs` that's not a field in this object type. """ values = {self.field(name): value for name, value in kwargs.items()} invalid_fields = set(values) - set(self.fields()) if invalid_fields: raise InvalidAttributeError(type(self), invalid_fields) query_string, params = query.update_fields(self, values) res = self.client.execute(query_string, params) res = res["update%s" % utils.title_case(self.type_name())] self._set_field_values(res)
def relationship(source, relationship, where, order_by): """ Constructs a query that fetches all items from a -to-many relationship. To be used like: >>> project = ... >>> query_str, params = relationship(Project, "datasets", Dataset) >>> datasets = PaginatedCollection( client, query_str, params, ["project", "datasets"], Dataset) The resulting query is intended to be used for pagination, it contains two python-string int-placeholders (%d) for 'skip' and 'first' pagination parameters. Args: source (DbObject or type): If a `DbObject` then the source of the relationship (the query originates from that particular object). If `type`, then the source of the relationship is implicit, even without the ID. Used for expanding from Organization. relationship (Relationship): The relationship. where (Comparison, LogicalExpression or None): The `where` clause for filtering. order_by (None or (Field, Field.Order): The `order_by` clause for sorting results. Return: (str, dict) tuple that is the query string and parameters. """ check_where_clause(relationship.destination_type, where) check_order_by_clause(relationship.destination_type, order_by) to_many = relationship.relationship_type == Relationship.Type.ToMany subquery = Query(relationship.graphql_name, relationship.destination_type, where, to_many, order_by) query_where = type(source).uid == source.uid if isinstance(source, Entity) \ else None query = Query(utils.camel_case(source.type_name()), subquery, query_where) return query.format_top("Get" + source.type_name() + utils.title_case(relationship.graphql_name))
def test_title(): assert utils.title_case(SNAKE) == TITLE assert utils.title_case(TITLE) == TITLE assert utils.title_case(CAMEL) == TITLE assert utils.title_case(MIXED) == TITLE