|
4 | 4 | import com.google.common.collect.Multimap; |
5 | 5 | import graphql.PublicApi; |
6 | 6 | import graphql.collect.ImmutableKit; |
| 7 | +import graphql.introspection.Introspection; |
7 | 8 | import graphql.util.Breadcrumb; |
8 | 9 | import graphql.util.NodeAdapter; |
9 | 10 | import graphql.util.NodeLocation; |
@@ -102,6 +103,73 @@ public static GraphQLSchema transformSchema(GraphQLSchema schema, GraphQLTypeVis |
102 | 103 | return schemaTransformer.transform(schema, visitor, postTransformation); |
103 | 104 | } |
104 | 105 |
|
| 106 | + /** |
| 107 | + * Transforms a GraphQLSchema with support for delete operations. |
| 108 | + * <p> |
| 109 | + * When a visitor uses {@link GraphQLTypeVisitor#deleteNode(TraverserContext)} to delete schema elements, |
| 110 | + * the traversal does not continue to the children of deleted nodes. This can cause issues when types |
| 111 | + * are only reachable through fields that get deleted, as those types won't be visited and transformed. |
| 112 | + * <p> |
| 113 | + * This method ensures all types in the schema are visited by adding them to the schema's additional types |
| 114 | + * before transformation. This guarantees that even types only reachable through deleted fields will be |
| 115 | + * properly visited and transformed. |
| 116 | + * <p> |
| 117 | + * Use this method instead of {@link #transformSchema(GraphQLSchema, GraphQLTypeVisitor)} when your |
| 118 | + * visitor deletes fields or types that may reference other types via circular references. |
| 119 | + * |
| 120 | + * @param schema the schema to transform |
| 121 | + * @param visitor the visitor call back |
| 122 | + * |
| 123 | + * @return a new GraphQLSchema instance. |
| 124 | + * |
| 125 | + * @see GraphQLTypeVisitor#deleteNode(TraverserContext) |
| 126 | + */ |
| 127 | + public static GraphQLSchema transformSchemaWithDeletes(GraphQLSchema schema, GraphQLTypeVisitor visitor) { |
| 128 | + return transformSchemaWithDeletes(schema, visitor, null); |
| 129 | + } |
| 130 | + |
| 131 | + /** |
| 132 | + * Transforms a GraphQLSchema with support for delete operations. |
| 133 | + * <p> |
| 134 | + * When a visitor uses {@link GraphQLTypeVisitor#deleteNode(TraverserContext)} to delete schema elements, |
| 135 | + * the traversal does not continue to the children of deleted nodes. This can cause issues when types |
| 136 | + * are only reachable through fields that get deleted, as those types won't be visited and transformed. |
| 137 | + * <p> |
| 138 | + * This method ensures all types in the schema are visited by adding them to the schema's additional types |
| 139 | + * before transformation. This guarantees that even types only reachable through deleted fields will be |
| 140 | + * properly visited and transformed. |
| 141 | + * <p> |
| 142 | + * Use this method instead of {@link #transformSchema(GraphQLSchema, GraphQLTypeVisitor, Consumer)} when your |
| 143 | + * visitor deletes fields or types that may reference other types via circular references. |
| 144 | + * |
| 145 | + * @param schema the schema to transform |
| 146 | + * @param visitor the visitor call back |
| 147 | + * @param postTransformation a callback that can be used as a final step to the schema (can be null) |
| 148 | + * |
| 149 | + * @return a new GraphQLSchema instance. |
| 150 | + * |
| 151 | + * @see GraphQLTypeVisitor#deleteNode(TraverserContext) |
| 152 | + */ |
| 153 | + public static GraphQLSchema transformSchemaWithDeletes(GraphQLSchema schema, GraphQLTypeVisitor visitor, Consumer<GraphQLSchema.Builder> postTransformation) { |
| 154 | + // Add all types to additionalTypes to ensure they are all visited during transformation. |
| 155 | + // This is necessary because when a node is deleted, its children are not traversed. |
| 156 | + // Types that are only reachable through deleted fields would otherwise not be visited. |
| 157 | + GraphQLSchema schemaWithAllTypes = schema.transform(builder -> { |
| 158 | + for (GraphQLNamedType type : schema.getTypeMap().values()) { |
| 159 | + if (!isRootType(schema, type) && !Introspection.isIntrospectionTypes(type)) { |
| 160 | + builder.additionalType(type); |
| 161 | + } |
| 162 | + } |
| 163 | + }); |
| 164 | + return transformSchema(schemaWithAllTypes, visitor, postTransformation); |
| 165 | + } |
| 166 | + |
| 167 | + private static boolean isRootType(GraphQLSchema schema, GraphQLNamedType type) { |
| 168 | + return type == schema.getQueryType() |
| 169 | + || type == schema.getMutationType() |
| 170 | + || type == schema.getSubscriptionType(); |
| 171 | + } |
| 172 | + |
105 | 173 | /** |
106 | 174 | * Transforms a {@link GraphQLSchemaElement} and returns a new element. |
107 | 175 | * |
|
0 commit comments