diff --git a/package-lock.json b/package-lock.json
index de97614..543fb8e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
{
"name": "vscode-apimanagement",
- "version": "1.0.2-beta",
+ "version": "1.0.3-beta",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@azure/abort-controller": {
- "version": "1.0.3",
+ "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.2.tgz",
"integrity": "sha512-XUyTo+bcyxHEf+jlN2MXA7YU9nxVehaubngHV1MIZZaqYmZqykkoeAz/JMMEeR7t3TcyDwbFa3Zw8BZywmIx4g==",
"requires": {
@@ -4412,6 +4412,11 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
},
+ "graphql": {
+ "version": "15.5.0",
+ "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.5.0.tgz",
+ "integrity": "sha512-OmaM7y0kaK31NKG31q4YbD2beNYa6jBBKtMFT6gLYJljHLJr42IqJ8KX08u3Li/0ifzTU5HjmoOOrwa5BRLeDA=="
+ },
"growl": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
diff --git a/package.json b/package.json
index 6ee1a00..cd48d7d 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "vscode-apimanagement",
"displayName": "Azure API Management",
"description": "An Azure API Management extension for Visual Studio Code.",
- "version": "1.0.3",
+ "version": "1.0.3-beta",
"publisher": "ms-azuretools",
"icon": "resources/apim-icon-newone.png",
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
@@ -40,15 +40,20 @@
"onCommand:azureApiManagement.deleteOperation",
"onCommand:azureApiManagement.importOpenApiByFile",
"onCommand:azureApiManagement.importOpenApiByLink",
+ "onCommand:azureApiManagement.importGraphqlAPIByLink",
"onCommand:azureApiManagement.testOperation",
+ "onCommand:azureApiManagement.importGraphqlSchemaByFile",
"onCommand:azureApiManagement.openInPortal",
"onCommand:azureApiManagement.showApi",
"onCommand:azureApiManagement.showArmApi",
+ "onCommand:azureApiManagement.showArmGraphqlApi",
+ "onCommand:azureApiManagement.showGraphqlAPIQuery",
"onCommand:azureApiManagement.showArmApiOperation",
"onCommand:azureApiManagement.showArmProduct",
"onCommand:azureApiManagement.showServicePolicy",
"onCommand:azureApiManagement.showApiPolicy",
"onCommand:azureApiManagement.showOperationPolicy",
+ "onCommand:azureApiManagement.showGraphqlSchema",
"onCommand:azureApiManagement.createNamedValue",
"onCommand:azureApiManagement.deleteNamedValue",
"onCommand:azureApiManagement.updateNamedValue",
@@ -169,11 +174,26 @@
"title": "%azureApiManagement.importOpenApiByLink%",
"category": "Azure API Management"
},
+ {
+ "command": "azureApiManagement.importGraphqlAPIByLink",
+ "title": "%azureApiManagement.importGraphqlAPIByLink%",
+ "category": "Azure API Management"
+ },
{
"command": "azureApiManagement.testOperation",
"title": "%azureApiManagement.testOperation%",
"category": "Azure API Management"
},
+ {
+ "command": "azureApiManagement.importGraphqlSchemaByFile",
+ "title": "%azureApiManagement.importGraphqlSchemaByFile%",
+ "category": "Azure API Management"
+ },
+ {
+ "command": "azureApiManagement.showGraphqlSchema",
+ "title": "%azureApiManagement.showGraphqlSchema%",
+ "category": "Azure API Management"
+ },
{
"command": "azureApiManagement.openInPortal",
"title": "%azureApiManagement.openInPortal%",
@@ -215,6 +235,16 @@
"title": "%azureApiManagement.showArmApi%",
"category": "Azure API Management"
},
+ {
+ "command": "azureApiManagement.showGraphqlAPIQuery",
+ "title": "%azureApiManagement.showGraphqlAPIQuery%",
+ "category": "Azure API Management"
+ },
+ {
+ "command": "azureApiManagement.showArmGraphqlApi",
+ "title": "%azureApiManagement.showArmGraphqlApi%",
+ "category": "Azure API Management"
+ },
{
"command": "azureApiManagement.showArmApiOperation",
"title": "%azureApiManagement.showArmApiOperation%",
@@ -491,6 +521,11 @@
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementApis",
"group": "1@2"
},
+ {
+ "command": "azureApiManagement.importGraphqlAPIByLink",
+ "when": "view == azureApiManagementExplorer && viewItem == azureApiManagementApis",
+ "group": "1@3"
+ },
{
"command": "azureApiManagement.importFunctionApp",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementApis",
@@ -581,6 +616,21 @@
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementNamedValues",
"group": "1@1"
},
+ {
+ "command": "azureApiManagement.showGraphqlAPIQuery",
+ "when": "view == azureApiManagementExplorer && viewItem == azureApiManagementGraphqlObjectType",
+ "group": "1@1"
+ },
+ {
+ "command": "azureApiManagement.importGraphqlSchemaByFile",
+ "when": "view == azureApiManagementExplorer && viewItem == azureApiManagementGraphql",
+ "group": "1@1"
+ },
+ {
+ "command": "azureApiManagement.showGraphqlSchema",
+ "when": "view == azureApiManagementExplorer && viewItem == azureApiManagementGraphql",
+ "group": "1@2"
+ },
{
"command": "azureApiManagement.deleteNamedValue",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementNamedValue",
@@ -723,6 +773,7 @@
"await-notify": "1.0.1",
"decompress": "^4.2.1",
"fs-extra": "^4.0.2",
+ "graphql": "^15.5.0",
"guid-typescript": "^1.0.9",
"gulp-decompress": "^3.0.0",
"gulp-download": "0.0.1",
diff --git a/package.nls.json b/package.nls.json
index a623e6a..8b18f9a 100644
--- a/package.nls.json
+++ b/package.nls.json
@@ -2,6 +2,7 @@
"azureApiManagement.openInPortal": "Open in Portal",
"azureApiManagement.importOpenApiByFile": "Import from OpenAPI file",
"azureApiManagement.importOpenApiByLink": "Import from OpenAPI link",
+ "azureApiManagement.importGraphqlAPIByLink": "Import Graphql API from OpenAPI link",
"azureApiManagement.showExplorer": "Show or hide the Azure API Management Explorer.",
"azureApiManagement.showSavePrompt": "Show warning dialog on remote file uploading.",
"azureApiManagement.createService": "Create API Management in Azure",
@@ -9,13 +10,17 @@
"azureApiManagement.copySubscriptionKey" : "Copy Subscription Key",
"azureApiManagement.showApi": "Edit OpenAPI",
"azureApiManagement.showArmApi": "Edit API",
+ "azureApiManagement.showArmGraphqlApi": "Edit Graphql API",
"azureApiManagement.showArmApiOperation": "Edit Operation",
+ "azureApiManagement.showGraphqlAPIQuery": "Test Grapql Query",
"azureApiManagement.showServicePolicy": "Edit Global policy",
"azureApiManagement.showApiPolicy": "Edit API policy",
"azureApiManagement.showOperationPolicy": "Edit Operation policy",
"azureApiManagement.deleteApi": "Delete API",
"azureApiManagement.deleteOperation": "Delete Operation",
"azureApiManagement.testOperation": "Test Operation",
+ "azureApiManagement.importGraphqlSchemaByFile": "Import Graphql API schema file",
+ "azureApiManagement.showGraphqlSchema": "Open Schema",
"azureApiManagement.advancedCreationDescription":"Enables advanced creation of Azure API Management Instance, which will prompt for several additional values instead of using a default.",
"azureApiManagement.createNamedValue": "Create Named value",
"azureApiManagement.deleteNamedValue": "Delete Named value",
diff --git a/resources/dark/leafnode.svg b/resources/dark/leafnode.svg
new file mode 100644
index 0000000..e2971d4
--- /dev/null
+++ b/resources/dark/leafnode.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/dark/mutation.svg b/resources/dark/mutation.svg
new file mode 100644
index 0000000..95d8beb
--- /dev/null
+++ b/resources/dark/mutation.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/dark/querylist.svg b/resources/dark/querylist.svg
new file mode 100644
index 0000000..748c55c
--- /dev/null
+++ b/resources/dark/querylist.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/dark/treelist.svg b/resources/dark/treelist.svg
new file mode 100644
index 0000000..dcf7226
--- /dev/null
+++ b/resources/dark/treelist.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/resources/light/leafnode.svg b/resources/light/leafnode.svg
new file mode 100644
index 0000000..c339a56
--- /dev/null
+++ b/resources/light/leafnode.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/light/mutation.svg b/resources/light/mutation.svg
new file mode 100644
index 0000000..ce678aa
--- /dev/null
+++ b/resources/light/mutation.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/light/querylist.svg b/resources/light/querylist.svg
new file mode 100644
index 0000000..77ad82c
--- /dev/null
+++ b/resources/light/querylist.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/resources/light/treelist.svg b/resources/light/treelist.svg
new file mode 100644
index 0000000..a461b02
--- /dev/null
+++ b/resources/light/treelist.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/src/azure/apim/TempApiContract.ts b/src/azure/apim/TempApiContract.ts
new file mode 100644
index 0000000..35fa51c
--- /dev/null
+++ b/src/azure/apim/TempApiContract.ts
@@ -0,0 +1,25 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { AuthenticationSettingsContract, Protocol, SubscriptionKeyParameterNamesContract } from "@azure/arm-apimanagement/src/models";
+
+export interface IApiContract {
+ id: string;
+ name: string;
+ properties: {
+ displayName: string;
+ apiRevision: string;
+ authenticationSettings?: AuthenticationSettingsContract;
+ description: string;
+ isCurrent: boolean;
+ path: string;
+ protocols?: Protocol[];
+ serviceUrl: string;
+ subscriptionKeyParameterNames?: SubscriptionKeyParameterNamesContract;
+ subscriptionRequired?: boolean;
+ // tslint:disable-next-line: no-reserved-keywords
+ type: string;
+ };
+}
diff --git a/src/azure/apim/TempSchema.ts b/src/azure/apim/TempSchema.ts
new file mode 100644
index 0000000..94472b8
--- /dev/null
+++ b/src/azure/apim/TempSchema.ts
@@ -0,0 +1,18 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+export class TempSchema {
+ public id: string;
+ public name: string;
+ // tslint:disable-next-line: no-reserved-keywords
+ public type: string;
+ public location?: string;
+ public properties: {
+ contentType: string,
+ document: {
+ value: string
+ }
+ };
+}
diff --git a/src/commands/createGraphqlApi.ts b/src/commands/createGraphqlApi.ts
new file mode 100644
index 0000000..0d7e3b6
--- /dev/null
+++ b/src/commands/createGraphqlApi.ts
@@ -0,0 +1,85 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+//import { ServiceClient } from "@azure/ms-rest-js";
+import { ServiceClient } from "@azure/ms-rest-js";
+import { ProgressLocation, window } from "vscode";
+import { createGenericClient } from 'vscode-azureextensionui';
+import { IActionContext } from "../../extension.bundle";
+import { ApisTreeItem, IApiTreeItemContext } from "../explorer/ApisTreeItem";
+import { ServiceTreeItem } from "../explorer/ServiceTreeItem";
+import { ext } from "../extensionVariables";
+import { localize } from "../localize";
+import { apiUtil } from "../utils/apiUtil";
+
+// tslint:disable-next-line: export-name
+export async function createGraphqlApi(context: IActionContext & Partial, node?: ApisTreeItem): Promise {
+ if (!node) {
+ const serviceNode = await ext.tree.showTreeItemPicker(ServiceTreeItem.contextValue, context);
+ node = serviceNode.apisTreeItem;
+ }
+
+ const link = await askLink();
+ const apiName = await apiUtil.askApiName();
+ const path = await apiUtil.askPath();
+ const body = {
+ name : apiName,
+ properties: {
+ displayName: apiName,
+ subscriptionRequired: true,
+ serviceUrl: link,
+ path: path,
+ type: "graphql",
+ protocols: [
+ "https"
+ ]
+ }
+ };
+
+ await window.withProgress(
+ {
+ location: ProgressLocation.Notification,
+ title: localize("", `Creating new Graphql API'...`),
+ cancellable: false
+ },
+ async () => {
+ const client: ServiceClient = await createGenericClient(node?.root.credentials);
+ const response = await client.sendRequest({
+ method: "PUT",
+ body: body,
+ // tslint:disable-next-line: no-non-null-assertion
+ url: `https://management.azure.com/subscriptions/${node!.root.subscriptionId}/resourceGroups/${node!.root.resourceGroupName}/providers/Microsoft.ApiManagement/service/${node!.root.serviceName}/apis/${apiName}?api-version=2021-04-01-preview`
+ });
+
+ if (response.status !== 200 && response.status !== 201 && response.status !== 202) {
+ // tslint:disable-next-line: no-non-null-assertion
+ throw new Error(localize("", response.bodyAsText!));
+ }
+ // tslint:disable-next-line: no-console
+ console.log(response);
+ }
+ ).then(async () => {
+ window.showInformationMessage(localize("", `New Graphql API has been created!`));
+ node?.refresh(context);
+ });
+}
+
+async function askLink() : Promise {
+ const promptStr: string = localize('apiLinkPrompt', 'Specify a Graphql OpenAPI link.');
+ return (await ext.ui.showInputBox({
+ prompt: promptStr,
+ placeHolder: 'https://',
+ validateInput: async (value: string): Promise => {
+ value = value ? value.trim() : '';
+ const regexp = /http(s?):\/\/[\d\w][\d\w]*(\.[\d\w][\d\w-]*)*(:\d+)?(\/[\d\w-\.\?,'/\\\+&=:%\$#_]*)?/;
+ const isUrlValid = regexp.test(value);
+ if (!isUrlValid) {
+ return localize("invalidOpenApiLink", "Provide a valid link. example - https://petstore.swagger.io/v2/swagger.json");
+ } else {
+ return undefined;
+ }
+ }
+ })).trim();
+}
diff --git a/src/commands/importGraphqlSchema.ts b/src/commands/importGraphqlSchema.ts
new file mode 100644
index 0000000..8f607b3
--- /dev/null
+++ b/src/commands/importGraphqlSchema.ts
@@ -0,0 +1,73 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { ServiceClient } from '@azure/ms-rest-js';
+import * as fse from 'fs-extra';
+import { OpenDialogOptions, ProgressLocation, Uri, window, workspace } from "vscode";
+import { createGenericClient, IActionContext } from "vscode-azureextensionui";
+import { GraphqlApiTreeItem } from "../explorer/GraphqlApiTreeItem";
+import { ServiceTreeItem } from "../explorer/ServiceTreeItem";
+import { ext } from "../extensionVariables";
+import { localize } from '../localize';
+
+// tslint:disable-next-line: export-name
+export async function importGraphqlSchemaByFile(actionContext: IActionContext, node?: GraphqlApiTreeItem): Promise {
+ if (!node) {
+ const serviceNode = await ext.tree.showTreeItemPicker(ServiceTreeItem.contextValue, actionContext);
+ node = serviceNode;
+ }
+
+ const uris = await askSchemaDocument();
+ const uri = uris[0];
+ // tslint:disable-next-line: no-unsafe-any
+ const fileContent = await fse.readFile(uri.fsPath);
+ const documentString = fileContent.toString();
+ const body = {
+ properties: {
+ contentType: "application/vnd.ms-azure-apim.graphql.schema",
+ document: {
+ value: documentString
+ }
+ }
+ };
+ window.withProgress(
+ {
+ location: ProgressLocation.Notification,
+ title: localize("addSchema", `Add Graphql Schema to Graphql API ${node.root.apiName} ...`),
+ cancellable: false
+ },
+ // tslint:disable-next-line:no-non-null-assertion
+ async () => {
+ const client: ServiceClient = await createGenericClient(node?.root.credentials);
+ await client.sendRequest({
+ method: "PUT",
+ body: body,
+ // tslint:disable-next-line: no-non-null-assertion
+ url: `https://management.azure.com/subscriptions/${node!.root.subscriptionId}/resourceGroups/${node!.root.resourceGroupName}/providers/Microsoft.ApiManagement/service/${node!.root.serviceName}/apis/${node?.root.apiName}/schemas/default?api-version=2021-04-01-preview`
+ });
+ }
+ ).then(async () => {
+ // tslint:disable-next-line:no-non-null-assertion
+ await node!.refresh(actionContext);
+ window.showInformationMessage(localize("addSchema", `Add Schema to graphql API '${node?.root.apiName}' succesfully.`));
+ });
+}
+
+async function askSchemaDocument(): Promise {
+ const openDialogOptions: OpenDialogOptions = {
+ canSelectFiles: true,
+ canSelectFolders: false,
+ canSelectMany: false,
+ openLabel: "Import",
+ filters: {
+ JSON: ["js"]
+ }
+ };
+ const rootPath = workspace.rootPath;
+ if (rootPath) {
+ openDialogOptions.defaultUri = Uri.file(rootPath);
+ }
+ return await ext.ui.showOpenDialog(openDialogOptions);
+}
diff --git a/src/commands/showGraphqlAPIQuery.ts b/src/commands/showGraphqlAPIQuery.ts
new file mode 100644
index 0000000..dc33a98
--- /dev/null
+++ b/src/commands/showGraphqlAPIQuery.ts
@@ -0,0 +1,119 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { ServiceClient } from '@azure/ms-rest-js';
+import { GraphQLFieldMap, GraphQLInputObjectType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLScalarType } from 'graphql';
+import * as vscode from 'vscode';
+import { createGenericClient, IActionContext } from "vscode-azureextensionui";
+import { IApiContract } from '../azure/apim/TempApiContract';
+import { GraphqlObjectTypeTreeItem } from "../explorer/GraphqlObjectTypeTreeItem";
+import { ext } from "../extensionVariables";
+import { createTemporaryFile } from "../utils/fsUtil";
+import { writeToEditor } from '../utils/vscodeUtils';
+
+export async function showGraphqlAPIQuery(actionContext: IActionContext, node?: GraphqlObjectTypeTreeItem): Promise {
+ if (!node) {
+ node = await ext.tree.showTreeItemPicker(GraphqlObjectTypeTreeItem.contextValue, actionContext);
+ }
+
+ const query = node.object;
+ const fileName: string = node.root.apiName.concat("-").concat(query.name).concat(".http");
+ const localFilePath: string = await createTemporaryFile(fileName);
+ let data = "";
+
+ const client: ServiceClient = await createGenericClient(node?.root.credentials);
+ const apiContractString = await client.sendRequest({
+ method: "GET",
+ // tslint:disable-next-line: no-non-null-assertion
+ url: `https://management.azure.com/subscriptions/${node.root.subscriptionId}/resourceGroups/${node.root.resourceGroupName}/providers/Microsoft.ApiManagement/service/${node.root.serviceName}/apis/${node.root.apiName}?api-version=2021-04-01-preview`
+ });
+ // tslint:disable-next-line: no-unsafe-any
+ const apiTemp : IApiContract = apiContractString.parsedBody;
+ let queryBuilder = "";
+ const serviceUrl = `https://${node.root.serviceName}.azure-api.net/${apiTemp.properties.path}`;
+ const args = query.args;
+ let argParams = "";
+ let fieldStr = "";
+ let queryParams = "";
+ let variables = "";
+ for (const arg of args) {
+ if (arg.type instanceof GraphQLScalarType) {
+ argParams = argParams.concat("$").concat(arg.name).concat(": ").concat(arg.type.name).concat(", ");
+ queryParams = queryParams.concat(arg.name).concat(": $").concat(`${arg.name}, `);
+ variables = variables.concat(`"${arg.name}": "", `);
+ } else if (arg.type instanceof GraphQLNonNull && arg.type.ofType instanceof GraphQLScalarType) {
+ argParams = argParams.concat("$").concat(arg.name).concat(": ").concat(arg.type.ofType.name).concat("!").concat(", ");
+ queryParams = queryParams.concat(arg.name).concat(": $").concat(`${arg.name}, `);
+ variables = variables.concat(`"${arg.name}": "", `);
+ } else if (arg.type instanceof GraphQLInputObjectType) {
+ let fieldsStr = "";
+ let queryStr = "";
+ let varaibleStr = "";
+ const argFields = arg.type.getFields();
+ for (const argKey of Object.keys(argFields)) {
+ const argValue = argFields[argKey];
+ if (argValue.type instanceof GraphQLScalarType) {
+ fieldsStr = fieldsStr.concat(`$${argValue.name}: ${argValue.type.name}, `);
+ queryStr = queryStr.concat(`${argValue.name}: $${argValue.type.name}, `);
+ varaibleStr = varaibleStr.concat(`"${argValue.name}": "", `);
+ }
+ }
+ fieldsStr = fieldsStr.trimRight();
+ fieldsStr = fieldsStr.substring(0, fieldsStr.length - 1);
+ queryStr = queryStr.trimRight();
+ queryStr = queryStr.substring(0, queryStr.length - 1);
+ varaibleStr = varaibleStr.trimRight();
+ varaibleStr = varaibleStr.substring(0, varaibleStr.length - 1);
+
+ argParams = argParams.concat("$").concat(`${arg.name}: { ${fieldsStr} }`).concat(", ");
+ queryParams = queryParams.concat(`${arg.name}: `).concat(`{ ${queryStr} }`).concat(", ");
+ variables = variables.concat(`"${arg.name}": `).concat(`{ ${varaibleStr} }`).concat(", ");
+ }
+ }
+ argParams = argParams.trimRight();
+ argParams = argParams.substring(0, argParams.length - 1);
+ queryParams = queryParams.trimRight();
+ queryParams = queryParams.substring(0, queryParams.length - 1);
+ variables = variables.trimRight();
+ variables = variables.substring(0, variables.length - 1);
+
+ if (query.type instanceof GraphQLObjectType) {
+ fieldStr = fieldStr.concat(getFields(query.type.getFields()));
+ } else if (query.type instanceof GraphQLList && query.type.ofType instanceof GraphQLObjectType) {
+ fieldStr = fieldStr.concat(getFields(query.type.ofType.getFields()));
+ }
+
+ queryBuilder = queryBuilder.concat(` query(${argParams}) \n`).concat(`\t{ ${query.name} (${queryParams}) \n\t{ ${fieldStr}}}`);
+
+ const document: vscode.TextDocument = await vscode.workspace.openTextDocument(localFilePath);
+ //await fse.writeFile(localFilePath, data);
+ const textEditor: vscode.TextEditor = await vscode.window.showTextDocument(document);
+ data = data.concat("Post ").concat(serviceUrl).concat("\n\n").concat(`{ "query": "${queryBuilder}", \n\t"variables": {${variables}}}`);
+ // tslint:disable-next-line: strict-boolean-expressions
+ if (!!textEditor) {
+ await writeToEditor(textEditor, data);
+ await textEditor.document.save();
+ }
+ vscode.commands.executeCommand('setContext', 'isEditorEnabled', true);
+}
+
+// tslint:disable-next-line: no-any
+function getFields(fields: GraphQLFieldMap): string {
+ let resStr = "";
+ for (const fieldKey of Object.keys(fields)) {
+ const fieldValue = fields[fieldKey];
+ resStr = resStr.concat(fieldValue.name);
+ if (fieldValue.type instanceof GraphQLObjectType) {
+ resStr = resStr.concat(": ");
+ const childStr = getFields(fieldValue.type.getFields());
+ resStr = resStr.concat("{ ").concat(childStr).concat("}, ");
+ } else {
+ resStr = resStr.concat(", ");
+ }
+ }
+ resStr = resStr.trimRight();
+ resStr = resStr.substring(0, resStr.length - 1);
+ return resStr;
+}
diff --git a/src/explorer/ApiTreeItem.ts b/src/explorer/ApiTreeItem.ts
index fd371e7..e3f3968 100644
--- a/src/explorer/ApiTreeItem.ts
+++ b/src/explorer/ApiTreeItem.ts
@@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { ApiManagementModels } from "@azure/arm-apimanagement";
import { ApiContract } from "@azure/arm-apimanagement/src/models";
import { ProgressLocation, window } from "vscode";
import { AzureParentTreeItem, AzureTreeItem, DialogResponses, ISubscriptionContext, UserCancelledError } from "vscode-azureextensionui";
@@ -31,7 +30,7 @@ export class ApiTreeItem extends AzureParentTreeItem {
constructor(
parent: AzureParentTreeItem,
- public apiContract: ApiManagementModels.ApiContract,
+ public apiContract: ApiContract,
apiVersion?: string) {
super(parent);
@@ -42,7 +41,9 @@ export class ApiTreeItem extends AzureParentTreeItem {
}
this._name = nonNullProp(this.apiContract, 'name');
- this._root = this.createRoot(parent.root, this._name);
+ this._root = this.apiContract.apiType !== undefined ?
+ this.createRoot(parent.root, this._name, this.apiContract.apiType!.toString())
+ : this.createRoot(parent.root, this._name, undefined);
this._operationsTreeItem = new ApiOperationsTreeItem(this);
this.policyTreeItem = new ApiPolicyTreeItem(this);
}
@@ -103,7 +104,7 @@ export class ApiTreeItem extends AzureParentTreeItem {
this.apiContract = api;
this._name = nonNullProp(api, 'name');
this._label = this.getRevisionDisplayName(api);
- this._root = this.createRoot(this.root, this._name);
+ this._root = this.createRoot(this.root, this._name, this.apiContract.apiType!.toString());
this._operationsTreeItem = new ApiOperationsTreeItem(this);
this.policyTreeItem = new ApiPolicyTreeItem(this);
}
@@ -117,9 +118,10 @@ export class ApiTreeItem extends AzureParentTreeItem {
}
}
- private createRoot(subRoot: ISubscriptionContext, apiName: string): IApiTreeRoot {
+ private createRoot(subRoot: ISubscriptionContext, apiName: string, apiType: string | undefined): IApiTreeRoot {
return Object.assign({}, subRoot, {
- apiName: apiName
+ apiName: apiName,
+ apiType: apiType
});
}
}
diff --git a/src/explorer/ApisTreeItem.ts b/src/explorer/ApisTreeItem.ts
index 7a36794..e254f72 100644
--- a/src/explorer/ApisTreeItem.ts
+++ b/src/explorer/ApisTreeItem.ts
@@ -5,7 +5,9 @@
import { ApiManagementModels } from "@azure/arm-apimanagement";
import { ApiContract, ApiCreateOrUpdateParameter } from "@azure/arm-apimanagement/src/models";
-import { AzExtTreeItem, AzureParentTreeItem, ICreateChildImplContext } from "vscode-azureextensionui";
+import { ServiceClient } from "@azure/ms-rest-js";
+import { AzExtTreeItem, AzureParentTreeItem, createGenericClient, ICreateChildImplContext } from "vscode-azureextensionui";
+import { IApiContract } from "../azure/apim/TempApiContract";
import { topItemCount } from "../constants";
import { localize } from "../localize";
import { IOpenApiImportObject } from "../openApi/OpenApiImportObject";
@@ -14,6 +16,7 @@ import { processError } from "../utils/errorUtil";
import { treeUtils } from "../utils/treeUtils";
import { ApiTreeItem } from "./ApiTreeItem";
import { ApiVersionSetTreeItem } from "./ApiVersionSetTreeItem";
+import { GraphqlApiTreeItem } from "./GraphqlApiTreeItem";
import { IServiceTreeRoot } from "./IServiceTreeRoot";
export interface IApiTreeItemContext extends ICreateChildImplContext {
@@ -45,18 +48,30 @@ export class ApisTreeItem extends AzureParentTreeItem {
}
let apisToLoad : ApiContract[] = this.selectedApis;
+ let apisToLoad2 : IApiContract[] = [];
if (this.selectedApis.length === 0) {
- const apiCollection: ApiManagementModels.ApiCollection = this._nextLink === undefined ?
+ const apiCollection1: ApiManagementModels.ApiCollection = this._nextLink === undefined ?
await this.root.client.api.listByService(this.root.resourceGroupName, this.root.serviceName, { expandApiVersionSet: true, top: topItemCount }) :
await this.root.client.api.listByServiceNext(this._nextLink);
- this._nextLink = apiCollection.nextLink;
+ this._nextLink = apiCollection1.nextLink;
- apisToLoad = apiCollection.map((s) => s).filter(s => apiUtil.isNotApiRevision(s));
+ apisToLoad = apiCollection1.map((s) => s).filter(s => apiUtil.isNotApiRevision(s));
+
+ const client: ServiceClient = await createGenericClient(this.root.credentials);
+ const apiCollectionString = await client.sendRequest({
+ method: "GET",
+ // tslint:disable-next-line: no-non-null-assertion
+ url: `${this.root.environment.resourceManagerEndpointUrl}/subscriptions/${this.root.subscriptionId}/resourceGroups/${this.root.resourceGroupName}/providers/Microsoft.ApiManagement/service/${this.root.serviceName}/apis?api-version=2021-04-01-preview`
+ });
+ // tslint:disable-next-line: no-unsafe-any
+ const apiCollectionTemp : IApiContract[] = apiCollectionString.parsedBody.value;
+ apisToLoad2 = this.findGraphqlApis(apiCollectionTemp);
}
const versionSetMap: Map = new Map();
- return await this.createTreeItemsWithErrorHandling(
+ // tslint:disable-next-line: no-unnecessary-local-variable
+ const children1 = await this.createTreeItemsWithErrorHandling(
apisToLoad,
"invalidApiManagementApi",
async (api: ApiManagementModels.ApiContract) => {
@@ -81,6 +96,31 @@ export class ApisTreeItem extends AzureParentTreeItem {
(api: ApiManagementModels.ApiContract) => {
return api.name;
});
+
+ const children2 = await this.createTreeItemsWithErrorHandling(
+ apisToLoad2,
+ "invalidApiManagementApi",
+ async (api: IApiContract) => {
+ if (api.properties.isCurrent !== undefined && api.properties.isCurrent === true) {
+ return new GraphqlApiTreeItem(this, api);
+ }
+ return undefined;
+ },
+ (api: IApiContract) => {
+ return api.name;
+ }
+ );
+ return children1.concat(children2);
+ }
+
+ public findGraphqlApis(apiCollection : IApiContract[]): IApiContract[] {
+ const collection : IApiContract[] = [];
+ for (const api of apiCollection) {
+ if (api.properties.type === 'graphql') {
+ collection.push(api);
+ }
+ }
+ return collection;
}
public async createChildImpl(context: IApiTreeItemContext): Promise {
diff --git a/src/explorer/GraphqlApiTreeItem.ts b/src/explorer/GraphqlApiTreeItem.ts
new file mode 100644
index 0000000..7a6162f
--- /dev/null
+++ b/src/explorer/GraphqlApiTreeItem.ts
@@ -0,0 +1,139 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { ServiceClient } from "@azure/ms-rest-js";
+import { buildSchema, GraphQLField, GraphQLFieldMap, GraphQLNamedType, GraphQLObjectType, GraphQLSchema } from "graphql";
+import { AzureParentTreeItem, AzureTreeItem, createGenericClient, ISubscriptionContext } from "vscode-azureextensionui";
+import { IApiContract } from "../azure/apim/TempApiContract";
+import { TempSchema } from "../azure/apim/TempSchema";
+import { nonNullProp } from "../utils/nonNull";
+import { treeUtils } from "../utils/treeUtils";
+import { ApiPolicyTreeItem } from "./ApiPolicyTreeItem";
+import { GraphqlMutationsTreeItem } from "./GraphqlMutationsTreeItem";
+import { GraphqlQueriesTreeItem } from "./GraphqlQueriesTreeItem";
+//import { GraphqlSubscriptionsTreeItem } from "./GraphqlSubscriptionsTreeItem";
+import { IApiTreeRoot } from "./IApiTreeRoot";
+import { IServiceTreeRoot } from "./IServiceTreeRoot";
+
+// tslint:disable: no-any
+export class GraphqlApiTreeItem extends AzureParentTreeItem {
+ public static contextValue: string = 'azureApiManagementGraphql';
+ public contextValue: string = GraphqlApiTreeItem.contextValue;
+ public readonly commandId: string = 'azureApiManagement.showArmApi';
+ public policyTreeItem: ApiPolicyTreeItem;
+
+ private _name: string;
+ private _label: string;
+ private _root: IApiTreeRoot;
+ private _queriesTreeItem: GraphqlQueriesTreeItem;
+ private _mutationsTreeItem: GraphqlMutationsTreeItem;
+ //private _subscriptionsTreeItem: GraphqlSubscriptionsTreeItem;
+
+ constructor(
+ parent: AzureParentTreeItem,
+ public apiContract: IApiContract,
+ apiVersion?: string) {
+ super(parent);
+
+ if (!apiVersion) {
+ const label = nonNullProp(this.apiContract.properties, 'displayName');
+ this._label = label.concat(" (GraphqlAPI)");
+ } else {
+ this._label = apiVersion.concat(" (GraphqlAPI)");
+ }
+
+ this._name = nonNullProp(this.apiContract, 'name');
+ this._root = this.createRoot(parent.root, this._name, this.apiContract.properties.type);
+ this.policyTreeItem = new ApiPolicyTreeItem(this);
+ }
+
+ public get id(): string {
+ return this._label;
+ }
+
+ public get label(): string {
+ return this._label;
+ }
+
+ public get root(): IApiTreeRoot {
+ return this._root;
+ }
+
+ public get iconPath(): { light: string, dark: string } {
+ return treeUtils.getThemedIconPath('api');
+ }
+
+ public async loadMoreChildrenImpl(): Promise[]> {
+ const client: ServiceClient = await createGenericClient(this.root.credentials);
+ const schemasString = await client.sendRequest({
+ method: "GET",
+ // tslint:disable-next-line: no-non-null-assertion
+ url: `https://management.azure.com/subscriptions/${this.root.subscriptionId}/resourceGroups/${this.root.resourceGroupName}/providers/Microsoft.ApiManagement/service/${this.root.serviceName}/apis/${this.root.apiName}/schemas?api-version=2021-04-01-preview`
+ });
+ // tslint:disable: no-unsafe-any
+ // tslint:disable-next-line: no-unnecessary-local-variable
+ const schemas : TempSchema[] = schemasString.parsedBody.value;
+
+ const valueList: GraphQLSchema[] = [];
+ // tslint:disable-next-line: no-any
+ const queryTypes: GraphQLField[] = [];
+ const mutationTypes: GraphQLField[] = [];
+ const subscriptionTypes: GraphQLField[] = [];
+ for (const schema of schemas) {
+ const builtSchema : GraphQLSchema = buildSchema(schema.properties.document.value);
+ valueList.push(builtSchema);
+ const typeMap = builtSchema.getTypeMap();
+ for (const key of Object.keys(typeMap)) {
+ const value: GraphQLNamedType = typeMap[key];
+ if (key === "Query" && value instanceof GraphQLObjectType) {
+ const fields: GraphQLFieldMap = value.getFields();
+ for (const fieldKey of Object.keys(fields)) {
+ const fieldValue = fields[fieldKey];
+ queryTypes.push(fieldValue);
+ }
+ } else if (key === "Mutation" && value instanceof GraphQLObjectType) {
+ const fields: GraphQLFieldMap = value.getFields();
+ for (const fieldKey of Object.keys(fields)) {
+ const fieldValue = fields[fieldKey];
+ mutationTypes.push(fieldValue);
+ }
+ } else if (key === "Subscription" && value instanceof GraphQLObjectType) {
+ const fields: GraphQLFieldMap = value.getFields();
+ for (const fieldKey of Object.keys(fields)) {
+ const fieldValue = fields[fieldKey];
+ subscriptionTypes.push(fieldValue);
+ }
+ }
+ }
+ }
+ this._queriesTreeItem = new GraphqlQueriesTreeItem(this, queryTypes);
+ this._mutationsTreeItem = new GraphqlMutationsTreeItem(this, mutationTypes);
+ /*this._subscriptionsTreeItem = new GraphqlSubscriptionsTreeItem(this, subscriptionTypes);
+
+ return [this._queriesTreeItem, this._mutationsTreeItem, this._subscriptionsTreeItem, this.policyTreeItem];
+ */
+ return [this._queriesTreeItem, this._mutationsTreeItem, this.policyTreeItem];
+ }
+
+ public hasMoreChildrenImpl(): boolean {
+ return false;
+ }
+
+ private createRoot(subRoot: ISubscriptionContext, apiName: string, apiType: string): IApiTreeRoot {
+ return Object.assign({}, subRoot, {
+ apiName: apiName,
+ apiType: apiType
+ });
+ }
+}
diff --git a/src/explorer/GraphqlArgFieldTreeItem.ts b/src/explorer/GraphqlArgFieldTreeItem.ts
new file mode 100644
index 0000000..d146257
--- /dev/null
+++ b/src/explorer/GraphqlArgFieldTreeItem.ts
@@ -0,0 +1,41 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { GraphQLInputField } from "graphql";
+import { AzureParentTreeItem, AzureTreeItem } from "vscode-azureextensionui";
+import { localize } from "../localize";
+import { treeUtils } from "../utils/treeUtils";
+import { IApiTreeRoot } from "./IApiTreeRoot";
+
+// tslint:disable: no-any
+export class GraphqlArgFieldTreeItem extends AzureTreeItem {
+
+ public get iconPath(): { light: string, dark: string } {
+ return treeUtils.getThemedIconPath('op');
+ }
+ public static contextValue: string = 'azureApiManagementGraphqlArgFieldList';
+ public _label: string;
+ public contextValue: string = GraphqlArgFieldTreeItem.contextValue;
+ public readonly childTypeLabel: string = localize('azureApiManagement.graphqlArgFieldList', 'graphqlArgFieldList');
+ public argPath: string[];
+
+ private argField: GraphQLInputField;
+
+ public get label() : string {
+ return this._label;
+ }
+
+ constructor(
+ parent: AzureParentTreeItem,
+ // tslint:disable-next-line: no-any
+ argField: GraphQLInputField,
+ argPath: string[]) {
+ super(parent);
+ this.argField = argField;
+ this._label = this.argField.name;
+ this.argPath = argPath;
+ this.argPath.push(this.argField.name);
+ }
+}
diff --git a/src/explorer/GraphqlArgsLeafTreeItem.ts b/src/explorer/GraphqlArgsLeafTreeItem.ts
new file mode 100644
index 0000000..d32dee4
--- /dev/null
+++ b/src/explorer/GraphqlArgsLeafTreeItem.ts
@@ -0,0 +1,41 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { GraphQLArgument } from "graphql";
+import { AzureParentTreeItem, AzureTreeItem } from "vscode-azureextensionui";
+import { localize } from "../localize";
+import { treeUtils } from "../utils/treeUtils";
+import { IApiTreeRoot } from "./IApiTreeRoot";
+
+// tslint:disable: no-any
+export class GraphqlArgsLeafTreeItem extends AzureTreeItem {
+
+ public get iconPath(): { light: string, dark: string } {
+ return treeUtils.getThemedIconPath('op');
+ }
+ public static contextValue: string = 'azureApiManagementGraphqlArgsLeaf';
+ public _label: string;
+ public contextValue: string = GraphqlArgsLeafTreeItem.contextValue;
+ public readonly childTypeLabel: string = localize('azureApiManagement.graphqlArgsLeaf', 'graphqlArgsLeaf');
+ public argPath: string[];
+
+ private arg: GraphQLArgument;
+
+ public get label() : string {
+ return this._label;
+ }
+
+ constructor(
+ parent: AzureParentTreeItem,
+ // tslint:disable-next-line: no-any
+ arg: GraphQLArgument,
+ argPath: string[]) {
+ super(parent);
+ this.arg = arg;
+ this._label = this.arg.name;
+ this.argPath = argPath;
+ this.argPath.push(arg.name);
+ }
+}
diff --git a/src/explorer/GraphqlArgsTreeItem.ts b/src/explorer/GraphqlArgsTreeItem.ts
new file mode 100644
index 0000000..2c91667
--- /dev/null
+++ b/src/explorer/GraphqlArgsTreeItem.ts
@@ -0,0 +1,68 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { GraphQLArgument, GraphQLInputField, GraphQLInputFieldMap, GraphQLInputObjectType, GraphQLInputType } from "graphql";
+import { AzExtTreeItem, AzureParentTreeItem } from "vscode-azureextensionui";
+import { localize } from "../localize";
+import { treeUtils } from "../utils/treeUtils";
+import { GraphqlArgFieldTreeItem } from "./GraphqlArgFieldTreeItem";
+import { IApiTreeRoot } from "./IApiTreeRoot";
+
+// tslint:disable: no-any
+export class GraphqlArgsTreeItem extends AzureParentTreeItem {
+
+ public get iconPath(): { light: string, dark: string } {
+ return treeUtils.getThemedIconPath('op');
+ }
+ public static contextValue: string = 'azureApiManagementGraphqlArgsList';
+ public _label: string;
+ public contextValue: string = GraphqlArgsTreeItem.contextValue;
+ public readonly childTypeLabel: string = localize('azureApiManagement.graphqlArgsList', 'graphqlArgsList');
+ public argPath: string[];
+ private _nextLink: string | undefined;
+
+ private arg: GraphQLArgument;
+
+ public get label() : string {
+ return this._label;
+ }
+
+ constructor(
+ parent: AzureParentTreeItem,
+ // tslint:disable-next-line: no-any
+ arg: GraphQLArgument,
+ argPath: string[]) {
+ super(parent);
+ this.arg = arg;
+ this._label = this.arg.name;
+ this.argPath = argPath;
+ this.argPath.push(arg.name);
+ }
+
+ public hasMoreChildrenImpl(): boolean {
+ return this._nextLink !== undefined;
+ }
+
+ public async loadMoreChildrenImpl(clearCache: boolean): Promise {
+ if (clearCache) {
+ this._nextLink = undefined;
+ }
+ const args : GraphQLInputType = this.arg.type;
+ if (args instanceof GraphQLInputObjectType) {
+ const fields : GraphQLInputFieldMap = args.getFields();
+ const fieldValues : GraphQLInputField[] = Object.values(fields);
+ return await this.createTreeItemsWithErrorHandling(
+ fieldValues,
+ "invalidApiManagementGraphqlObjectTypes",
+ async (objectType: GraphQLInputField) => {
+ return new GraphqlArgFieldTreeItem(this, objectType, this.argPath);
+ } ,
+ (objectType: GraphQLInputField) => {
+ return objectType.name;
+ });
+ }
+ return [];
+ }
+}
diff --git a/src/explorer/GraphqlFieldsLeafTreeItem.ts b/src/explorer/GraphqlFieldsLeafTreeItem.ts
new file mode 100644
index 0000000..b8414eb
--- /dev/null
+++ b/src/explorer/GraphqlFieldsLeafTreeItem.ts
@@ -0,0 +1,47 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { GraphQLField } from "graphql";
+import { AzureParentTreeItem, AzureTreeItem } from "vscode-azureextensionui";
+import { localize } from "../localize";
+import { treeUtils } from "../utils/treeUtils";
+import { IApiTreeRoot } from "./IApiTreeRoot";
+
+// tslint:disable: no-any
+export class GraphqlFieldsLeafTreeItem extends AzureTreeItem {
+
+ public get iconPath(): { light: string, dark: string } {
+ return treeUtils.getThemedIconPath('leafnode');
+ }
+
+ public get label() : string {
+ return this._label;
+ }
+ public static contextValue: string = 'azureApiManagementGraphqlFieldsLeafNode';
+ public _label: string;
+ public contextValue: string = GraphqlFieldsLeafTreeItem.contextValue;
+ public readonly childTypeLabel: string = localize('azureApiManagement.graphqlFieldsLeafNode', 'graphqlFieldsLeafNode');
+ public fieldPath: string[];
+
+ private field: GraphQLField;
+
+ constructor(
+ parent: AzureParentTreeItem,
+ // tslint:disable-next-line: no-any
+ field: GraphQLField,
+ fieldPath: string[]) {
+ super(parent);
+ this.field = field;
+ this._label = this.field.name;
+ this.fieldPath = fieldPath;
+ this.fieldPath.push(this.field.name);
+ }
+}
diff --git a/src/explorer/GraphqlFieldsTreeItem.ts b/src/explorer/GraphqlFieldsTreeItem.ts
new file mode 100644
index 0000000..7959ebb
--- /dev/null
+++ b/src/explorer/GraphqlFieldsTreeItem.ts
@@ -0,0 +1,81 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { GraphQLField, GraphQLObjectType } from "graphql";
+import { AzExtTreeItem, AzureParentTreeItem } from "vscode-azureextensionui";
+import { localize } from "../localize";
+import { treeUtils } from "../utils/treeUtils";
+import { GraphqlFieldsLeafTreeItem } from "./GraphqlFieldsLeafTreeItem";
+import { IApiTreeRoot } from "./IApiTreeRoot";
+
+// tslint:disable: no-any
+export class GraphqlFieldsTreeItem extends AzureParentTreeItem {
+
+ public get iconPath(): { light: string, dark: string } {
+ return treeUtils.getThemedIconPath('treelist');
+ }
+
+ public get label() : string {
+ return this._label;
+ }
+ public static contextValue: string = 'azureApiManagementGraphqlFieldsList';
+ public _label: string;
+ public contextValue: string = GraphqlFieldsTreeItem.contextValue;
+ public readonly childTypeLabel: string = localize('azureApiManagement.graphqlFieldsList', 'graphqlFieldsList');
+ public fieldPath: string[];
+ private _nextLink: string | undefined;
+
+ private field: GraphQLField;
+
+ constructor(
+ parent: AzureParentTreeItem,
+ // tslint:disable-next-line: no-any
+ field: GraphQLField,
+ fieldPath: string[]) {
+ super(parent);
+ this.field = field;
+ this._label = this.field.name;
+ this.fieldPath = fieldPath;
+ this.fieldPath.push(this.field.name);
+ }
+
+ public hasMoreChildrenImpl(): boolean {
+ return this._nextLink !== undefined;
+ }
+
+ public async loadMoreChildrenImpl(clearCache: boolean): Promise {
+ if (clearCache) {
+ this._nextLink = undefined;
+ }
+ const fieldChildren = this.field.type;
+ if (fieldChildren instanceof GraphQLObjectType) {
+ const fields = Object.values(fieldChildren.getFields());
+ return await this.createTreeItemsWithErrorHandling(
+ fields,
+ "invalidApiManagementGraphqlObjectTypes",
+ async (objectType: GraphQLField) => {
+ if (objectType.type instanceof GraphQLObjectType) {
+ return new GraphqlFieldsTreeItem(this, objectType, this.fieldPath);
+ } else {
+ return new GraphqlFieldsLeafTreeItem(this, objectType, this.fieldPath);
+ }
+ },
+ (objectType: GraphQLField) => {
+ return objectType.name;
+ });
+ }
+ return [];
+ }
+}
diff --git a/src/explorer/GraphqlMutationsTreeItem.ts b/src/explorer/GraphqlMutationsTreeItem.ts
new file mode 100644
index 0000000..9ce8305
--- /dev/null
+++ b/src/explorer/GraphqlMutationsTreeItem.ts
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { GraphQLField } from "graphql";
+import { AzExtTreeItem, AzureParentTreeItem } from "vscode-azureextensionui";
+import { localize } from "../localize";
+import { treeUtils } from "../utils/treeUtils";
+import { GraphqlObjectTypeTreeItem } from "./GraphqlObjectTypeTreeItem";
+import { IApiTreeRoot } from "./IApiTreeRoot";
+
+// tslint:disable: no-any
+export class GraphqlMutationsTreeItem extends AzureParentTreeItem {
+
+ public get iconPath(): { light: string, dark: string } {
+ return treeUtils.getThemedIconPath('mutation');
+ }
+ public static contextValue: string = 'azureApiManagementGraphqlMutationsList';
+ public label: string = "Mutation";
+ public contextValue: string = GraphqlMutationsTreeItem.contextValue;
+ public readonly childTypeLabel: string = localize('azureApiManagement.graphqlMutationList', 'graphqlMutationList');
+ private _nextLink: string | undefined;
+
+ private mutationTypes: GraphQLField[];
+
+ constructor(
+ parent: AzureParentTreeItem,
+ // tslint:disable-next-line: no-any
+ mutationTypes: GraphQLField[]) {
+ super(parent);
+ this.mutationTypes = mutationTypes;
+ }
+
+ public hasMoreChildrenImpl(): boolean {
+ return this._nextLink !== undefined;
+ }
+
+ public async loadMoreChildrenImpl(clearCache: boolean): Promise {
+ if (clearCache) {
+ this._nextLink = undefined;
+ }
+
+ return this.createTreeItemsWithErrorHandling(
+ this.mutationTypes,
+ "invalidApiManagementGraphqlObjectTypes",
+ async (objectType: GraphQLField) => new GraphqlObjectTypeTreeItem(this, objectType),
+ (objectType: GraphQLField) => {
+ return objectType.name;
+ });
+
+ }
+}
diff --git a/src/explorer/GraphqlObjectTypeTreeItem.ts b/src/explorer/GraphqlObjectTypeTreeItem.ts
new file mode 100644
index 0000000..beb5617
--- /dev/null
+++ b/src/explorer/GraphqlObjectTypeTreeItem.ts
@@ -0,0 +1,117 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { GraphQLArgument, GraphQLField, GraphQLFieldMap, GraphQLInputObjectType, GraphQLList, GraphQLObjectType, GraphQLOutputType } from "graphql";
+import { AzExtTreeItem, AzureParentTreeItem } from "vscode-azureextensionui";
+import { treeUtils } from "../utils/treeUtils";
+import { GraphqlArgsLeafTreeItem } from "./GraphqlArgsLeafTreeItem";
+import { GraphqlArgsTreeItem } from "./GraphqlArgsTreeItem";
+import { GraphqlFieldsLeafTreeItem } from "./GraphqlFieldsLeafTreeItem";
+import { GraphqlFieldsTreeItem } from "./GraphqlFieldsTreeItem";
+import { IApiTreeRoot } from "./IApiTreeRoot";
+
+// tslint:disable: no-any
+export class GraphqlObjectTypeTreeItem extends AzureParentTreeItem {
+ public static contextValue: string = 'azureApiManagementGraphqlObjectType';
+ public contextValue: string = GraphqlObjectTypeTreeItem.contextValue;
+ public readonly commandId: string = 'azureApiManagement.showGraphqlAPIQuery';
+
+ private _name: string;
+ private _label: string;
+
+ public get iconPath(): { light: string, dark: string } {
+ return treeUtils.getThemedIconPath('treelist');
+ }
+
+ public get label() : string {
+ return this._label;
+ }
+
+ public get id(): string {
+ return this._name;
+ }
+
+ constructor(
+ parent: AzureParentTreeItem,
+ // tslint:disable-next-line: no-any
+ public readonly object: GraphQLField) {
+ super(parent);
+ this._label = object.name;
+ this._name = object.name;
+ }
+
+ public async loadMoreChildrenImpl(): Promise {
+ const args: GraphQLArgument[] = this.object.args;
+ let allNodes: AzExtTreeItem[] = [];
+ const argsNodes = await this.createTreeItemsWithErrorHandling(
+ args,
+ "invalidApiManagementGraphqlObjectTypes",
+ async (objectType: GraphQLArgument) => {
+ if (objectType.type instanceof GraphQLInputObjectType) {
+ return new GraphqlArgsTreeItem(this, objectType, [this.object.name]);
+ } else {
+ return new GraphqlArgsLeafTreeItem(this, objectType, [this.object.name]);
+ }
+ },
+ (objectType: GraphQLArgument) => {
+ return objectType.name;
+ });
+ allNodes = allNodes.concat(argsNodes);
+
+ const types: GraphQLOutputType = this.object.type;
+ if (types instanceof GraphQLList && types.ofType instanceof GraphQLObjectType) {
+ const objectTypes: GraphQLObjectType = types.ofType;
+ const fields: GraphQLFieldMap = objectTypes.getFields();
+ const fieldValues = Object.values(fields);
+ const fieldNodes = await this.createTreeItemsWithErrorHandling(
+ fieldValues,
+ "invalidApiManagementGraphqlObjectTypes",
+ async (objectType: GraphQLField) => {
+ if (objectType.type instanceof GraphQLObjectType) {
+ return new GraphqlFieldsTreeItem(this, objectType, [this.object.name]);
+ } else {
+ return new GraphqlFieldsLeafTreeItem(this, objectType, [this.object.name]);
+ }
+ },
+ (objectType: GraphQLField) => {
+ return objectType.name;
+ });
+ allNodes = allNodes.concat(fieldNodes);
+ } else if (types instanceof GraphQLObjectType) {
+ const fields: GraphQLFieldMap = types.getFields();
+ const fieldValues = Object.values(fields);
+ const fieldNodes = await this.createTreeItemsWithErrorHandling(
+ fieldValues,
+ "invalidApiManagementGraphqlObjectTypes",
+ async (objectType: GraphQLField) => {
+ if (objectType.type instanceof GraphQLObjectType) {
+ return new GraphqlFieldsTreeItem(this, objectType, [this.object.name]);
+ } else {
+ return new GraphqlFieldsLeafTreeItem(this, objectType, [this.object.name]);
+ }
+ },
+ (objectType: GraphQLField) => {
+ return objectType.name;
+ });
+ allNodes = allNodes.concat(fieldNodes);
+ }
+ return allNodes;
+ }
+
+ public hasMoreChildrenImpl(): boolean {
+ return false;
+ }
+}
diff --git a/src/explorer/GraphqlQueriesTreeItem.ts b/src/explorer/GraphqlQueriesTreeItem.ts
new file mode 100644
index 0000000..0018bec
--- /dev/null
+++ b/src/explorer/GraphqlQueriesTreeItem.ts
@@ -0,0 +1,59 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { GraphQLField } from "graphql";
+import { AzExtTreeItem, AzureParentTreeItem } from "vscode-azureextensionui";
+import { localize } from "../localize";
+import { treeUtils } from "../utils/treeUtils";
+import { GraphqlObjectTypeTreeItem } from "./GraphqlObjectTypeTreeItem";
+import { IApiTreeRoot } from "./IApiTreeRoot";
+
+// tslint:disable: no-any
+export class GraphqlQueriesTreeItem extends AzureParentTreeItem {
+
+ public get iconPath(): { light: string, dark: string } {
+ return treeUtils.getThemedIconPath('querylist');
+ }
+ public static contextValue: string = 'azureApiManagementGraphqlList';
+ public label: string = "Query";
+ public contextValue: string = GraphqlQueriesTreeItem.contextValue;
+ public readonly childTypeLabel: string = localize('azureApiManagement.graphqlList', 'graphqlList');
+ private _nextLink: string | undefined;
+
+ private queryTypes: GraphQLField[];
+
+ constructor(
+ parent: AzureParentTreeItem,
+ queryTypes: GraphQLField[]) {
+ super(parent);
+ this.queryTypes = queryTypes;
+ }
+
+ public hasMoreChildrenImpl(): boolean {
+ return this._nextLink !== undefined;
+ }
+
+ public async loadMoreChildrenImpl(clearCache: boolean): Promise {
+ if (clearCache) {
+ this._nextLink = undefined;
+ }
+
+ return this.createTreeItemsWithErrorHandling(
+ this.queryTypes,
+ "invalidApiManagementGraphqlObjectTypes",
+ async (objectType: GraphQLField) => new GraphqlObjectTypeTreeItem(this, objectType),
+ (objectType: GraphQLField) => {
+ return objectType.name;
+ });
+ }
+}
diff --git a/src/explorer/GraphqlSchemaTreeItem.ts b/src/explorer/GraphqlSchemaTreeItem.ts
new file mode 100644
index 0000000..d772020
--- /dev/null
+++ b/src/explorer/GraphqlSchemaTreeItem.ts
@@ -0,0 +1,126 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { ApiContract } from "@azure/arm-apimanagement/src/models";
+import { ProgressLocation, window } from "vscode";
+import { AzureParentTreeItem, AzureTreeItem, DialogResponses, ISubscriptionContext, UserCancelledError } from "vscode-azureextensionui";
+import { localize } from "../localize";
+import { nonNullProp } from "../utils/nonNull";
+import { treeUtils } from "../utils/treeUtils";
+import { ApiOperationsTreeItem } from "./ApiOperationsTreeItem";
+import { ApiOperationTreeItem } from "./ApiOperationTreeItem";
+import { ApiPolicyTreeItem } from "./ApiPolicyTreeItem";
+import { IApiTreeRoot } from "./IApiTreeRoot";
+import { IServiceTreeRoot } from "./IServiceTreeRoot";
+import { OperationPolicyTreeItem } from "./OperationPolicyTreeItem";
+
+// tslint:disable: no-non-null-assertion
+export class GraphqlSchemaTreeItem extends AzureParentTreeItem {
+ public static contextValue: string = 'azureApiManagementGraphqlSchema';
+ public contextValue: string = GraphqlSchemaTreeItem.contextValue;
+ public policyTreeItem: ApiPolicyTreeItem;
+
+ private _name: string;
+ private _label: string;
+ private _root: IApiTreeRoot;
+ private _operationsTreeItem: ApiOperationsTreeItem;
+
+ constructor(
+ parent: AzureParentTreeItem,
+ public apiContract: ApiContract,
+ apiVersion?: string) {
+ super(parent);
+
+ if (!apiVersion) {
+ this._label = nonNullProp(this.apiContract, 'displayName');
+ } else {
+ this._label = apiVersion;
+ }
+
+ this._name = nonNullProp(this.apiContract, 'name');
+ this._root = this.apiContract.apiType !== undefined ?
+ this.createRoot(parent.root, this._name, this.apiContract.apiType!.toString())
+ : this.createRoot(parent.root, this._name, undefined);
+ this._operationsTreeItem = new ApiOperationsTreeItem(this);
+ this.policyTreeItem = new ApiPolicyTreeItem(this);
+ }
+
+ public get id(): string {
+ return this._name;
+ }
+
+ public get label() : string {
+ return this._label;
+ }
+
+ public get root(): IApiTreeRoot {
+ return this._root;
+ }
+
+ public get iconPath(): { light: string, dark: string } {
+ return treeUtils.getThemedIconPath('api');
+ }
+
+ public async loadMoreChildrenImpl(): Promise[]> {
+ return [this._operationsTreeItem, this.policyTreeItem];
+ }
+
+ public hasMoreChildrenImpl(): boolean {
+ return false;
+ }
+
+ public async deleteTreeItemImpl(): Promise {
+ const message: string = localize("confirmDeleteApi", `Are you sure you want to delete API '${this.root.apiName}' and its contents?`);
+ const result = await window.showWarningMessage(message, { modal: true }, DialogResponses.deleteResponse, DialogResponses.cancel);
+ if (result === DialogResponses.deleteResponse) {
+ const deletingMessage: string = localize("deletingApi", `Deleting API "${this.root.apiName}"...`);
+ await window.withProgress({ location: ProgressLocation.Notification, title: deletingMessage }, async () => {
+ await this.root.client.api.deleteMethod(this.root.resourceGroupName, this.root.serviceName, this.root.apiName, '*');
+ });
+ // don't wait
+ window.showInformationMessage(localize("deletedApi", `Successfully deleted API "${this.root.apiName}".`));
+
+ } else {
+ throw new UserCancelledError();
+ }
+ }
+
+ public pickTreeItemImpl(expectedContextValues: (string | RegExp)[]): AzureTreeItem | undefined {
+ for (const expectedContextValue of expectedContextValues) {
+ switch (expectedContextValue) {
+ case OperationPolicyTreeItem.contextValue:
+ case ApiOperationTreeItem.contextValue:
+ return this._operationsTreeItem;
+ default:
+ }
+ }
+ return undefined;
+ }
+
+ public async reloadApi(api: ApiContract): Promise {
+ this.apiContract = api;
+ this._name = nonNullProp(api, 'name');
+ this._label = this.getRevisionDisplayName(api);
+ this._root = this.createRoot(this.root, this._name, this.apiContract.apiType!.toString());
+ this._operationsTreeItem = new ApiOperationsTreeItem(this);
+ this.policyTreeItem = new ApiPolicyTreeItem(this);
+ }
+
+ private getRevisionDisplayName(api: ApiContract): string {
+ if (api.isCurrent !== undefined && api.isCurrent === true) {
+ return api.displayName!;
+ } else {
+ const revNumber = api.name!.split(';rev=')[1];
+ return api.displayName!.concat(';rev=', revNumber);
+ }
+ }
+
+ private createRoot(subRoot: ISubscriptionContext, apiName: string, apiType: string | undefined): IApiTreeRoot {
+ return Object.assign({}, subRoot, {
+ apiName: apiName,
+ apiType: apiType
+ });
+ }
+}
diff --git a/src/explorer/GraphqlSubscriptionsTreeItem.ts b/src/explorer/GraphqlSubscriptionsTreeItem.ts
new file mode 100644
index 0000000..e29a4f3
--- /dev/null
+++ b/src/explorer/GraphqlSubscriptionsTreeItem.ts
@@ -0,0 +1,65 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { GraphQLField } from "graphql";
+import { AzExtTreeItem, AzureParentTreeItem } from "vscode-azureextensionui";
+import { localize } from "../localize";
+import { treeUtils } from "../utils/treeUtils";
+import { GraphqlObjectTypeTreeItem } from "./GraphqlObjectTypeTreeItem";
+import { IApiTreeRoot } from "./IApiTreeRoot";
+
+// tslint:disable: no-any
+export class GraphqlSubscriptionsTreeItem extends AzureParentTreeItem {
+
+ public get iconPath(): { light: string, dark: string } {
+ return treeUtils.getThemedIconPath('list');
+ }
+ public static contextValue: string = 'azureApiManagementGraphqlSubscriptionsList';
+ public label: string = "Subscription";
+ public contextValue: string = GraphqlSubscriptionsTreeItem.contextValue;
+ public readonly childTypeLabel: string = localize('azureApiManagement.graphqlSubscriptionList', 'graphqlSubscriptionList');
+ private _nextLink: string | undefined;
+
+ private mutationTypes: GraphQLField[];
+
+ constructor(
+ parent: AzureParentTreeItem,
+ // tslint:disable-next-line: no-any
+ mutationTypes: GraphQLField[]) {
+ super(parent);
+ this.mutationTypes = mutationTypes;
+ }
+
+ public hasMoreChildrenImpl(): boolean {
+ return this._nextLink !== undefined;
+ }
+
+ public async loadMoreChildrenImpl(clearCache: boolean): Promise {
+ if (clearCache) {
+ this._nextLink = undefined;
+ }
+
+ return this.createTreeItemsWithErrorHandling(
+ this.mutationTypes,
+ "invalidApiManagementGraphqlObjectTypes",
+ async (objectType: GraphQLField) => new GraphqlObjectTypeTreeItem(this, objectType),
+ (objectType: GraphQLField) => {
+ return objectType.name;
+ });
+
+ }
+}
diff --git a/src/explorer/IApiTreeRoot.ts b/src/explorer/IApiTreeRoot.ts
index 05414bb..7f4c6ac 100644
--- a/src/explorer/IApiTreeRoot.ts
+++ b/src/explorer/IApiTreeRoot.ts
@@ -7,4 +7,5 @@ import { IServiceTreeRoot } from "./IServiceTreeRoot";
export interface IApiTreeRoot extends IServiceTreeRoot {
apiName: string;
+ apiType: string | undefined;
}
diff --git a/src/explorer/editors/arm/ApiResourceEditor.ts b/src/explorer/editors/arm/ApiResourceEditor.ts
index 7a7487a..9d22cf5 100644
--- a/src/explorer/editors/arm/ApiResourceEditor.ts
+++ b/src/explorer/editors/arm/ApiResourceEditor.ts
@@ -4,7 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "@azure/arm-apimanagement";
-import { AzureTreeItem } from "vscode-azureextensionui";
+import { ServiceClient } from "@azure/ms-rest-js";
+import { AzureTreeItem, createGenericClient } from "vscode-azureextensionui";
+import { IApiContract } from "../../../azure/apim/TempApiContract";
import { IApiTreeRoot } from "../../IApiTreeRoot";
import { BaseArmResourceEditor } from "./BaseArmResourceEditor";
@@ -15,11 +17,36 @@ export class ApiResourceEditor extends BaseArmResourceEditor {
super();
}
- public async getDataInternal(context: AzureTreeItem): Promise {
+ public async getDataInternal(context: AzureTreeItem): Promise {
+ if (context.root.apiType !== undefined && context.root.apiType === 'graphql') {
+ const client: ServiceClient = await createGenericClient(context.root.credentials);
+ const apiString = await client.sendRequest({
+ method: "GET",
+ // tslint:disable-next-line: no-non-null-assertion
+ url: `https://management.azure.com/subscriptions/${context.root.subscriptionId}/resourceGroups/${context.root.resourceGroupName}/providers/Microsoft.ApiManagement/service/${context.root.serviceName}/apis/${context.root.apiName}?api-version=2021-04-01-preview`
+ });
+ // tslint:disable: no-unsafe-any
+ // tslint:disable-next-line: no-unnecessary-local-variable
+ const apiTemp : IApiContract = apiString.parsedBody;
+ return apiTemp;
+ }
return await context.root.client.api.get(context.root.resourceGroupName, context.root.serviceName, context.root.apiName);
}
- public async updateDataInternal(context: AzureTreeItem, payload: ApiManagementModels.ApiCreateOrUpdateParameter): Promise {
+ public async updateDataInternal(context: AzureTreeItem, payload: ApiManagementModels.ApiCreateOrUpdateParameter): Promise {
+ if (context.root.apiType !== undefined && context.root.apiType === 'graphql') {
+ const client: ServiceClient = await createGenericClient(context.root.credentials);
+ const apiString = await client.sendRequest({
+ method: "PUT",
+ body: payload,
+ // tslint:disable-next-line: no-non-null-assertion
+ url: `https://management.azure.com/subscriptions/${context.root.subscriptionId}/resourceGroups/${context.root.resourceGroupName}/providers/Microsoft.ApiManagement/service/${context.root.serviceName}/apis/${context.root.apiName}?api-version=2021-04-01-preview`
+ });
+ // tslint:disable: no-unsafe-any
+ // tslint:disable-next-line: no-unnecessary-local-variable
+ const apiTemp : IApiContract = apiString.parsedBody;
+ return apiTemp;
+ }
return await context.root.client.api.createOrUpdate(context.root.resourceGroupName, context.root.serviceName, context.root.apiName, payload);
}
}
diff --git a/src/explorer/editors/graphqlApiSchema/GraphqlApiSchemaEditor.ts b/src/explorer/editors/graphqlApiSchema/GraphqlApiSchemaEditor.ts
new file mode 100644
index 0000000..9562982
--- /dev/null
+++ b/src/explorer/editors/graphqlApiSchema/GraphqlApiSchemaEditor.ts
@@ -0,0 +1,74 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.md in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { ServiceClient } from "@azure/ms-rest-js";
+import { ProgressLocation, window } from "vscode";
+import { createGenericClient } from "vscode-azureextensionui";
+import { showSavePromptConfigKey } from "../../../constants";
+import { localize } from "../../../localize";
+import { GraphqlApiTreeItem } from "../../GraphqlApiTreeItem";
+import { Editor } from "../Editor";
+
+// tslint:disable: no-unsafe-any
+export class GraphqlApiSchemaEditor extends Editor {
+ constructor() {
+ super(showSavePromptConfigKey);
+ }
+
+ public async getData(context: GraphqlApiTreeItem): Promise {
+ const client: ServiceClient = await createGenericClient(context.root.credentials);
+ const schemasString = await client.sendRequest({
+ method: "GET",
+ // tslint:disable-next-line: no-non-null-assertion
+ url: `https://management.azure.com/subscriptions/${context.root.subscriptionId}/resourceGroups/${context.root.resourceGroupName}/providers/Microsoft.ApiManagement/service/${context.root.serviceName}/apis/${context.root.apiName}/schemas/default?api-version=2021-04-01-preview`
+ });
+ return schemasString.parsedBody.properties.document.value;
+ }
+
+ public async updateData(context: GraphqlApiTreeItem, data: string): Promise {
+ return window.withProgress(
+ {
+ location: ProgressLocation.Notification,
+ title: localize("updateAPISchema", `Applying changes to Graphql API schema '${context.root.apiName}' in API Management instance ${context.root.serviceName}...`),
+ cancellable: false
+ },
+ async () => {
+ const body = {
+ properties: {
+ contentType: "application/vnd.ms-azure-apim.graphql.schema",
+ document: {
+ value: data
+ }
+ }
+ };
+
+ const client: ServiceClient = await createGenericClient(context.root.credentials);
+ await client.sendRequest({
+ method: "PUT",
+ body: body,
+ // tslint:disable-next-line: no-non-null-assertion
+ url: `https://management.azure.com/subscriptions/${context.root.subscriptionId}/resourceGroups/${context.root.resourceGroupName}/providers/Microsoft.ApiManagement/service/${context.root.serviceName}/apis/${context.root.apiName}/schemas/default?api-version=2021-04-01-preview`
+ });
+ }
+ ).then(async () => {
+ window.showInformationMessage(localize("updateAPISchema", `Changes to API '${context.apiContract.name}' were succefully uploaded to cloud.`));
+ //await context.refresh();
+ return this.getData(context);
+ });
+ }
+
+ public async getFilename(context: GraphqlApiTreeItem): Promise {
+ return `${context.root.serviceName}-${context.root.apiName}-schema.js`;
+ }
+ public async getDiffFilename(context: GraphqlApiTreeItem): Promise {
+ return `${context.root.serviceName}-${context.root.apiName}-schema-temp.js`;
+ }
+ public async getSaveConfirmationText(context: GraphqlApiTreeItem): Promise {
+ return localize("", `Saving will update the Grapql API Schema '${context.apiContract.name}'.`);
+ }
+ public async getSize(_context: GraphqlApiTreeItem): Promise {
+ throw new Error("Method not implemented.");
+ }
+}
diff --git a/src/explorer/editors/policy/ApiPolicyEditor.ts b/src/explorer/editors/policy/ApiPolicyEditor.ts
index 2a86e19..dc63da2 100644
--- a/src/explorer/editors/policy/ApiPolicyEditor.ts
+++ b/src/explorer/editors/policy/ApiPolicyEditor.ts
@@ -4,20 +4,53 @@
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "@azure/arm-apimanagement";
-import { AzureTreeItem } from "vscode-azureextensionui";
+import { ServiceClient } from "@azure/ms-rest-js";
+import { AzureTreeItem, createGenericClient } from "vscode-azureextensionui";
import { emptyPolicyXml, policyFormat } from "../../../constants";
import { IApiTreeRoot } from "../../IApiTreeRoot";
import { BasePolicyEditor } from "./BasePolicyEditor";
export class ApiPolicyEditor extends BasePolicyEditor {
public async getPolicy(context: AzureTreeItem): Promise {
+ if (context.root.apiType !== undefined && context.root.apiType === 'graphql') {
+
+ const client: ServiceClient = await createGenericClient(context.root.credentials);
+ const policyString = await client.sendRequest({
+ method: "GET",
+ // tslint:disable-next-line: no-non-null-assertion
+ url: `https://management.azure.com/subscriptions/${context.root.subscriptionId}/resourceGroups/${context.root.resourceGroupName}/providers/Microsoft.ApiManagement/service/${context.root.serviceName}/apis/${context.root.apiName}/policies/policy?api-version=2021-04-01-preview`
+ });
+ // tslint:disable: no-unsafe-any
+ // tslint:disable-next-line: no-unnecessary-local-variable
+ const policyTemp = policyString.parsedBody;
+ return policyTemp.properties.value;
+ }
const policy = await context.root.client.apiPolicy.get(context.root.resourceGroupName, context.root.serviceName, context.root.apiName, { format: policyFormat });
return policy._response.bodyAsText;
}
public async updatePolicy(context: AzureTreeItem, policy: ApiManagementModels.PolicyContract): Promise {
- const policyResult = await context.root.client.apiPolicy.createOrUpdate(context.root.resourceGroupName, context.root.serviceName, context.root.apiName, policy);
- return policyResult._response.bodyAsText;
+ if (context.root.apiType !== undefined && context.root.apiType === 'graphql') {
+ const body = {
+ properties: {
+ value: policy.value,
+ format: policy.format
+ }
+ };
+ const client: ServiceClient = await createGenericClient(context.root.credentials);
+ const policyString = await client.sendRequest({
+ method: "PUT",
+ body: body,
+ // tslint:disable-next-line: no-non-null-assertion
+ url: `https://management.azure.com/subscriptions/${context.root.subscriptionId}/resourceGroups/${context.root.resourceGroupName}/providers/Microsoft.ApiManagement/service/${context.root.serviceName}/apis/${context.root.apiName}/policies/policy?api-version=2021-04-01-preview`
+ });
+ // tslint:disable: no-unsafe-any
+ // tslint:disable-next-line: no-unnecessary-local-variable
+ const policyTemp = policyString.parsedBody;
+ return policyTemp.properties.value;
+ }
+ const policyResult = await context.root.client.apiPolicy.createOrUpdate(context.root.resourceGroupName, context.root.serviceName, context.root.apiName, policy);
+ return policyResult._response.bodyAsText;
}
public getDefaultPolicy() : string {
diff --git a/src/extension.ts b/src/extension.ts
index 966425c..e71bf02 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -12,6 +12,7 @@ import { addApiFilter } from './commands/addApiFilter';
import { addApiToGateway } from './commands/addApiToGateway';
import { addApiToProduct } from './commands/addApiToProduct';
import { copySubscriptionKey } from './commands/copySubscriptionKey';
+import { createGraphqlApi } from './commands/createGraphqlApi';
import { createService } from './commands/createService';
import { debugPolicy } from './commands/debugPolicies/debugPolicy';
import { deleteNode } from './commands/deleteNode';
@@ -21,6 +22,7 @@ import { generateFunctions } from './commands/generateFunctions';
import { generateNewGatewayToken } from './commands/generateNewGatewayToken';
import { importFunctionApp } from './commands/importFunctionApp/importFunctionApp';
import { importFunctionAppToApi } from './commands/importFunctionApp/importFunctionApp';
+import { importGraphqlSchemaByFile } from './commands/importGraphqlSchema';
import { importOpenApi } from './commands/importOpenApi';
import { importWebApp, importWebAppToApi } from './commands/importWebApp/importWebApp';
import { createNamedValue, updateNamedValue } from './commands/manageNamedValue';
@@ -31,6 +33,7 @@ import { openWorkingFolder } from './commands/openWorkingFolder';
import { revisions } from './commands/revisions';
import { setCustomHostName } from './commands/setCustomHostName';
import { setupWorkingFolder } from './commands/setupWorkingFolder';
+import { showGraphqlAPIQuery } from './commands/showGraphqlAPIQuery';
import { testOperation } from './commands/testOperation';
import { doubleClickDebounceDelay } from './constants';
import { activate } from './debugger/extension';
@@ -42,6 +45,7 @@ import { AzureAccountTreeItem } from './explorer/AzureAccountTreeItem';
import { ApiResourceEditor } from './explorer/editors/arm/ApiResourceEditor';
import { OperationResourceEditor } from './explorer/editors/arm/OperationResourceEditor';
import { ProductResourceEditor } from './explorer/editors/arm/ProductResourceEditor';
+import { GraphqlApiSchemaEditor } from './explorer/editors/graphqlApiSchema/GraphqlApiSchemaEditor';
import { OpenApiEditor } from './explorer/editors/openApi/OpenApiEditor';
import { ApiPolicyEditor } from './explorer/editors/policy/ApiPolicyEditor';
import { OperationPolicyEditor } from './explorer/editors/policy/OperationPolicyEditor';
@@ -50,6 +54,8 @@ import { ServicePolicyEditor } from './explorer/editors/policy/ServicePolicyEdit
import { GatewayApisTreeItem } from './explorer/GatewayApisTreeItem';
import { GatewayApiTreeItem } from './explorer/GatewayApiTreeItem';
import { GatewayTreeItem } from './explorer/GatewayTreeItem';
+import { GraphqlApiTreeItem } from './explorer/GraphqlApiTreeItem';
+import { GraphqlObjectTypeTreeItem } from './explorer/GraphqlObjectTypeTreeItem';
import { NamedValuesTreeItem } from './explorer/NamedValuesTreeItem';
import { NamedValueTreeItem } from './explorer/NamedValueTreeItem';
import { OperationPolicyTreeItem } from './explorer/OperationPolicyTreeItem';
@@ -104,6 +110,7 @@ function registerCommands(tree: AzExtTreeDataProvider): void {
registerCommand('azureApiManagement.testOperation', testOperation);
registerCommand('azureApiManagement.importOpenApiByFile', async (context: IActionContext, node?: ApisTreeItem) => { await importOpenApi(context, node, false); });
registerCommand('azureApiManagement.importOpenApiByLink', async (context: IActionContext, node?: ApisTreeItem) => { await importOpenApi(context, node, true); });
+ registerCommand('azureApiManagement.importGraphqlAPIByLink', async (context: IActionContext, node?: ApisTreeItem) => { await createGraphqlApi(context, node); });
registerCommand('azureApiManagement.createNamedValue', async (context: IActionContext, node?: NamedValuesTreeItem) => { await createNamedValue(context, node); });
registerCommand('azureApiManagement.deleteNamedValue', async (context: IActionContext, node?: AzureTreeItem) => await deleteNode(context, NamedValueTreeItem.contextValue, node));
registerCommand('azureApiManagement.updateNamedValue', updateNamedValue);
@@ -123,6 +130,7 @@ function registerCommands(tree: AzExtTreeDataProvider): void {
registerCommand('azureApiManagement.generateKubernetesDeployment', generateKubernetesDeployment);
registerCommand('azureApiManagement.generateNewGatewayToken', generateNewGatewayToken);
registerCommand('azureApiManagement.debugPolicy', debugPolicy);
+ registerCommand('azureApiManagement.importGraphqlSchemaByFile', importGraphqlSchemaByFile);
registerCommand('azureApiManagement.openExtensionWorkspaceFolder', openWorkingFolder);
registerCommand('azureApiManagement.initializeExtensionWorkspaceFolder', setupWorkingFolder);
@@ -180,6 +188,21 @@ function registerEditors(context: vscode.ExtensionContext) : void {
vscode.commands.executeCommand('setContext', 'isEditorEnabled', true);
}, doubleClickDebounceDelay);
+ const graphqlApiSchemaEditor: GraphqlApiSchemaEditor = new GraphqlApiSchemaEditor();
+ context.subscriptions.push(graphqlApiSchemaEditor);
+ registerEvent('azureApiManagement.graphqlApiSchemaEditor.onDidSaveTextDocument',
+ vscode.workspace.onDidSaveTextDocument,
+ async (actionContext: IActionContext, doc: vscode.TextDocument) => {
+ await graphqlApiSchemaEditor.onDidSaveTextDocument(actionContext, context.globalState, doc);
+ });
+ registerCommand('azureApiManagement.showGraphqlSchema', async (actionContext: IActionContext, node?: GraphqlApiTreeItem) => {
+ if (!node) {
+ node = await ext.tree.showTreeItemPicker(GraphqlApiTreeItem.contextValue, actionContext);
+ }
+ await graphqlApiSchemaEditor.showEditor(node);
+ vscode.commands.executeCommand('setContext', 'isEditorEnabled', true);
+ }, doubleClickDebounceDelay);
+
const apiEditor: OpenApiEditor = new OpenApiEditor();
context.subscriptions.push(apiEditor);
registerEvent('azureApiManagement.apiEditor.onDidSaveTextDocument',
@@ -193,6 +216,8 @@ function registerEditors(context: vscode.ExtensionContext) : void {
vscode.commands.executeCommand('setContext', 'isEditorEnabled', true);
}, doubleClickDebounceDelay);
+ registerCommand('azureApiManagement.showGraphqlAPIQuery', async (actionContext: IActionContext, node?: GraphqlObjectTypeTreeItem) => await showGraphqlAPIQuery(actionContext, node), doubleClickDebounceDelay);
+
const servicePolicyEditor: ServicePolicyEditor = new ServicePolicyEditor();
context.subscriptions.push(servicePolicyEditor);
registerEvent('azureApiManagement.servicePolicyEditor.onDidSaveTextDocument',
diff --git a/src/test.http b/src/test.http
new file mode 100644
index 0000000..116fc2c
--- /dev/null
+++ b/src/test.http
@@ -0,0 +1,4 @@
+
+POST https://alzasloneuap03.azure-api.net/graphql-api
+
+{ "query": "query($id: ID!) { ship(id: $id) { id, active } }", "variables": { "id": "GOMSTREE" } }
diff --git a/src/utils/apiUtil.ts b/src/utils/apiUtil.ts
index 97db1dd..1c6412f 100644
--- a/src/utils/apiUtil.ts
+++ b/src/utils/apiUtil.ts
@@ -25,6 +25,13 @@ export namespace apiUtil {
})).trim();
}
+ export async function askPath(): Promise {
+ const pathPrompt: string = localize('', "Enter API Path.");
+ return (await ext.ui.showInputBox({
+ prompt: pathPrompt
+ })).trim();
+ }
+
export function genApiId(apiName: string): string {
const identifier = displayNameToIdentifier(apiName);
return `/apis/${identifier}`;