From 482460bdb9ee5ab466d2ba835c7e58fefda53d7f Mon Sep 17 00:00:00 2001 From: Samuel Michael Date: Thu, 10 Feb 2022 18:18:17 -0500 Subject: [PATCH 1/4] operationName for Apollo Federation Gateway #595 Added operationName key to dynamically add query, mutation, or subscription name to data object or anonymous in unnamed next is to make the key editable to user/opt-in --- .../introspection/GraphQLIntrospectionService.java | 2 +- .../ide/project/GraphQLUIProjectService.java | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/com/intellij/lang/jsgraphql/ide/introspection/GraphQLIntrospectionService.java b/src/main/com/intellij/lang/jsgraphql/ide/introspection/GraphQLIntrospectionService.java index 08e88af8..62b7c177 100644 --- a/src/main/com/intellij/lang/jsgraphql/ide/introspection/GraphQLIntrospectionService.java +++ b/src/main/com/intellij/lang/jsgraphql/ide/introspection/GraphQLIntrospectionService.java @@ -147,7 +147,7 @@ public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification noti final GraphQLSettings graphQLSettings = GraphQLSettings.getSettings(myProject); String query = buildIntrospectionQuery(graphQLSettings); - final String requestJson = "{\"query\":\"" + StringEscapeUtils.escapeJavaScript(query) + "\"}"; + final String requestJson = "{\"operationName\": \"IntrospectionQuery\", \"query\":\"" + StringEscapeUtils.escapeJavaScript(query) + "\"}"; HttpPost request = createRequest(endpoint, url, requestJson); Task.Backgroundable task = new IntrospectionQueryTask(request, schemaPath, introspectionSourceFile, retry, graphQLSettings, endpoint, url); ProgressManager.getInstance().run(task); diff --git a/src/main/com/intellij/lang/jsgraphql/ide/project/GraphQLUIProjectService.java b/src/main/com/intellij/lang/jsgraphql/ide/project/GraphQLUIProjectService.java index 9da86509..bf4715a0 100644 --- a/src/main/com/intellij/lang/jsgraphql/ide/project/GraphQLUIProjectService.java +++ b/src/main/com/intellij/lang/jsgraphql/ide/project/GraphQLUIProjectService.java @@ -80,6 +80,8 @@ import java.security.GeneralSecurityException; import java.util.List; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; public class GraphQLUIProjectService implements Disposable, FileEditorManagerListener, GraphQLConfigurationListener { @@ -311,7 +313,17 @@ public void executeGraphQL(Editor editor, VirtualFile virtualFile) { final GraphQLQueryContext context = GraphQLQueryContextHighlightVisitor.getQueryContextBufferAndHighlightUnused(editor); Map requestData = new HashMap<>(); - requestData.put("query", context.query); + Pattern pattern = Pattern.compile("(query|mutation|subscription) (.*) ", Pattern.CASE_INSENSITIVE); + Pattern pattern2 = Pattern.compile("(query|mutation|subscription)", Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(context.query); + Matcher matcher2 = pattern2.matcher(context.query); + if (matcher.find()) { + requestData.put("operationName", matcher.group(2)); + requestData.put("query", context.query); + } else if (matcher2.find()) { + requestData.put("operationName", "anonymous"); + requestData.put("query", matcher2.group(1) + " anonymous" + context.query.split("(query|mutation|subscription)")[1]); + } try { requestData.put("variables", getQueryVariables(editor)); } catch (JsonSyntaxException jse) { From 11964369556118f0bd4044a3196ac967a62b1ded Mon Sep 17 00:00:00 2001 From: Samuel Michael Date: Fri, 11 Feb 2022 01:24:00 -0500 Subject: [PATCH 2/4] make the key editable to user/opt-in with checkbox and text field for configuration --- .../lang/jsgraphql/GraphQLSettings.java | 20 +++++++++ .../GraphQLIntrospectionService.java | 14 +++++-- .../ide/project/GraphQLUIProjectService.java | 39 ++++++++++++------ .../ui/GraphQLProjectSettingsForm.form | 41 +++++++++++++++---- .../ui/GraphQLProjectSettingsForm.java | 11 ++++- 5 files changed, 102 insertions(+), 23 deletions(-) diff --git a/src/main/com/intellij/lang/jsgraphql/GraphQLSettings.java b/src/main/com/intellij/lang/jsgraphql/GraphQLSettings.java index c3ff9669..aaf0d12b 100644 --- a/src/main/com/intellij/lang/jsgraphql/GraphQLSettings.java +++ b/src/main/com/intellij/lang/jsgraphql/GraphQLSettings.java @@ -62,11 +62,20 @@ public String getIntrospectionQuery() { return myState.introspectionQuery; } + public String getOperationName() { + return myState.operationName; + } + public void setIntrospectionQuery(String introspectionQuery) { myState.introspectionQuery = introspectionQuery; settingsChanged(); } + public void setOperationName(String operationName) { + myState.operationName = operationName; + settingsChanged(); + } + public boolean isEnableIntrospectionDefaultValues() { return myState.enableIntrospectionDefaultValues; } @@ -114,18 +123,29 @@ public void setFederationSupportEnabled(boolean enableFederationSupport) { settingsChanged(); } + public boolean isOperationNameEnabled() { + return myState.enableOperationName; + } + + public void setOperationNameEnabled(boolean enableOperationName) { + myState.enableOperationName = enableOperationName; + settingsChanged(); + } + /** * The state class that is persisted as XML * NOTE!!!: 1. Class must be static, and 2. Fields must be public for settings serialization to work */ public static class GraphQLSettingsState { public String introspectionQuery = ""; + public String operationName = ""; public boolean enableIntrospectionDefaultValues = true; public boolean enableIntrospectionRepeatableDirectives = false; public boolean openEditorWithIntrospectionResult = true; public boolean enableRelayModernFrameworkSupport; public boolean enableFederationSupport = false; + public boolean enableOperationName = false; } } diff --git a/src/main/com/intellij/lang/jsgraphql/ide/introspection/GraphQLIntrospectionService.java b/src/main/com/intellij/lang/jsgraphql/ide/introspection/GraphQLIntrospectionService.java index 62b7c177..6ed4c237 100644 --- a/src/main/com/intellij/lang/jsgraphql/ide/introspection/GraphQLIntrospectionService.java +++ b/src/main/com/intellij/lang/jsgraphql/ide/introspection/GraphQLIntrospectionService.java @@ -146,9 +146,17 @@ public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification noti try { final GraphQLSettings graphQLSettings = GraphQLSettings.getSettings(myProject); String query = buildIntrospectionQuery(graphQLSettings); - - final String requestJson = "{\"operationName\": \"IntrospectionQuery\", \"query\":\"" + StringEscapeUtils.escapeJavaScript(query) + "\"}"; - HttpPost request = createRequest(endpoint, url, requestJson); + HttpPost request; + if (graphQLSettings.isOperationNameEnabled()) { + String operationName = graphQLSettings.getOperationName().isEmpty() ? "operationName" : graphQLSettings.getOperationName(); + final String requestJson = "{" + "\"" + operationName + "\"" + ": \"IntrospectionQuery\", \"query\":\"" + StringEscapeUtils.escapeJavaScript( + query) + "\"}"; + request = createRequest(endpoint, url, requestJson); + } else { + final String requestJson = "{\"query\":\"" + StringEscapeUtils.escapeJavaScript( + query) + "\"}"; + request = createRequest(endpoint, url, requestJson); + } Task.Backgroundable task = new IntrospectionQueryTask(request, schemaPath, introspectionSourceFile, retry, graphQLSettings, endpoint, url); ProgressManager.getInstance().run(task); } catch (IllegalStateException | IllegalArgumentException e) { diff --git a/src/main/com/intellij/lang/jsgraphql/ide/project/GraphQLUIProjectService.java b/src/main/com/intellij/lang/jsgraphql/ide/project/GraphQLUIProjectService.java index bf4715a0..fd4f6727 100644 --- a/src/main/com/intellij/lang/jsgraphql/ide/project/GraphQLUIProjectService.java +++ b/src/main/com/intellij/lang/jsgraphql/ide/project/GraphQLUIProjectService.java @@ -17,6 +17,7 @@ import com.intellij.json.JsonFileType; import com.intellij.lang.jsgraphql.GraphQLFileType; import com.intellij.lang.jsgraphql.GraphQLParserDefinition; +import com.intellij.lang.jsgraphql.GraphQLSettings; import com.intellij.lang.jsgraphql.ide.actions.GraphQLEditConfigAction; import com.intellij.lang.jsgraphql.ide.actions.GraphQLExecuteEditorAction; import com.intellij.lang.jsgraphql.ide.actions.GraphQLToggleVariablesAction; @@ -305,24 +306,20 @@ private JComponent createToolbar(ActionGroup actionGroup, JComponent parent) { } public void executeGraphQL(Editor editor, VirtualFile virtualFile) { + final GraphQLSettings graphQLSettings = GraphQLSettings.getSettings(myProject); final GraphQLEndpointsModel endpointsModel = editor.getUserData(GRAPH_QL_ENDPOINTS_MODEL); if (endpointsModel != null) { final GraphQLConfigEndpoint selectedEndpoint = endpointsModel.getSelectedItem(); if (selectedEndpoint != null && selectedEndpoint.url != null) { - final GraphQLConfigVariableAwareEndpoint endpoint = new GraphQLConfigVariableAwareEndpoint(selectedEndpoint, myProject, virtualFile); + final GraphQLConfigVariableAwareEndpoint endpoint = new GraphQLConfigVariableAwareEndpoint(selectedEndpoint, myProject, + virtualFile); final GraphQLQueryContext context = GraphQLQueryContextHighlightVisitor.getQueryContextBufferAndHighlightUnused(editor); Map requestData = new HashMap<>(); - Pattern pattern = Pattern.compile("(query|mutation|subscription) (.*) ", Pattern.CASE_INSENSITIVE); - Pattern pattern2 = Pattern.compile("(query|mutation|subscription)", Pattern.CASE_INSENSITIVE); - Matcher matcher = pattern.matcher(context.query); - Matcher matcher2 = pattern2.matcher(context.query); - if (matcher.find()) { - requestData.put("operationName", matcher.group(2)); + if (graphQLSettings.isOperationNameEnabled()) { + handleOperationName(graphQLSettings, context, requestData); + } else { requestData.put("query", context.query); - } else if (matcher2.find()) { - requestData.put("operationName", "anonymous"); - requestData.put("query", matcher2.group(1) + " anonymous" + context.query.split("(query|mutation|subscription)")[1]); } try { requestData.put("variables", getQueryVariables(editor)); @@ -333,7 +330,8 @@ public void executeGraphQL(Editor editor, VirtualFile virtualFile) { errorEditor.getContentComponent().grabFocus(); final VirtualFile errorFile = FileDocumentManager.getInstance().getFile(errorEditor.getDocument()); if (errorFile != null) { - final List errors = CodeSmellDetector.getInstance(myProject).findCodeSmells(Collections.singletonList(errorFile)); + final List errors = CodeSmellDetector.getInstance(myProject).findCodeSmells( + Collections.singletonList(errorFile)); for (CodeSmellInfo error : errors) { errorMessage = error.getDescription(); errorEditor.getCaretModel().moveToOffset(error.getTextRange().getStartOffset()); @@ -371,6 +369,22 @@ public void run(@NotNull ProgressIndicator indicator) { } } + private void handleOperationName(GraphQLSettings graphQLSettings, GraphQLQueryContext context, Map requestData) { + String operationName = graphQLSettings.getOperationName().isEmpty() ? "operationName" : graphQLSettings.getOperationName(); + Pattern pattern = Pattern.compile("(query|mutation|subscription) (.*) ", Pattern.CASE_INSENSITIVE); + Pattern pattern2 = Pattern.compile("(query|mutation|subscription)", Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(context.query); + Matcher matcher2 = pattern2.matcher(context.query); + if (matcher.find()) { + requestData.put(operationName, matcher.group(2)); + requestData.put("query", context.query); + } else if (matcher2.find()) { + requestData.put(operationName, "anonymous"); + requestData.put("query", + matcher2.group(1) + " anonymous" + context.query.split("(query|mutation|subscription)")[1]); + } + } + private void runQuery(Editor editor, VirtualFile virtualFile, GraphQLQueryContext context, String url, HttpPost request) { GraphQLIntrospectionService introspectionService = GraphQLIntrospectionService.getInstance(myProject); try { @@ -390,7 +404,8 @@ private void runQuery(Editor editor, VirtualFile virtualFile, GraphQLQueryContex sw.stop(); } - final boolean reformatJson = contentType != null && contentType.getValue() != null && contentType.getValue().startsWith("application/json"); + final boolean reformatJson = contentType != null && contentType.getValue() != null && contentType.getValue().startsWith( + "application/json"); final Integer errorCount = getErrorCount(responseJson); ApplicationManager.getApplication().invokeLater(() -> { TextEditor queryResultEditor = GraphQLToolWindow.getQueryResultEditor(myProject); diff --git a/src/main/com/intellij/lang/jsgraphql/ui/GraphQLProjectSettingsForm.form b/src/main/com/intellij/lang/jsgraphql/ui/GraphQLProjectSettingsForm.form index 43be108d..dafff06e 100644 --- a/src/main/com/intellij/lang/jsgraphql/ui/GraphQLProjectSettingsForm.form +++ b/src/main/com/intellij/lang/jsgraphql/ui/GraphQLProjectSettingsForm.form @@ -3,7 +3,7 @@ - + @@ -13,7 +13,7 @@ - + @@ -23,7 +23,7 @@ - + @@ -33,7 +33,7 @@ - + @@ -41,7 +41,7 @@ - + @@ -51,7 +51,7 @@ - + @@ -59,13 +59,40 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/com/intellij/lang/jsgraphql/ui/GraphQLProjectSettingsForm.java b/src/main/com/intellij/lang/jsgraphql/ui/GraphQLProjectSettingsForm.java index 5a454998..1ef60425 100644 --- a/src/main/com/intellij/lang/jsgraphql/ui/GraphQLProjectSettingsForm.java +++ b/src/main/com/intellij/lang/jsgraphql/ui/GraphQLProjectSettingsForm.java @@ -33,6 +33,9 @@ public class GraphQLProjectSettingsForm { private JCheckBox enableFederation; private JCheckBox openEditorWithIntrospectionResult; private JCheckBox enableIntrospectionRepeatableDirectives; + private JTextField operationNameTextField; + private JLabel operationNameLabel; + private JCheckBox enableOperationName; private GraphQLSettings mySettings; @@ -60,16 +63,20 @@ void apply() { mySettings.setRelaySupportEnabled(enableRelay.isSelected()); mySettings.setFederationSupportEnabled(enableFederation.isSelected()); + mySettings.setOperationNameEnabled(enableOperationName.isSelected()); + mySettings.setOperationName(operationNameTextField.getText()); } void reset() { introspectionQueryTextField.setText(mySettings.getIntrospectionQuery()); + operationNameTextField.setText(mySettings.getOperationName()); enableIntrospectionDefaultValues.setSelected(mySettings.isEnableIntrospectionDefaultValues()); enableIntrospectionRepeatableDirectives.setSelected(mySettings.isEnableIntrospectionRepeatableDirectives()); openEditorWithIntrospectionResult.setSelected(mySettings.isOpenEditorWithIntrospectionResult()); enableRelay.setSelected(mySettings.isRelaySupportEnabled()); enableFederation.setSelected(mySettings.isFederationSupportEnabled()); + enableOperationName.setSelected(mySettings.isOperationNameEnabled()); } boolean isModified() { @@ -78,9 +85,11 @@ boolean isModified() { boolean introspectionSettingsChanged() { return !Objects.equals(mySettings.getIntrospectionQuery(), introspectionQueryTextField.getText()) || + !Objects.equals(mySettings.getOperationName(), operationNameTextField.getText()) || mySettings.isEnableIntrospectionDefaultValues() != enableIntrospectionDefaultValues.isSelected() || mySettings.isEnableIntrospectionRepeatableDirectives() != enableIntrospectionRepeatableDirectives.isSelected() || - mySettings.isOpenEditorWithIntrospectionResult() != openEditorWithIntrospectionResult.isSelected(); + mySettings.isOpenEditorWithIntrospectionResult() != openEditorWithIntrospectionResult.isSelected() || + mySettings.isOperationNameEnabled() != openEditorWithIntrospectionResult.isSelected(); } boolean librarySettingsChanged() { From 36026bf1db9bad681a1339fa10d0bb2f62e5f522 Mon Sep 17 00:00:00 2001 From: Samuel Michael Date: Fri, 11 Feb 2022 01:36:54 -0500 Subject: [PATCH 3/4] fix ctrl c + v typo --- .../intellij/lang/jsgraphql/ui/GraphQLProjectSettingsForm.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/com/intellij/lang/jsgraphql/ui/GraphQLProjectSettingsForm.java b/src/main/com/intellij/lang/jsgraphql/ui/GraphQLProjectSettingsForm.java index 1ef60425..b348184f 100644 --- a/src/main/com/intellij/lang/jsgraphql/ui/GraphQLProjectSettingsForm.java +++ b/src/main/com/intellij/lang/jsgraphql/ui/GraphQLProjectSettingsForm.java @@ -89,7 +89,7 @@ boolean introspectionSettingsChanged() { mySettings.isEnableIntrospectionDefaultValues() != enableIntrospectionDefaultValues.isSelected() || mySettings.isEnableIntrospectionRepeatableDirectives() != enableIntrospectionRepeatableDirectives.isSelected() || mySettings.isOpenEditorWithIntrospectionResult() != openEditorWithIntrospectionResult.isSelected() || - mySettings.isOperationNameEnabled() != openEditorWithIntrospectionResult.isSelected(); + mySettings.isOperationNameEnabled() != enableOperationName.isSelected(); } boolean librarySettingsChanged() { From d1569a5123fdc43cf2c8e0ce60080b5a9a154096 Mon Sep 17 00:00:00 2001 From: Samuel Michael Date: Fri, 11 Feb 2022 02:49:26 -0500 Subject: [PATCH 4/4] fix regex to account for queries with params e.g. query myQuery( by capturing first set of (alphanumeric/underscores) in-between keyword and first non-alphanumeric character --- .../lang/jsgraphql/ide/project/GraphQLUIProjectService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/com/intellij/lang/jsgraphql/ide/project/GraphQLUIProjectService.java b/src/main/com/intellij/lang/jsgraphql/ide/project/GraphQLUIProjectService.java index fd4f6727..21814490 100644 --- a/src/main/com/intellij/lang/jsgraphql/ide/project/GraphQLUIProjectService.java +++ b/src/main/com/intellij/lang/jsgraphql/ide/project/GraphQLUIProjectService.java @@ -371,7 +371,7 @@ public void run(@NotNull ProgressIndicator indicator) { private void handleOperationName(GraphQLSettings graphQLSettings, GraphQLQueryContext context, Map requestData) { String operationName = graphQLSettings.getOperationName().isEmpty() ? "operationName" : graphQLSettings.getOperationName(); - Pattern pattern = Pattern.compile("(query|mutation|subscription) (.*) ", Pattern.CASE_INSENSITIVE); + Pattern pattern = Pattern.compile("(query|mutation|subscription)\\s(\\w+)", Pattern.CASE_INSENSITIVE); Pattern pattern2 = Pattern.compile("(query|mutation|subscription)", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(context.query); Matcher matcher2 = pattern2.matcher(context.query);