1+ package graphql .util ;
2+
3+ import graphql .Assert ;
4+ import graphql .Directives ;
5+ import graphql .analysis .QueryTraverser ;
6+ import graphql .analysis .QueryVisitor ;
7+ import graphql .analysis .QueryVisitorFieldEnvironment ;
8+ import graphql .analysis .QueryVisitorFragmentSpreadEnvironment ;
9+ import graphql .analysis .QueryVisitorInlineFragmentEnvironment ;
10+ import graphql .introspection .Introspection ;
11+ import graphql .language .AstPrinter ;
12+ import graphql .language .AstTransformer ;
13+ import graphql .language .Document ;
14+ import graphql .language .Field ;
15+ import graphql .language .FragmentDefinition ;
16+ import graphql .language .FragmentSpread ;
17+ import graphql .language .Node ;
18+ import graphql .language .NodeVisitorStub ;
19+ import graphql .language .TypeName ;
20+ import graphql .parser .Parser ;
21+ import graphql .schema .GraphQLArgument ;
22+ import graphql .schema .GraphQLDirective ;
23+ import graphql .schema .GraphQLEnumType ;
24+ import graphql .schema .GraphQLEnumValueDefinition ;
25+ import graphql .schema .GraphQLFieldDefinition ;
26+ import graphql .schema .GraphQLInputObjectField ;
27+ import graphql .schema .GraphQLInputObjectType ;
28+ import graphql .schema .GraphQLInterfaceType ;
29+ import graphql .schema .GraphQLNamedSchemaElement ;
30+ import graphql .schema .GraphQLObjectType ;
31+ import graphql .schema .GraphQLScalarType ;
32+ import graphql .schema .GraphQLSchema ;
33+ import graphql .schema .GraphQLSchemaElement ;
34+ import graphql .schema .GraphQLType ;
35+ import graphql .schema .GraphQLTypeVisitorStub ;
36+ import graphql .schema .GraphQLUnionType ;
37+ import graphql .schema .SchemaTransformer ;
38+ import graphql .schema .idl .ScalarInfo ;
39+
40+ import java .util .ArrayList ;
41+ import java .util .Collections ;
42+ import java .util .LinkedHashMap ;
43+ import java .util .List ;
44+ import java .util .Map ;
45+ import java .util .concurrent .atomic .AtomicInteger ;
46+
47+ import static graphql .util .TreeTransformerUtil .changeNode ;
48+
49+ public class Anonymizer {
50+
51+ public static class AnonymizeResult {
52+ private GraphQLSchema schema ;
53+ private List <String > queries ;
54+
55+ public AnonymizeResult (GraphQLSchema schema , List <String > queries ) {
56+ this .schema = schema ;
57+ this .queries = queries ;
58+ }
59+
60+ public GraphQLSchema getSchema () {
61+ return schema ;
62+ }
63+
64+ public List <String > getQueries () {
65+ return queries ;
66+ }
67+ }
68+
69+ public static AnonymizeResult anonymizeSchemaAndQueries (GraphQLSchema schema , List <String > queries ) {
70+ return anonymizeSchemaAndQueries (schema , queries , Collections .emptyMap ());
71+ }
72+
73+ public static AnonymizeResult anonymizeSchemaAndQueries (GraphQLSchema schema , List <String > queries , Map <String , Object > variables ) {
74+
75+ AtomicInteger objectCounter = new AtomicInteger (1 );
76+ AtomicInteger inputObjectCounter = new AtomicInteger (1 );
77+ AtomicInteger inputObjectFieldCounter = new AtomicInteger (1 );
78+ AtomicInteger fieldCounter = new AtomicInteger (1 );
79+ AtomicInteger scalarCounter = new AtomicInteger (1 );
80+ AtomicInteger directiveCounter = new AtomicInteger (1 );
81+ AtomicInteger argumentCounter = new AtomicInteger (1 );
82+ AtomicInteger interfaceCounter = new AtomicInteger (1 );
83+ AtomicInteger unionCounter = new AtomicInteger (1 );
84+ AtomicInteger enumCounter = new AtomicInteger (1 );
85+ AtomicInteger enumValueCounter = new AtomicInteger (1 );
86+ Map <GraphQLNamedSchemaElement , String > newNameMap = new LinkedHashMap <>();
87+
88+ SchemaTransformer schemaTransformer = new SchemaTransformer ();
89+ GraphQLSchema newSchema = schemaTransformer .transform (schema , new GraphQLTypeVisitorStub () {
90+ @ Override
91+ public TraversalControl visitGraphQLArgument (GraphQLArgument graphQLArgument , TraverserContext <GraphQLSchemaElement > context ) {
92+ String newName = "argument" + argumentCounter .getAndIncrement ();
93+ newNameMap .put (graphQLArgument , newName );
94+ GraphQLArgument newElement = graphQLArgument .transform (builder -> {
95+ builder .name (newName );
96+ });
97+ return changeNode (context , newElement );
98+ }
99+
100+ @ Override
101+ public TraversalControl visitGraphQLInterfaceType (GraphQLInterfaceType graphQLInterfaceType , TraverserContext <GraphQLSchemaElement > context ) {
102+ if (Introspection .isIntrospectionTypes (graphQLInterfaceType )) {
103+ return TraversalControl .ABORT ;
104+ }
105+ String newName = "Interface" + interfaceCounter .getAndIncrement ();
106+ newNameMap .put (graphQLInterfaceType , newName );
107+ GraphQLInterfaceType newElement = graphQLInterfaceType .transform (builder -> {
108+ builder .name (newName );
109+ });
110+ return changeNode (context , newElement );
111+ }
112+
113+ @ Override
114+ public TraversalControl visitGraphQLEnumType (GraphQLEnumType graphQLEnumType , TraverserContext <GraphQLSchemaElement > context ) {
115+ if (Introspection .isIntrospectionTypes (graphQLEnumType )) {
116+ return TraversalControl .ABORT ;
117+ }
118+ String newName = "Enum" + enumCounter .getAndIncrement ();
119+ newNameMap .put (graphQLEnumType , newName );
120+ GraphQLEnumType newElement = graphQLEnumType .transform (builder -> {
121+ builder .name (newName );
122+ });
123+ return changeNode (context , newElement );
124+ }
125+
126+ @ Override
127+ public TraversalControl visitGraphQLEnumValueDefinition (GraphQLEnumValueDefinition enumValueDefinition , TraverserContext <GraphQLSchemaElement > context ) {
128+ String newName = "EnumValue" + enumValueCounter .getAndIncrement ();
129+ newNameMap .put (enumValueDefinition , newName );
130+ GraphQLEnumValueDefinition newElement = enumValueDefinition .transform (builder -> {
131+ builder .name (newName );
132+ });
133+ return changeNode (context , newElement );
134+ }
135+
136+ @ Override
137+ public TraversalControl visitGraphQLFieldDefinition (GraphQLFieldDefinition graphQLFieldDefinition , TraverserContext <GraphQLSchemaElement > context ) {
138+ String newName = "field" + fieldCounter .getAndIncrement ();
139+ newNameMap .put (graphQLFieldDefinition , newName );
140+ GraphQLFieldDefinition newElement = graphQLFieldDefinition .transform (builder -> {
141+ builder .name (newName );
142+ });
143+ return changeNode (context , newElement );
144+ }
145+
146+ @ Override
147+ public TraversalControl visitGraphQLDirective (GraphQLDirective graphQLDirective , TraverserContext <GraphQLSchemaElement > context ) {
148+ if (Directives .isBuiltInDirective (graphQLDirective )) {
149+ return TraversalControl .ABORT ;
150+ }
151+ String newName = "Directive" + directiveCounter .getAndIncrement ();
152+ newNameMap .put (graphQLDirective , newName );
153+ GraphQLDirective newElement = graphQLDirective .transform (builder -> {
154+ builder .name (newName );
155+ });
156+ return changeNode (context , newElement );
157+ }
158+
159+ @ Override
160+ public TraversalControl visitGraphQLInputObjectField (GraphQLInputObjectField graphQLInputObjectField , TraverserContext <GraphQLSchemaElement > context ) {
161+ String newName = "InputField" + inputObjectFieldCounter .getAndIncrement ();
162+ newNameMap .put (graphQLInputObjectField , newName );
163+ GraphQLInputObjectField newElement = graphQLInputObjectField .transform (builder -> {
164+ builder .name (newName );
165+ });
166+ return changeNode (context , newElement );
167+ }
168+
169+ @ Override
170+ public TraversalControl visitGraphQLInputObjectType (GraphQLInputObjectType graphQLInputObjectType , TraverserContext <GraphQLSchemaElement > context ) {
171+ if (Introspection .isIntrospectionTypes (graphQLInputObjectType )) {
172+ return TraversalControl .ABORT ;
173+ }
174+ String newName = "InputObject" + inputObjectCounter .getAndIncrement ();
175+ newNameMap .put (graphQLInputObjectType , newName );
176+ GraphQLInputObjectType newElement = graphQLInputObjectType .transform (builder -> {
177+ builder .name (newName );
178+ });
179+ return changeNode (context , newElement );
180+ }
181+
182+
183+ @ Override
184+ public TraversalControl visitGraphQLObjectType (GraphQLObjectType graphQLObjectType , TraverserContext <GraphQLSchemaElement > context ) {
185+ if (Introspection .isIntrospectionTypes (graphQLObjectType )) {
186+ return TraversalControl .ABORT ;
187+ }
188+ String newName = "Object" + objectCounter .getAndIncrement ();
189+ newNameMap .put (graphQLObjectType , newName );
190+ GraphQLObjectType newElement = graphQLObjectType .transform (builder -> {
191+ builder .name (newName );
192+ });
193+ return changeNode (context , newElement );
194+ }
195+
196+ @ Override
197+ public TraversalControl visitGraphQLScalarType (GraphQLScalarType graphQLScalarType , TraverserContext <GraphQLSchemaElement > context ) {
198+ if (ScalarInfo .isGraphqlSpecifiedScalar (graphQLScalarType )) {
199+ return TraversalControl .ABORT ;
200+ }
201+ String newName = "Scalar" + scalarCounter .getAndIncrement ();
202+ newNameMap .put (graphQLScalarType , newName );
203+ GraphQLScalarType newElement = graphQLScalarType .transform (builder -> {
204+ builder .name (newName );
205+ });
206+ return changeNode (context , newElement );
207+ }
208+
209+ @ Override
210+ public TraversalControl visitGraphQLUnionType (GraphQLUnionType graphQLUnionType , TraverserContext <GraphQLSchemaElement > context ) {
211+ if (Introspection .isIntrospectionTypes (graphQLUnionType )) {
212+ return TraversalControl .ABORT ;
213+ }
214+ String newName = "Union" + unionCounter .getAndIncrement ();
215+ newNameMap .put (graphQLUnionType , newName );
216+ GraphQLUnionType newElement = graphQLUnionType .transform (builder -> {
217+ builder .name (newName );
218+ });
219+ return changeNode (context , newElement );
220+ }
221+ });
222+
223+ List <String > newQueries = new ArrayList <>();
224+ for (String query : queries ) {
225+ String newQuery = rewriteQuery (query , schema , newNameMap , variables );
226+ newQueries .add (newQuery );
227+ }
228+ AnonymizeResult result = new AnonymizeResult (newSchema , newQueries );
229+ return result ;
230+ }
231+
232+ private static String rewriteQuery (String query , GraphQLSchema schema , Map <GraphQLNamedSchemaElement , String > newNames , Map <String , Object > variables ) {
233+ AtomicInteger fragmentCounter = new AtomicInteger (1 );
234+ Map <Node , String > nodeToNewName = new LinkedHashMap <>();
235+ Document document = new Parser ().parseDocument (query );
236+ QueryTraverser queryTraverser = QueryTraverser .newQueryTraverser ().document (document ).schema (schema ).variables (variables ).build ();
237+ queryTraverser .visitDepthFirst (new QueryVisitor () {
238+ @ Override
239+ public void visitField (QueryVisitorFieldEnvironment queryVisitorFieldEnvironment ) {
240+ String newName = Assert .assertNotNull (newNames .get (queryVisitorFieldEnvironment .getFieldDefinition ()));
241+ nodeToNewName .put (queryVisitorFieldEnvironment .getField (), newName );
242+ }
243+
244+ @ Override
245+ public void visitInlineFragment (QueryVisitorInlineFragmentEnvironment queryVisitorInlineFragmentEnvironment ) {
246+
247+ }
248+
249+ @ Override
250+ public void visitFragmentSpread (QueryVisitorFragmentSpreadEnvironment queryVisitorFragmentSpreadEnvironment ) {
251+ FragmentDefinition fragmentDefinition = queryVisitorFragmentSpreadEnvironment .getFragmentDefinition ();
252+ String newName ;
253+ if (!nodeToNewName .containsKey (fragmentDefinition )) {
254+ newName = "Fragment" + fragmentCounter .getAndIncrement ();
255+ nodeToNewName .put (fragmentDefinition , newName );
256+ } else {
257+ newName = nodeToNewName .get (fragmentDefinition );
258+ }
259+ nodeToNewName .put (queryVisitorFragmentSpreadEnvironment .getFragmentSpread (), newName );
260+ }
261+
262+ });
263+
264+ AstTransformer astTransformer = new AstTransformer ();
265+ Document newDocument = (Document ) astTransformer .transform (document , new NodeVisitorStub () {
266+
267+ @ Override
268+ public TraversalControl visitField (Field node , TraverserContext <Node > context ) {
269+ String newName = Assert .assertNotNull (nodeToNewName .get (node ));
270+ return changeNode (context , node .transform (builder -> builder .name (newName )));
271+ }
272+
273+ @ Override
274+ public TraversalControl visitFragmentDefinition (FragmentDefinition node , TraverserContext <Node > context ) {
275+ String newName = Assert .assertNotNull (nodeToNewName .get (node ));
276+ GraphQLType currentCondition = Assert .assertNotNull (schema .getType (node .getTypeCondition ().getName ()));
277+ String newCondition = newNames .get (currentCondition );
278+ return changeNode (context , node .transform (builder -> builder .name (newName ).typeCondition (new TypeName (newCondition ))));
279+ }
280+
281+ @ Override
282+ public TraversalControl visitFragmentSpread (FragmentSpread node , TraverserContext <Node > context ) {
283+ String newName = Assert .assertNotNull (nodeToNewName .get (node ));
284+ return changeNode (context , node .transform (builder -> builder .name (newName )));
285+ }
286+ });
287+ return AstPrinter .printAstCompact (newDocument )
288+ ;
289+ }
290+
291+ }
0 commit comments