Skip to content

Commit

Permalink
feat: check rule file name, use gpt-4o, add resource info in telemetry (
Browse files Browse the repository at this point in the history
#223)

* feat: check rule file name

* refactor: add Azure Resource info in telemetry

* feat: use gpt-4o
  • Loading branch information
formulahendry authored Aug 2, 2024
1 parent c57a1bf commit 7974b72
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/common/telemetryEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export enum TelemetryProperties {
duration = "duration",
slashCommand = 'slashCommand',
option = 'option',
treeItemFullId = 'treeItemFullId',
};

export enum ErrorProperties {
Expand Down
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const makrdownDocuments = "markdown-documents";
export const defaultRulesetFile = "https://raw.githubusercontent.com/Azure/APICenter-Analyzer/preview/resources/rulesets/oas.yaml";
export const azureApiGuidelineRulesetFile = "https://raw.githubusercontent.com/azure/azure-api-style-guide/main/spectral.yaml";
export const spectralOwaspRulesetFile = "https://unpkg.com/@stoplight/spectral-owasp-ruleset/dist/ruleset.mjs";
export const MODEL_SELECTOR: vscode.LanguageModelChatSelector = { vendor: 'copilot', family: 'gpt-4' };
export const MODEL_SELECTOR: vscode.LanguageModelChatSelector = { vendor: 'copilot', family: 'gpt-4o' };
export const ExceedTokenLimit = "Message exceeds token limit";
export const SpectralExtensionId = "stoplight.spectral";
export const tenantSetting: string = 'tenant';
Expand Down
3 changes: 3 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ async function registerCommandWithTelemetry(commandId: string, callback: Command
} finally {
const end: number = Date.now();
properties[TelemetryProperties.duration] = ((end - start) / 1000).toString();
if (args[0] && args[0] instanceof AzExtTreeItem && args[0].fullId) {
properties[TelemetryProperties.treeItemFullId] = args[0].fullId;
}
if (parsedError) {
properties[ErrorProperties.errorType] = parsedError.errorType;
properties[ErrorProperties.errorMessage] = parsedError.message;
Expand Down
6 changes: 6 additions & 0 deletions src/test/unit/testConstants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

// Rule files: ruleset.yml
export const zipFileBase64 = "UEsDBAoAAAAAABp341gAAAAAAAAAAAAAAAAKAAAAZnVuY3Rpb25zL1BLAwQUAAgACAAad+NYAAAAAAAAAAAAAAAAEwAAAGZ1bmN0aW9ucy9lcXVhbHMuanNtkD9PwzAQxfdK/Q6PiKGVAuyugpiYEUOXqlJNcilGTuyezwhU5bujOM2fgeVk3f387r0zjXcsuKJk0kLv0VIgeY1tKca16FCza5C9BHHemvOnPAVPpbC2D6Vjynbr1XpFP0mkolpHK/9LbdYr4NoXwLQ+ikIbrc2HjvM9E9RIAPLrSSFzH19USpaPbV1Vpke1fWPnicVQUKi1DTQxfjGZ9IBvbSMpCMcZ7aYX0yUapkrhkCUyO95mAzPUjWg+k+y1zUfPWxTP45rStaE/ZhJAh2KE0plS9HqhgbuiGNjtbJRJIrc4zMYXGYCGQtBnUjjt05ImBgFdora4vyat7vGUzz/mhMfdLc8YZ7v7A1BLBwjYhjSrDQEAAP4BAABQSwMEFAAIAAgAGnfjWAAAAAAAAAAAAAAAAAsAAABydWxlc2V0LnltbF2OUQqEMBBD/wu9Qxj21z1AL+EZujJ1C3WqrRUW8e6LtiA4X5mE8OKKDKuPko1WQAdeig1Zq1QCV2/6dedzaWDinO3IBrTvnFJMx0E1Gf3GYkCvtxcXm7l+WVoTcI1lQBVDz6Sf7y31NhvKSbOfgbT6A1BLBwhHQAIHcgAAAK4AAABQSwECLQMKAAAAAAAad+NYAAAAAAAAAAAAAAAACgAAAAAAAAAAABAA7UEAAAAAZnVuY3Rpb25zL1BLAQItAxQACAAIABp341jYhjSrDQEAAP4BAAATAAAAAAAAAAAAIAC2gSgAAABmdW5jdGlvbnMvZXF1YWxzLmpzUEsBAi0DFAAIAAgAGnfjWEdAAgdyAAAArgAAAAsAAAAAAAAAAAAgALaBdgEAAHJ1bGVzZXQueW1sUEsFBgAAAAADAAMAsgAAACECAAAAAA==";
// Rule files: a.txt, ruleset.yaml, z.txt
export const zipFileBase64_multiFiles = "UEsDBBQACAAIABgYAVkAAAAAAAAAAAAAAAAFAAAAYS50eHRLTEwEAFBLBwgtcwfwBQAAAAMAAABQSwMECgAAAAAABBYBWQAAAAAAAAAAAAAAAAoAAABmdW5jdGlvbnMvUEsDBBQACAAIAAQWAVkAAAAAAAAAAAAAAAAVAAAAZnVuY3Rpb25zL2dyZWV0aW5nLmpzHctBCsMgEAXQveAd/qKLFEIP0F4gJ8gmFCLJpAhmRnQEQbx7SLcPHtUoSbHT4UpQHIU39cKYXfC7Uxo8x6IjJN6cR2zCSlWfaNYAQCItibE0nJSz+9Eb60QhCB7tf/trRf9+rOnWXFBLBwj5E4fpZwAAAG4AAABQSwMEFAAIAAgABBYBWQAAAAAAAAAAAAAAAAwAAABydWxlc2V0LnlhbWxNjk0KgzAQhfdC7jCELlsPkEN00a24CPaZBuKkzCRFEO9eqAa6/d4v1gJ+qqNh0DemIj657PVKPqVxNN1ceSoxszrTEd0oCFAiB9NJTThoY4+a8ANEC1R9gCO7bRDJsu/2UEL8gB3ZS99i/RxFy90vOC3lBT57iNoB9zf9BVBLBwjXoNbVfQAAALUAAABQSwMEFAAIAAgAHBgBWQAAAAAAAAAAAAAAAAUAAAB6LnR4dKuqqgIAUEsHCMo9J8MFAAAAAwAAAFBLAQItAxQACAAIABgYAVktcwfwBQAAAAMAAAAFAAAAAAAAAAAAIAC2gQAAAABhLnR4dFBLAQItAwoAAAAAAAQWAVkAAAAAAAAAAAAAAAAKAAAAAAAAAAAAEADtQTgAAABmdW5jdGlvbnMvUEsBAi0DFAAIAAgABBYBWfkTh+lnAAAAbgAAABUAAAAAAAAAAAAgALaBYAAAAGZ1bmN0aW9ucy9ncmVldGluZy5qc1BLAQItAxQACAAIAAQWAVnXoNbVfQAAALUAAAAMAAAAAAAAAAAAIAC2gQoBAABydWxlc2V0LnlhbWxQSwECLQMUAAgACAAcGAFZyj0nwwUAAAADAAAABQAAAAAAAAAAACAAtoHBAQAAei50eHRQSwUGAAAAAAUABQAbAQAA+QEAAAAA";
// Rule files: a.txt, ruleset2.yaml, z.txt
export const zipFileBase64_noValidRuleFile = "UEsDBBQACAAIABgYAVkAAAAAAAAAAAAAAAAFAAAAYS50eHRLTEwEAFBLBwgtcwfwBQAAAAMAAABQSwMECgAAAAAABBYBWQAAAAAAAAAAAAAAAAoAAABmdW5jdGlvbnMvUEsDBBQACAAIAAQWAVkAAAAAAAAAAAAAAAAVAAAAZnVuY3Rpb25zL2dyZWV0aW5nLmpzHctBCsMgEAXQveAd/qKLFEIP0F4gJ8gmFCLJpAhmRnQEQbx7SLcPHtUoSbHT4UpQHIU39cKYXfC7Uxo8x6IjJN6cR2zCSlWfaNYAQCItibE0nJSz+9Eb60QhCB7tf/trRf9+rOnWXFBLBwj5E4fpZwAAAG4AAABQSwMEFAAIAAgABBYBWQAAAAAAAAAAAAAAAA0AAABydWxlc2V0Mi55YW1sTY5NCoMwEIX3Qu4whC5bD5BDdNGtuAj2mQbipMwkRRDvXqgGuv3eL9YCfqqjYdA3piI+uez1Sj6lcTTdXHkqMbM60xHdKAhQIgfTSU04aGOPmvADRAtUfYAju20QybLv9lBC/IAd2UvfYv0cRcvdLzgt5QU+e4jaAfc3/QVQSwcI16DW1X0AAAC1AAAAUEsDBBQACAAIABwYAVkAAAAAAAAAAAAAAAAFAAAAei50eHSrqqoCAFBLBwjKPSfDBQAAAAMAAABQSwECLQMUAAgACAAYGAFZLXMH8AUAAAADAAAABQAAAAAAAAAAACAAtoEAAAAAYS50eHRQSwECLQMKAAAAAAAEFgFZAAAAAAAAAAAAAAAACgAAAAAAAAAAABAA7UE4AAAAZnVuY3Rpb25zL1BLAQItAxQACAAIAAQWAVn5E4fpZwAAAG4AAAAVAAAAAAAAAAAAIAC2gWAAAABmdW5jdGlvbnMvZ3JlZXRpbmcuanNQSwECLQMUAAgACAAEFgFZ16DW1X0AAAC1AAAADQAAAAAAAAAAACAAtoEKAQAAcnVsZXNldDIueWFtbFBLAQItAxQACAAIABwYAVnKPSfDBQAAAAMAAAAFAAAAAAAAAAAAIAC2gcIBAAB6LnR4dFBLBQYAAAAABQAFABwBAAD6AQAAAAA=";
45 changes: 43 additions & 2 deletions src/test/unit/tree/rules/rulesTreeItem.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as sinon from "sinon";
import { ApiCenterService } from "../../../../azure/ApiCenter/ApiCenterService";
import { ApiCenter, ApiCenterRulesetExport } from "../../../../azure/ApiCenter/contracts";
import { RulesTreeItem } from "../../../../tree/rules/RulesTreeItem";
import { zipFileBase64 } from "../../testConstants";
import { zipFileBase64, zipFileBase64_multiFiles, zipFileBase64_noValidRuleFile } from "../../testConstants";
import path = require("path");

describe("rulesTreeItem", () => {
Expand All @@ -19,10 +19,10 @@ describe("rulesTreeItem", () => {
beforeEach(() => {
});
afterEach(() => {
sandbox.restore();
if (rulesTreeItem && rulesTreeItem.rulesFolderPath) {
fs.promises.rm(rulesTreeItem.rulesFolderPath, { recursive: true, force: true });
}
sandbox.restore();
});
it("rulesTreeItem not enabled ", async () => {
rulesTreeItem = new RulesTreeItem(
Expand Down Expand Up @@ -60,4 +60,45 @@ describe("rulesTreeItem", () => {
assert.ok(fs.existsSync(path.join(rulesTreeItem.rulesFolderPath, "functions", "equals.js")));
assert.ok(fs.existsSync(path.join(rulesTreeItem.rulesFolderPath, "ruleset.yml")));
});
it("multiple files in root rules folder", async () => {
rulesTreeItem = new RulesTreeItem(
{} as AzExtParentTreeItem,
{
id: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test/providers/Microsoft.ApiCenter/services/test",
name: "testRulesTreeItem2",
} as ApiCenter,
true,
);
sandbox.stub(ApiCenterService.prototype, "exportRuleset").resolves({ value: zipFileBase64_multiFiles } as ApiCenterRulesetExport);

let res = await rulesTreeItem.loadMoreChildrenImpl(false, {} as IActionContext);

assert.equal(res.length, 2);
assert.equal(res[0].commandId, "azure-api-center.openRule");
assert.equal(res[0].contextValue, "azureApiCenterRule");
assert.equal(res[1].contextValue, "azureApiCenterFunctions");

assert.ok(fs.existsSync(path.join(rulesTreeItem.rulesFolderPath, "functions", "greeting.js")));
assert.ok(fs.existsSync(path.join(rulesTreeItem.rulesFolderPath, "ruleset.yaml")));
});
it("no valid rule file in root rules folder", async () => {
rulesTreeItem = new RulesTreeItem(
{} as AzExtParentTreeItem,
{
id: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test/providers/Microsoft.ApiCenter/services/test",
name: "testRulesTreeItem3",
} as ApiCenter,
true,
);
sandbox.stub(ApiCenterService.prototype, "exportRuleset").resolves({ value: zipFileBase64_noValidRuleFile } as ApiCenterRulesetExport);

await assert.rejects(
async () => {
await rulesTreeItem.loadMoreChildrenImpl(false, {} as IActionContext);
},
{
message: `No rule file ('ruleset.yml' or 'ruleset.yaml' or 'ruleset.json') found in the rules folder '${rulesTreeItem.getRulesFolderPath()}'. Please add a rule file to enable API Analysis.`,
}
);
});
});
4 changes: 2 additions & 2 deletions src/tree/SubscriptionTreeItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { AzExtParentTreeItem, AzExtTreeItem, ISubscriptionContext } from "@microsoft/vscode-azext-utils";
import { ResourceGraphService } from "../azure/ResourceGraph/ResourceGraphService";
import { TelemetryClient } from "../common/telemetryClient";
import { TelemetryEvent } from "../common/telemetryEvent";
import { TelemetryEvent, TelemetryProperties } from "../common/telemetryEvent";
import { treeUtils } from "../utils/treeUtils";
import { ApiCenterTreeItem } from "./ApiCenterTreeItem";

Expand Down Expand Up @@ -49,7 +49,7 @@ class SubscriptionTreeItem extends AzExtParentTreeItem {
}

public async loadMoreChildrenImpl(): Promise<AzExtTreeItem[]> {
TelemetryClient.sendEvent(TelemetryEvent.treeviewListApiCenters);
TelemetryClient.sendEvent(TelemetryEvent.treeviewListApiCenters, { [TelemetryProperties.treeItemFullId]: this.fullId });

const resourceGraphService = new ResourceGraphService(this.subscription);

Expand Down
9 changes: 8 additions & 1 deletion src/tree/rules/RulesTreeItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { RuleTreeItem } from "./RuleTreeItem";

const functionsDir = "functions";
const rulesDir = ".api-center-rules";
const validRuleFileNames = ["ruleset.yml", "ruleset.yaml", "ruleset.json"];

export class RulesTreeItem extends AzExtParentTreeItem {
public static contextValue: string = "azureApiCenterRules";
Expand Down Expand Up @@ -58,7 +59,13 @@ export class RulesTreeItem extends AzExtParentTreeItem {
await this.exportRulesToLocalFolder(this.rulesFolderPath);
}

const ruleFilename = (await getFilenamesInFolder(this.rulesFolderPath))[0];
const filenames = await getFilenamesInFolder(this.rulesFolderPath);
const ruleFilename = filenames.find((filename) => validRuleFileNames.includes(filename.toLowerCase()));

if (!ruleFilename) {
throw new Error(vscode.l10n.t(UiStrings.NoRuleFileFound, validRuleFileNames.join("' or '"), this.rulesFolderPath));
}

const ruleFullFilePath = path.join(this.rulesFolderPath, ruleFilename);
const functionsFilenames = await getFilenamesInFolder(path.join(this.rulesFolderPath, functionsDir));

Expand Down
1 change: 1 addition & 0 deletions src/uiStrings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,5 @@ export class UiStrings {
static readonly TreeitemLabelRules = vscode.l10n.t("Rules");
static readonly RulesNotEnabled = vscode.l10n.t("API Analysis is not enabled. Click here to enable it.");
static readonly OpenApiCenterFolder = vscode.l10n.t("Rules folder is required to be opened to enable live API linting. Click 'Yes' to open the rules folder in current window.");
static readonly NoRuleFileFound = vscode.l10n.t("No rule file ('{0}') found in the rules folder '{1}'. Please add a rule file to enable API Analysis.");
}

0 comments on commit 7974b72

Please sign in to comment.