def perform_move(self): parent_canonical_url = self._read_parent_canonical_url() from_tree = parse_url(self.from_path, self.schema.root) aggregate_query, from_spec, is_aggregate = from_tree.aggregation(None) aggregate_query.extend([ {"$project": { "_parent_type": self.parent_spec_name, "_parent_id": self.parent_id, "_parent_field_name": self.parent_field_name, "_parent_canonical_url": parent_canonical_url or "/", "_canonical_url": { "$concat": [parent_canonical_url, "/", self.parent_field_name, "/ID", {"$toString": "$_id"}], } }}, {"$merge": { "into": "resource_%s" % from_tree.spec.name, "on": "_id", "whenMatched": "merge", "whenNotMatched": "discard", }}, ]) from_tree.root_collection().aggregate(aggregate_query)
def get_spec_for(self, path, user=None): path = path.strip().strip('/') tree = parse_url(path, self.schema.root) aggregate_query, spec, is_aggregate = tree.aggregation(None, user) return ( spec, is_aggregate, type(tree) in (CollectionResourceRef, RootResourceRef), type(tree) in (LinkCollectionResourceRef, ), )
def affected_ids(self): from_tree = parse_url(self.from_path, self.schema.root) from_agg, from_spec, _ = self.from_path_agg() affected_ids = set() for calc_spec_name, calc_field_name, aggregation in self.affected_aggs(): affected_aggregation = from_agg + aggregation affected_aggregation.append({"$project": {"_id": True}}) cursor = from_tree.root_collection().aggregate( affected_aggregation) found = set((calc_spec_name, calc_field_name, r['_id']) for r in cursor) affected_ids.update(found) return affected_ids
def affected_aggs_to_path(self): to_tree = parse_url(self.to_path, self.schema.root) to_agg, to_spec, _ = to_tree.aggregation(None) aggs = [] for (calc_spec_name, calc_field_name), calc_tree in self.schema.calc_trees.items(): if to_tree.get_resource_dependencies().intersection(calc_tree.get_resource_dependencies()): aggregations = ReverseAggregator(self.schema).get_for_resource(calc_tree, to_spec.name, None, calc_spec_name, calc_field_name) for aggregation in aggregations: if aggregation: aggs.append((calc_spec_name, calc_field_name, aggregation)) return aggs
def execute(self): from_tree = parse_url(self.from_path, self.schema.root) aggregate_query, spec, is_aggregate = from_tree.aggregation(None) update_ids = [r['_id'] for r in from_tree.root_collection().aggregate(aggregate_query)] # add to new linkcollection # remove from original linkcollection # TODO: perform updates for create / delete return None
def execute(self): # collect ids affected_ids = self.affected_ids() # perform update self.perform_move() affected_ids_to_path = self.affected_ids_to_path() affected_ids.update(affected_ids_to_path) # perform update from_tree = parse_url(self.from_path, self.schema.root) spec = from_tree.spec for calc_spec_name, calc_field_name, affected_id in affected_ids: affected_id = self.schema.encodeid(affected_id) self.updater.update_calc(calc_spec_name, calc_field_name, affected_id) self.updater._recalc_for_field_update(spec, calc_spec_name, calc_field_name, affected_id) return None
def get(self, path, args=None, user=None): args = args or {} expand = args.get('expand') page = int(args.get('page', 0)) page_size = int(args.get('page_size', 10)) expand_dict = create_expand_dict(expand) path = path.strip().strip('/') if not path: return self._get_root() try: tree = parse_url(path, self.schema.root) except SyntaxError as te: return None aggregate_query, spec, is_aggregate = tree.aggregation(None) if path.split('/')[0] == 'ego': aggregate_query = [{ "$match": { "username": user.username } }] + aggregate_query if is_aggregate: page_agg = self.create_pagination_aggregations(page, page_size) if expand: if user and self._check_expand_ego_grants( path, expand_dict, user.read_grants): expand_agg = self.create_field_expansion_aggregations( spec, expand_dict) else: expand_agg = self.create_field_expansion_aggregations( spec, expand_dict, user) page_agg['$facet']["results"].extend(expand_agg) aggregate_query.append(page_agg) # run mongo query from from root_resource collection cursor = tree.root_collection().aggregate(aggregate_query) page_results = next(cursor) results = list(page_results['results']) count = page_results['count'][0]['total'] if page_results[ 'count'] else 0 if user and count: # TODO: also need to check read access to target if link # checking for /ego paths first, then all other paths self._check_grants(path, results[0]['_canonical_url'], user.read_grants) return { "results": [ self.encode_resource(spec, row, expand_dict) for row in results ], "count": count, "next": self._next_link(path, args, count, page, page_size), "previous": self._previous_link(path, args, count, page, page_size), '_meta': { 'spec': { 'name': spec.name, }, 'is_collection': True, 'can_create': type(tree) in (CollectionResourceRef, OrderedCollectionResourceRef, RootResourceRef), 'can_link': type(tree) in (LinkCollectionResourceRef, ), } } else: if expand: if user and self._check_expand_ego_grants( path, expand_dict, user.read_grants): expand_agg = self.create_field_expansion_aggregations( spec, expand_dict) else: expand_agg = self.create_field_expansion_aggregations( spec, expand_dict, user) aggregate_query.extend(expand_agg) # run mongo query from from root_resource collection cursor = tree.root_collection().aggregate(aggregate_query) result = next(cursor, None) if user and result: # TODO: also need to check read access to target if link # checking for /ego paths first, then all other paths self._check_grants(path, result['_canonical_url'], user.read_grants) if result: return self.encode_resource(spec, result, expand_dict) else: return None
def put(self, path, data, user=None): path = path.strip().strip('/') from_path = data['_from'].strip('/') if data.get('_from') else None at_index = data.get('_at') if '/' in path: parent_path, field_name = path.rsplit('/', 1) try: tree = parse_canonical_url(parent_path, self.schema.root) except SyntaxError as te: raise HTTPError('', 404, "Not Found", None, None) aggregate_query, spec, is_aggregate = tree.aggregation(None) field_spec = spec.fields[field_name] if path.split('/')[0] == 'ego': aggregate_query = [{ "$match": { "username": user.username } }] + aggregate_query # if we're using a simplified parser we can probably just pull the id off the path cursor = tree.root_collection().aggregate(aggregate_query) parent_resource = next(cursor) # check permissions if user: self._check_grants( path, os.path.join(parent_resource['_canonical_url'], field_name), user.put_grants) self._check_grants(from_path, from_path, user.read_grants) self._check_grants(from_path, from_path, user.delete_grants) # do put update try: parse_url(from_path, self.schema.root) except SyntaxError as te: raise HTTPError('', 400, from_path, None, None) if field_spec.field_type == 'collection': return self.updater.move_resource(parent_resource['_id'], spec.name, field_name, path, from_path) else: raise HTTPError('', 400, from_path, None, None) else: if path not in self.schema.root.fields: raise HTTPError('', 404, "Not Found", None, None) root_field_spec = self.schema.root.fields[path] root_spec = self.schema.specs[root_field_spec.target_spec_name] # check permissions if user: self._check_grants(path, path, user.put_grants) self._check_grants(from_path, from_path, user.read_grants) self._check_grants(from_path, from_path, user.delete_grants) if root_field_spec.target_spec_name == 'user': data['password'] = generate_password_hash(data['password']) # do put update try: if from_path: parse_url(from_path, self.schema.root) except SyntaxError as te: raise HTTPError('', 400, from_path, None, None) return self.updater.move_resource(None, 'root', path, path, from_path)
def to_path_agg(self): to_tree = parse_url(self.to_path, self.schema.root) aggregate_query, to_spec, is_aggregate = to_tree.aggregation(None) return aggregate_query, to_spec, is_aggregate
def from_path_agg(self): from_tree = parse_url(self.from_path, self.schema.root) aggregate_query, from_spec, is_aggregate = from_tree.aggregation(None) return aggregate_query, from_spec, is_aggregate