|
1 | 1 | package no.fellesstudentsystem.schema_transformer.transform; |
2 | 2 |
|
3 | 3 | import graphql.language.DirectivesContainer; |
| 4 | +import graphql.language.UnionTypeDefinition; |
4 | 5 | import graphql.schema.*; |
5 | 6 | import graphql.util.TraversalControl; |
6 | 7 | import graphql.util.TraverserContext; |
7 | 8 |
|
8 | | -import java.util.List; |
9 | | -import java.util.Set; |
| 9 | +import java.util.*; |
10 | 10 | import java.util.stream.Collectors; |
11 | 11 | import java.util.stream.Stream; |
12 | 12 |
|
@@ -120,7 +120,55 @@ private TraversalControl visitFieldElement(GraphQLNamedSchemaElement node, Trave |
120 | 120 | } |
121 | 121 | }; |
122 | 122 |
|
123 | | - return SchemaTransformer.transformSchema(schema, visitorFields); // Seems the objects get removed automatically when no fields point to them. |
| 123 | + // Make sure we always visit every type |
| 124 | + var schemaCopy = schema; |
| 125 | + schema = schema.transform(builder -> { |
| 126 | + for (var type : schemaCopy.getTypeMap().values()) { |
| 127 | + if (type != schemaCopy.getQueryType() && type != schemaCopy.getMutationType() && type != schemaCopy.getSubscriptionType()) { |
| 128 | + builder.additionalType(type); |
| 129 | + } |
| 130 | + } |
| 131 | + }); |
| 132 | + |
| 133 | + var prunedSchema = SchemaTransformer.transformSchema(schema, visitorFields); // Seems the objects get removed automatically when no fields point to them. |
| 134 | + return removeUnreachableTypes(prunedSchema); |
| 135 | + } |
| 136 | + |
| 137 | + private GraphQLSchema removeUnreachableTypes(GraphQLSchema schema) { |
| 138 | + var reachableTypes = findReachableTypes(schema); |
| 139 | + return schema.transform(builder -> builder.clearAdditionalTypes().additionalTypes(reachableTypes)); |
| 140 | + } |
| 141 | + |
| 142 | + private Set<GraphQLType> findReachableTypes(GraphQLSchema schema) { |
| 143 | + Set<String> reachableNames = new HashSet<>(); |
| 144 | + Set<GraphQLType> reachableTypes = new HashSet<>(); |
| 145 | + |
| 146 | + var schemaRoots = Stream.of(schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()) |
| 147 | + .filter(Objects::nonNull).toList(); |
| 148 | + Queue<GraphQLType> toVisit = new LinkedList<>(schemaRoots); |
| 149 | + |
| 150 | + while (!toVisit.isEmpty()) { |
| 151 | + GraphQLType type = GraphQLTypeUtil.unwrapAll(toVisit.poll()); |
| 152 | + if (type instanceof GraphQLNamedType namedType) { |
| 153 | + var alreadyVisited = reachableNames.add(namedType.getName()); |
| 154 | + if (alreadyVisited) { |
| 155 | + continue; |
| 156 | + } |
| 157 | + |
| 158 | + reachableTypes.add(namedType); |
| 159 | + |
| 160 | + var children = schema.getTypeMap().get(namedType.getName()).getChildren(); |
| 161 | + for (var child : children) { |
| 162 | + if (child instanceof GraphQLNamedType namedChild) { |
| 163 | + if (!reachableNames.contains(namedChild.getName())) { |
| 164 | + toVisit.add(namedChild); |
| 165 | + } |
| 166 | + } |
| 167 | + } |
| 168 | + } |
| 169 | + } |
| 170 | + |
| 171 | + return reachableTypes; |
124 | 172 | } |
125 | 173 |
|
126 | 174 | private boolean containerIsEmpty(GraphQLInputObjectType node) { |
|
0 commit comments