Skip to content

Commit

Permalink
feat: export api spec (#91)
Browse files Browse the repository at this point in the history
* feat: export open api

* feat: restore support export

* feat: support export api

* feat: export open api

* feat: restore support export

* feat: update

* feat: update

* feat: update

* feat: update method name

* feat: update

* feat: support command palette to export api

* test: add test cases

* test: add extension test cases

* test: revert extension tests

* test: update

* feat: update

* feat: add

* feat: update
  • Loading branch information
wenytang-ms authored Apr 15, 2024
1 parent 1377ba5 commit b912daf
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 37 deletions.
12 changes: 4 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@
"category": "Azure API Center"
},
{
"command": "azure-api-center.exportOpenApi",
"title": "%azure-api-center.commands.exportOpenApi.title%",
"command": "azure-api-center.exportApi",
"title": "%azure-api-center.commands.exportApi.title%",
"category": "Azure API Center"
},
{
Expand Down Expand Up @@ -194,8 +194,8 @@
"when": "view == apiCenterTreeView && viewItem =~ /never/"
},
{
"command": "azure-api-center.exportOpenApi",
"when": "view == apiCenterTreeView && viewItem =~ /never/"
"command": "azure-api-center.exportApi",
"when": "view == apiCenterTreeView && viewItem =~ /azureApiCenterApiVersionDefinitionTreeItem/"
},
{
"command": "azure-api-center.showOpenApi",
Expand Down Expand Up @@ -233,10 +233,6 @@
"command": "azure-api-center.importOpenApiByLink",
"when": "never"
},
{
"command": "azure-api-center.exportOpenApi",
"when": "never"
},
{
"command": "azure-api-center.showOpenApi",
"when": "never"
Expand Down
2 changes: 1 addition & 1 deletion package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"azure-api-center.commands.generate-api-client.title": "Generate API Client",
"azure-api-center.commands.importOpenApiByFile.title": "Import OpenAPI from File",
"azure-api-center.commands.importOpenApiByLink.title": "Import OpenAPI from Link",
"azure-api-center.commands.exportOpenApi.title": "Export OpenAPI",
"azure-api-center.commands.exportApi.title": "Export API",
"azure-api-center.commands.showOpenApi.title": "Edit OpenAPI",
"azure-api-center.commands.generateHttpFile.title": "Generate HTTP File",
"azure-api-center.commands.registerApi.title": "Register API",
Expand Down
4 changes: 4 additions & 0 deletions src/azure/ApiCenter/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,7 @@ export enum SpecificationName {
other = 'Other',
};

export enum ApiSpecExportResultFormat {
inline = 'inline',
link = 'link',
};
60 changes: 60 additions & 0 deletions src/commands/exportApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { getResourceGroupFromId } from "@microsoft/vscode-azext-azureutils";
import { IActionContext } from "@microsoft/vscode-azext-utils";
import * as fs from "fs-extra";
import * as path from "path";
import * as vscode from "vscode";
import { ApiCenterService } from "../azure/ApiCenter/ApiCenterService";
import { ApiSpecExportResultFormat } from "../azure/ApiCenter/contracts";
import { TelemetryClient } from '../common/telemetryClient';
import { ext } from "../extensionVariables";
import { ApiVersionDefinitionTreeItem } from "../tree/ApiVersionDefinitionTreeItem";
import { createTemporaryFolder } from "../utils/fsUtil";
export namespace ExportAPI {
export async function exportApi(
context: IActionContext,
node?: ApiVersionDefinitionTreeItem): Promise<void> {
if (!node) {
node = await ext.treeDataProvider.showTreeItemPicker<ApiVersionDefinitionTreeItem>(new RegExp(`${ApiVersionDefinitionTreeItem.contextValue}*`), context);
}

const apiCenterService = new ApiCenterService(
node?.subscription!,
getResourceGroupFromId(node?.id!),
node?.apiCenterName!);
const exportedSpec = await apiCenterService.exportSpecification(
node?.apiCenterApiName!,
node?.apiCenterApiVersionName!,
node?.apiCenterApiVersionDefinition.name!);
await writeToTempFile(node!, exportedSpec.format, exportedSpec.value);
}

function getFolderName(treeItem: ApiVersionDefinitionTreeItem): string {
return `${treeItem.apiCenterName}-${treeItem.apiCenterApiName}`;
}

function getFilename(treeItem: ApiVersionDefinitionTreeItem): string {
return `${treeItem.apiCenterApiVersionDefinition.name}`;
}

async function writeToTempFile(node: ApiVersionDefinitionTreeItem, specFormat: string, specValue: string) {
if (specFormat === ApiSpecExportResultFormat.inline) {
await ExportAPI.showTempFile(node, specValue);
} else {
// Currently at server side did not exist link, so just monitor this event.
TelemetryClient.sendEvent("azure-api-center.exportApi", { format: specFormat });
}
}

export async function showTempFile(node: ApiVersionDefinitionTreeItem, fileContent: string) {
const folderName = getFolderName(node);
const folderPath = await createTemporaryFolder(folderName);
const fileName = getFilename(node);
const localFilePath: string = path.join(folderPath, fileName);
await fs.ensureFile(localFilePath);
const document: vscode.TextDocument = await vscode.workspace.openTextDocument(localFilePath);
await vscode.workspace.fs.writeFile(vscode.Uri.file(localFilePath), Buffer.from(fileContent));
await vscode.window.showTextDocument(document);
}
}
26 changes: 0 additions & 26 deletions src/commands/exportOpenApi.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { registerAzureUtilsExtensionVariables } from '@microsoft/vscode-azext-az
import { AzExtTreeDataProvider, AzExtTreeItem, CommandCallback, IActionContext, IParsedError, createAzExtOutputChannel, isUserCancelledError, parseError, registerCommand, registerEvent } from '@microsoft/vscode-azext-utils';
import { cleanupSearchResult } from './commands/cleanUpSearch';
import { showOpenApi } from './commands/editOpenApi';
import { exportOpenApi } from './commands/exportOpenApi';
import { ExportAPI } from './commands/exportApi';
import { generateApiLibrary } from './commands/generateApiLibrary';
import { generateHttpFile } from './commands/generateHttpFile';
import { importOpenApi } from './commands/importOpenApi';
Expand Down Expand Up @@ -69,7 +69,7 @@ export async function activate(context: vscode.ExtensionContext) {
// TODO: move all three to their separate files
registerCommandWithTelemetry('azure-api-center.importOpenApiByFile', async (context: IActionContext, node?: ApiVersionDefinitionTreeItem) => { await importOpenApi(context, node, false); });
registerCommandWithTelemetry('azure-api-center.importOpenApiByLink', async (context: IActionContext, node?: ApiVersionDefinitionTreeItem) => { await importOpenApi(context, node, true); });
registerCommandWithTelemetry('azure-api-center.exportOpenApi', async (context: IActionContext, node?: ApiVersionDefinitionTreeItem) => { await exportOpenApi(context, node); });
registerCommandWithTelemetry('azure-api-center.exportApi', async (context: IActionContext, node?: ApiVersionDefinitionTreeItem) => { await ExportAPI.exportApi(context, node); });

// TODO: move this to a separate file
const openApiEditor: OpenApiEditor = new OpenApiEditor();
Expand Down
71 changes: 71 additions & 0 deletions src/test/unit/commands/exportApi.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { AzExtParentTreeItem, AzExtTreeItem, IActionContext } from "@microsoft/vscode-azext-utils";
import * as sinon from "sinon";
import { ApiCenterService } from "../../../azure/ApiCenter/ApiCenterService";
import { ApiCenterApiVersionDefinition } from "../../../azure/ApiCenter/contracts";
import { ExportAPI } from "../../../commands/exportApi";
import { TelemetryClient } from "../../../common/telemetryClient";
import { ApiVersionDefinitionTreeItem } from "../../../tree/ApiVersionDefinitionTreeItem";
abstract class ParentTreeItemBase extends AzExtParentTreeItem {
private _childIndex: number = 0;
public async loadMoreChildrenImpl(clearCache: boolean, context: IActionContext): Promise<AzExtTreeItem[]> {
const children: AzExtTreeItem[] = [];
return children;
}
public hasMoreChildrenImpl(): boolean {
return this._childIndex < 10;
}
protected abstract createChildTreeItem(index: number): AzExtTreeItem;
}

class RootTreeItem extends ParentTreeItemBase {
public label: string = 'root';
public contextValue: string = 'root';

protected createChildTreeItem(index: number): AzExtTreeItem {
return new ApiVersionDefinitionTreeItem(this, "fakeApiCenterName", "fakeApiCenterApiName", "fakeApiCenterApiVersionName", {} as ApiCenterApiVersionDefinition);
}
}

suite("export API test cases", () => {
let sandbox = null as any;
let root: RootTreeItem;
let node: ApiVersionDefinitionTreeItem;
suiteSetup(() => {
sandbox = sinon.createSandbox();
sinon.stub(TelemetryClient, "sendEvent").returns();
});
setup(() => {
root = new RootTreeItem(undefined);
node = new ApiVersionDefinitionTreeItem(root,
"fakeApiCenterName",
"fakeApiCenterApiName",
"fakeApiCenterApiVersionName",
{
properties: {
specification: {
name: "fakeName"
}
}
} as ApiCenterApiVersionDefinition
);
sandbox.stub(node, "subscription").value("fakeSub");
sandbox.stub(node, "id").value("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test/providers/Microsoft.ApiCenter/services/test/workspaces/default/apis/test/versions/v1/definitions/openapi");
});
teardown(() => {
sandbox.restore();
});
test('export API happy path with link type', async () => {
const spyShowTempFile = sandbox.spy(ExportAPI, "showTempFile");
sandbox.stub(ApiCenterService.prototype, "exportSpecification").resolves({ format: "link", value: "fakeValue" });
await ExportAPI.exportApi({} as IActionContext, node);
sandbox.assert.notCalled(spyShowTempFile);
});
test('export API happy path with inline type', async () => {
let stubShowTempFile = sandbox.stub(ExportAPI, "showTempFile").resolves();
sandbox.stub(ApiCenterService.prototype, "exportSpecification").resolves({ format: "inline", value: "fakeValue" });
await ExportAPI.exportApi({} as IActionContext, node);
sandbox.assert.calledOnce(stubShowTempFile);
});
});

0 comments on commit b912daf

Please sign in to comment.