Batch mode (and a handful of other joy) (#2258)

* WIP

* Regenerated
This commit is contained in:
Garrett Serack 2017-05-08 18:45:40 -07:00 коммит произвёл Johannes Bader
Родитель 5b4c129074
Коммит 46f67be213
24 изменённых файлов: 904 добавлений и 309 удалений

28
.vscode/launch.json поставляемый
Просмотреть файл

@ -2,13 +2,37 @@
"version": "0.2.0",
"configurations": [
{
"diagnosticLogging": true,
"type": "node2",
"type": "node",
"protocol": "inspector",
"request": "launch",
"name": "run tests",
"program": "${workspaceRoot}/src/autorest-core/node_modules/mocha/bin/_mocha",
"args": [
"test",
"-g",
"TestConfiguration"
],
"cwd": "${workspaceRoot}/src/autorest-core"
},
{
"type": "node",
"protocol": "inspector",
"request": "launch",
"name": "run bootstrapper",
"program": "${workspaceRoot}/src/autorest/app.js",
"cwd": "${workspaceRoot}",
"preLaunchTask": "build/typescript"
},
{
"type": "node",
"protocol": "inspector",
"request": "launch",
"name": "run autorest",
"program": "${workspaceRoot}/src/autorest-core/app.js",
"args" : [
"C:/work/github/azure-rest-api-specs/profile/all.md"
],
"cwd": "${workspaceRoot}"
}
]
}

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

@ -1,3 +1,2 @@
ERROR: Syntax error: Invalid YAML object.
- /Samples/test/error-behavior/config-bad-syntax/readme.md:6:0
Error occurred. Exiting.

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

