Coverage for graphqler / compiler / resolvers / object_dependency_resolver.py: 100%

32 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-20 10:09 -0400

1"""This module will be used to create a object dependency resolver 

2The representation of this will be essentially a text-based graph 

3 

4The dependencies will be generated from object - object 

5while methods such as queries / mutations will be related to objects 

6based on either output type or semantic understanding 

7""" 

8 

9from graphqler.config import BUILT_IN_TYPES, BUILT_IN_TYPE_KINDS 

10 

11 

12class ObjectDependencyResolver: 

13 def __init__(self): 

14 pass 

15 

16 def resolve(self, objects) -> dict: 

17 """Resolve dependencies by adding the 'hardDependsOn' key and 'softDependsOn' key 

18 hardDependsOn: the object must be created for this object to be instantiated 

19 softDependsOn: the object can be null on instantiation of this object 

20 

21 Returns: 

22 dict: The new enriched objects with an extra 'dependsOn' key 

23 """ 

24 for gql_object_key, gql_object in objects.items(): 

25 objects[gql_object_key] = self.parse_gql_object(gql_object) 

26 

27 return objects 

28 

29 def get_base_oftype(self, oftype: dict) -> dict: 

30 if "ofType" in oftype and oftype["ofType"] is not None: 

31 return self.get_base_oftype(oftype["ofType"]) 

32 else: 

33 return oftype 

34 

35 def parse_gql_object(self, gql_object: dict) -> dict: 

36 """Parse a single object, noting down all of the other object this object depends on 

37 hardDependsOn: Where the object has fields on other objects and it's kind is NON_NULL 

38 softDependsOn: Where the object has fields on other objects and it can be NULL 

39 

40 

41 !weird edge cases found from this method: 

42 - when the field kind is a SCALAR, the ofType will be null 

43 

44 Args: 

45 gql_object (dict): The graphql object 

46 

47 Returns: 

48 dict: The enriched object with extra 'softDependsOn' key and 'hardDependsOn' 

49 """ 

50 soft_dependent_objects = [] 

51 hard_dependent_objects = [] 

52 for field in gql_object["fields"]: 

53 # There are 3 cases: 

54 # - if the kind is SCALAR/OBJECT/INTERFACE/UNION/ENUM/INPUT_OBJECT, 

55 # - if kind is NON_NULL 

56 # - if kind is LIST 

57 if field["kind"] in BUILT_IN_TYPE_KINDS and field["kind"] != "NON_NULL" and field["kind"] != "LIST": 

58 if field["type"] not in BUILT_IN_TYPES: 

59 soft_dependent_objects.append(field["type"]) 

60 if field["kind"] == "NON_NULL": 

61 base_oftype = self.get_base_oftype(field["ofType"]) 

62 if base_oftype["kind"] not in BUILT_IN_TYPES: 

63 hard_dependent_objects.append(base_oftype["name"]) 

64 elif field["kind"] == "LIST": 

65 base_oftype = self.get_base_oftype(field["ofType"]) 

66 if base_oftype["kind"] not in BUILT_IN_TYPES: 

67 # TODO: Figure out if lists can have hard dependencies (a non-zero lengthed list) 

68 soft_dependent_objects.append(base_oftype["name"]) 

69 

70 # TODO: Figure out a way see if we can handle custom scalars dynamically (?) - right now, they show up as none 

71 # Remove nulls from dependency list and make them unique 

72 soft_dependent_objects = list(set([o for o in soft_dependent_objects if o is not None])) 

73 hard_dependent_objects = list(set([o for o in hard_dependent_objects if o is not None])) 

74 

75 gql_object["softDependsOn"] = soft_dependent_objects 

76 gql_object["hardDependsOn"] = hard_dependent_objects 

77 return gql_object