7
7
*/
8
8
package com .intellij .lang .jsgraphql .ide .editor ;
9
9
10
+ import com .google .common .collect .Sets ;
10
11
import com .google .gson .Gson ;
11
12
import com .intellij .ide .actions .CreateFileAction ;
12
13
import com .intellij .ide .impl .DataManagerImpl ;
13
14
import com .intellij .lang .jsgraphql .GraphQLSettings ;
14
15
import com .intellij .lang .jsgraphql .ide .project .graphqlconfig .GraphQLConfigManager ;
15
16
import com .intellij .lang .jsgraphql .ide .project .graphqlconfig .model .GraphQLConfigEndpoint ;
16
17
import com .intellij .lang .jsgraphql .ide .project .graphqlconfig .model .GraphQLConfigVariableAwareEndpoint ;
18
+ import com .intellij .lang .jsgraphql .schema .GraphQLSchemaKeys ;
17
19
import com .intellij .lang .jsgraphql .v1 .ide .project .JSGraphQLLanguageUIProjectService ;
18
20
import com .intellij .notification .Notification ;
19
21
import com .intellij .notification .NotificationAction ;
24
26
import com .intellij .openapi .actionSystem .AnAction ;
25
27
import com .intellij .openapi .actionSystem .AnActionEvent ;
26
28
import com .intellij .openapi .application .ApplicationManager ;
29
+ import com .intellij .openapi .components .ServiceManager ;
27
30
import com .intellij .openapi .editor .Editor ;
28
31
import com .intellij .openapi .fileEditor .FileEditor ;
29
32
import com .intellij .openapi .fileEditor .FileEditorManager ;
30
33
import com .intellij .openapi .fileEditor .TextEditor ;
34
+ import com .intellij .openapi .progress .ProgressIndicator ;
31
35
import com .intellij .openapi .progress .ProgressManager ;
36
+ import com .intellij .openapi .progress .Task ;
32
37
import com .intellij .openapi .project .Project ;
33
38
import com .intellij .openapi .vfs .VirtualFile ;
34
39
import com .intellij .psi .PsiDirectory ;
35
40
import com .intellij .psi .impl .file .PsiDirectoryFactory ;
36
41
import graphql .introspection .IntrospectionQuery ;
42
+ import graphql .language .Description ;
37
43
import graphql .language .Document ;
44
+ import graphql .language .Node ;
45
+ import graphql .language .ScalarTypeDefinition ;
38
46
import graphql .schema .idl .SchemaPrinter ;
39
47
import org .apache .commons .httpclient .HttpClient ;
40
48
import org .apache .commons .httpclient .methods .PostMethod ;
49
57
import java .util .Date ;
50
58
import java .util .Map ;
51
59
import java .util .Optional ;
60
+ import java .util .Set ;
52
61
53
62
import static com .intellij .lang .jsgraphql .v1 .ide .project .JSGraphQLLanguageUIProjectService .setHeadersFromOptions ;
54
63
55
64
public class GraphQLIntrospectionHelper {
56
65
57
- public static void performIntrospectionQueryAndUpdateSchemaPathFile (Project project , GraphQLConfigEndpoint endpoint ) {
66
+ private GraphQLIntrospectionTask latestIntrospection = null ;
67
+
68
+ public static GraphQLIntrospectionHelper getService (@ NotNull Project project ) {
69
+ return ServiceManager .getService (project , GraphQLIntrospectionHelper .class );
70
+ }
71
+
72
+ public GraphQLIntrospectionHelper (Project project ) {
73
+ if (project != null ) {
74
+ project .getMessageBus ().connect ().subscribe (GraphQLConfigManager .TOPIC , () -> latestIntrospection = null );
75
+ }
76
+ }
77
+
78
+ public void performIntrospectionQueryAndUpdateSchemaPathFile (Project project , GraphQLConfigEndpoint endpoint ) {
58
79
final VirtualFile configFile = GraphQLConfigManager .getService (project ).getClosestConfigFile (endpoint .configPackageSet .getConfigBaseDir ());
59
80
if (configFile != null ) {
60
81
final String schemaPath = endpoint .configPackageSet .getConfigData ().schemaPath ;
@@ -65,12 +86,14 @@ public static void performIntrospectionQueryAndUpdateSchemaPathFile(Project proj
65
86
return ;
66
87
}
67
88
68
- GraphQLIntrospectionHelper . performIntrospectionQueryAndUpdateSchemaPathFile (project , new GraphQLConfigVariableAwareEndpoint (endpoint , project ), schemaPath , configFile );
89
+ performIntrospectionQueryAndUpdateSchemaPathFile (project , new GraphQLConfigVariableAwareEndpoint (endpoint , project ), schemaPath , configFile );
69
90
}
70
91
71
92
}
72
93
73
- public static void performIntrospectionQueryAndUpdateSchemaPathFile (Project project , GraphQLConfigVariableAwareEndpoint endpoint , String schemaPath , VirtualFile introspectionSourceFile ) {
94
+ public void performIntrospectionQueryAndUpdateSchemaPathFile (Project project , GraphQLConfigVariableAwareEndpoint endpoint , String schemaPath , VirtualFile introspectionSourceFile ) {
95
+
96
+ latestIntrospection = new GraphQLIntrospectionTask (endpoint , () -> performIntrospectionQueryAndUpdateSchemaPathFile (project , endpoint , schemaPath , introspectionSourceFile ));
74
97
75
98
final NotificationAction retry = new NotificationAction ("Retry" ) {
76
99
@@ -99,34 +122,37 @@ public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification noti
99
122
100
123
setHeadersFromOptions (endpoint , method );
101
124
102
- ProgressManager .getInstance ().runProcessWithProgressSynchronously (() -> {
103
- ProgressManager .getInstance ().getProgressIndicator ().setIndeterminate (true );
104
- try {
105
- httpClient .executeMethod (method );
106
- final String responseJson = Optional .ofNullable (method .getResponseBodyAsString ()).orElse ("" );
107
- ApplicationManager .getApplication ().invokeLater (() -> {
108
- try {
109
- JSGraphQLLanguageUIProjectService .getService (project ).showQueryResult (responseJson );
110
- final String schemaAsSDL = printIntrospectionJsonAsGraphQL (responseJson );
111
- createOrUpdateIntrospectionSDLFile (schemaAsSDL , introspectionSourceFile , schemaPath , project );
112
- } catch (Exception e ) {
113
- Notifications .Bus .notify (new Notification ("GraphQL" , "GraphQL Introspection Error" , e .getMessage (), NotificationType .WARNING ).addAction (retry ), project );
114
- }
115
- });
116
- } catch (IOException e ) {
117
- Notifications .Bus .notify (new Notification ("GraphQL" , "GraphQL Query Error" , url + ": " + e .getMessage (), NotificationType .WARNING ).addAction (retry ), project );
125
+ final Task .Backgroundable task = new Task .Backgroundable (project , "Executing GraphQL Introspection Query" , false ) {
126
+ @ Override
127
+ public void run (@ NotNull ProgressIndicator indicator ) {
128
+ indicator .setIndeterminate (true );
129
+ try {
130
+ httpClient .executeMethod (method );
131
+ final String responseJson = Optional .ofNullable (method .getResponseBodyAsString ()).orElse ("" );
132
+ ApplicationManager .getApplication ().invokeLater (() -> {
133
+ try {
134
+ JSGraphQLLanguageUIProjectService .getService (project ).showQueryResult (responseJson , JSGraphQLLanguageUIProjectService .QueryResultDisplay .ON_ERRORS_ONLY );
135
+ IntrospectionOutputFormat format = schemaPath .endsWith (".json" ) ? IntrospectionOutputFormat .JSON : IntrospectionOutputFormat .SDL ;
136
+ final String schemaText = format == IntrospectionOutputFormat .SDL ? printIntrospectionJsonAsGraphQL (responseJson ) : responseJson ;
137
+ createOrUpdateIntrospectionOutputFile (schemaText , format , introspectionSourceFile , schemaPath , project );
138
+ } catch (Exception e ) {
139
+ Notifications .Bus .notify (new Notification ("GraphQL" , "GraphQL Introspection Error" , e .getMessage (), NotificationType .WARNING ).addAction (retry ), project );
140
+ }
141
+ });
142
+ } catch (IOException e ) {
143
+ Notifications .Bus .notify (new Notification ("GraphQL" , "GraphQL Query Error" , url + ": " + e .getMessage (), NotificationType .WARNING ).addAction (retry ), project );
144
+ }
118
145
}
119
-
120
- }, "Executing GraphQL Introspection Query" , false , project );
121
-
146
+ };
147
+ ProgressManager .getInstance ().run (task );
122
148
123
149
} catch (UnsupportedEncodingException | IllegalStateException | IllegalArgumentException e ) {
124
150
Notifications .Bus .notify (new Notification ("GraphQL" , "GraphQL Query Error" , url + ": " + e .getMessage (), NotificationType .ERROR ).addAction (retry ), project );
125
151
}
126
152
}
127
153
128
154
@ SuppressWarnings ("unchecked" )
129
- public static String printIntrospectionJsonAsGraphQL (String introspectionJson ) {
155
+ public String printIntrospectionJsonAsGraphQL (String introspectionJson ) {
130
156
Map <String , Object > introspectionAsMap = new Gson ().fromJson (introspectionJson , Map .class );
131
157
if (!introspectionAsMap .containsKey ("__schema" )) {
132
158
// possibly a full query result
@@ -142,30 +168,73 @@ public static String printIntrospectionJsonAsGraphQL(String introspectionJson) {
142
168
}
143
169
}
144
170
final Document schemaDefinition = new GraphQLIntrospectionResultToSchema ().createSchemaDefinition (introspectionAsMap );
145
- return new SchemaPrinter ().print (schemaDefinition );
171
+ final SchemaPrinter .Options options = SchemaPrinter .Options .defaultOptions ().includeScalarTypes (true ).includeSchemaDefintion (true );
172
+ final StringBuilder sb = new StringBuilder (new SchemaPrinter (options ).print (schemaDefinition ));
173
+
174
+ // graphql-java currently appears to discard custom scalars as part of the schema that the printer constructs, so include them manually here
175
+ final Set <String > knownScalars = Sets .newHashSet ();
176
+ for (Node definition : schemaDefinition .getChildren ()) {
177
+ if (definition instanceof ScalarTypeDefinition ) {
178
+ final ScalarTypeDefinition scalarTypeDefinition = (ScalarTypeDefinition ) definition ;
179
+ String scalarName = scalarTypeDefinition .getName ();
180
+ if (knownScalars .add (scalarName )) {
181
+ sb .append ("\n " );
182
+ final Description description = scalarTypeDefinition .getDescription ();
183
+ if (description != null ) {
184
+ if (description .isMultiLine ()) {
185
+ sb .append ("\" \" \" " ).append (description .getContent ()).append ("\" \" \" " );
186
+ } else {
187
+ sb .append ("\" " ).append (description .getContent ()).append ("\" " );
188
+ }
189
+ sb .append ("\n " );
190
+ }
191
+ sb .append ("scalar " ).append (scalarName );
192
+ }
193
+ }
194
+ }
195
+ return sb .toString ();
146
196
}
147
197
198
+ public GraphQLIntrospectionTask getLatestIntrospection () {
199
+ return latestIntrospection ;
200
+ }
201
+
202
+ enum IntrospectionOutputFormat {
203
+ JSON ,
204
+ SDL
205
+ }
148
206
149
- static void createOrUpdateIntrospectionSDLFile (String schemaAsSDL , VirtualFile introspectionSourceFile , String outputFileName , Project project ) {
207
+ void createOrUpdateIntrospectionOutputFile (String schemaText , IntrospectionOutputFormat format , VirtualFile introspectionSourceFile , String outputFileName , Project project ) {
150
208
ApplicationManager .getApplication ().runWriteAction (() -> {
151
209
try {
152
- final String schemaAsSDLWithHeader = "# This file was generated based on \" " + introspectionSourceFile .getName () + "\" at " + new Date () + ". Do not edit manually.\n \n " + schemaAsSDL ;
210
+ final String header ;
211
+ switch (format ) {
212
+ case SDL :
213
+ header = "# This file was generated based on \" " + introspectionSourceFile .getName () + "\" at " + new Date () + ". Do not edit manually.\n \n " ;
214
+ break ;
215
+ case JSON :
216
+ header = "" ;
217
+ break ;
218
+ default :
219
+ throw new IllegalArgumentException ("unsupported output format: " + format );
220
+ }
153
221
String relativeOutputFileName = StringUtils .replaceChars (outputFileName , '\\' , '/' );
154
222
VirtualFile outputFile = introspectionSourceFile .getParent ().findFileByRelativePath (relativeOutputFileName );
155
223
if (outputFile == null ) {
156
224
PsiDirectory directory = PsiDirectoryFactory .getInstance (project ).createDirectory (introspectionSourceFile .getParent ());
157
225
CreateFileAction .MkDirs dirs = new CreateFileAction .MkDirs (relativeOutputFileName , directory );
158
226
outputFile = dirs .directory .getVirtualFile ().createChildData (introspectionSourceFile , dirs .newName );
159
227
}
228
+ outputFile .putUserData (GraphQLSchemaKeys .IS_GRAPHQL_INTROSPECTION_JSON , true );
160
229
final FileEditor fileEditor = FileEditorManager .getInstance (project ).openFile (outputFile , true , true )[0 ];
161
- setEditorTextAndFormatLines (schemaAsSDLWithHeader , fileEditor );
230
+ setEditorTextAndFormatLines (header + schemaText , fileEditor );
162
231
} catch (IOException ioe ) {
163
232
Notifications .Bus .notify (new Notification ("GraphQL" , "GraphQL IO Error" , "Unable to create file '" + outputFileName + "' in directory '" + introspectionSourceFile .getParent ().getPath () + "': " + ioe .getMessage (), NotificationType .ERROR ));
164
233
}
165
234
});
166
235
}
167
236
168
- static void setEditorTextAndFormatLines (String text , FileEditor fileEditor ) {
237
+ void setEditorTextAndFormatLines (String text , FileEditor fileEditor ) {
169
238
if (fileEditor instanceof TextEditor ) {
170
239
final Editor editor = ((TextEditor ) fileEditor ).getEditor ();
171
240
editor .getDocument ().setText (text );
0 commit comments