Skip to content

Commit

Permalink
JSONRPC: Add new methods (#12975)
Browse files Browse the repository at this point in the history
- Add new methods "version", "getMetadata", "getDeploymentGraph"
- Remove method "validate"
- Add .NET test class for JSONRPC
###### Microsoft Reviewers: [Open in
CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/Azure/bicep/pull/12975)
  • Loading branch information
anthony-c-martin authored Jan 11, 2024
1 parent d760fef commit 1389c59
Show file tree
Hide file tree
Showing 10 changed files with 612 additions and 248 deletions.
77 changes: 61 additions & 16 deletions src/Bicep.Cli.E2eTests/src/jsonrpc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@
*/

import { MessageConnection } from "vscode-jsonrpc";
import { pathToExampleFile } from "./utils/fs";
import { compileRequestType, openConnection, validateRequestType } from "./utils/jsonrpc";
import { pathToExampleFile, writeTempFile } from "./utils/fs";
import { compileRequestType, getDeploymentGraphRequestType, getMetadataRequestType, openConnection, versionRequestType } from "./utils/jsonrpc";

let connection: MessageConnection;
beforeAll(async () => (connection = await openConnection()));
afterAll(() => connection.dispose());

describe("bicep jsonrpc", () => {
it("should return a version number", async () => {
const result = await version(connection);

expect(result.version).toMatch(/^\d+\.\d+\.\d+/);
});

it("should build a bicep file", async () => {
const result = await compile(
connection,
Expand All @@ -26,6 +32,29 @@ describe("bicep jsonrpc", () => {
expect(result.contents?.length).toBeGreaterThan(0);
});

it("should return a deployment graph", async () => {
const bicepPath = writeTempFile("jsonrpc", "metadata.bicep", `
resource foo 'My.Rp/foo@2020-01-01' = {
name: 'foo'
}
resource bar 'My.Rp/foo@2020-01-01' existing = {
name: 'bar'
dependsOn: [foo]
}
resource baz 'My.Rp/foo@2020-01-01' = {
name: 'baz'
dependsOn: [bar]
}
`);

const result = await getDeploymentGraph(connection, bicepPath);

expect(result.nodes).toHaveLength(3);
expect(result.edges).toHaveLength(2);
});

it("should return diagnostics if the bicep file has errors", async () => {
const result = await compile(
connection,
Expand All @@ -39,29 +68,45 @@ describe("bicep jsonrpc", () => {
expect(error.message).toBe('The name "osDiskSizeGb" does not exist in the current context.');
});

// preflight doesn't work in this test suite as it requires authentication. Change xit -> it to test locally.
xit("should validate a bicepparam file", async () => {
const result = await validate(
it("should return metadata for a bicep file", async () => {
const bicepPath = writeTempFile("jsonrpc", "metadata.bicep", `
metadata description = 'my file'
@description('foo param')
param foo string
@description('bar output')
output bar string = foo
`);

const result = await getMetadata(
connection,
pathToExampleFile("bicepparam", "main.bicepparam"),
);
bicepPath);

expect(result.error!.code).toBe("InvalidTemplateDeployment");
expect(result.error!.details![0].code).toBe("PreflightValidationCheckFailed");
expect(result.error!.details![0].details![0].code).toBe("AccountNameInvalid");
}, 60000);
expect(result.metadata.filter(x => x.name === 'description')[0].value).toEqual('my file');
expect(result.parameters.filter(x => x.name === 'foo')[0].description).toEqual('foo param');
expect(result.outputs.filter(x => x.name === 'bar')[0].description).toEqual('bar output');
});
});

async function version(connection: MessageConnection) {
return await connection.sendRequest(versionRequestType, {});
}

async function compile(connection: MessageConnection, bicepFile: string) {
return await connection.sendRequest(compileRequestType, {
path: bicepFile,
});
}

async function validate(connection: MessageConnection, bicepparamFile: string) {
return await connection.sendRequest(validateRequestType, {
subscriptionId: 'a1bfa635-f2bf-42f1-86b5-848c674fc321',
resourceGroup: 'ant-test',
path: bicepparamFile,
async function getMetadata(connection: MessageConnection, bicepFile: string) {
return await connection.sendRequest(getMetadataRequestType, {
path: bicepFile,
});
}

async function getDeploymentGraph(connection: MessageConnection, bicepFile: string) {
return await connection.sendRequest(getDeploymentGraphRequestType, {
path: bicepFile,
});
}
97 changes: 75 additions & 22 deletions src/Bicep.Cli.E2eTests/src/utils/jsonrpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,44 @@ import {
} from "vscode-jsonrpc/node";
import { bicepCli } from "./fs";

interface VersionRequest {}

interface VersionResponse {
version: string;
}

interface GetDeploymentGraphRequest {
path: string;
}

interface GetDeploymentGraphResponse {
nodes: GetDeploymentGraphResponseNode[];
edges: GetDeploymentGraphResponseEdge[];
}

interface GetDeploymentGraphResponseNode {
range: Range;
name: string;
type: string;
isExisting: boolean;
relativePath?: string;
}

interface GetDeploymentGraphResponseEdge {
source: string;
target: string;
}

interface Position {
line: number;
char: number;
}

interface Range {
start: Position;
end: Position;
}

interface CompileRequest {
path: string;
}
Expand All @@ -22,41 +60,56 @@ interface CompileResponse {
}

interface CompileResponseDiagnostic {
line: number;
char: number;
range: Range;
level: string;
code: string;
message: string;
}

export const compileRequestType = new RequestType<
CompileRequest,
CompileResponse,
never
>("bicep/compile");

interface ValidateRequest {
subscriptionId: string;
resourceGroup: string;
interface GetMetadataRequest {
path: string;
}

interface ValidateResponse {
error?: ValidateResponseError;
interface GetMetadataResponse {
metadata: MetadataDefinition[];
parameters: ParamDefinition[];
outputs: ParamDefinition[];
}

interface ValidateResponseError {
code: string;
message: string;
target?: string;
details?: ValidateResponseError[];
interface MetadataDefinition {
name: string;
value: string;
}

interface ParamDefinition {
range: Range;
name: string;
description?: string;
}

export const validateRequestType = new RequestType<
ValidateRequest,
ValidateResponse,
export const versionRequestType = new RequestType<
VersionRequest,
VersionResponse,
never
>("bicep/version");

export const compileRequestType = new RequestType<
CompileRequest,
CompileResponse,
never
>("bicep/compile");

export const getMetadataRequestType = new RequestType<
GetMetadataRequest,
GetMetadataResponse,
never
>("bicep/getMetadata");

export const getDeploymentGraphRequestType = new RequestType<
GetDeploymentGraphRequest,
GetDeploymentGraphResponse,
never
>("bicep/validate");
>("bicep/getDeploymentGraph");

function generateRandomPipeName(): string {
const randomSuffix = randomBytes(21).toString("hex");
Expand Down
Loading

0 comments on commit 1389c59

Please sign in to comment.