Skip to content

Commit 4ffc455

Browse files
vepanimasintellij-monorepo-bot
authored andcommitted
[graphql] WEB-68173 build an introspection query depending on the server capabilities
GitOrigin-RevId: 53d404839f838fcbaba13cf5cf659127766f3752
1 parent 8fb2b29 commit 4ffc455

21 files changed

+1124
-692
lines changed

resources/META-INF/plugin.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@
223223
<registryKey key="graphql.schema.build.timeout" defaultValue="500" description="GraphQL schema build timeout in ms"/>
224224
<registryKey key="graphql.schema.size.definitions.limit" defaultValue="4000"
225225
description="A limit on the total number of type definitions in a GraphQL schema"/>
226+
<registryKey key="graphql.introspection.detect.schema.capabilities"
227+
defaultValue="[Adaptive*|Latest|Legacy]"
228+
description="Enables preliminary request to detect GraphQL server capabilities before schema introspection"/>
226229

227230
<!-- Inspections -->
228231
<localInspection language="GraphQL" key="graphql.inspection.display.name.unresolved.reference"

resources/messages/GraphQLBundle.properties

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ graphql=GraphQL
22

33
# Notifications
44
graphql.notification.introspection.error.title=GraphQL introspection
5-
graphql.notification.introspection.error.body=A valid schema could not be built using the introspection result.<br/>Error: {0}
6-
graphql.notification.introspection.spec.error.body=A valid schema could not be built using the introspection result. The endpoint may not follow the GraphQL Specification.<br/>Error: {0}
5+
graphql.notification.introspection.error.body=A valid schema could not be built using the introspection result.
6+
graphql.notification.introspection.spec.error.body=A valid schema could not be built using the introspection result. The endpoint may not follow the GraphQL Specification.
77
graphql.notification.introspection.parse.error=The server introspection response cannot be parsed as a valid JSON object.
88
graphql.notification.introspection.empty.schema.path=Please provide a valid path to schema in the config file. \
99
The first schema pattern should contain the correct path to a file in the file system. {0}
@@ -13,12 +13,13 @@ graphql.notification.introspection.endpoint.config.not.found=Configuration for t
1313
graphql.notification.introspection.unable.to.build.path=Unable to build a file path to save the introspection result
1414
graphql.notification.error.title=GraphQL error
1515
graphql.notification.ssl.cert.error.title=SSL certificate error
16-
graphql.notification.stack.trace=Stack trace
16+
graphql.notification.show.query.error.details.action=Show error
1717
graphql.notification.retry.without.defaults=Retry (skip default values from now on)
1818
graphql.notification.retry=Retry
19+
graphql.notification.content.request.to.url.failed=GraphQL request to {0} failed.
1920
graphql.notification.unable.to.open.editor=Unable to open an editor for ''{0}''
20-
graphql.notification.unable.to.create.file=Unable to create file ''{0}'' in directory ''{1}''.<br/>Error: {2}
21-
graphql.notification.unable.to.delete.file=Unable to delete file ''{0}'' in directory ''{1}''.<br/>Error: {2}
21+
graphql.notification.unable.to.create.file=Unable to create file ''{0}'' in directory ''{1}''.
22+
graphql.notification.unable.to.delete.file=Unable to delete file ''{0}'' in directory ''{1}''.
2223
graphql.notification.invalid.config.file=Invalid GraphQL configuration file
2324
graphql.notification.open.file=Open ''{0}''
2425
graphql.notification.unable.to.parse.file=Unable to parse {0}
@@ -80,9 +81,10 @@ graphql.config.node.interpreter.error=Node.js interpreter not found
8081
graphql.config.reload=Reload
8182

8283
# Introspection
83-
graphql.introspection.missing.data=Expected `data` key to be present in query result.
84+
graphql.introspection.missing.data=Expected `data` key to be present in a query result.
8485
graphql.introspection.missing.schema=Expected `__schema` key to be present in query result data.
8586
graphql.introspection.errors=Introspection query returned errors: {0}
87+
graphql.introspection.capabilities.detection.failed.errors=Introspection capabilities detection query returned errors: {0}
8688
graphql.introspection.run.query=Run introspection query to generate GraphQL SDL schema file
8789

8890
# Environment variables

src/main/com/intellij/lang/jsgraphql/GraphQLSettings.kt

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@
88
package com.intellij.lang.jsgraphql
99

1010
import com.intellij.lang.jsgraphql.GraphQLSettings.GraphQLSettingsState
11-
import com.intellij.openapi.components.PersistentStateComponent
12-
import com.intellij.openapi.components.Service
13-
import com.intellij.openapi.components.State
14-
import com.intellij.openapi.components.Storage
11+
import com.intellij.openapi.components.*
1512
import com.intellij.openapi.project.Project
1613

