Refactor/Cleanup extension base package (#4370)

This commit is contained in:
Timothee Guerin 2021-11-17 14:35:56 -08:00 коммит произвёл GitHub
Родитель cfa8997aa1
Коммит 1447f04f75
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
31 изменённых файлов: 760 добавлений и 620 удалений

Просмотреть файл

@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@autorest/cadl",
"comment": "**Uptake** breaking change from e",
"type": "minor"
}
],
"packageName": "@autorest/cadl",
"email": "tiguerin@microsoft.com"
}

Просмотреть файл

@ -0,0 +1,16 @@
{
"changes": [
{
"packageName": "@autorest/extension-base",
"comment": "**BREAKING** Refactor host, session. Rename PascalCase methods, cleanup",
"type": "minor"
},
{
"packageName": "@autorest/extension-base",
"comment": "**Added** testing functionality for extension via @autorest/extension-base/testing import",
"type": "minor"
}
],
"packageName": "@autorest/extension-base",
"email": "tiguerin@microsoft.com"
}

Просмотреть файл

@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@autorest/modelerfour",
"comment": "**Uptake** breaking change from e",
"type": "minor"
}
],
"packageName": "@autorest/modelerfour",
"email": "tiguerin@microsoft.com"
}

Просмотреть файл

@ -1,15 +1,16 @@
import { fileURLToPath } from "url";
import { Channel, Host } from "@autorest/extension-base";
import { AutorestExtensionHost, Channel } from "@autorest/extension-base";
import { compileAdl } from "./cadl-compiler.js";
export async function setupAdlCompilerPlugin(host: Host) {
const inputFiles = await host.GetValue("inputFileUris");
const entrypoint = inputFiles[0];
export async function setupAdlCompilerPlugin(host: AutorestExtensionHost) {
const inputFiles = await host.getValue<string[]>("inputFileUris");
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const entrypoint = inputFiles![0];
const result = await compileAdl(fileURLToPath(entrypoint));
if ("diagnostics" in result) {
for (const diagnostic of result.diagnostics) {
host.Message({
host.message({
Channel: Channel.Error,
Text: diagnostic.message,
Source:
@ -28,7 +29,7 @@ export async function setupAdlCompilerPlugin(host: Host) {
}
for (const [name, content] of Object.entries(result.compiledFiles)) {
host.WriteFile(name, content, undefined, "swagger-document");
host.writeFile({ filename: name, content, artifactType: "swagger-document" });
}
}

Просмотреть файл

@ -2,13 +2,13 @@ import { AutoRestExtension } from "@autorest/extension-base";
import { setupAdlCompilerPlugin } from "./cadl-compiler-plugin.js";
export async function initializePlugins(pluginHost: AutoRestExtension) {
pluginHost.Add("adl-compiler", setupAdlCompilerPlugin);
pluginHost.add("adl-compiler", setupAdlCompilerPlugin);
}
async function main() {
const pluginHost = new AutoRestExtension();
await initializePlugins(pluginHost);
await pluginHost.Run();
await pluginHost.run();
}
main().catch((e) => {

Просмотреть файл

@ -4,15 +4,15 @@
*--------------------------------------------------------------------------------------------*/
import { codeModelSchema, CodeModel } from "@autorest/codemodel";
import { Host, startSession } from "@autorest/extension-base";
import { AutorestExtensionHost, startSession } from "@autorest/extension-base";
import { serialize } from "@azure-tools/codegen";
import { Checker } from "./checker";
export async function processRequest(host: Host) {
const debug = (await host.GetValue("debug")) || false;
export async function processRequest(host: AutorestExtensionHost) {
const debug = (await host.getValue("debug")) || false;
try {
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
const session = await startSession<CodeModel>(host, codeModelSchema);
const options = <any>await session.getValue("modelerfour", {});
// process
@ -28,11 +28,19 @@ export async function processRequest(host: Host) {
// output the model to the pipeline
if (options["emit-yaml-tags"] !== false) {
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
host.writeFile({
filename: "code-model-v4.yaml",
content: serialize(result, codeModelSchema),
artifactType: "code-model-v4",
});
}
if (options["emit-yaml-tags"] !== true) {
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
host.writeFile({
filename: "code-model-v4-no-tags.yaml",
content: serialize(result),
artifactType: "code-model-v4-no-tags",
});
}
} catch (error: any) {
if (debug) {

Просмотреть файл

@ -4,15 +4,15 @@
*--------------------------------------------------------------------------------------------*/
import { codeModelSchema, CodeModel } from "@autorest/codemodel";
import { Host, startSession } from "@autorest/extension-base";
import { AutorestExtensionHost, startSession } from "@autorest/extension-base";
import { serialize } from "@azure-tools/codegen";
import { Example } from "./example";
export async function processRequest(host: Host) {
const debug = (await host.GetValue("debug")) || false;
export async function processRequest(host: AutorestExtensionHost) {
const debug = (await host.getValue("debug")) || false;
try {
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
const session = await startSession<CodeModel>(host, codeModelSchema);
// process
const plugin = new Example(session);
@ -21,8 +21,16 @@ export async function processRequest(host: Host) {
const result = plugin.process();
// output the model to the pipeline
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
host.writeFile({
filename: "code-model-v4.yaml",
content: serialize(result, codeModelSchema),
artifactType: "code-model-v4",
});
host.writeFile({
filename: "code-model-v4-no-tags.yaml",
content: serialize(result),
artifactType: "code-model-v4-no-tags",
});
} catch (error: any) {
if (debug) {
// eslint-disable-next-line no-console

Просмотреть файл

@ -4,15 +4,15 @@
*--------------------------------------------------------------------------------------------*/
import { codeModelSchema, CodeModel } from "@autorest/codemodel";
import { Host, startSession } from "@autorest/extension-base";
import { AutorestExtensionHost, startSession } from "@autorest/extension-base";
import { serialize } from "@azure-tools/codegen";
import { Flattener } from "./flattener";
export async function processRequest(host: Host) {
const debug = (await host.GetValue("debug")) || false;
export async function processRequest(host: AutorestExtensionHost) {
const debug = (await host.getValue("debug")) || false;
try {
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
const session = await startSession<CodeModel>(host, codeModelSchema);
const options = <any>await session.getValue("modelerfour", {});
// process
@ -23,11 +23,19 @@ export async function processRequest(host: Host) {
// output the model to the pipeline
if (options["emit-yaml-tags"] !== false) {
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
host.writeFile({
filename: "code-model-v4.yaml",
content: serialize(result, codeModelSchema),
artifactType: "code-model-v4",
});
}
if (options["emit-yaml-tags"] !== true) {
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
host.writeFile({
filename: "code-model-v4-no-tags.yaml",
content: serialize(result),
artifactType: "code-model-v4-no-tags",
});
}
} catch (error: any) {
if (debug) {

Просмотреть файл

@ -4,15 +4,15 @@
*--------------------------------------------------------------------------------------------*/
import { codeModelSchema, CodeModel } from "@autorest/codemodel";
import { Host, startSession } from "@autorest/extension-base";
import { AutorestExtensionHost, startSession } from "@autorest/extension-base";
import { serialize } from "@azure-tools/codegen";
import { Grouper } from "./grouper";
export async function processRequest(host: Host) {
const debug = (await host.GetValue("debug")) || false;
export async function processRequest(host: AutorestExtensionHost) {
const debug = (await host.getValue("debug")) || false;
try {
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
const session = await startSession<CodeModel>(host, codeModelSchema);
const options = <any>await session.getValue("modelerfour", {});
// process
@ -23,11 +23,19 @@ export async function processRequest(host: Host) {
// output the model to the pipeline
if (options["emit-yaml-tags"] !== false) {
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
host.writeFile({
filename: "code-model-v4.yaml",
content: serialize(result, codeModelSchema),
artifactType: "code-model-v4",
});
}
if (options["emit-yaml-tags"] !== true) {
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
host.writeFile({
filename: "code-model-v4-no-tags.yaml",
content: serialize(result),
artifactType: "code-model-v4-no-tags",
});
}
} catch (error: any) {
if (debug) {

Просмотреть файл

@ -8,18 +8,18 @@ import { processRequest as preNamer } from "./prenamer/plugin-prenamer";
import { processRequest as prechecker } from "./quality-precheck/prechecker";
export async function initializePlugins(pluginHost: AutoRestExtension) {
pluginHost.Add("prechecker", prechecker);
pluginHost.Add("modelerfour", modelerfour);
pluginHost.Add("grouper", grouper);
pluginHost.Add("pre-namer", preNamer);
pluginHost.Add("flattener", flattener);
pluginHost.Add("checker", checker);
pluginHost.add("prechecker", prechecker);
pluginHost.add("modelerfour", modelerfour);
pluginHost.add("grouper", grouper);
pluginHost.add("pre-namer", preNamer);
pluginHost.add("flattener", flattener);
pluginHost.add("checker", checker);
}
async function main() {
const pluginHost = new AutoRestExtension();
await initializePlugins(pluginHost);
await pluginHost.Run();
await pluginHost.run();
}
main().catch((e) => {

Просмотреть файл

@ -1072,7 +1072,7 @@ export class ModelerFour {
if (isSchemaBinary(schema)) {
// handle inconsistency in file format handling.
this.session.hint(
this.session.warning(
`'The schema ${schema?.["x-ms-metadata"]?.name || name} with 'type: ${schema.type}', format: ${
schema.format
}' will be treated as a binary blob for binary media types.`,

Просмотреть файл

@ -3,17 +3,17 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { codeModelSchema, CodeModel } from "@autorest/codemodel";
import { Host, startSession } from "@autorest/extension-base";
import { deserialize, serialize } from "@azure-tools/codegen";
import { codeModelSchema } from "@autorest/codemodel";
import { AutorestExtensionHost, startSession } from "@autorest/extension-base";
import { serialize } from "@azure-tools/codegen";
import * as OpenAPI from "@azure-tools/openapi";
import { ModelerFour } from "./modelerfour";
export async function processRequest(host: Host) {
const debug = (await host.GetValue("debug")) || false;
export async function processRequest(host: AutorestExtensionHost) {
const debug = (await host.getValue("debug")) || false;
try {
const session = await startSession<OpenAPI.Model>(host, undefined, undefined, "prechecked-openapi-document");
const session = await startSession<OpenAPI.Model>(host, undefined, "prechecked-openapi-document");
const options = <any>await session.getValue("modelerfour", {});
// process
@ -29,10 +29,18 @@ export async function processRequest(host: Host) {
// output the model to the pipeline
if (options["emit-yaml-tags"] !== false) {
host.WriteFile("code-model-v4.yaml", serialize(codeModel, codeModelSchema), undefined, "code-model-v4");
host.writeFile({
filename: "code-model-v4.yaml",
content: serialize(codeModel, codeModelSchema),
artifactType: "code-model-v4",
});
}
if (options["emit-yaml-tags"] !== true) {
host.WriteFile("code-model-v4-no-tags.yaml", serialize(codeModel), undefined, "code-model-v4-no-tags");
host.writeFile({
filename: "code-model-v4-no-tags.yaml",
content: serialize(codeModel),
artifactType: "code-model-v4-no-tags",
});
}
} catch (error: any) {
if (debug) {

Просмотреть файл

@ -4,15 +4,15 @@
*--------------------------------------------------------------------------------------------*/
import { codeModelSchema, CodeModel } from "@autorest/codemodel";
import { Host, startSession } from "@autorest/extension-base";
import { AutorestExtensionHost, startSession } from "@autorest/extension-base";
import { serialize } from "@azure-tools/codegen";
import { PreNamer } from "./prenamer";
export async function processRequest(host: Host) {
const debug = (await host.GetValue("debug")) || false;
export async function processRequest(host: AutorestExtensionHost) {
const debug = (await host.getValue("debug")) || false;
try {
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
const session = await startSession<CodeModel>(host, codeModelSchema);
const options = <any>await session.getValue("modelerfour", {});
// process
@ -23,10 +23,18 @@ export async function processRequest(host: Host) {
// output the model to the pipeline
if (options["emit-yaml-tags"] !== false) {
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
host.writeFile({
filename: "code-model-v4.yaml",
content: serialize(result, codeModelSchema),
artifactType: "code-model-v4",
});
}
if (options["emit-yaml-tags"] !== true) {
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
host.writeFile({
filename: "code-model-v4-no-tags.yaml",
content: serialize(result),
artifactType: "code-model-v4-no-tags",
});
}
} catch (error: any) {
if (debug) {

Просмотреть файл

@ -23,7 +23,7 @@ export class DuplicateSchemaMerger {
break;
}
}
this.session.verbose(`Found and removed ${count} duplicate schema`, {});
this.session.verbose(`Found and removed ${count} duplicate schema`);
this.findDifferentSchemasWithSameName(spec);
return spec;
@ -162,7 +162,6 @@ export class DuplicateSchemaMerger {
this.session.verbose(
`Schema ${name} has multiple identical declarations, reducing to just one - removing: ${schemasToRemove.length}, keeping: ${schemaToKeep.key}`,
["PreCheck", "ReducingSchema"],
);
return newSpec;

Просмотреть файл

@ -1,5 +1,5 @@
import { Session, Host, startSession, JsonPointerSegments } from "@autorest/extension-base";
import { ShadowedNodePath, shadowPosition } from "@azure-tools/codegen";
import { Session, AutorestExtensionHost, startSession } from "@autorest/extension-base";
import { shadowPosition } from "@azure-tools/codegen";
import {
Model as oai3,
Refable,
@ -9,21 +9,20 @@ import {
JsonType,
StringFormat,
} from "@azure-tools/openapi";
import { getDiff } from "recursive-diff";
import { Interpretations } from "../modeler/interpretations";
import { ModelerFourOptions } from "../modeler/modelerfour-options";
import { DuplicateSchemaMerger } from "./duplicate-schema-merger";
export async function processRequest(host: Host) {
const debug = (await host.GetValue("debug")) || false;
export async function processRequest(host: AutorestExtensionHost) {
const debug = (await host.getValue("debug")) || false;
try {
const session = await startSession<oai3>(host);
// process
const plugin = await new QualityPreChecker(session).init();
const plugin = new QualityPreChecker(session);
const input = plugin.input;
// go!
@ -34,13 +33,16 @@ export async function processRequest(host: Host) {
session.checkpoint();
}
host.WriteFile(
"prechecked-openapi-document.yaml",
JSON.stringify(result, null, 2),
undefined,
"prechecked-openapi-document",
);
host.WriteFile("original-openapi-document.yaml", JSON.stringify(input, null, 2), undefined, "openapi-document");
host.writeFile({
filename: "prechecked-openapi-document.yaml",
content: JSON.stringify(result, null, 2),
artifactType: "prechecked-openapi-document",
});
host.writeFile({
filename: "original-openapi-document.yaml",
content: JSON.stringify(input, null, 2),
artifactType: "openapi-document",
});
} catch (error: any) {
if (debug) {
// eslint-disable-next-line no-console
@ -52,21 +54,15 @@ export async function processRequest(host: Host) {
export class QualityPreChecker {
input: oai3;
options: ModelerFourOptions = {};
private options: ModelerFourOptions;
protected interpret: Interpretations;
constructor(protected session: Session<oai3>) {
this.input = shadowPosition(session.model); // shadow(session.model, filename);
this.options = this.session.configuration.modelerfour ?? {};
this.interpret = new Interpretations(session);
}
async init() {
// get our configuration for this run.
this.options = await this.session.getValue("modelerfour", {});
return this;
}
private resolve<T>(item: Refable<T>): Dereferenced<T> {
return dereference(this.input, item);
}
@ -239,7 +235,7 @@ export class QualityPreChecker {
for (const { name, schema } of this.listSchemas()) {
if (<any>schema.type === "file" || <any>schema.format === "file" || <any>schema.format === "binary") {
// handle inconsistency in file format handling.
this.session.hint(
this.session.warning(
`'The schema ${schema?.["x-ms-metadata"]?.name || name} with 'type: ${schema.type}', format: ${
schema.format
}' will be treated as a binary blob for binary media types.`,
@ -353,6 +349,7 @@ export class QualityPreChecker {
` - ${schemaName}: ${schema.type}`,
` - ${parentName}: ${parent.type}`,
];
this.session.error(lines.join("\n"), ["PreCheck", "AllOfTypeDifferent"], parentRef);
}
}

Просмотреть файл

@ -14,7 +14,7 @@ class PreCheckerClient {
static async create(spec: Model, config: ModelerFourOptions = {}): Promise<PreCheckerClient> {
const { session, errors } = await createTestSessionFromModel<Model>({ modelerfour: config }, spec);
const prechecker = await new QualityPreChecker(session).init();
const prechecker = new QualityPreChecker(session);
expect(errors.length).toBe(0);
return new PreCheckerClient(prechecker.input, prechecker.process());
@ -161,7 +161,7 @@ describe("Prechecker", () => {
);
const { session, errors } = await createTestSessionFromModel<Model>({}, spec);
const prechecker = await new QualityPreChecker(session).init();
const prechecker = new QualityPreChecker(session);
prechecker.process();
expect(errors).toHaveLength(1);
expect(errors[0]).toEqual({

Просмотреть файл

@ -1,9 +1,9 @@
import { readdirSync } from "fs";
import { codeModelSchema } from "@autorest/codemodel";
import { createTestSessionFromFiles } from "@autorest/extension-base/testing";
import { serialize } from "@azure-tools/codegen";
import { Model } from "@azure-tools/openapi";
import { ModelerFour } from "../../src/modeler/modelerfour";
import { createTestSessionFromFiles } from "../utils";
const cfg = {
modelerfour: {

Просмотреть файл

@ -1,42 +1,4 @@
import { Session, startSession } from "@autorest/extension-base";
import { readFile } from "@azure-tools/async-io";
import { deserialize, fail } from "@azure-tools/codegen";
import { Model } from "@azure-tools/openapi";
export interface TestSessionInput {
model: any;
filename: string;
content: string;
}
export interface TestSession<T> {
session: Session<T>;
errors: Array<any>;
}
async function readData(folder: string, ...files: Array<string>): Promise<Map<string, TestSessionInput>> {
const results = new Map<string, { model: any; filename: string; content: string }>();
for (const filename of files) {
const content = await readFile(`${folder}/${filename}`);
const model = deserialize<any>(content, filename);
results.set(filename, {
model,
filename,
content,
});
}
return results;
}
export async function createTestSessionFromFiles<TInputModel>(
config: any,
folder: string,
inputs: Array<string>,
): Promise<TestSession<TInputModel>> {
const models = await readData(folder, ...inputs);
return createTestSession(config, models);
}
import { createTestSession, TestSession } from "@autorest/extension-base/testing";
export async function createTestSessionFromModel<TInputModel>(
config: any,
@ -50,30 +12,3 @@ export async function createTestSessionFromModel<TInputModel>(
},
]);
}
export async function createTestSession<TInputModel>(
config: any,
inputs: Array<TestSessionInput> | Map<string, TestSessionInput>,
): Promise<TestSession<TInputModel>> {
const models = Array.isArray(inputs) ? inputs.reduce((m, x) => m.set(x.filename, x), new Map()) : inputs;
const errors: Array<any> = [];
const session = await startSession<TInputModel>({
ReadFile: (filename: string) =>
Promise.resolve(models.get(filename)?.content ?? fail(`missing input '${filename}'`)),
GetValue: (key: string) => Promise.resolve(key ? config[key] : config),
ListInputs: (artifactType?: string) => Promise.resolve([...models.values()].map((x) => x.filename)),
ProtectFiles: (path: string) => Promise.resolve(),
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string) => Promise.resolve(),
Message: (message: any): void => {
if (message.Channel === "warning" || message.Channel === "error" || message.Channel === "verbose") {
// console.error(`${message.Channel} ${message.Text}`);
if (message.Channel === "error") {
errors.push(message);
}
}
},
UpdateConfigurationFile: (filename: string, content: string) => {},
GetConfigurationFile: (filename: string) => Promise.resolve(""),
});
return { session, errors };
}

Просмотреть файл

@ -3,6 +3,10 @@
"version": "3.3.2",
"description": "Library for creating AutoRest extensions",
"main": "dist/index.js",
"exports": {
".": "./dist/index.js",
"./testing": "./dist/testing/index.js"
},
"typings": "./dist/index.d.ts",
"engines": {
"node": ">=12.0.0"

Просмотреть файл

@ -0,0 +1,71 @@
import { createMessageConnection, RequestType0, RequestType2 } from "vscode-jsonrpc";
import { AutorestExtensionHost, AutorestExtensionRpcHost } from "./extension-host";
import { Channel } from "./types";
namespace IAutoRestPluginTargetTypes {
export const GetPluginNames = new RequestType0<Array<string>, Error, void>("GetPluginNames");
export const Process = new RequestType2<string, string, boolean, Error, void>("Process");
}
export type AutoRestPluginHandler = (initiator: AutorestExtensionHost) => Promise<void>;
export class AutoRestExtension {
private readonly plugins: Record<string, AutoRestPluginHandler> = {};
public add(name: string, handler: AutoRestPluginHandler): void {
this.plugins[name] = handler;
}
public async run(
input: NodeJS.ReadableStream = process.stdin,
output: NodeJS.WritableStream = process.stdout,
): Promise<void> {
// connection setup
const channel = createMessageConnection(input, output, {
error(message) {
// eslint-disable-next-line no-console
console.error("rpc:error: ", message);
},
info(message) {
// eslint-disable-next-line no-console
console.error("rpc:info: ", message);
},
log(message) {
// eslint-disable-next-line no-console
console.error("rpc:log: ", message);
},
warn(message) {
// eslint-disable-next-line no-console
console.error("rpc:warn: ", message);
},
});
channel.onRequest(IAutoRestPluginTargetTypes.GetPluginNames, async () => Object.keys(this.plugins));
channel.onRequest(IAutoRestPluginTargetTypes.Process, async (pluginName: string, sessionId: string) => {
const host = new AutorestExtensionRpcHost(channel, sessionId);
try {
const handler = this.plugins[pluginName];
if (!handler) {
throw new Error(`Plugin host could not find requested plugin '${pluginName}'.`);
}
await handler(host);
return true;
} catch (e: any) {
if (await host.getValue<boolean>("debug")) {
// eslint-disable-next-line no-console
console.error(`PLUGIN FAILURE: ${e.message}, ${e.stack}, ${JSON.stringify(e, null, 2)}`);
}
host.message({
Channel: Channel.Fatal,
Text: "" + e,
Details: e,
});
return false;
}
});
// activate
channel.listen();
}
}

Просмотреть файл

@ -1,271 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createSandbox, deserialize, ShadowedNodePath } from "@azure-tools/codegen";
import { Schema, DEFAULT_SCHEMA } from "js-yaml";
import {
Channel,
Message,
Mapping,
RawSourceMap,
JsonPointerSegments,
Position,
PathPosition,
SourceLocation,
LogSource,
} from "./types";
import { Host } from ".";
const safeEval = createSandbox();
async function getModel<T>(service: Host, yamlSchema: Schema = DEFAULT_SCHEMA, artifactType?: string) {
const files = await service.ListInputs(artifactType);
const filename = files[0];
if (files.length === 0) {
throw new Error("Inputs missing.");
}
const content = await service.ReadFile(filename);
return {
filename,
model: deserialize<T>(content, filename, yamlSchema),
};
}
export class Session<TInputModel> {
private context!: any;
private _debug = false;
private _verbose = false;
model!: TInputModel;
filename!: string;
/* @internal */ constructor(public readonly service: Host) {}
/* @internal */ async init<TProject>(project?: TProject, schema: Schema = DEFAULT_SCHEMA, artifactType?: string) {
const m = await getModel<TInputModel>(this.service, schema, artifactType);
this.model = m.model;
this.filename = m.filename;
void this.initContext(project);
this._debug = await this.getValue("debug", false);
this._verbose = await this.getValue("verbose", false);
return this;
}
/* @internal */ async initContext<TP>(project?: TP) {
this.context = this.context || {
$config: await this.service.GetValue(""),
$project: project,
$lib: {
path: require("path"),
},
};
return this;
}
async readFile(filename: string): Promise<string> {
return this.service.ReadFile(filename);
}
async getValue<V>(key: string, defaultValue?: V): Promise<V> {
// check if it's in the model first
const m = <any>this.model;
let value = m && m.language && m.language.default ? m.language.default[key] : undefined;
// fall back to the configuration
if (value == null || value === undefined) {
value = await this.service.GetValue(key);
}
// try as a safe eval execution.
if (value === null || value === undefined) {
try {
value = safeEval(key, this.context);
} catch {
value = null;
}
}
if (defaultValue === undefined && value === null) {
throw new Error(`No value for configuration key '${key}' was provided`);
}
if (typeof value === "string") {
value = await this.resolveVariables(value);
}
// ensure that any content variables are resolved at the end.
return <V>(value !== null ? value : defaultValue);
}
async setValue<V>(key: string, value: V) {
(<any>this.model).language.default[key] = value;
}
async listInputs(artifactType?: string | undefined): Promise<string[]> {
return this.service.ListInputs(artifactType);
}
async protectFiles(path: string): Promise<void> {
return this.service.ProtectFiles(path);
}
writeFile(
filename: string,
content: string,
sourceMap?: Array<Mapping> | RawSourceMap | undefined,
artifactType?: string | undefined,
): void {
return this.service.WriteFile(filename, content, sourceMap, artifactType);
}
message(message: Message): void {
if (message.Channel === Channel.Debug && this._debug === false) {
return;
}
if (message.Channel === Channel.Verbose && this._verbose === false) {
return;
}
return this.service.Message(message);
}
updateConfigurationFile(filename: string, content: string): void {
return this.service.UpdateConfigurationFile(filename, content);
}
async getConfigurationFile(filename: string): Promise<string> {
return this.service.GetConfigurationFile(filename);
}
protected errorCount = 0;
protected static async getModel<T>(service: Host) {
const files = await service.ListInputs();
const filename = files[0];
if (files.length === 0) {
throw new Error("Inputs missing.");
}
return {
filename,
model: deserialize<T>(await service.ReadFile(filename), filename),
};
}
cache = new Array<any>();
replacer(key: string, value: any) {
if (typeof value === "object" && value !== null) {
if (this.cache.indexOf(value) !== -1) {
// Duplicate reference found
try {
// If this value does not reference a parent it can be deduped
return JSON.parse(JSON.stringify(value));
} catch (error) {
// discard key if value cannot be deduped
return;
}
}
// Store value in our collection
this.cache.push(value);
}
return value;
}
async resolveVariables(input: string): Promise<string> {
let output = input;
for (const rx of [/\$\((.*?)\)/g, /\$\{(.*?)\}/g]) {
/* eslint-disable */
for (let match; (match = rx.exec(input)); ) {
const text = match[0];
const inner = match[1];
let value = await this.getValue<any>(inner, null);
if (value !== undefined && value !== null) {
if (typeof value === "object") {
value = JSON.stringify(value, this.replacer, 2);
}
if (value === "{}") {
value = "true";
}
output = output.replace(text, value);
}
}
}
return output;
}
public checkpoint() {
if (this.errorCount > 0) {
throw new Error(`${this.errorCount} errors occured -- cannot continue.`);
}
}
protected msg(channel: Channel, message: string, key: string[], source?: LogSource, details?: any) {
const sourcePosition = source ? getPosition(this.filename, source) : undefined;
const sources = sourcePosition ? [sourcePosition] : [];
this.message({
Channel: channel,
Key: key,
Source: sources,
Text: message,
Details: details,
});
}
public warning(message: string, key: string[], source?: LogSource, details?: any) {
this.msg(Channel.Warning, message, key, source, details);
}
public hint(message: string, key: string[], source?: LogSource, details?: any) {
this.msg(Channel.Hint, message, key, source, details);
}
public error(message: string, key: string[], source?: LogSource, details?: any) {
this.errorCount++;
this.msg(Channel.Error, message, key, source, details);
}
public fatal(message: string, key: string[], source?: LogSource, details?: any) {
this.errorCount++;
this.msg(Channel.Fatal, message, key, source, details);
}
protected output(channel: Channel, message: string, details?: any) {
this.message({
Channel: channel,
Text: message,
Details: details,
});
}
public debug(message: string, details: any) {
this.output(Channel.Debug, message, details);
}
public verbose(message: string, details: any) {
this.output(Channel.Verbose, message, details);
}
public log(message: string, details: any) {
this.output(Channel.Information, message, details);
}
}
export async function startSession<TInputModel>(
service: Host,
project?: any,
schema: Schema = DEFAULT_SCHEMA,
artifactType?: string,
) {
return await new Session<TInputModel>(service).init(project, schema, artifactType);
}
function getPosition(document: string, source: LogSource): SourceLocation | undefined {
if (typeof source === "string") {
return { Position: { path: source }, document };
}
if (source[ShadowedNodePath]) {
return { Position: { path: source[ShadowedNodePath] }, document };
}
if ("path" in source || "line" in source) {
return { Position: source as any, document };
}
return undefined;
}

Просмотреть файл

@ -1,179 +0,0 @@
import { basename, dirname } from "path";
import { Readable } from "stream";
import {
createMessageConnection,
Logger,
RequestType0,
RequestType1,
RequestType2,
NotificationType2,
NotificationType4,
} from "vscode-jsonrpc";
import { Mapping, Message, RawSourceMap, Channel } from "./types";
namespace IAutoRestPluginTargetTypes {
export const GetPluginNames = new RequestType0<Array<string>, Error, void>("GetPluginNames");
export const Process = new RequestType2<string, string, boolean, Error, void>("Process");
}
interface IAutoRestPluginTarget {
GetPluginNames(): Promise<Array<string>>;
Process(pluginName: string, sessionId: string): Promise<boolean>;
}
namespace IAutoRestPluginInitiatorTypes {
export const ReadFile = new RequestType2<string, string, string, Error, void>("ReadFile");
export const GetValue = new RequestType2<string, string, any, Error, void>("GetValue");
export const ListInputs = new RequestType2<string, string | undefined, Array<string>, Error, void>("ListInputs");
export const ProtectFiles = new NotificationType2<string, string, void>("ProtectFiles");
export const WriteFile = new NotificationType4<
string,
string,
string,
Array<Mapping> | RawSourceMap | undefined,
void
>("WriteFile");
export const Message = new NotificationType2<string, Message, void>("Message");
}
export interface IAutoRestPluginInitiator {
ReadFile(filename: string): Promise<string>;
GetValue(key: string): Promise<any>;
ListInputs(artifactType?: string): Promise<Array<string>>;
ProtectFiles(path: string): Promise<void>;
WriteFile(filename: string, content: string, sourceMap?: Array<Mapping> | RawSourceMap, artifactType?: string): void;
Message(message: Message): void;
UpdateConfigurationFile(filename: string, content: string): void;
GetConfigurationFile(filename: string): Promise<string>;
}
export type AutoRestPluginHandler = (initiator: IAutoRestPluginInitiator) => Promise<void>;
export class AutoRestExtension {
private readonly plugins: { [name: string]: AutoRestPluginHandler } = {};
public Add(name: string, handler: AutoRestPluginHandler): void {
this.plugins[name] = handler;
}
public async Run(
input: NodeJS.ReadableStream = process.stdin,
output: NodeJS.WritableStream = process.stdout,
): Promise<void> {
// connection setup
const channel = createMessageConnection(input, output, {
error(message) {
// eslint-disable-next-line no-console
console.error("error: ", message);
},
info(message) {
// eslint-disable-next-line no-console
console.error("info: ", message);
},
log(message) {
// eslint-disable-next-line no-console
console.error("log: ", message);
},
warn(message) {
// eslint-disable-next-line no-console
console.error("warn: ", message);
},
});
channel.onRequest(IAutoRestPluginTargetTypes.GetPluginNames, async () => Object.keys(this.plugins));
channel.onRequest(IAutoRestPluginTargetTypes.Process, async (pluginName: string, sessionId: string) => {
try {
const handler = this.plugins[pluginName];
if (!handler) {
throw new Error(`Plugin host could not find requested plugin '${pluginName}'.`);
}
await handler({
async ProtectFiles(path: string): Promise<void> {
channel.sendNotification(IAutoRestPluginInitiatorTypes.ProtectFiles, sessionId, path);
},
UpdateConfigurationFile(filename: string, content: string): void {
channel.sendNotification(IAutoRestPluginInitiatorTypes.Message, sessionId, {
Channel: Channel.Configuration,
Key: [filename],
Text: content,
});
},
async GetConfigurationFile(filename: string): Promise<string> {
const configurations = await channel.sendRequest(
IAutoRestPluginInitiatorTypes.GetValue,
sessionId,
"configurationFiles",
);
const filenames = Object.getOwnPropertyNames(configurations);
if (filenames.length > 0) {
const basePath = dirname(filenames[0]);
for (const configFile of filenames) {
if (configFile.startsWith(basePath) && filename === basename(configFile)) {
return configurations[configFile];
}
}
}
return "";
},
async ReadFile(filename: string): Promise<string> {
return await channel.sendRequest(IAutoRestPluginInitiatorTypes.ReadFile, sessionId, filename);
},
async GetValue(key: string): Promise<any> {
return await channel.sendRequest(IAutoRestPluginInitiatorTypes.GetValue, sessionId, key);
},
async ListInputs(artifactType?: string): Promise<Array<string>> {
return await channel.sendRequest(IAutoRestPluginInitiatorTypes.ListInputs, sessionId, artifactType);
},
WriteFile(
filename: string,
content: string,
sourceMap?: Array<Mapping> | RawSourceMap,
artifactType?: string,
): void {
if (artifactType) {
channel.sendNotification(IAutoRestPluginInitiatorTypes.Message, sessionId, {
Channel: Channel.File,
Details: {
content: content,
type: artifactType,
uri: filename,
sourceMap: sourceMap,
},
Text: content,
Key: [artifactType, filename],
});
} else {
channel.sendNotification(
IAutoRestPluginInitiatorTypes.WriteFile,
sessionId,
filename,
content,
sourceMap,
);
}
},
Message(message: Message): void {
channel.sendNotification(IAutoRestPluginInitiatorTypes.Message, sessionId, message);
},
});
return true;
} catch (e: any) {
if (await channel.sendRequest(IAutoRestPluginInitiatorTypes.GetValue, sessionId, "debug")) {
// eslint-disable-next-line no-console
console.error(`PLUGIN FAILURE: ${e.message}, ${e.stack}, ${JSON.stringify(e, null, 2)}`);
}
channel.sendNotification(IAutoRestPluginInitiatorTypes.Message, sessionId, <Message>{
Channel: <any>"fatal",
Text: "" + e,
Details: e,
});
return false;
}
});
// activate
channel.listen();
}
}

Просмотреть файл

@ -0,0 +1,168 @@
import { basename, dirname } from "path";
import { RequestType2, NotificationType2, NotificationType4, MessageConnection } from "vscode-jsonrpc";
import { AutorestExtensionLogger } from "./extension-logger";
import { Mapping, Message, RawSourceMap, Channel } from "./types";
namespace IAutoRestPluginInitiatorTypes {
export const ReadFile = new RequestType2<string, string, string, Error, void>("ReadFile");
export const GetValue = new RequestType2<string, string, any, Error, void>("GetValue");
export const ListInputs = new RequestType2<string, string | undefined, Array<string>, Error, void>("ListInputs");
export const ProtectFiles = new NotificationType2<string, string, void>("ProtectFiles");
export const WriteFile = new NotificationType4<
string,
string,
string,
Array<Mapping> | RawSourceMap | undefined,
void
>("WriteFile");
export const Message = new NotificationType2<string, Message, void>("Message");
}
export interface WriteFileOptions {
/**
* @param filename Name of the file.
*/
filename: string;
/**
* @param content Content of the file.
*/
content: string;
/**
* @param sourceMap Source map that can be used to trace back source position in case of error.
*/
sourceMap?: Mapping[] | RawSourceMap;
/**
* @param artifactType Artifact type
*/
artifactType?: string;
}
export interface AutorestExtensionHost {
logger: AutorestExtensionLogger;
protectFiles(path: string): Promise<void>;
readFile(filename: string): Promise<string>;
getValue<T>(key: string): Promise<T | undefined>;
listInputs(artifactType?: string): Promise<Array<string>>;
writeFile({ filename, content, sourceMap, artifactType }: WriteFileOptions): void;
message(message: Message): void;
/**
* @deprecated
*/
UpdateConfigurationFile(filename: string, content: string): void;
/**
* @deprecated
*/
GetConfigurationFile(filename: string): Promise<string>;
}
export class AutorestExtensionRpcHost implements AutorestExtensionHost {
public logger: AutorestExtensionLogger;
public constructor(private channel: MessageConnection, private sessionId: string) {
this.logger = new AutorestExtensionLogger((x) => this.message(x));
}
/**
* Protect files that will not be cleared when using clear-output-folder.
* @param path Path to the file/folder to protect.
*/
public async protectFiles(path: string): Promise<void> {
this.channel.sendNotification(IAutoRestPluginInitiatorTypes.ProtectFiles, this.sessionId, path);
}
public async readFile(filename: string): Promise<string> {
return await this.channel.sendRequest(IAutoRestPluginInitiatorTypes.ReadFile, this.sessionId, filename);
}
/**
* Get a configuration key form the resolved configuration.
* @param key Path to the configuration entry. (Keys dot seperated)
* @returns Value of the configuration.
*/
public async getValue<T>(key: string): Promise<T | undefined> {
return await this.channel.sendRequest(IAutoRestPluginInitiatorTypes.GetValue, this.sessionId, key);
}
/**
* List inputs
* @param artifactType @optional Filter by artifact type.
* @returns name of the input files.
*/
public async listInputs(artifactType?: string): Promise<Array<string>> {
return await this.channel.sendRequest(IAutoRestPluginInitiatorTypes.ListInputs, this.sessionId, artifactType);
}
/**
* Write a file.
*/
public writeFile({ filename, content, sourceMap, artifactType }: WriteFileOptions): void {
if (artifactType) {
this.channel.sendNotification(IAutoRestPluginInitiatorTypes.Message, this.sessionId, {
Channel: Channel.File,
Details: {
content: content,
type: artifactType,
uri: filename,
sourceMap: sourceMap,
},
Text: content,
Key: [artifactType, filename],
});
} else {
this.channel.sendNotification(
IAutoRestPluginInitiatorTypes.WriteFile,
this.sessionId,
filename,
content,
sourceMap,
);
}
}
/**
* Send a message.
* @param message Message to send.
*/
public message(message: Message): void {
this.channel.sendNotification(IAutoRestPluginInitiatorTypes.Message, this.sessionId, message);
}
/**
* @deprecated
*/
public UpdateConfigurationFile(filename: string, content: string): void {
this.channel.sendNotification(IAutoRestPluginInitiatorTypes.Message, this.sessionId, {
Channel: Channel.Configuration,
Key: [filename],
Text: content,
});
}
/**
* @deprecated
*/
public async GetConfigurationFile(filename: string): Promise<string> {
const configurations = await this.channel.sendRequest(
IAutoRestPluginInitiatorTypes.GetValue,
this.sessionId,
"configurationFiles",
);
const filenames = Object.getOwnPropertyNames(configurations);
if (filenames.length > 0) {
const basePath = dirname(filenames[0]);
for (const configFile of filenames) {
if (configFile.startsWith(basePath) && filename === basename(configFile)) {
return configurations[configFile];
}
}
}
return "";
}
}

Просмотреть файл

@ -0,0 +1,80 @@
import { SourceLocation, Channel, Message } from "./types";
export type LogLevel = "debug" | "verbose" | "info" | "warning" | "error" | "fatal";
export interface LogInfo {
level: LogLevel;
message: string;
/**
* Code for the log.
* @example -> ["MyPlugin", "CannotDoThat"] -> "MyPlugin/CannotDoThat"
*/
key?: string[];
/**
* Position where this log can be traced to.
*/
source?: SourceLocation;
/**
* Additional details.
*/
details?: any;
}
export class AutorestExtensionLogger {
public constructor(private sendMessage: (message: Message) => void) {}
public debug(message: string) {
this.log({ level: "debug", message });
}
public verbose(message: string) {
this.log({ level: "verbose", message });
}
public info(message: string) {
this.log({ level: "info", message });
}
public warning(message: string, key: string[], source?: SourceLocation, details?: any) {
this.log({ level: "warning", message, key, source, details });
}
public error(message: string, key: string[], source?: SourceLocation, details?: any) {
this.log({ level: "error", message, key, source, details });
}
public fatal(message: string, key: string[], source?: SourceLocation, details?: any) {
this.log({ level: "fatal", message, key, source, details });
}
public log(info: LogInfo) {
const sources = info.source ? [info.source] : [];
this.sendMessage({
Channel: getChannel(info.level),
Key: info.key,
Source: sources,
Text: info.message,
Details: info.details,
});
}
}
function getChannel(level: LogLevel): Channel {
switch (level) {
case "debug":
return Channel.Debug;
case "verbose":
return Channel.Verbose;
case "info":
return Channel.Information;
case "warning":
return Channel.Warning;
case "error":
return Channel.Error;
case "fatal":
return Channel.Fatal;
}
}

Просмотреть файл

@ -1,13 +1,5 @@
export { AutoRestExtension, IAutoRestPluginInitiator as Host } from "./extension-base";
export {
ArtifactMessage,
Message,
Artifact,
Channel,
Mapping,
RawSourceMap,
SourceLocation,
JsonPointerSegments,
Position,
} from "./types";
export * from "./convenience";
export { AutoRestExtension } from "./autorest-extension";
export * from "./extension-host";
export * from "./extension-logger";
export * from "./types";
export * from "./session";

Просмотреть файл

@ -0,0 +1,175 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createSandbox, deserialize, ShadowedNodePath } from "@azure-tools/codegen";
import { Schema, DEFAULT_SCHEMA } from "js-yaml";
import { AutorestExtensionHost, WriteFileOptions } from "./extension-host";
import { LogLevel } from "./extension-logger";
import { Channel, Message, SourceLocation, LogSource } from "./types";
export interface SessionOptions<T> {
host: AutorestExtensionHost;
filename: string;
model: T;
configuration: any;
}
const safeEval = createSandbox();
export class Session<TInputModel> {
public model: TInputModel;
public filename: string;
public configuration: Record<string, any>;
private context: any;
protected errorCount = 0;
private _debug = false;
private _verbose = false;
private service: AutorestExtensionHost;
/* @internal */ constructor(options: SessionOptions<TInputModel>) {
this.service = options.host;
this.filename = options.filename;
this.model = options.model;
this.configuration = options.configuration;
this._debug = options.configuration.debug;
this._verbose = options.configuration.verbose;
this.context = {
$config: options.configuration,
$lib: {
path: require("path"),
},
};
}
public async readFile(filename: string): Promise<string> {
return this.service.readFile(filename);
}
public async getValue<V>(key: string, defaultValue?: V): Promise<V> {
let value = await this.service.getValue(key);
// try as a safe eval execution.
if (value === null || value === undefined) {
try {
value = safeEval(key, this.context);
} catch {
value = null;
}
}
if (defaultValue === undefined && value === null) {
throw new Error(`No value for configuration key '${key}' was provided`);
}
// ensure that any content variables are resolved at the end.
return <V>(value !== null ? value : defaultValue);
}
async listInputs(artifactType?: string | undefined): Promise<string[]> {
return this.service.listInputs(artifactType);
}
async protectFiles(path: string): Promise<void> {
return this.service.protectFiles(path);
}
public writeFile(options: WriteFileOptions): void {
return this.service.writeFile(options);
}
public message(message: Message): void {
if (message.Channel === Channel.Debug && this._debug === false) {
return;
}
if (message.Channel === Channel.Verbose && this._verbose === false) {
return;
}
return this.service.message(message);
}
public checkpoint() {
if (this.errorCount > 0) {
throw new Error(`${this.errorCount} errors occured -- cannot continue.`);
}
}
public debug(message: string) {
this.msg("debug", message);
}
public verbose(message: string) {
this.msg("verbose", message);
}
public info(message: string) {
this.msg("info", message);
}
public warning(message: string, key: string[], source?: LogSource, details?: any) {
this.msg("warning", message, key, source, details);
}
public error(message: string, key: string[], source?: LogSource, details?: any) {
this.errorCount++;
this.msg("error", message, key, source, details);
}
public fatal(message: string, key: string[], source?: LogSource, details?: any) {
this.errorCount++;
this.msg("fatal", message, key, source, details);
}
protected msg(level: LogLevel, message: string, key?: string[], source?: LogSource, details?: any) {
const sourcePosition = source ? getPosition(this.filename, source) : undefined;
this.service.logger.log({
level,
message,
key,
source: sourcePosition,
details,
});
}
}
async function getModel<T>(service: AutorestExtensionHost, yamlSchema: Schema = DEFAULT_SCHEMA, artifactType?: string) {
const files = await service.listInputs(artifactType);
const filename = files[0];
if (files.length === 0) {
throw new Error("Inputs missing.");
}
const content = await service.readFile(filename);
return {
filename,
model: deserialize<T>(content, filename, yamlSchema),
};
}
export async function startSession<TInputModel>(
host: AutorestExtensionHost,
schema: Schema = DEFAULT_SCHEMA,
artifactType?: string,
) {
const { model, filename } = await getModel<TInputModel>(host, schema, artifactType);
const configuration = await host.getValue("");
return new Session<TInputModel>({ host, filename, model, configuration });
}
function getPosition(document: string, source: LogSource): SourceLocation | undefined {
if (typeof source === "string") {
return { Position: { path: source }, document };
}
if (source[ShadowedNodePath]) {
return { Position: { path: source[ShadowedNodePath] }, document };
}
if ("path" in source || "line" in source) {
return { Position: source as any, document };
}
return undefined;
}

Просмотреть файл

@ -0,0 +1 @@
export * from "./test-session";

Просмотреть файл

@ -0,0 +1,77 @@
import { readFile } from "fs/promises";
import { deserialize, fail } from "@azure-tools/codegen";
import { WriteFileOptions } from "../extension-host";
import { AutorestExtensionLogger } from "../extension-logger";
import { Session, startSession } from "../session";
export interface TestSessionInput {
model: any;
filename: string;
content: string;
}
export interface TestSession<T> {
session: Session<T>;
errors: Array<any>;
}
async function readData(folder: string, ...files: Array<string>): Promise<Map<string, TestSessionInput>> {
const results = new Map<string, { model: any; filename: string; content: string }>();
for (const filename of files) {
const buffer = await readFile(`${folder}/${filename}`);
const content = buffer.toString();
const model = deserialize<any>(content, filename);
results.set(filename, {
model,
filename,
content,
});
}
return results;
}
export async function createTestSessionFromFiles<TInputModel>(
config: any,
folder: string,
inputs: Array<string>,
): Promise<TestSession<TInputModel>> {
const models = await readData(folder, ...inputs);
return createTestSession(config, models);
}
/**
* Create a Test session to use for testing extension.
* @param config Autorest configuration to use in this session.
* @param inputs List of inputs.
* @returns Test session
*/
export async function createTestSession<TInputModel>(
config: any,
inputs: Array<TestSessionInput> | Map<string, TestSessionInput>,
): Promise<TestSession<TInputModel>> {
const models = Array.isArray(inputs) ? inputs.reduce((m, x) => m.set(x.filename, x), new Map()) : inputs;
const errors: Array<any> = [];
const sendMessage = (message: any): void => {
if (message.Channel === "warning" || message.Channel === "error" || message.Channel === "verbose") {
if (message.Channel === "error") {
errors.push(message);
}
}
};
const session = await startSession<TInputModel>({
logger: new AutorestExtensionLogger(sendMessage),
readFile: (filename: string) =>
Promise.resolve(models.get(filename)?.content ?? fail(`missing input '${filename}'`)),
getValue: (key: string) => Promise.resolve(key ? config[key] : config),
listInputs: (artifactType?: string) => Promise.resolve([...models.values()].map((x) => x.filename)),
protectFiles: (path: string) => Promise.resolve(),
writeFile: (options: WriteFileOptions) => Promise.resolve(),
message: sendMessage,
UpdateConfigurationFile: (filename: string, content: string) => {},
GetConfigurationFile: (filename: string) => Promise.resolve(""),
});
return { session, errors };
}

Просмотреть файл

@ -30,34 +30,34 @@ export interface Artifact {
*/
export enum Channel {
/** Information is considered the mildest of responses; not necesarily actionable. */
Information = <any>"information",
Information = "information",
/** Warnings are considered important for best practices, but not catastrophic in nature. */
Warning = <any>"warning",
Warning = "warning",
/** Errors are considered blocking issues that block a successful operation. */
Error = <any>"error",
Error = "error",
/** Debug messages are designed for the developer to communicate internal autorest implementation details. */
Debug = <any>"debug",
Debug = "debug",
/** Verbose messages give the user additional clarity on the process. */
Verbose = <any>"verbose",
Verbose = "verbose",
/** Catastrophic failure, likely abending the process. */
Fatal = <any>"fatal",
Fatal = "fatal",
/** Hint messages offer guidance or support without forcing action. */
Hint = <any>"hint",
Hint = "hint",
/** File represents a file output from an extension. Details are a Artifact and are required. */
File = <any>"file",
File = "file",
/** content represents an update/creation of a configuration file. The final uri will be in the same folder as the primary config file. */
Configuration = <any>"configuration",
Configuration = "configuration",
/** Protect is a path to not remove during a clear-output-folder. */
Protect = <any>"protect",
Protect = "protect",
}
export interface Message {

2
packages/libs/extension-base/testing.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,2 @@
// Workaround for es6 modules exports not working well with typescript for now. https://github.com/microsoft/TypeScript/issues/33079
export * from "./dist/testing";

Просмотреть файл

@ -0,0 +1,2 @@
// Workaround for es6 modules exports not working well with typescript for now. https://github.com/microsoft/TypeScript/issues/33079
module.exports = require("./dist/testing");