Exemple #1
0
    def fset(self, resource: 'Resource', new_value):
        """
        Set resource state data, calling the resolver's on_set callbak.
        """
        resolver = self.resolver
        target_class = self.resolver.target
        target_schema = target_class.ravel.schema
        old_value = resource.internal.state.get(resolver.name)

        # cast plain lists and dicts to target Resource or Batch objects
        if self.resolver.many:
            if not is_batch(new_value):
                assert is_sequence(new_value)
                new_value = self.resolver.target.Batch(
                    target_class(x) if isinstance(x, dict) else x
                    for x in new_value)
        elif isinstance(new_value, dict) \
                and resolver.name not in target_schema.fields:
            new_value = self.resolver.target(new_value)

        # write new value to instance state
        resource.internal.state[resolver.name] = new_value

        # trigger on_set callback
        resolver.on_set(resource, old_value, new_value)
Exemple #2
0
 def _recurse_on_entity(self, entity: 'Entity', links: Dict):
     if is_batch(entity):
         rel_resources = entity
     else:
         rel_resources = [entity]
     for rel_resource in rel_resources:
         self._dump_recursive(rel_resource, links)
Exemple #3
0
    def build(self, source) -> 'Query':
        query = self.right_loader_property.resolver.owner.select()

        if is_resource(source):
            source_value = getattr(source, self.left_field.name)
            if not source_value:
                console.debug(
                    message=(f'relationship {get_class_name(source)}.'
                             f'{self.relationship.name} aborting execution'
                             f'because {self.left_field.name} is None'),
                    data={
                        'resource': source._id,
                    })
                # NOTE: ^ if you don't want this, then clear the field from
                # source using source.clean(field_name)
                return None
            query.where(self.right_loader_property == source_value)
        else:
            assert is_batch(source)
            left_field_name = self.left_field.name
            source_values = {getattr(res, left_field_name) for res in source}
            if not source_values:
                console.warning(
                    message=(f'relationship {get_class_name(source)}.'
                             f'{self.relationship.name} aborting query '
                             f'because {self.left_field.name} is empty'),
                    data={
                        'resources': source._id,
                    })
                # NOTE: ^ if you don't want this, then clear the field from
                # source using source.clean(field_name)
                return None
            query.where(self.right_loader_property.including(source_values))

        return query
Exemple #4
0
def _dump_result_obj(obj):
    if is_resource(obj) or is_batch(obj):
        return obj.dump()
    elif isinstance(obj, (list, set, tuple)):
        return [_dump_result_obj(x) for x in obj]
    elif isinstance(obj, dict):
        return {k: _dump_result_obj(v) for k, v in obj.items()}
    else:
        return obj
Exemple #5
0
    def resolve(self, entity: 'Entity', request=None):
        if request is None:
            request = Request(self)

        if is_resource(entity):
            return self.resolve_resource(entity, request)
        else:
            assert is_batch(entity)
            return self.resolve_batch(entity, request)
Exemple #6
0
 def process_value(obj):
     if is_resource(obj):
         if obj._id is not None:
             return obj._id
         else:
             return obj.internal.state
     elif is_batch(obj):
         raise NotImplemented(
             'TODO: implement support for batch objects')
     else:
         return obj
Exemple #7
0
    def __init__(
        self,
        target=None,
        name=None,
        owner=None,
        decorator=None,
        on_resolve=None,
        on_resolve_batch=None,
        on_select=None,
        on_save=None,
        on_set=None,
        on_get=None,
        on_delete=None,
        on_dump=None,
        private=False,
        nullable=True,
        required=False,
        immutable=False,
        many=False,
    ):
        self.name = name
        self.owner = owner
        self.private = private
        self.nullable = nullable
        self.decorator = decorator
        self.required = required
        self.target_callback = None
        self.target = None
        self.schema = None
        self.many = many
        self.immutable = immutable

        self.on_resolve = on_resolve or self.on_resolve
        self.on_resolve_batch = on_resolve_batch or self.on_resolve_batch
        self.on_select = on_select or self.on_select
        self.on_save = on_save or self.on_save
        self.on_dump = on_dump or self.on_dump
        self.on_get = on_get or self.on_get
        self.on_set = on_set or self.on_set
        self.on_delete = on_delete or self.on_delete

        if is_resource_type(target):
            self.target = target
            self.many = False
        elif is_batch(target):
            self.target = target.owner
            self.many = True
        elif target is not None:
            assert callable(target)
            self.target_callback = target
Exemple #8
0
 def _dump_result_obj(self, obj) -> Union[Dict, List]:
     """
     Currently, GrpcService is intended to be used for internal
     service-to-service comm. This is why we "dump" Resources here by simply
     returning their internal state dicts.
     """
     if is_resource(obj):
         return obj.internal.state
     elif is_batch(obj):
         return [x.internal.state for x in obj]
     elif isinstance(obj, (list, set, tuple)):
         return [self._dump_result_obj(x) for x in obj]
     elif isinstance(obj, dict):
         return {k: self._dump_result_obj(v) for k, v in obj.items()}
     else:
         return obj
Exemple #9
0
    def _dump_recursive(self, parent_resource: 'Resource', keys: Set) -> Dict:

        if parent_resource is None:
            return None

        if keys:
            keys_to_dump = keys if isinstance(keys, set) else set(keys)
        else:
            keys_to_dump = parent_resource.internal.state.keys()

        record = {}
        for k in keys_to_dump:
            v = parent_resource.internal.state.get(k)
            resolver = parent_resource.ravel.resolvers.get(k)
            assert resolver is not None

            if resolver.private:
                continue

            if k in parent_resource.ravel.resolvers.relationships:
                # handle the dumping of Relationships specially
                rel = resolver
                if rel.many:
                    assert is_batch(v)
                    child_batch = v
                    record[k] = [
                        self.dump(child_resource)
                        for child_resource in child_batch
                    ]
                else:
                    if v is not None:
                        assert is_resource(v)
                    child_resource = v
                    record[k] = self.dump(child_resource)
            else:
                # dump non-Relationship state
                if k in {ID, REV}:
                    k = k[1:]
                record[k] = resolver.dump(self, v)

        return record
Exemple #10
0
    def exists_many(cls, entity: 'Entity') -> bool:
        """
        Does a simple check if a Resource exists by id.
        """
        store = cls.ravel.local.store

        if not entity:
            return False

        if is_batch(entity):
            args = (entity._id, )
        else:
            assert is_sequence(entity)
            id_list = entity
            args = (id_list, )
            for id_value in id_list:
                value, errors = cls._id.resolver.field.process(id_value)
                if errors:
                    raise ValueError(str(errors))

        return store.dispatch('exists_many', args=args)
Exemple #11
0
    def _execute_requests(self, query, resources, requests):
        for request in requests:
            request.query = query
            resolver = request.resolver
            # first try resolving in batch
            if len(resources) > 1:
                results = resolver.resolve_batch(resources, request)
                if results:
                    for resource in resources:
                        value = results.get(resource)
                        if resolver.many and value is None:
                            value = self.target.Batch()
                        elif (not resolver.many) and is_batch(value):
                            value = None
                        resource.internal.state[resolver.name] = value
                    continue

            # default on resolving for each resource separatesly
            for resource in resources:
                value = resolver.resolve(resource, request)
                resource.internal.state[resolver.name] = value
Exemple #12
0
 def default(self, target):
     if is_resource(target) or is_batch(target):
         return target.dump()
     else:
         return super().default(target)