def test_supports_loading_multiple_keys_in_one_call(): def call_fn(keys): return Promise.resolve(keys) identity_loader = DataLoader(call_fn) promise_all = identity_loader.load_many([1, 2]) assert isinstance(promise_all, Promise) values = promise_all.get() assert values == [1, 2] promise_all = identity_loader.load_many([]) assert isinstance(promise_all, Promise) values = promise_all.get() assert values == []
class ExternalRESTField(graphene.Field): def __init__(self, rest_object_class, source_field_name='id', filter_field_name='id', is_top_level=False, many=False, *args, **kwargs): assert is_top_level or not (source_field_name == 'id' and filter_field_name == 'id') self.source_field_name = source_field_name self.filter_field_name = filter_field_name self.rest_object_class = rest_object_class self.is_top_level = is_top_level self.request_maker = RequestMaker( filter_by_parent_fields=(not is_top_level), filter_field_name=filter_field_name ) def batch_load_fn(source_values): self.request_maker.filter_values = source_values response = self.request_maker.make_request() return Promise.resolve(response.json()['results']) self.data_loader = DataLoader(batch_load_fn) self.many = many if self.many: super().__init__(graphene.List(rest_object_class), *args, **kwargs) else: super().__init__(rest_object_class, *args, **kwargs) def get_resolver(self, parent_resolver): if self.resolver: return self.resolver else: return self.generate_resolver(get_actual_object_class(self.rest_object_class)) def generate_resolver(self, rest_object_class, *class_args, **class_kwargs): def endpoint_resolver_promise(parent_object, results): relevant_results = list(filter(lambda h: equals_or_contains(h[self.filter_field_name], getattr(parent_object, self.source_field_name)), results)) if not self.many: assert len(relevant_results) == 1 relevant_results = relevant_results[0] obj = reduce_fields_to_objects(rest_object_class, relevant_results, is_list=self.many) return obj def endpoint_resolver(parent_object, args, context, info): # This is called for every parent object where we want nested objects. # Therefore we don't want to do unnecessary computation (ex: # processing query params/headers from the original request) # Instead, we do initial processing in request_maker.initialize_x # and final processing in request_maker.generate_x self.request_maker.headers = context.headers self.request_maker.data = context.data self.request_maker.base_url = rest_object_class.base_url self.request_maker.query_string = context.query_string self.request_maker.graphql_arguments = args if self.is_top_level: response = self.request_maker.make_request() return reduce_fields_to_objects(rest_object_class, response.json()['results']) else: source_values = getattr(parent_object, self.source_field_name) if not is_non_str_iterable(source_values): source_values = [source_values] result = self.data_loader.load_many(source_values) return result.then( functools.partial(endpoint_resolver_promise, parent_object) ) return endpoint_resolver