1714
/**
@@ -43,12 +40,6 @@ class GraphQLSettings : PersistentStateComponent<GraphQLSettingsState> {
4340
state.enableIntrospectionDefaultValues = enableIntrospectionDefaultValues
4441
}
4542

46-
var isEnableIntrospectionRepeatableDirectives: Boolean
47-
get() = state.enableIntrospectionRepeatableDirectives
48-
set(enableIntrospectionRepeatableDirectives) {
49-
state.enableIntrospectionRepeatableDirectives = enableIntrospectionRepeatableDirectives
50-
}
51-
5243
var isOpenEditorWithIntrospectionResult: Boolean
5344
get() = state.openEditorWithIntrospectionResult
5445
set(openEditorWithIntrospectionResult) {
@@ -76,19 +67,17 @@ class GraphQLSettings : PersistentStateComponent<GraphQLSettingsState> {
7667
}
7768

7869
class GraphQLSettingsState {
79-
var introspectionQuery = ""
80-
var enableIntrospectionDefaultValues = true
81-
var enableIntrospectionRepeatableDirectives = false
82-
var openEditorWithIntrospectionResult = true
83-
var enableRelayModernFrameworkSupport = false
84-
var enableFederationSupport = false
85-
var enableApolloKotlinSupport = false
70+
var introspectionQuery: String = ""
71+
var enableIntrospectionDefaultValues: Boolean = true
72+
var openEditorWithIntrospectionResult: Boolean = true
73+
74+
var enableRelayModernFrameworkSupport: Boolean = false
75+
var enableFederationSupport: Boolean = false
76+
var enableApolloKotlinSupport: Boolean = false
8677
}
8778

8879
companion object {
8980
@JvmStatic
90-
fun getSettings(project: Project): GraphQLSettings {
91-
return project.getService(GraphQLSettings::class.java)
92-
}
81+
fun getSettings(project: Project): GraphQLSettings = project.service<GraphQLSettings>()
9382
}
9483
}

src/main/com/intellij/lang/jsgraphql/ide/config/migration/GraphQLMigrateLegacyConfigAction.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import com.intellij.lang.jsgraphql.ide.config.GraphQLConfigFactory
77
import com.intellij.lang.jsgraphql.ide.config.loader.GraphQLConfigLoader
88
import com.intellij.lang.jsgraphql.ide.config.serialization.GraphQLConfigPrinter
99
import com.intellij.lang.jsgraphql.ide.notifications.GRAPHQL_NOTIFICATION_GROUP_ID
10-
import com.intellij.lang.jsgraphql.ide.notifications.formatExceptionMessage
10+
import com.intellij.lang.jsgraphql.ide.notifications.addShowQueryErrorDetailsAction
1111
import com.intellij.notification.Notification
1212
import com.intellij.notification.NotificationType
1313
import com.intellij.notification.Notifications
@@ -76,10 +76,9 @@ class GraphQLMigrateLegacyConfigAction : AnAction() {
7676
"graphql.notification.unable.to.create.file",
7777
GraphQLConfigFactory.PREFERRED_CONFIG,
7878
dir.path,
79-
formatExceptionMessage(e)
8079
),
8180
NotificationType.ERROR,
82-
)
81+
).apply { addShowQueryErrorDetailsAction(project, this, e) }
8382
)
8483
return@runWriteCommandAction
8584
}
@@ -96,10 +95,9 @@ class GraphQLMigrateLegacyConfigAction : AnAction() {
9695
"graphql.notification.unable.to.delete.file",
9796
sourceFile.name,
9897
dir.path,
99-
formatExceptionMessage(e)
10098
),
10199
NotificationType.ERROR,
102-
)
100+
).apply { addShowQueryErrorDetailsAction(project, this, e) }
103101
)
104102
}
105103

src/main/com/intellij/lang/jsgraphql/ide/introspection/GraphQLIntrospectionJsonToSDLLineMarkerProvider.java

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,11 @@
1414
import com.intellij.json.psi.JsonObject;
1515
import com.intellij.json.psi.JsonProperty;
1616
import com.intellij.lang.jsgraphql.GraphQLBundle;
17-
import com.intellij.lang.jsgraphql.GraphQLSettings;
1817
import com.intellij.lang.jsgraphql.ide.notifications.GraphQLNotificationUtil;
1918
import com.intellij.notification.Notification;
2019
import com.intellij.notification.NotificationType;
2120
import com.intellij.notification.Notifications;
2221
import com.intellij.openapi.editor.markup.GutterIconRenderer;
23-
import com.intellij.openapi.progress.ProcessCanceledException;
2422
import com.intellij.openapi.project.Project;
2523
import com.intellij.openapi.util.Ref;
2624
import com.intellij.openapi.vfs.VirtualFile;
@@ -29,67 +27,67 @@
2927
import org.jetbrains.annotations.NotNull;
3028
import org.jetbrains.annotations.Nullable;
3129

30+
import java.util.concurrent.CancellationException;
31+
3232
/**
3333
* Line marker which shows an action to turn a GraphQL Introspection JSON result into a GraphQL schema expressed in GraphQL SDL.
3434
*/
3535
public final class GraphQLIntrospectionJsonToSDLLineMarkerProvider implements LineMarkerProvider {
3636
@Override
3737
public @Nullable LineMarkerInfo<?> getLineMarkerInfo(@NotNull PsiElement element) {
38-
final VirtualFile virtualFile = element.isValid() ? element.getContainingFile().getVirtualFile() : null;
38+
VirtualFile virtualFile = element.isValid() ? element.getContainingFile().getVirtualFile() : null;
3939
if (virtualFile != null && !virtualFile.isInLocalFileSystem()) {
4040
// skip in-memory JSON files such as the query result viewer
4141
return null;
4242
}
4343
if (!(element instanceof JsonProperty jsonProperty)) {
4444
return null;
4545
}
46-
final Project project = element.getProject();
47-
final JsonProperty parentProperty = PsiTreeUtil.getParentOfType(element, JsonProperty.class);
46+
Project project = element.getProject();
47+
JsonProperty parentProperty = PsiTreeUtil.getParentOfType(element, JsonProperty.class);
4848
if (parentProperty != null && !"data".equals(parentProperty.getName())) {
4949
return null;
5050
}
5151

5252
// top level property or inside data property
53-
final String propertyName = jsonProperty.getName();
53+
String propertyName = jsonProperty.getName();
5454
if (!"__schema".equals(propertyName) || !(jsonProperty.getValue() instanceof JsonObject)) {
5555
return null;
5656
}
5757

5858
for (JsonProperty property : ((JsonObject)jsonProperty.getValue()).getPropertyList()) {
5959
if ("types".equals(property.getName()) && property.getValue() instanceof JsonArray) {
6060
// likely a GraphQL schema with a { __schema: { types: [] } }
61-
final GraphQLIntrospectionService introspectionService = GraphQLIntrospectionService.getInstance(project);
62-
final Ref<Runnable> generateAction = Ref.create();
61+
Ref<Runnable> generateAction = Ref.create();
6362
generateAction.set(() -> {
6463
try {
65-
final String introspectionJson = element.getContainingFile().getText();
66-
final String schemaAsSDL = introspectionService.printIntrospectionAsGraphQL(introspectionJson);
64+
String introspectionJson = element.getContainingFile().getText();
65+
String schemaAsSDL = GraphQLIntrospectionService.printIntrospectionAsGraphQL(project, introspectionJson);
6766

68-
final VirtualFile jsonFile = element.getContainingFile().getVirtualFile();
69-
final String outputFileName = jsonFile.getName() + ".graphql";
67+
VirtualFile jsonFile = element.getContainingFile().getVirtualFile();
68+
String outputFileName = jsonFile.getNameWithoutExtension() + ".graphql";
7069

71-
introspectionService.createOrUpdateIntrospectionOutputFile(
72-
schemaAsSDL,
73-
GraphQLIntrospectionService.IntrospectionOutputFormat.SDL,
70+
GraphQLIntrospectionService.createOrUpdateIntrospectionOutputFile(
71+
project,
72+
new GraphQLIntrospectionService.IntrospectionOutput(schemaAsSDL, GraphQLIntrospectionService.IntrospectionOutputFormat.SDL),
7473
outputFileName,
7574
jsonFile.getParent()
7675
);
7776
}
78-
catch (ProcessCanceledException e) {
77+
catch (CancellationException e) {
7978
throw e;
8079
}
8180
catch (Exception e) {
8281
Notification notification = new Notification(
8382
GraphQLNotificationUtil.GRAPHQL_NOTIFICATION_GROUP_ID,
8483
GraphQLBundle.message("graphql.notification.introspection.error.title"),
85-
GraphQLNotificationUtil.formatExceptionMessage(e),
84+
GraphQLBundle.message("graphql.notification.introspection.error.body"),
8685
NotificationType.ERROR
87-
);
86+
).setImportant(true);
8887

89-
GraphQLNotificationUtil.addRetryFailedSchemaIntrospectionAction(
90-
notification, GraphQLSettings.getSettings(project), e, generateAction.get());
91-
introspectionService.addIntrospectionStackTraceAction(notification, e);
92-
Notifications.Bus.notify(notification.setImportant(true));
88+
GraphQLNotificationUtil.addRetryQueryForPossiblyInvalidIntrospectionSchemaAction(project, notification, e, generateAction.get());
89+
GraphQLNotificationUtil.addShowQueryErrorDetailsAction(project, notification, e);
90+
Notifications.Bus.notify(notification);
9391
}
9492
});
9593

0 commit comments

Comments
 (0)