@ -0,0 +1,10 @@
Error occurred. Exiting.
{ Error: Error occurred. Exiting.
at ...
at ...
at ...
at ...
at ...
at ...
at ...
at ...

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

@ -7,4 +7,3 @@ ERROR: Syntax Error Encountered: Syntax error: bad indentation of a mapping entr
ERROR: Syntax Error Encountered: Syntax error: bad indentation of a mapping entry
- /Samples/test/error-behavior/openapi-md-bad-syntax/tiny.md:65:18
FATAL: loader - FAILED
Error occurred. Exiting.

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

@ -0,0 +1,9 @@
Process() Cancelled due to exception : Error occurred. Exiting.
Error occurred. Exiting.
{ Error: Error occurred. Exiting.
at ...
at ...
at ...
at ...
at ...
at ...

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

@ -1,4 +1,3 @@
ERROR: Referenced file '/Samples/test/error-behavior/openapi-yaml-bad-file-reference/i-do-not-exist.json' not found
- /Samples/test/error-behavior/openapi-yaml-bad-file-reference/tiny.yaml:23:10
FATAL: loader - FAILED
Error occurred. Exiting.

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

@ -0,0 +1,8 @@
Process() Cancelled due to exception : Error occurred. Exiting.
Error occurred. Exiting.
{ Error: Error occurred. Exiting.
at ...
at ...
at ...
at ...
at ...

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

@ -0,0 +1 @@
Process() Cancelled due to exception : Plugin modeler reported failure.

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

@ -14,6 +14,7 @@ import { Parse, Stringify } from './lib/ref/yaml';
import { CreateObject, nodes } from './lib/ref/jsonpath';
import { OutstandingTaskAwaiter } from "./lib/outstanding-task-awaiter";
import { AutoRest } from "./lib/autorest-core";
import { ShallowCopy } from "./lib/source-map/merging"
import { Message, Channel } from "./lib/message";
import { resolve as currentDirectory } from "path";
import { ChildProcess } from "child_process";
@ -23,19 +24,21 @@ import { isLegacy, CreateConfiguration } from "./legacyCli";
import { DataStore } from "./lib/data-store/data-store";
import { RealFileSystem } from "./lib/file-system";
import { Exception, OperationCanceledException } from './lib/exception';
import { Console } from "./lib/console"
import { exists } from "./lib/ref/async"
/**
* Legacy AutoRest
*/
function awaitable(child: ChildProcess): Promise<number> {
return new Promise((resolve, reject) => {
return new Promise<number>((resolve, reject) => {
child.addListener("error", reject);
child.addListener("exit", resolve);
});
}
async function legacyMain(autorestArgs: string[]): Promise<void> {
async function legacyMain(autorestArgs: string[]): Promise<number> {
if (autorestArgs.indexOf("-FANCY") !== -1) {
// generate virtual config file
const currentDirUri = CreateFolderUri(currentDirectory());
@ -46,7 +49,7 @@ async function legacyMain(autorestArgs: string[]): Promise<void> {
if (autorestArgs[0] === "init") {
const clientNameGuess = (config["override-info"] || {}).title || Parse<any>(await ReadUri(config["input-file"][0])).info.title;
await autorestInit(clientNameGuess, Array.isArray(config["input-file"]) ? config["input-file"] as any : []);
return;
return 0;
}
// autorest init-min
if (autorestArgs[0] === "init-min") {
@ -61,7 +64,7 @@ ${Stringify(config).replace(/^---\n/, "")}
~~~
`.replace(/~/g, "`"));
return;
return 0;
}
// autorest init-cli
if (autorestArgs[0] === "init-cli") {
@ -74,7 +77,7 @@ ${Stringify(config).replace(/^---\n/, "")}
}
}
console.log(args.join(" "));
return;
return 0;
}
config["base-folder"] = currentDirUri;
@ -82,24 +85,7 @@ ${Stringify(config).replace(/^---\n/, "")}
await api.AddConfiguration(config);
const outstanding = new OutstandingTaskAwaiter();
api.GeneratedFile.Subscribe((_, file) => outstanding.Await(WriteString(file.uri, file.content)));
//api.Debug.Subscribe((_, m) => console.log(m.Text));
//api.Verbose.Subscribe((_, m) => console.log(m.Text));
api.Message.Subscribe((_, m) => {
switch (m.Channel) {
case Channel.Information:
console.log(m.Text);
break;
case Channel.Warning:
console.warn(m.Text);
break;
case Channel.Error:
console.error(m.Text);
break;
case Channel.Fatal:
console.error(m.Text);
break;
}
});
subscribeMessages(api, () => { });
const result = await api.Process().finish;
if (result != true) {
@ -115,6 +101,8 @@ ${Stringify(config).replace(/^---\n/, "")}
const exitCode = await awaitable(autorestExe);
process.exit(exitCode);
}
return 0;
}
@ -150,6 +138,33 @@ function parseArgs(autorestArgs: string[]): CommandLineArgs {
return result;
}
function subscribeMessages(api: AutoRest, errorCounter: () => void) {
api.Message.Subscribe((_, m) => {
switch (m.Channel) {
case Channel.Information:
console.log(m.Text);
break;
case Channel.Warning:
console.warn(m.Text);
break;
case Channel.Error:
errorCounter();
console.error(m.Text);
break;
case Channel.Debug:
console.log(m.Text);
break;
case Channel.Verbose:
console.log(m.Text);
break;
case Channel.Fatal:
errorCounter();
console.error(m.Text);
break;
}
});
}
async function autorestInit(title: string = "API-NAME", inputs: string[] = ["LIST INPUT FILES HERE"]) {
const cwdUri = CreateFolderUri(currentDirectory());
for (let i = 0; i < inputs.length; ++i) {
@ -200,43 +215,219 @@ csharp:
`.replace(/~/g, "`"));
}
async function currentMain(autorestArgs: string[]): Promise<void> {
let exitcode = 0;
const outstanding = new OutstandingTaskAwaiter();
let args: CommandLineArgs;
async function currentMain(autorestArgs: string[]): Promise<number> {
if (autorestArgs[0] === "init") {
await autorestInit();
return;
return 0;
}
const args = parseArgs(autorestArgs);
// parse the args from the command line
args = parseArgs(autorestArgs);
// identify where we are starting from.
const currentDirUri = CreateFolderUri(currentDirectory());
// get an instance of AutoRest and add the command line switches to the configuration.
const api = new AutoRest(new RealFileSystem(), ResolveUri(currentDirUri, args.configFileOrFolder || "."));
for (const s of args.switches) {
await api.AddConfiguration(s);
}
const outstanding = new OutstandingTaskAwaiter();
api.AddConfiguration(args.switches);
// listen for output messages and file writes
subscribeMessages(api, () => exitcode++);
api.GeneratedFile.Subscribe((_, file) => outstanding.Await(WriteString(file.uri, file.content)));
//api.Debug.Subscribe((_, m) => console.log(m.Text));
//api.Verbose.Subscribe((_, m) => console.log(m.Text));
api.Message.Subscribe((_, m) => {
switch (m.Channel) {
case Channel.Information:
console.log(m.Text);
break;
case Channel.Warning:
console.warn(m.Text);
break;
case Channel.Error:
console.error(m.Text);
break;
case Channel.Fatal:
console.error(m.Text);
break;
const config = (await api.view);
try {
// is this a batch process?
if (config["batch"]) {
return await batch(api);
}
// maybe a merge process
if (config["merge"]) {
return await merge(api);
}
// Just regular ol' AutoRest!
const result = await api.Process().finish;
if (result != true) {
throw result;
}
});
const result = await api.Process().finish;
if (result != true) {
throw result;
}
await outstanding.Wait();
finally {
// wait for any outstanding file writes to complete before we bail.
await outstanding.Wait();
}
// return the exit code to the caller.
return exitcode;
}
async function merge(api: AutoRest): Promise<number> {
// get the configuration
const config = await api.view;
for (const configFile of config.InputFileUris) {
// let's get out of here if things are not going well.
if (exitcode > 0) {
break;
}
}
return 0;
}
function shallowMerge(existing: any, more: any) {
if (existing && more) {
for (const key of Object.getOwnPropertyNames(more)) {
const value = more[key];
if (value !== undefined) {
/* if (existing[key]) {
Console.Log(`> Warning: ${key} is overwritten.`);
} */
existing[key] = value;
}
}
return existing;
}
if (existing) {
return existing;
}
return more;
}
function getRds(schema: any, path: string): Array<string> {
const rx = /.*\/(.*)\/(.*).json/;
const m = rx.exec(path) || [];
const apiversion = m[1];
const namespace = m[2];
const result = [];
if (schema.resourceDefinitions) {
for (const name of Object.getOwnPropertyNames(schema.resourceDefinitions)) {
result.push(`{ "$ref": "http://schema.management.azure.com/schemas/${apiversion}/${namespace}.json#/resourceDefinitions/${name}" }, `);
}
}
return result;
}
async function batch(api: AutoRest): Promise<number> {
// get the configuration
const outputs = new Map<string, string>();
const schemas = new Array<string>();
const config = await api.view;
for (const batchConfig of config.GetNestedConfiguration("batch")) { // really, there should be only one
for (const eachFile of batchConfig["input-file"]) {
const path = ResolveUri(config.configFileFolderUri, eachFile);
const content = await ReadUri(path);
if (!AutoRest.IsSwaggerFile(content)) {
exitcode++;
Console.Error(`File ${path} is not a OpenAPI file.`);
continue;
}
// Create the autorest instance for that item
const instance = new AutoRest(new RealFileSystem(), config.configFileFolderUri);
instance.GeneratedFile.Subscribe((_, file) => {
if (file.uri.endsWith(".json")) {
const more = JSON.parse(file.content);
if (!outputs.has(file.uri)) {
// Console.Log(` Writing *${file.uri}*`);
outputs.set(file.uri, file.content);
outstanding.Await(WriteString(file.uri, file.content))
schemas.push(...getRds(more, file.uri));
return;
} else {
const existing = JSON.parse(<string>outputs.get(file.uri));
// Console.Log(` Updating *${file.uri}*`);
schemas.push(...getRds(more, file.uri));
existing.resourceDefinitions = shallowMerge(existing.resourceDefinitions, more.resourceDefinitions);
existing.definitions = shallowMerge(existing.definitions, more.definitions);
const content = JSON.stringify(existing, null, 2);
outputs.set(file.uri, content);
outstanding.Await(WriteString(file.uri, content));
}
}
});
subscribeMessages(instance, () => exitcode++);
// set configuration for that item
instance.AddConfiguration(ShallowCopy(batchConfig, "input-file"));
instance.AddConfiguration({ "input-file": eachFile });
const newView = await instance.view;
// console.log(`Inputs: ${newView["input-file"]}`);
Console.Log(`Running autorest for *${path}* `);
// ok, kick off the process for that one.
await instance.Process().finish.then((result) => {
// console.log(`done: ${path}`);
exitcode++;
if (result != true) {
throw result;
}
});
}
}
await outstanding;
return exitcode;
}
async function deprecatedBatch(api: AutoRest): Promise<number> {
// get the configuration
const config = await api.view;
for (const batchConfig of config.GetNestedConfiguration("batch")) { // really, there should be only one
for (const eachGeneration of batchConfig.GetNestedConfiguration("for-each")) {
for (const configFile of eachGeneration["input-file"]) { // really, there should be only one here too.
const path = ResolveUri(config.configFileFolderUri, configFile);
const content = await ReadUri(path);
if (!AutoRest.IsConfigurationFile(content)) {
exitcode++;
console.error(`File ${path} is not a AutoRest configuration file.`);
continue;
}
// Create the autorest instance for that item
const instance = new AutoRest(new RealFileSystem(), path);
instance.GeneratedFile.Subscribe((_, file) => {
console.log(`writing ${file.uri}`);
return outstanding.Await(WriteString(file.uri, file.content))
});
subscribeMessages(instance, () => exitcode++);
// set configuration for that item
instance.AddConfiguration(ShallowCopy(eachGeneration, "input-file"));
instance.AddConfiguration(ShallowCopy(batchConfig, "for-each"));
const newView = await instance.view;
// console.log(`Inputs: ${newView["input-file"]}`);
console.log(`Running autorest for ${path} `);
// ok, kick off the process for that one.
await instance.Process().finish.then((result) => {
console.log(`done: ${path}`);
exitcode++;
if (result != true) {
throw result;
}
});
break;
}
}
}
return exitcode;
}
/**
@ -244,8 +435,12 @@ async function currentMain(autorestArgs: string[]): Promise<void> {
*/
async function main() {
let autorestArgs: Array<string> = [];
try {
const autorestArgs = process.argv.slice(2);
let exitcode: number = 0;
autorestArgs = process.argv.slice(2);
// temporary: --help displays legacy AutoRest's -Help message
if (autorestArgs.indexOf("--help") !== -1) {
@ -254,9 +449,9 @@ async function main() {
}
if (isLegacy(autorestArgs)) {
await legacyMain(autorestArgs);
exitcode = await legacyMain(autorestArgs);
} else {
await currentMain(autorestArgs);
exitcode = await currentMain(autorestArgs);
}
// for relaxed profiling (assuming that no one calls `main` from electron... use AAAL!)
@ -265,7 +460,11 @@ async function main() {
process.exit(0);
} catch (e) {
if (e instanceof Exception) {
console.error(e.message);
console.log(e.message);
if (autorestArgs.indexOf("--debug")) {
console.log(e);
}
process.exit(e.exitCode);
}

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

@ -2,7 +2,7 @@ import { Stringify } from './ref/yaml';
import { RunPipeline } from './pipeline/pipeline';
import { SmartPosition, Position } from './ref/source-map';
import { DataStore, Metadata } from './data-store/data-store';
import { IEnumerable, From } from './ref/linq';
import { IEnumerable, From, Push } from './ref/linq';
import { IEvent, EventDispatcher, EventEmitter } from "./events";
import { IFileSystem } from "./file-system";
import { Configuration, ConfigurationView, MessageEmitter } from './configuration';
@ -31,7 +31,7 @@ export class AutoRest extends EventEmitter {
})();
}
/**
*
*
* @param rootUri The rootUri of the workspace. Is null if no workspace is open.
* @param fileSystem The implementation of the filesystem to load and save files from the host application.
*/
@ -46,7 +46,7 @@ export class AutoRest extends EventEmitter {
* @param content - the file content to evaluate
*/
public static async IsSwaggerFile(content: string): Promise<boolean> {
// this checks to see if the document is a swagger document
// this checks to see if the document is a swagger document
try {
// quick check to see if it's json already
let doc = JSON.parse(content);
@ -120,8 +120,8 @@ export class AutoRest extends EventEmitter {
}
}
public AddConfiguration(configuratuion: any): void {
this._configurations.push(configuratuion);
public AddConfiguration(configuration: any): void {
Push(this._configurations, configuration);
this.Invalidate();
}
@ -132,7 +132,7 @@ export class AutoRest extends EventEmitter {
}
public get HasConfiguration(): Promise<boolean> {
return new Promise(async (r, f) => {
return new Promise<boolean>(async (r, f) => {
(await this.view);
r(false);
});
@ -166,8 +166,6 @@ export class AutoRest extends EventEmitter {
return false;
}
this.Message.Dispatch({ Channel: Channel.Debug, Text: `Starting Process() Run Pipeline.` });
if (view.InputFileUris.length === 0) {
throw new Exception("No input files provided.\n\nUse --help to get help information.", 0);
}
@ -215,7 +213,7 @@ export class AutoRest extends EventEmitter {
@EventEmitter.Event public Finished: IEvent<AutoRest, boolean | Error>;
/**
* Event: Signals when a File is generated
* Event: Signals when a File is generated
*/
@EventEmitter.Event public GeneratedFile: IEvent<AutoRest, Artifact>;
/**

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

@ -7,7 +7,7 @@ import { OperationAbortedException } from "./exception";
import { TryDecodeEnhancedPositionFromName } from "./source-map/source-map";
import { Supressor } from "./pipeline/supression";
import { matches, stringify } from "./ref/jsonpath";
import { MergeOverwriteOrAppend } from "./source-map/merging";
import { MergeOverwriteOrAppend, resolveRValue, ShallowCopy } from "./source-map/merging";
import { DataHandleRead, DataStore } from './data-store/data-store';
import { EventEmitter, IEvent } from "./events";
import { CodeBlock, EvaluateGuard, ParseCodeBlocks } from "./parsing/literate-yaml";
@ -19,6 +19,8 @@ import { Channel, Message, SourceLocation, Range } from "./message";
import { Artifact } from "./artifact";
import { CancellationTokenSource, CancellationToken } from "./ref/cancallation";
const RESOLVE_MACROS_AT_RUNTIME = true;
export interface AutoRestConfigurationImpl {
__info?: string | null;
"input-file": string[] | string;
@ -28,6 +30,9 @@ export interface AutoRestConfigurationImpl {
"message-format"?: "json";
"vscode"?: any; // activates VS Code specific behavior and does *NOT* influence the core's behavior (only consumed by VS Code extension)
"debug"?: boolean;
"verbose"?: boolean;
// plugin specific
"output-file"?: string;
"output-folder"?: string;
@ -50,25 +55,25 @@ export interface AutoRestConfigurationImpl {
}
// TODO: operate on DataHandleRead and create source map!
function MergeConfigurations(a: AutoRestConfigurationImpl, b: AutoRestConfigurationImpl): AutoRestConfigurationImpl {
function MergeConfigurations(higherPriority: AutoRestConfigurationImpl, lowerPriority: AutoRestConfigurationImpl): AutoRestConfigurationImpl {
// check guard
if (b.__info && !EvaluateGuard(b.__info, a)) {
if (lowerPriority.__info && !EvaluateGuard(lowerPriority.__info, higherPriority)) {
// guard false? => skip
return a;
return higherPriority;
}
// merge
return MergeOverwriteOrAppend(a, b);
return MergeOverwriteOrAppend(higherPriority, lowerPriority);
}
function ValuesOf<T>(obj: any): Iterable<T> {
if (obj === undefined) {
function ValuesOf<T>(value: any): Iterable<T> {
if (value === undefined) {
return [];
}
if (obj instanceof Array) {
return obj;
if (value instanceof Array) {
return value;
}
return [obj];
return [value];
}
export interface Directive {
@ -118,7 +123,7 @@ export class DirectiveView {
export class MessageEmitter extends EventEmitter {
/**
* Event: Signals when a File is generated
* Event: Signals when a File is generated
*/
@EventEmitter.Event public GeneratedFile: IEvent<MessageEmitter, Artifact>;
/**
@ -137,7 +142,25 @@ export class MessageEmitter extends EventEmitter {
/* @internal */ public get CancellationToken(): CancellationToken { return this.cancellationTokenSource.token; }
}
function ProxifyConfigurationView(cfgView: any) {
return new Proxy(cfgView, {
get: (target, property) => {
const value = (<any>target)[property];
if (value && value instanceof Array) {
const result = [];
for (const each of value) {
result.push(resolveRValue(each, "", target, null));
}
return result;
}
return resolveRValue(value, <string>property, null, cfgView);
}
});
}
export class ConfigurationView {
[name: string]: any;
private suppressor: Supressor;
@ -147,34 +170,73 @@ export class ConfigurationView {
...configs: Array<AutoRestConfigurationImpl> // decreasing priority
) {
// TODO: fix configuration loading, note that there was no point in passing that DataStore used
// TODO: fix configuration loading, note that there was no point in passing that DataStore used
// for loading in here as all connection to the sources is lost when passing `Array<AutoRestConfigurationImpl>` instead of `DataHandleRead`s...
// theoretically the `ValuesOf` approach and such won't support blaming (who to blame if $.directives[3] sucks? which code block was it from)
// long term, we simply gotta write a `Merge` method that adheres to the rules we need in here.
this.config = <any>{
this.rawConfig = <any>{
"directive": [],
"input-file": [],
"output-artifact": []
"output-artifact": [],
};
for (const config of configs) {
this.config = MergeConfigurations(this.config, config);
this.rawConfig = MergeConfigurations(this.rawConfig, config);
}
// default values that are the least priority.
this.rawConfig = MergeConfigurations(this.rawConfig, <any>{
"base-folder": ".",
"output-folder": "generated",
"debug": false,
"verbose": false,
"disable-validation": false
});
if (RESOLVE_MACROS_AT_RUNTIME) {
// if RESOLVE_MACROS_AT_RUNTIME is set
// this will insert a Proxy object in most of the uses of
// the configuration, and will do a macro resolution when the
// value is retrieved.
// I have turned on this behavior by default. I'm not sure that
// I need it at this point, but I'm leaving this code here since
// It's possible that I do.
this.config = ProxifyConfigurationView(this.rawConfig);
} else {
this.config = this.rawConfig;
}
this.suppressor = new Supressor(this);
this.Message({ Channel: Channel.Debug, Text: `Creating ConfigurationView : ${configs.length} sections.` });
}
/* @internal */ public get DataStore(): DataStore { return this.messageEmitter.DataStore; }
/* @internal */ public get CancellationToken(): CancellationToken { return this.messageEmitter.CancellationToken; }
/* @internal */ public get CancellationTokenSource(): CancellationTokenSource { return this.messageEmitter.CancellationTokenSource; }
/* @internal */ public get GeneratedFile(): IEvent<MessageEmitter, Artifact> {
return this.messageEmitter.GeneratedFile;
public get Keys(): Array<string> {
return Object.getOwnPropertyNames(this.config);
}
public Dump(title: string = "") {
console.log(`\n${title}\n===================================`)
for (const each of Object.getOwnPropertyNames(this.config)) {
console.log(`${each} : ${(<any>this.config)[each]}`);
};
}
/* @internal */ public get Indexer(): ConfigurationView {
return new Proxy<ConfigurationView>(this, {
get: (target, property) => {
return property in target.config ? (<any>target.config)[property] : this[property];
}
});
}
/* @internal */ public get DataStore(): DataStore { return this.messageEmitter.DataStore; }
/* @internal */ public get CancellationToken(): CancellationToken { return this.messageEmitter.CancellationToken; }
/* @internal */ public get CancellationTokenSource(): CancellationTokenSource { return this.messageEmitter.CancellationTokenSource; }
/* @internal */ public get GeneratedFile(): IEvent<MessageEmitter, Artifact> { return this.messageEmitter.GeneratedFile; }
private config: AutoRestConfigurationImpl;
private rawConfig: AutoRestConfigurationImpl;
private ResolveAsFolder(path: string): string {
return EnsureIsFolderUri(ResolveUri(this.BaseFolderUri, path));
@ -185,7 +247,7 @@ export class ConfigurationView {
}
private get BaseFolderUri(): string {
return EnsureIsFolderUri(ResolveUri(this.configFileFolderUri, this.config["base-folder"] || "."));
return EnsureIsFolderUri(ResolveUri(this.configFileFolderUri, this.config["base-folder"] as string));
}
// public methods
@ -202,7 +264,7 @@ export class ConfigurationView {
}
public get OutputFolderUri(): string {
return this.ResolveAsFolder(this.config["output-folder"] || "generated");
return this.ResolveAsFolder(this.config["output-folder"] as string);
}
public IsOutputArtifactRequested(artifact: string): boolean {
@ -217,17 +279,32 @@ export class ConfigurationView {
return this.config;
}
public * GetPluginViews(pluginName: string): Iterable<ConfigurationView> {
public get DebugMode(): boolean {
return this.config["debug"] as boolean;
}
public get VerboseMode(): boolean {
return this.config["verbose"] as boolean;
}
public * GetNestedConfiguration(pluginName: string): Iterable<ConfigurationView> {
for (const section of ValuesOf<any>((this.config as any)[pluginName])) {
if (section) {
yield new ConfigurationView(this.messageEmitter, this.configFileFolderUri, section === true ? {} : section, this.config);
yield new ConfigurationView(this.messageEmitter, this.configFileFolderUri, section === true ? {} : section, this.config).Indexer;
}
}
}
// message pipeline (source map resolution, filter, ...)
public Message(m: Message): void {
this.messageEmitter.Message.Dispatch({ Channel: Channel.Debug, Text: `Incoming validation message (${m.Text}) - starting processing` });
if (m.Channel === Channel.Debug && !this.DebugMode) {
return;
}
if (m.Channel === Channel.Verbose && !this.VerboseMode) {
return;
}
try {
// update source locations to point to loaded Swagger
if (m.Source) {
@ -323,10 +400,8 @@ export class ConfigurationView {
this.messageEmitter.Message.Dispatch(mx);
}
} catch (e) {
console.error(e);
this.messageEmitter.Message.Dispatch({ Channel: Channel.Error, Text: `${e}` });
}
this.messageEmitter.Message.Dispatch({ Channel: Channel.Debug, Text: `Incoming validation message (${m.Text}) - finished processing` });
}
}
@ -383,7 +458,7 @@ export class Configuration {
configSegments.push(...blocks);
}
return new ConfigurationView(messageEmitter, configFileFolderUri, ...configSegments);
return new ConfigurationView(messageEmitter, configFileFolderUri, ...configSegments).Indexer;
}
public constructor(
@ -429,4 +504,4 @@ export class Configuration {
return null;
}
}
}

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

@ -0,0 +1,60 @@
import * as marked from 'marked'
import * as chalk from 'chalk'
import * as moment from 'moment';
const markedTerminal = require('marked-terminal')
marked.setOptions({
renderer: new markedTerminal({
heading: chalk.green.bold,
firstHeading: chalk.green.bold,
showSectionPrefix: false,
strong: chalk.bold.blue,
em: chalk.cyan,
blockquote: chalk.yellow,
tab: 2
})
})
export class Console {
public static quiet: boolean = false;
public static debug: boolean = false;
public static verbose: boolean = false;
public static Log(text: any) {
if (!this.quiet) {
console.log(marked(`${text}`.trim()).trim());
}
}
private static get Timestamp(): string {
const m = new Date();
const hh = `${m.getHours()}`;
const mm = `${m.getMinutes()}`;
const ss = `${m.getSeconds()}`;
return chalk.red(`${chalk.gray(hh)}:${chalk.gray(mm)}:${chalk.gray(ss)}`);
}
public static Debug(text: any) {
if (this.debug) {
console.log(chalk.bold.yellow(`[${this.Timestamp}] `) + marked(`${text}`.trim()).trim());
}
}
public static Verbose(text: any) {
if (this.verbose) {
console.log(chalk.bold.magenta(`[${this.Timestamp}] `) + marked(`${text}`.trim()).trim());
}
}
public static Error(text: any) {
console.error(chalk.bold.red(`${text}`.trim()).trim());
}
public static Exit(reason: any) {
this.Error(reason || "Unknown Error");
process.exit(1);
}
}

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

@ -61,13 +61,4 @@ export class RealFileSystem implements IFileSystem {
async WriteFile(uri: string, content: string): Promise<void> {
return WriteString(uri, content);
}
}
/// this stuff is to force __asyncValues to get emitted: see https://github.com/Microsoft/TypeScript/issues/14725
async function* yieldFromMap(): AsyncIterable<string> {
yield* ["hello", "world"];
};
async function foo() {
for await (const each of yieldFromMap()) {
}
}

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

@ -5,7 +5,7 @@ import { OperationAbortedException } from '../exception';
*--------------------------------------------------------------------------------------------*/
import { Descendants, Kind, CloneAst, YAMLMapping, newScalar, ParseNode } from "../ref/yaml";
import { MergeYamls, IdentitySourceMapping } from "../source-map/merging";
import { MergeYamls, IdentitySourceMapping, resolveRValue } from "../source-map/merging";
import { Mapping } from "../ref/source-map";
import { DataHandleRead, DataHandleWrite, DataStoreView } from "../data-store/data-store";
import { Parse as ParseLiterate } from "./literate";
@ -264,7 +264,26 @@ async function ParseCodeBlocksInternal(config: ConfigurationView, hLiterate: Dat
}
export function EvaluateGuard(rawFenceGuard: string, contextObject: any): boolean {
const match = /\$\((.*)\)/.exec(rawFenceGuard);
// trim the language from the front first
let match = /^\S*\s*(.*)/.exec(rawFenceGuard);
let fence = match && match[1];
if (!fence) {
// no fence at all.
return true;
}
try {
const expressionFence = `${resolveRValue(fence, "", contextObject, null, 2)}`;
// is there unresolved values? May be old-style, or not valid value yet.
if (expressionFence.indexOf("$(") == -1) {
return safeEval<boolean>(expressionFence);
}
} catch (E) {
// not a legal expression?
}
// fall back to original behavior, where the whole expression is in the $( ... )
match = /\$\((.*)\)/.exec(fence);
const guardExpression = match && match[1];
if (!guardExpression) {
return true;

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

@ -177,7 +177,7 @@ function BuildPipeline(config: ConfigurationView): { pipeline: { [name: string]:
const addNodesAndSuffixes = (suffix: string, inputs: string[], configScope: JsonPath, inputNodes: { name: string, suffixes: string[] }[]) => {
if (inputNodes.length === 0) {
const config = configCache[stringify(configScope)];
const configs = scope ? [...config.GetPluginViews(scope)] : [config];
const configs = scope ? [...config.GetNestedConfiguration(scope)] : [config];
for (let i = 0; i < configs.length; ++i) {
const newSuffix = configs.length === 1 ? "" : "/" + i;
suffixes.push(suffix + newSuffix);
@ -320,4 +320,4 @@ export async function RunPipeline(configView: ConfigurationView, fileSystem: IFi
barrier.Await(getTask(name));
}
await barrier.Wait();
}
}

18
src/autorest-core/lib/polyfill.min.js поставляемый
Просмотреть файл

@ -5,6 +5,18 @@ return it(jt(this),t,arguments.length>1?arguments[1]:void 0)},forEach:function f
if("root"===e.tryLoc)return handle("end");if(e.tryLoc<=this.prev){var u=i.call(e,"catchLoc"),c=i.call(e,"finallyLoc");if(u&&c){if(this.prev<e.catchLoc)return handle(e.catchLoc,!0);if(this.prev<e.finallyLoc)return handle(e.finallyLoc)}else if(u){if(this.prev<e.catchLoc)return handle(e.catchLoc,!0)}else{if(!c)throw new Error("try statement without catch or finally");if(this.prev<e.finallyLoc)return handle(e.finallyLoc)}}}},abrupt:function(t,n){for(var r=this.tryEntries.length-1;r>=0;--r){var e=this.tryEntries[r];if(e.tryLoc<=this.prev&&i.call(e,"finallyLoc")&&this.prev<e.finallyLoc){var o=e;break}}o&&("break"===t||"continue"===t)&&o.tryLoc<=n&&n<=o.finallyLoc&&(o=null);var u=o?o.completion:{};return u.type=t,u.arg=n,o?this.next=o.finallyLoc:this.complete(u),p},complete:function(t,n){if("throw"===t.type)throw t.arg;"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=t.arg,this.next="end"):"normal"===t.type&&n&&(this.next=n)},finish:function(t){for(var n=this.tryEntries.length-1;n>=0;--n){var r=this.tryEntries[n];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),resetTryEntry(r),p}},catch:function(t){for(var n=this.tryEntries.length-1;n>=0;--n){var r=this.tryEntries[n];if(r.tryLoc===t){var e=r.completion;if("throw"===e.type){var i=e.arg;resetTryEntry(r)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(t,n,r){return this.delegate={iterator:values(t),resultName:n,nextLoc:r},p}}}("object"==typeof t?t:"object"==typeof window?window:"object"==typeof self?self:this)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}]},{},[1]);
}
// to enable bluebird stack traces, set global.ENABLE_BLUEBIRD to true
if (global.ENABLE_BLUEBIRD ) {
const Promise = require("bluebird")
Promise.config({
longStackTraces: true,
warnings: {
wForgottenReturn: false
}
})
global.Promise = Promise;
}
if( !global.__awaiter ) {
global.__awaiter = (thisArg, _arguments, P, generator) => new Promise((resolve, reject) => {
var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }
@ -18,7 +30,7 @@ if( !global.__awaiter ) {
return o = __asyncValues(o), i[Symbol.iterator] = function () { return this; }, i;
function verb(n, f) { return function (v) { return { value: ["delegate", (o[n] || f).call(o, v)], done: false }; }; }
};
global.__asyncGenerator = function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), q = [], c, i;
@ -32,13 +44,13 @@ if( !global.__awaiter ) {
function reject(value) { resume("throw", value); }
function settle(f, v) { c = void 0, f(v), next(); }
};
global.__asyncValues = function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator];
return m ? m.call(o) : typeof __values === "function" ? __values(o) : o[Symbol.iterator]();
};
global.__decorate = function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);

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

@ -11,4 +11,19 @@ export async function ToArray<T>(iterable: AsyncIterable<T>): Promise<Array<T>>
result.push(each);
}
return result;
}
export function Push<T>(destination: Array<T>, source: any) {
if (source) {
if (IsIterable(source)) {
destination.push(...source);
}
else {
destination.push(source)
}
}
}
export function IsIterable(target: any) {
return target && target[Symbol.iterator] && typeof target !== 'string'
}

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

@ -65,8 +65,91 @@ function Merge(a: any, b: any, path: JsonPath = []): any {
throw new Error(`'${stringify(path)}' has incomaptible values (${yaml.Stringify(a)}, ${yaml.Stringify(b)}).`);
}
export function ShallowCopy(input: any, ...filter: Array<string>): any {
if (!input) {
return input;
}
const keys = input.Keys ? input.Keys : Object.getOwnPropertyNames(input);
const result: any = {};
for (const key of keys) {
if (filter.indexOf(key) == -1) {
const value = input[key];
if (value !== undefined) {
result[key] = value;
}
}
}
return result;
}
// Note: I am not convinced this works precisely as it should
// but it works well enough for my needs right now
// I will revisit it later.
const macroRegEx = /\$\(([a-zA-Z0-9_-]*)\)/ig
export function resolveRValue(value: any, propertyName: string, higherPriority: any, lowerPriority: any, jsAware: number = 0): any {
if (value) {
// resolves the actual macro value.
const resolve = (macroExpression: string, macroKey: string) => {
// if the original set has it, use that.
if (higherPriority && higherPriority[macroKey]) {
return resolveRValue(higherPriority[macroKey], macroKey, lowerPriority, null, jsAware - 1);
}
if (lowerPriority) {
// check to see if the value is in the overrides set before the key itself.
const keys = Object.getOwnPropertyNames(lowerPriority);
const macroKeyLocation = keys.indexOf(macroKey);
if (macroKeyLocation > -1) {
if (macroKeyLocation < keys.indexOf(propertyName)) {
// the macroKey is in the overrides, and it precedes the propertyName itself
return resolveRValue(lowerPriority[macroKey], macroKey, higherPriority, lowerPriority, jsAware - 1);
}
}
}
// can't find the macro. maybe later.
return macroExpression;
};
// resolve the macro value for strings
if (typeof value === "string") {
const match = macroRegEx.exec(value.trim());
if (match) {
if (match[0] === match.input) {
// the target value should be the result without string twiddling
if (jsAware > 0) {
return `'${resolve(match[0], match[1])}'`;
}
return resolve(match[0], match[1]);
}
// it looks like we should do a string replace.
return value.replace(macroRegEx, resolve)
}
}
// resolve macro values for array values
if (value instanceof Array) {
const result = [];
for (const each of value) {
// since we're not naming the parameter,
// if there isn't a higher priority,
// we can fall back to a wide-lookup in lowerPriority.
result.push(resolveRValue(each, "", higherPriority || lowerPriority, null));
}
return result;
}
}
if (jsAware > 0) {
return `'${value}'`;
}
return value;
}
export function MergeOverwriteOrAppend(a: any, b: any, concatListPathFilter: (path: JsonPath) => boolean = _ => false, path: JsonPath = []): any {
if (a === null || b == null) {
if (a === null || b === null) {
return null; // TODO: overthink, we could use this to force mute something even if it's "concat" mode...
}
@ -91,17 +174,17 @@ export function MergeOverwriteOrAppend(a: any, b: any, concatListPathFilter: (pa
// forward if only present in one of the nodes
if (a[key] === undefined) {
result[key] = b[key];
result[key] = resolveRValue(b[key], key, a, b);
continue;
}
if (b[key] === undefined) {
result[key] = a[key];
result[key] = resolveRValue(a[key], key, null, a);
continue;
}
// try merge objects otherwise
const aMember = a[key];
const bMember = b[key];
const aMember = resolveRValue(a[key], key, b, a);
const bMember = resolveRValue(b[key], key, a, b);
result[key] = MergeOverwriteOrAppend(aMember, bMember, concatListPathFilter, subpath);
}
return result;

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

@ -21,16 +21,22 @@
},
"typings": "./index.d.ts",
"devDependencies": {
"@types/chalk": "*",
"@types/marked": "*",
"@types/commonmark": "^0.22.29",
"@types/jsonpath": "^0.1.29",
"@types/pify": "0.0.28",
"@types/source-map": "^0.5.0",
"bluebird": "*",
"mocha": "3.2.0",
"mocha-typescript": "1.0.22",
"source-map-support": "^0.4.14",
"typescript": "^2.3.0-dev.20170314"
},
"dependencies": {
"chalk": "^1.1.2",
"marked": "^0.3.6",
"marked-terminal": "^2.0.0",
"commonmark": "^0.27.0",
"file-url": "^2.0.2",
"get-uri": "^2.0.0",

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

@ -0,0 +1,88 @@
// polyfills for language support
require("../lib/polyfill.min.js");
import { suite, test, slow, timeout, skip, only } from "mocha-typescript";
import * as assert from "assert";
import { IFileSystem, MemoryFileSystem } from "../lib/file-system"
import * as AutoRest from "../lib/autorest-core"
@suite class TestConfiguration {
@test async "Test config"() {
// test out subscribe
let f = new MemoryFileSystem(new Map<string, string>([
['readme.md', `
# this is a test
see https://aka.ms/autorest
~~~ yaml
my-value: $(sample-value)
sample-value: one
sample-other: $(sample-value)
input-file:
- other.md
items:
- foo
- bar
- bin
- $(sample-value)/two
output-folder: foo
csharp:
sample-value: two
output-folder: $(output-folder)/csharp
~~~
`],
['other.md', `
# My Doc.
# some text
`]]));
const autorest = new AutoRest.AutoRest(f, MemoryFileSystem.DefaultVirtualRootUri + "readme.md");
let cfg = await autorest.view;
// console.log(cfg.Raw);
// output folder should be 'foo'
assert.equal(cfg['output-folder'], "foo");
// my-value should not get resolved here because the dependent variable is after
assert.equal(cfg['my-value'], '$(sample-value)');
// sample-other should get resolved to the value of sample-value
assert.equal(cfg['sample-other'], 'one');
// verify that the items object that uses a macro works too
assert.equal(cfg['items'][3], "one/two");
for (const each of cfg.GetNestedConfiguration("csharp")) {
// console.log(each.Raw);
// verify the output folder is relative
assert.equal(each.GetEntry('output-folder'), "foo/csharp");
// verify that the items object that uses a macro works too
// assert.equal((<any>(each.Raw))['items'][3], "two/two");
// now, this got resolved alot earlier.
// dunno if we need it the other way or not.
assert.equal(each['items'][3], "one/two");
}
// override the output-folder from the cmdline
autorest.AddConfiguration({ "output-folder": "OUTPUT" });
cfg = await autorest.view;
assert.equal(cfg['output-folder'], "OUTPUT");
for (const each of cfg.GetNestedConfiguration("csharp")) {
assert.equal(each['output-folder'], "OUTPUT/csharp");
}
}
}

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

@ -1,6 +1,6 @@
{
"name": "autorest",
"version": "0.13.1",
"version": "0.13.2",
"description": "The AutoRest tool generates client libraries for accessing RESTful web services. Input to AutoRest is a spec that describes the REST API using the Open API Initiative format.",
"repository": {
"type": "git",

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

@ -117,42 +117,6 @@
],
"description": "An alert rule."
},
"LocationThresholdRuleCondition": {
"type": "object",
"properties": {
"dataSource": {
"oneOf": [
{
"$ref": "#/definitions/RuleDataSource"
},
{
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
}
],
"description": "the resource from which the rule collects its data."
},
"windowSize": {
"type": "string",
"format": "duration",
"description": "the period of time (in ISO 8601 duration format) that is used to monitor alert activity based on the threshold. If specified then it must be between 5 minutes and 1 day."
},
"failedLocationCount": {
"oneOf": [
{
"type": "integer"
},
{
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
}
],
"description": "the number of locations that must fail to activate the alert."
}
},
"required": [
"failedLocationCount"
],
"description": "A rule condition based on a certain number of locations failing."
},
"ManagementEventAggregationCondition": {
"type": "object",
"properties": {
@ -192,7 +156,43 @@
},
"description": "A management event aggregation condition."
},
"ManagementEventRuleCondition": {
"Microsoft.Azure.Management.Insights.Models.LocationThresholdRuleCondition": {
"type": "object",
"properties": {
"dataSource": {
"oneOf": [
{
"$ref": "#/definitions/RuleDataSource"
},
{
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
}
],
"description": "the resource from which the rule collects its data."
},
"windowSize": {
"type": "string",
"format": "duration",
"description": "the period of time (in ISO 8601 duration format) that is used to monitor alert activity based on the threshold. If specified then it must be between 5 minutes and 1 day."
},
"failedLocationCount": {
"oneOf": [
{
"type": "integer"
},
{
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
}
],
"description": "the number of locations that must fail to activate the alert."
}
},
"required": [
"failedLocationCount"
],
"description": "A rule condition based on a certain number of locations failing."
},
"Microsoft.Azure.Management.Insights.Models.ManagementEventRuleCondition": {
"type": "object",
"properties": {
"dataSource": {
@ -220,113 +220,7 @@
},
"description": "A management event rule condition."
},
"RuleAction": {
"type": "object",
"allOf": [
{
"properties": {
"odata.type": {
"oneOf": [
{
"type": "string",
"enum": [
"Microsoft.Azure.Management.Insights.Models.RuleEmailAction",
"Microsoft.Azure.Management.Insights.Models.RuleWebhookAction"
]
},
{
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
}
]
}
}
},
{
"anyOf": [
{
"$ref": "#/definitions/RuleEmailAction"
},
{
"$ref": "#/definitions/RuleWebhookAction"
}
]
}
],
"description": "The action that is performed when the alert rule becomes active, and when an alert condition is resolved."
},
"RuleCondition": {
"type": "object",
"allOf": [
{
"properties": {
"odata.type": {
"oneOf": [
{
"type": "string",
"enum": [
"Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition",
"Microsoft.Azure.Management.Insights.Models.LocationThresholdRuleCondition",
"Microsoft.Azure.Management.Insights.Models.ManagementEventRuleCondition"
]
},
{
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
}
]
}
}
},
{
"anyOf": [
{
"$ref": "#/definitions/ThresholdRuleCondition"
},
{
"$ref": "#/definitions/LocationThresholdRuleCondition"
},
{
"$ref": "#/definitions/ManagementEventRuleCondition"
}
]
}
],
"description": "The condition that results in the alert rule being activated."
},
"RuleDataSource": {
"type": "object",
"allOf": [
{
"properties": {
"odata.type": {
"oneOf": [
{
"type": "string",
"enum": [
"Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource",
"Microsoft.Azure.Management.Insights.Models.RuleManagementEventDataSource"
]
},
{
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
}
]
}
}
},
{
"anyOf": [
{
"$ref": "#/definitions/RuleMetricDataSource"
},
{
"$ref": "#/definitions/RuleManagementEventDataSource"
}
]
}
],
"description": "The resource from which the rule collects its data."
},
"RuleEmailAction": {
"Microsoft.Azure.Management.Insights.Models.RuleEmailAction": {
"type": "object",
"properties": {
"sendToServiceOwners": {
@ -357,17 +251,7 @@
},
"description": "Specifies the action to send email when the rule condition is evaluated."
},
"RuleManagementEventClaimsDataSource": {
"type": "object",
"properties": {
"emailAddress": {
"type": "string",
"description": "the email address."
}
},
"description": "The claims for a rule management event data source."
},
"RuleManagementEventDataSource": {
"Microsoft.Azure.Management.Insights.Models.RuleManagementEventDataSource": {
"type": "object",
"properties": {
"eventName": {
@ -420,7 +304,7 @@
},
"description": "A rule management event data source."
},
"RuleMetricDataSource": {
"Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource": {
"type": "object",
"properties": {
"resourceUri": {
@ -434,7 +318,7 @@
},
"description": "A rule metric data source."
},
"RuleWebhookAction": {
"Microsoft.Azure.Management.Insights.Models.RuleWebhookAction": {
"type": "object",
"properties": {
"serviceUri": {
@ -458,7 +342,7 @@
},
"description": "Specifies the action to post to service when the rule condition is evaluated."
},
"ThresholdRuleCondition": {
"Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition": {
"type": "object",
"properties": {
"dataSource": {
@ -529,6 +413,122 @@
"threshold"
],
"description": "A rule condition based on a metric crossing a threshold."
},
"RuleAction": {
"type": "object",
"allOf": [
{
"properties": {
"odata.type": {
"oneOf": [
{
"type": "string",
"enum": [
"Microsoft.Azure.Management.Insights.Models.RuleEmailAction",
"Microsoft.Azure.Management.Insights.Models.RuleWebhookAction"
]
},
{
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
}
]
}
}
},
{
"anyOf": [
{
"$ref": "#/definitions/Microsoft.Azure.Management.Insights.Models.RuleEmailAction"
},
{
"$ref": "#/definitions/Microsoft.Azure.Management.Insights.Models.RuleWebhookAction"
}
]
}
],
"description": "The action that is performed when the alert rule becomes active, and when an alert condition is resolved."
},
"RuleCondition": {
"type": "object",
"allOf": [
{
"properties": {
"odata.type": {
"oneOf": [
{
"type": "string",
"enum": [
"Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition",
"Microsoft.Azure.Management.Insights.Models.LocationThresholdRuleCondition",
"Microsoft.Azure.Management.Insights.Models.ManagementEventRuleCondition"
]
},
{
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
}
]
}
}
},
{
"anyOf": [
{
"$ref": "#/definitions/Microsoft.Azure.Management.Insights.Models.ThresholdRuleCondition"
},
{
"$ref": "#/definitions/Microsoft.Azure.Management.Insights.Models.LocationThresholdRuleCondition"
},
{
"$ref": "#/definitions/Microsoft.Azure.Management.Insights.Models.ManagementEventRuleCondition"
}
]
}
],
"description": "The condition that results in the alert rule being activated."
},
"RuleDataSource": {
"type": "object",
"allOf": [
{
"properties": {
"odata.type": {
"oneOf": [
{
"type": "string",
"enum": [
"Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource",
"Microsoft.Azure.Management.Insights.Models.RuleManagementEventDataSource"
]
},
{
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
}
]
}
}
},
{
"anyOf": [
{
"$ref": "#/definitions/Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource"
},
{
"$ref": "#/definitions/Microsoft.Azure.Management.Insights.Models.RuleManagementEventDataSource"
}
]
}
],
"description": "The resource from which the rule collects its data."
},
"RuleManagementEventClaimsDataSource": {
"type": "object",
"properties": {
"emailAddress": {
"type": "string",
"description": "the email address."
}
},
"description": "The claims for a rule management event data source."
}
}
}

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

@ -334,6 +334,23 @@
},
"description": "Sample input data for the service's input(s)."
},
"Graph": {
"type": "object",
"properties": {
"package": {
"oneOf": [
{
"$ref": "#/definitions/GraphPackage"
},
{
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
}
],
"description": "The definition of the graph package making up this web service."
}
},
"description": "Properties specific to a Graph based web service."
},
"GraphEdge": {
"type": "object",
"properties": {
@ -899,29 +916,12 @@
{
"anyOf": [
{
"$ref": "#/definitions/WebServicePropertiesForGraph"
"$ref": "#/definitions/Graph"
}
]
}
],
"description": "The set of properties specific to the Azure ML web service resource."
},
"WebServicePropertiesForGraph": {
"type": "object",
"properties": {
"package": {
"oneOf": [
{
"$ref": "#/definitions/GraphPackage"
},
{
"$ref": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#/definitions/expression"
}
],
"description": "The definition of the graph package making up this web service."
}
},
"description": "Properties specific to a Graph based web service."
}
}
}

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

@ -93,7 +93,7 @@ namespace AutoRest.AzureResourceSchema
resNameParam = resNameParam.Trim(new[] { '{', '}' });
// look up the type
var param = method.Parameters.Where(p => p.Name == resNameParam).FirstOrDefault();
var param = method.Parameters.Where(p => p.SerializedName == resNameParam).FirstOrDefault();
if (param != null)
{
// create a schema for it
@ -116,12 +116,12 @@ namespace AutoRest.AzureResourceSchema
{
foreach (Property property in body.ComposedProperties)
{
if (!resourceDefinition.Properties.Keys.Contains(property.Name.RawValue))
if (!resourceDefinition.Properties.Keys.Contains(property.SerializedName))
{
JsonSchema propertyDefinition = ParseType(property, property.ModelType, resourceSchema.Definitions, serviceClient.ModelTypes);
if (propertyDefinition != null)
{
resourceDefinition.AddProperty(property.Name.RawValue, propertyDefinition, property.IsRequired || property.Name.RawValue == "properties");
resourceDefinition.AddProperty(property.SerializedName, propertyDefinition, property.IsRequired || property.SerializedName == "properties");
}
}
}
@ -207,7 +207,7 @@ namespace AutoRest.AzureResourceSchema
if (IsPathVariable(pathSegment))
{
string parameterName = pathSegment.Substring(1, pathSegment.Length - 2);
Parameter parameter = method.Parameters.FirstOrDefault(methodParameter => methodParameter.Name.RawValue == parameterName);
Parameter parameter = method.Parameters.FirstOrDefault(methodParameter => methodParameter.SerializedName == parameterName);
if (parameter == null)
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Found undefined parameter reference {0} in create resource method \"{1}/{2}/{3}\".", pathSegment, resourceMethodPrefix, resourceProvider, methodUrlPathAfterProvider));
@ -238,7 +238,7 @@ namespace AutoRest.AzureResourceSchema
{
foreach (EnumValue parameterValue in parameterType.Values)
{
newResourceTypes.Add(string.Join("/", resourceType, parameterValue.Name));
newResourceTypes.Add(string.Join("/", resourceType, parameterValue.SerializedName));
}
}
@ -319,7 +319,7 @@ namespace AutoRest.AzureResourceSchema
private static JsonSchema ParseCompositeType(Property property, CompositeType compositeType, bool includeBaseModelTypeProperties, IDictionary<string, JsonSchema> definitions, IEnumerable<CompositeType> modelTypes)
{
string definitionName = compositeType.Name.RawValue;
string definitionName = compositeType.SerializedName;
if (!definitions.ContainsKey(definitionName))
{
@ -368,7 +368,7 @@ namespace AutoRest.AzureResourceSchema
derivedTypeDefinitionRefs.AddAnyOf(new JsonSchema()
{
Ref = "#/definitions/" + subType.Name,
Ref = "#/definitions/" + subType.SerializedName,
});
const string discriminatorValueExtensionName = "x-ms-discriminator-value";
@ -395,7 +395,7 @@ namespace AutoRest.AzureResourceSchema
JsonSchema subPropertyDefinition = ParseType(subProperty, subProperty.ModelType, definitions, modelTypes);
if (subPropertyDefinition != null)
{
baseTypeDefinition.AddProperty(subProperty.Name.RawValue, subPropertyDefinition, subProperty.IsRequired);
baseTypeDefinition.AddProperty(subProperty.SerializedName.Else(subProperty.Name.RawValue) , subPropertyDefinition, subProperty.IsRequired);
}
}
}
@ -438,7 +438,7 @@ namespace AutoRest.AzureResourceSchema
foreach (EnumValue enumValue in enumType.Values)
{
result.AddEnum(enumValue.Name);
result.AddEnum(enumValue.SerializedName);
}
if (property != null)