@autorest/powershell
Version:
AutoRest PowerShell Cmdlet Generator
666 lines • 41.8 kB
JavaScript
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.createCommandsV2 = exports.Inferrer = exports.titleToAzureServiceName = void 0;
const codemodel_1 = require("@autorest/codemodel");
const codegen_1 = require("@azure-tools/codegen");
const linq_1 = require("@azure-tools/linq");
const extension_base_1 = require("@autorest/extension-base");
const linq_2 = require("@azure-tools/linq");
const verbs_1 = require("../internal/verbs");
const components_1 = require("../utils/components");
const model_state_1 = require("../utils/model-state");
//import { Schema as SchemaV3 } from '../utils/schema';
const command_operation_1 = require("../utils/command-operation");
const command_operation_2 = require("../utils/command-operation");
const codemodel_2 = require("@autorest/codemodel");
const resourceName_1 = require("../utils/resourceName");
const http_operation_1 = require("../utils/http-operation");
const specialWords = { in: ['sign'] };
// UNUSED: Moved to plugin-tweak-model.ts in remodeler
// For now, we are not dynamically changing the service-name. Instead, we would figure out a method to change it during the creation of service readme's.
function titleToAzureServiceName(title) {
const titleCamelCase = (0, codegen_1.pascalCase)((0, codegen_1.deconstruct)(title)).trim();
const serviceName = titleCamelCase
// Remove: !StartsWith(Management)AndContains(Management), Client, Azure, Microsoft, APIs, API, REST
.replace(/(?!^Management)(?=.*)Management|Client|Azure|Microsoft|APIs|API|REST/g, '')
// Remove: EndsWith(ServiceResourceProvider), EndsWith(ResourceProvider), EndsWith(DataPlane), EndsWith(Data)
.replace(/ServiceResourceProvider$|ResourceProvider$|DataPlane$|Data$/g, '');
return serviceName || titleCamelCase;
}
exports.titleToAzureServiceName = titleToAzureServiceName;
const pluralizationService = new codegen_1.EnglishPluralizationService();
pluralizationService.addWord('Database', 'Databases');
pluralizationService.addWord('database', 'databases');
pluralizationService.addWord('Premise', 'Premises');
pluralizationService.addWord('premise', 'premises');
function fn(active, remaining, result) {
if ((0, linq_1.length)(active) || (0, linq_1.length)(remaining)) {
if ((0, linq_1.length)(remaining)) {
fn([...active, remaining[0]], remaining.slice(1), result);
fn(active, remaining.slice(1), result);
}
else {
result.push(active);
}
}
return result;
}
function combinations(elements) {
return fn([], elements, []);
}
function filterSpecialWords(preposition, parts) {
let start = 0;
let idx = -1;
if (specialWords[preposition] !== undefined) {
do {
idx = parts.indexOf(preposition, start);
if (idx <= 0 || !specialWords[preposition].includes(parts[idx - 1])) {
return idx;
}
else {
start = idx + 1;
}
// eslint-disable-next-line no-constant-condition
} while (true);
}
return parts.indexOf(preposition);
}
function splitOnPreposition(preposition, parts) {
const i = filterSpecialWords(preposition, parts);
if (i > 0) {
return [
parts.slice(0, i),
parts.slice(i + 1)
];
}
return undefined;
}
function splitOnAnyPreposition(parts) {
for (const p of ['with', 'at', 'by', 'for', 'in', 'of']) {
const result = splitOnPreposition(p, parts);
if (result && result[0].length > 0) {
// we found it, let's give it back.
return result;
}
}
return undefined;
}
class Inferrer {
constructor(state) {
this.state = state;
this.commonParameters = new Array();
// skip-for-time-being
// isNameConflict(model: codemodel.Model, variant: CommandVariant, vname: string) {
// for (const each of values(model.commands.operations)) {
// if (each.details.default.name === vname) {
// return true;
// }
// }
// return false;
// }
// for tracking unique operation identities
this.operationIdentities = new Set();
this.reservedPathParam = new Set(['SubscriptionId', 'resourceGroupName']);
}
async init() {
this.commonParameters = await this.state.getValue('azure', false) ? [
// 'resourceGroupName',
'subscriptionId'
] : [];
this.verbMap = await this.state.getValue('verb-mapping') || {};
this.isAzure = await this.state.getValue('azure', false);
this.prefix = await this.state.getValue('prefix');
this.serviceName = titleToAzureServiceName(await this.state.getValue('service-name'));
if (this.isAzure) {
this.supportJsonInput = await this.state.getValue('support-json-input', true);
}
else {
this.supportJsonInput = await this.state.getValue('support-json-input', false);
}
this.state.setValue('service-name', this.serviceName);
this.subjectPrefix = await this.state.getValue('subject-prefix');
this.state.setValue('isAzure', this.isAzure);
this.state.setValue('prefix', this.prefix);
const model = this.state.model;
this.state.message({
Channel: extension_base_1.Channel.Debug, Text: `[CMDLET-PREFIX] => '${model.language.default.prefix}'`
});
model.language.default.serviceName = this.serviceName;
this.state.message({
Channel: extension_base_1.Channel.Debug, Text: `[SERVICE-NAME] => '${model.language.default.serviceName}'`
});
model.language.default.subjectPrefix = this.subjectPrefix;
this.state.message({
Channel: extension_base_1.Channel.Debug, Text: `[SUBJECT-PREFIX] => '${model.language.default.subjectPrefix}'`
});
return this;
}
async createCommands() {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
const model = this.state.model;
this.state.model.commands = {
operations: new linq_1.Dictionary(),
parameters: new linq_1.Dictionary(),
};
const disableGetPut = await this.state.getValue('disable-getput', false);
const disableTransformIdentityType = await this.state.getValue('disable-transform-identity-type', false);
const disableTransformIdentityTypeForOperation = await this.state.getValue('disable-transform-identity-type-for-operation', []);
const optsToExclude = new Array();
if (disableTransformIdentityTypeForOperation) {
for (const item of (0, linq_1.values)(disableTransformIdentityTypeForOperation)) {
optsToExclude.push(item);
}
}
this.state.message({ Channel: extension_base_1.Channel.Debug, Text: 'detecting high level commands...' });
for (const operationGroup of (0, linq_1.values)(model.operationGroups)) {
let hasPatch = false;
let commandType;
const getOperations = [];
let putOperation;
let patchOperation;
for (const operation of (0, linq_1.values)(operationGroup.operations)) {
if (((_d = (_c = (_b = (_a = operation.requests) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.protocol) === null || _c === void 0 ? void 0 : _c.http) === null || _d === void 0 ? void 0 : _d.method.toLowerCase()) === 'patch') {
hasPatch = true;
patchOperation = operation;
// bez: remove patch operation to avoid conflicts with replacements
if (this.isAzure && !disableTransformIdentityType && this.IsManagedIdentityOperation(operation)) {
continue;
}
}
else if (((_h = (_g = (_f = (_e = operation.requests) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.protocol) === null || _g === void 0 ? void 0 : _g.http) === null || _h === void 0 ? void 0 : _h.method.toLowerCase()) === 'get') {
getOperations.push(operation);
}
else if (((_m = (_l = (_k = (_j = operation.requests) === null || _j === void 0 ? void 0 : _j[0]) === null || _k === void 0 ? void 0 : _k.protocol) === null || _l === void 0 ? void 0 : _l.http) === null || _m === void 0 ? void 0 : _m.method.toLowerCase()) === 'put') {
putOperation = operation;
if (this.isAzure && !disableTransformIdentityType && this.IsManagedIdentityOperation(operation)) {
commandType = command_operation_1.CommandType.ManagedIdentityNew;
}
}
for (const variant of await this.inferCommandNames(operation, operationGroup.$key, this.state)) {
await this.addVariants(operation.parameters, operation, variant, '', this.state, undefined, commandType);
}
}
// bez: if we have get+put, try to replace
if (this.isAzure && getOperations && putOperation && ((_o = putOperation.requests) === null || _o === void 0 ? void 0 : _o.length) == 1) {
const getOperation = getOperations.find(getOperation => { var _a, _b, _c, _d, _e, _f, _g, _h; return ((_d = (_c = (_b = (_a = getOperation.requests) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.protocol) === null || _c === void 0 ? void 0 : _c.http) === null || _d === void 0 ? void 0 : _d.path) === ((_h = (_g = (_f = (_e = putOperation === null || putOperation === void 0 ? void 0 : putOperation.requests) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.protocol) === null || _g === void 0 ? void 0 : _g.http) === null || _h === void 0 ? void 0 : _h.path); });
const supportsCombineGetPutOperation = getOperation && this.supportsGetPut(getOperation, putOperation);
if (!disableTransformIdentityType && supportsCombineGetPutOperation &&
(hasPatch && patchOperation && this.IsManagedIdentityOperation(patchOperation)
|| !hasPatch && putOperation && this.IsManagedIdentityOperation(putOperation))) {
const variant = (await this.inferCommandNames(getOperation, operationGroup.$key, this.state))[0];
await this.addVariants(putOperation.parameters, putOperation, variant, '', this.state, [getOperation], command_operation_1.CommandType.ManagedIdentityUpdate);
}
else if (!disableTransformIdentityType && !supportsCombineGetPutOperation && hasPatch && patchOperation && this.IsManagedIdentityOperation(patchOperation)) {
if (!optsToExclude.includes((_p = patchOperation.operationId) !== null && _p !== void 0 ? _p : '')) {
const transformIdentityTypeErrorMessage = `Parameter IdentityType in operation '${patchOperation.operationId}' can not be transformed as the best practice design. See https://github.com/Azure/azure-powershell/blob/main/documentation/development-docs/design-guidelines/managed-identity-best-practices.md#frequently-asked-question to mitigate this issue.`;
this.state.message({ Channel: extension_base_1.Channel.Error, Text: transformIdentityTypeErrorMessage });
throw new Error(transformIdentityTypeErrorMessage);
}
// bez: add patch operation back and disable transforming identity type
for (const variant of await this.inferCommandNames(patchOperation, operationGroup.$key, this.state)) {
await this.addVariants(patchOperation.parameters, patchOperation, variant, '', this.state);
}
}
else if (!disableGetPut && !hasPatch && supportsCombineGetPutOperation) {
/* generate variants for Update(Get+Put) for subjects only if:
- there is a get operation
- there is a put operation
- get operation path is the same as put operation path
- there is only one put request schema
- get operation response schema type is the same as put operation request schema type
*/
const variant = (await this.inferCommandNames(getOperation, operationGroup.$key, this.state))[0];
await this.addVariants(putOperation.parameters, putOperation, variant, '', this.state, [getOperation], command_operation_1.CommandType.GetPut);
}
}
else if (this.isAzure && !disableTransformIdentityType && patchOperation && this.IsManagedIdentityOperation(patchOperation)) {
if (!optsToExclude.includes((_q = patchOperation.operationId) !== null && _q !== void 0 ? _q : '')) {
const transformIdentityTypeErrorMessage = `Parameter IdentityType in operation '${patchOperation.operationId}' can not be transformed as the best practice design. See https://github.com/Azure/azure-powershell/blob/main/documentation/development-docs/design-guidelines/managed-identity-best-practices.md#frequently-asked-question to mitigate this issue.`;
this.state.message({ Channel: extension_base_1.Channel.Error, Text: transformIdentityTypeErrorMessage });
throw new Error(transformIdentityTypeErrorMessage);
}
// bez: add variants back and disable transforming identity type as no put or get
for (const variant of await this.inferCommandNames(patchOperation, operationGroup.$key, this.state)) {
await this.addVariants(patchOperation.parameters, patchOperation, variant, '', this.state);
}
}
}
// for (const operation of values(model.http.operations)) {
// for (const variant of await this.inferCommandNames(operation, this.state)) {
// // no common parameters (standard variations)
// await this.addVariants(operation.parameters, operation, variant, '', this.state);
// }
// }
return model;
}
/**
* Judge if the response of get operation can be piped as the input of put operation
* 1. there is only one put request schema
* 2. get operation response schema type is the same as put operation request schema type
*/
supportsGetPut(getOperation, putOperation) {
var _a, _b, _c, _d, _e, _f;
const hasQueryParameter = (_a = getOperation === null || getOperation === void 0 ? void 0 : getOperation.parameters) === null || _a === void 0 ? void 0 : _a.find(p => { var _a; return ((_a = p.protocol.http) === null || _a === void 0 ? void 0 : _a.in) === 'query' && p.language.default.name !== 'apiVersion'; });
//parameter.protocal.http.in === 'body' probably only applies to open api 2.0
const schema = (_e = (_d = (_c = (_b = putOperation === null || putOperation === void 0 ? void 0 : putOperation.requests) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.parameters) === null || _d === void 0 ? void 0 : _d.find(p => { var _a; return ((_a = p.protocol.http) === null || _a === void 0 ? void 0 : _a.in) === 'body'; })) === null || _e === void 0 ? void 0 : _e.schema;
return (_f = (getOperation && !hasQueryParameter && schema && [...(0, linq_1.values)(getOperation === null || getOperation === void 0 ? void 0 : getOperation.responses)].filter(each => each.schema !== schema).length === 0)) !== null && _f !== void 0 ? _f : false;
}
containsIdentityType(op) {
var _a, _b, _c, _d, _e, _f;
const body = ((_b = (_a = op.requests) === null || _a === void 0 ? void 0 : _a[0].parameters) === null || _b === void 0 ? void 0 : _b.find((p) => !p.origin || p.origin.indexOf('modelerfour:synthesized') < 0)) || null;
// property identity in the body parameter
const identityProperty = (body && body.schema && (0, codemodel_1.isObjectSchema)(body.schema)) ? (_d = (_c = (0, linq_1.values)((0, codemodel_1.getAllProperties)(body.schema)).where(property => !property.language.default.readOnly && (0, codemodel_1.isObjectSchema)(property.schema) && property.language.default.name === 'identity')) === null || _c === void 0 ? void 0 : _c.toArray()) === null || _d === void 0 ? void 0 : _d[0] : null;
const identityTypeProperty = (identityProperty && identityProperty.schema && (0, codemodel_1.isObjectSchema)(identityProperty.schema)) ? (_f = (_e = (0, linq_1.values)((0, codemodel_1.getAllProperties)(identityProperty.schema)).where(property => !property.language.default.readOnly && property.language.default.name === 'type')) === null || _e === void 0 ? void 0 : _e.toArray()) === null || _f === void 0 ? void 0 : _f[0] : null;
return identityTypeProperty !== null && identityTypeProperty !== undefined;
}
IsManagedIdentityOperation(op) {
return this.containsIdentityType(op);
}
inferCommand(operation, group, suffix = []) {
operation = operation.filter(each => each !== 'all');
// no instant match
switch ((0, linq_1.length)(operation)) {
case 0:
throw new Error('Missing operation id?');
case 1:
// simple operation, just an id with a single value
// OPERATION => OPERATION-GROUP
return [
this.createCommandVariant(operation[0], [group], suffix, this.state.model)
];
case 2:
// should try to infer [SUBJECT] and [ACTION] from operation
if (verbs_1.verbs.has(operation[0])) {
// [ACTION][SUBJECT]
return [
this.createCommandVariant(operation[0], [group, operation[1]], suffix, this.state.model)
];
}
if (verbs_1.verbs.has(operation[1])) {
// [SUBJECT][ACTION]
return [
this.createCommandVariant(operation[1], [group, operation[0]], suffix, this.state.model)
];
}
// can't tell which is the [ACTION] -- let's return it the way we used to,
// but now let's mention that we are doing that.
this.state.warning(`Operation ${operation[0]}/${operation[1]} is inferred without finding action.`, [], {});
return [
this.createCommandVariant(operation[0], [group, operation[1]], suffix, this.state.model)
];
}
// three or more words.
// first, see if it's an 'or'
const either = splitOnPreposition('or', operation);
if (either) {
// looks like we got two sets of operations from this.
return [
...this.inferCommand([...either[0], ...either[1].slice(1)], group, suffix),
...this.inferCommand(either[1], group, suffix),
];
}
// any other preposition?
const parts = splitOnAnyPreposition(operation);
if (parts) {
// so this is something like DoActionWithStyle
return [...this.inferCommand(parts[0], group, parts[1])];
}
// if not, then seek out a verb from there.
for (let i = 0; i < (0, linq_1.length)(operation); i++) {
if (verbs_1.verbs.has(operation[i])) {
// if the action is first
if (i === 0) {
// everything else is the subject
return [this.createCommandVariant(operation[i], group ? [group, ...operation.slice(i + 1)] : operation.slice(i + 1), suffix, this.state.model)];
}
if (i === (0, linq_1.length)(operation) - 1) {
// if it's last, the subject would be the first thing
return [this.createCommandVariant(operation[i], group ? [group, ...operation.slice(0, i)] : operation.slice(0, i), suffix, this.state.model)];
}
// otherwise
// things before are part of the subject
// things after the verb should be part of the suffix
return [this.createCommandVariant(operation[i], group ? [group, ...operation.slice(i, i)] : operation.slice(i, i), [...suffix, ...operation.slice(i + 1)], this.state.model)];
}
}
// so couldn't tell what the action was.
// fallback to the original behavior with a warning.
this.state.warning(`Operation ${operation[0]}/${operation[1]} is inferred without finding action.`, [], {});
return [this.createCommandVariant(operation[0], group ? [group, ...operation.slice(1)] : operation.slice(1), [...suffix, ...operation.slice(1)], this.state.model)];
}
async inferCommandNames(op, group, state) {
const method = op.language.default.name;
// skip-for-time-being
// if (!method) {
// if (!group) {
// // no operation id at all?
// const path = op.path.replace(/{.*?}/g, '').replace(/\/+/g, '/').replace(/\/$/g, '');
// method = path.split('/').last;
// } else {
// // no group given, use string as method
// method = group;
// }
// group = pascalCase(op.tags) || '';
// }
const groupWords = [...(0, codegen_1.removeSequentialDuplicates)((0, codegen_1.deconstruct)(group))];
groupWords[groupWords.length - 1] = pluralizationService.singularize(groupWords.last);
group = (0, codegen_1.pascalCase)(groupWords);
const operation = (0, codegen_1.deconstruct)(method);
// instant match
if (this.verbMap[method]) {
return [this.createCommandVariant(method, [group], [], state.model)];
}
// dig deeper.
return this.inferCommand(operation, group);
}
async addVariant(vname, body, bodyParameterName, parameters, operation, variant, state, preOperations, commandType) {
// beth: filter command description for New/Update command
// positive test case: The operation to create or update the extension.
// negative test case: Starts an UpdateRun
const createOrUpdateRegex = /((create or update)|(creates or updates)|(create)|(update)|(creates)|(updates))([^a-zA-Z0-9])/gi;
operation.language.default.description = operation.language.default.description.replace(createOrUpdateRegex, `${variant.action.toLowerCase()} `);
const op = await this.addCommandOperation(vname, parameters, operation, variant, state, preOperations, commandType);
// if this has a body with it, let's add that parameter
if (body && body.schema) {
op.details.default.hasBody = true;
op.parameters.push(new components_1.IParameter(bodyParameterName, body.schema, {
required: body.required,
details: {
default: {
description: body.schema.language.default.description,
name: (0, codegen_1.pascalCase)(bodyParameterName),
isBodyParameter: true,
}
}
}));
// let's add a variant where it's expanded out.
// *IF* the body is an object or dictionary
if (body.schema.type === codemodel_1.SchemaType.Object || body.schema.type === codemodel_1.SchemaType.Dictionary || body.schema.type === codemodel_1.SchemaType.Any) {
const opExpanded = await this.addCommandOperation(`${vname}Expanded`, parameters, operation, variant, state, preOperations, commandType);
opExpanded.details.default.dropBodyParameter = true;
opExpanded.parameters.push(new components_1.IParameter(`${bodyParameterName}Body`, body.schema, {
required: body.required,
details: {
default: {
description: body.schema.language.default.description,
name: (0, codegen_1.pascalCase)(`${bodyParameterName}Body`),
isBodyParameter: true,
}
}
}));
}
}
return op;
}
async addCommandOperation(vname, parameters, operation, variant, state, preOperations, commandType) {
// skip-for-time-being following code seems redundant -----
// let apiversion = '';
var _a, _b, _c;
// for (const each of items(operation.responses)) {
// for (const rsp of items(each)) {
// if (rsp.schema && rsp.schema.details && rsp.schema.details.default && rsp.schema.details.default.apiversion) {
// apiversion = rsp.schema.details.default.apiversion;
// break;
// }
// }
// }
// ----------------------------------------------------------
// if vname is > 64 characters, let's trim it
// after trimming it, make sure there aren't any other operation with a name that's exactly the same
if ((0, linq_1.length)(vname) > 64) {
const names = (0, codegen_1.deconstruct)(vname);
let newVName = '';
for (let i = 0; i < (0, linq_1.length)(names); i++) {
newVName = (0, codegen_1.pascalCase)(names.slice(0, i));
if ((0, linq_1.length)(newVName) > 60) {
break;
}
}
vname = newVName;
}
// if we have an identical vname, let's add 'etc'
let fname = `${variant.verb}-${variant.subject}-${vname}`;
let n = 1;
while (this.operationIdentities.has(fname)) {
vname = `${vname.replace(/\d*$/g, '')}${n++}`;
fname = `${variant.verb}-${variant.subject}-${vname}`;
}
this.operationIdentities.add(fname);
variant.variant = vname;
vname = (0, codegen_1.pascalCase)(vname);
let commandName = operation.language.default.name;
let operations = [operation];
const language = { default: { ...operation.language.default } };
//handle when there are pre operataions
if (preOperations && preOperations.length > 0) {
commandName = 'Update';
language.default.name = 'Update';
operations = [...preOperations, ...operations];
}
// skip-for-time-being x-ms-metadata looks not supported any more.
//const xmsMetadata = operation.pathExtensions ? operation.pathExtensions['x-ms-metadata'] ? clone(operation.pathExtensions['x-ms-metadata']) : {} : {};
// Add operation type to support x-ms-mutability
let operationType = command_operation_2.OperationType.Other;
if (operation.requests) {
if (((_a = operation.requests[0].protocol.http) === null || _a === void 0 ? void 0 : _a.method) === 'put' && (variant.action.toLowerCase() === 'create' || variant.action.toLowerCase() === 'update' && variant.verb.toLowerCase() === 'set')) {
// put create and put set
operationType = command_operation_2.OperationType.Create;
}
else if ((((_b = operation.requests[0].protocol.http) === null || _b === void 0 ? void 0 : _b.method) === 'patch' || ((_c = operation.requests[0].protocol.http) === null || _c === void 0 ? void 0 : _c.method) === 'put') && variant.action.toLowerCase() === 'update') {
// patch update, get+put update and exclude set update
operationType = command_operation_2.OperationType.Update;
}
}
return state.model.commands.operations[`${(0, linq_1.length)(state.model.commands.operations)}`] = new command_operation_1.CommandOperation(commandName, {
asjob: language.default.asjob ? true : false,
operationType: operationType,
extensions: {},
...variant,
details: {
...language,
default: {
...language.default,
subject: variant.subject,
subjectPrefix: variant.subjectPrefix,
verb: variant.verb,
name: vname,
alias: variant.alias,
externalDocs: operation.externalDocs
}
},
// operationId is not needed any more
operationId: '',
parameters: parameters.map(httpParameter => {
// make it's own copy of the parameter since after this,
// the parameter can be altered for each operation individually.
const each = (0, linq_2.clone)(httpParameter, false, undefined, undefined, ['schema', 'origin']);
each.language.default = {
...each.language.default,
name: (0, codegen_1.pascalCase)(each.language.default.name),
httpParameter
};
each.details = {};
each.details.default = {
...each.language.default,
name: (0, codegen_1.pascalCase)(each.language.default.name),
httpParameter
};
each.name = each.language.default.serializedName;
return each;
}),
// skip-for-time-being, this callGraph is used in the header comments
callGraph: operations
}, commandType);
}
async addVariants(parameters, operation, variant, vname, state, preOperations, commandType) {
var _a, _b, _c, _d, _e, _f;
// now synthesize parameter set variants multiplexed by the variants.
const [constants, requiredParameters] = (0, linq_1.values)(parameters).bifurcate(parameter => parameter.language.default.constantValue || parameter.language.default.fromHost ? true : false);
const constantParameters = constants.map(each => `'${each.language.default.constantValue}'`);
// the body parameter
// xichen: How to handle if has multiple requests?
const body = ((_b = (_a = operation.requests) === null || _a === void 0 ? void 0 : _a[0].parameters) === null || _b === void 0 ? void 0 : _b.find((p) => !p.origin || p.origin.indexOf('modelerfour:synthesized') < 0)) || null;
// skip-for-time-being, looks x-ms-requestBody-name is not supported any more
//const bodyParameterName = (operation.requestBody && operation.requestBody.extensions) ? operation.requestBody.extensions['x-ms-requestBody-name'] || 'bodyParameter' : '';
const bodyParameterName = body ? body.language.default.name : '';
// all the properties in the body parameter
const bodyProperties = (body && body.schema && (0, codemodel_1.isObjectSchema)(body.schema)) ? (0, linq_1.values)((0, codemodel_1.getAllProperties)(body.schema)).where(property => !property.language.default.readOnly).toArray() : [];
// smash body property names together
const bodyPropertyNames = bodyProperties.joinWith(each => each.language.default.name);
// for each polymorphic body, we should do a separate variant that takes the polymorphic body type instead of the base type
// skip-for-time-being, this is for polymorphism
//const polymorphicBodies = (body && body.schema && body.schema.details.default.polymorphicChildren && length(body.schema.details.default.polymorphicChildren)) ? (<Array<Schema>>body.schema.details.default.polymorphicChildren).joinWith(child => child.details.default.name) : '';
// wait! "update" should be "set" if it's a PUT
if (variant.verb === 'Update' && operation.requests && ((_d = (_c = operation.requests[0].protocol) === null || _c === void 0 ? void 0 : _c.http) === null || _d === void 0 ? void 0 : _d.method) === codemodel_1.HttpMethod.Put) {
variant.verb = 'Set';
}
//for operations which has pre operations (only support GET+PUT for now), change it's action to update
if (preOperations && preOperations.length > 0) {
variant.action = variant.verb = 'Update';
}
// create variant
// skip-for-time-being, since operationId looks not included in m4.
//state.message({ Channel: Channel.Debug, Text: `${variant.verb}-${variant.subject} // ${operation.operationId} => ${JSON.stringify(variant)} taking ${requiredParameters.joinWith(each => each.name)}; ${constantParameters} ; ${bodyPropertyNames} ${polymorphicBodies ? `; Polymorphic bodies: ${polymorphicBodies} ` : ''}` });
await this.addVariant((0, codegen_1.pascalCase)([variant.action, vname]), body, bodyParameterName, [...constants, ...requiredParameters], operation, variant, state, preOperations, commandType);
if (await state.getValue('disable-via-identity', false)) {
return;
}
// eslint-disable-next-line prefer-const
let [pathParams, otherParams] = (0, linq_1.values)(requiredParameters).bifurcate(each => { var _a, _b; return ((_b = (_a = each === null || each === void 0 ? void 0 : each.protocol) === null || _a === void 0 ? void 0 : _a.http) === null || _b === void 0 ? void 0 : _b.in) === codemodel_1.ParameterLocation.Path; });
//exclude subscriptionId and resourceGroupName from path parameters
pathParams = pathParams.filter(pathParam => !this.reservedPathParam.has(pathParam.language.default.name));
//if parent pipline input is disabled, only generate identity for current resource itself
if (!await state.getValue('enable-parent-pipeline-input', this.isAzure)) {
if ((0, linq_1.length)(pathParams) > 0 && variant.action.toLowerCase() != 'list') {
await this.addVariant((0, codegen_1.pascalCase)([variant.action, vname, 'via-identity']), body, bodyParameterName, [...constants, ...otherParams], operation, variant, state, preOperations, commandType);
}
return;
}
const disableGetEnableList = await this.state.getValue('enable-parent-pipeline-input-for-list', false);
/*
for resource /A1/A2/.../An-1/An, generate variants that take
ViaIdentity: An as identity
ViaIdentity{An-1}: An-1 as identity + An Name
...
ViaIdentity{A1}: A1 as identity + [A2 + A3 + ... + An-1 + An] Names
*/
for (let i = pathParams.length - 1; i >= 0; i--) {
if ((!disableGetEnableList && variant.action.toLowerCase() === 'list') || (disableGetEnableList && i === pathParams.length - 1 && variant.action.toLowerCase() === 'get')) {
continue;
}
let resourceName = (0, resourceName_1.getResourceNameFromPath)((_f = (_e = operation.requests) === null || _e === void 0 ? void 0 : _e[0].protocol.http) === null || _f === void 0 ? void 0 : _f.path, pathParams[i].language.default.name, true);
//cannot get resource name from path, give up generate ViaIdentity variant
if (!resourceName) {
break;
}
//variant for current resource is simply named ViaIdentity otherwise ViaIdentity${resourceName}
if (i === pathParams.length - 1 && variant.action.toLowerCase() !== 'list') {
resourceName = '';
}
await this.addVariant((0, codegen_1.pascalCase)([variant.action, vname, `via-identity${resourceName}`]), body, bodyParameterName, [...constants, ...otherParams, ...pathParams.slice(i + 1)], operation, variant, state, preOperations, commandType);
}
if (this.supportJsonInput && (0, http_operation_1.hasValidBodyParameters)(operation) &&
commandType != command_operation_1.CommandType.GetPut && commandType != command_operation_1.CommandType.ManagedIdentityUpdate) {
const createStringParameter = (name, description, serializedName) => {
const schema = new codemodel_2.Schema(name, description, codemodel_1.SchemaType.String);
const language = {
default: {
name: name,
description: description,
serializedName: serializedName,
},
};
schema.language = language;
const httpParameter = {
implementation: 'Method',
language: language,
schema: schema,
required: true,
};
const parameter = new components_1.IParameter(name, schema, {
description: description,
required: true,
details: {
default: {
description: description,
name: name,
isBodyParameter: false,
httpParameter: httpParameter,
},
},
schema: schema,
allowEmptyValue: false,
deprecated: false,
});
parameter.httpParameter = httpParameter;
return parameter;
};
const jsonVariant = (0, codegen_1.pascalCase)([variant.action, vname]);
const parameter = new components_1.IParameter(`${bodyParameterName}Body`, body.schema, {
details: {
default: {
description: body.schema.language.default.description,
name: (0, codegen_1.pascalCase)(`${bodyParameterName}Body`),
isBodyParameter: true,
}
}
});
const opJsonString = await this.addVariant(`${jsonVariant}ViaJsonString`, null, '', [...constants, ...requiredParameters], operation, variant, state, preOperations, commandType);
opJsonString.details.default.dropBodyParameter = true;
opJsonString.parameters = opJsonString.parameters.filter(each => each.details.default.isBodyParameter !== true);
opJsonString.parameters.push(createStringParameter('JsonString', `Json string supplied to the ${jsonVariant} operation`, 'jsonString'));
opJsonString.parameters.push(parameter);
opJsonString.details.default.dropBodyParameter = true;
const opJsonFilePath = await this.addVariant(`${jsonVariant}ViaJsonFilePath`, null, '', [...constants, ...requiredParameters], operation, variant, state, preOperations, commandType);
opJsonFilePath.details.default.dropBodyParameter = true;
opJsonFilePath.parameters = opJsonFilePath.parameters.filter(each => each.details.default.isBodyParameter !== true);
opJsonFilePath.parameters.push(createStringParameter('JsonFilePath', `Path of Json file supplied to the ${jsonVariant} operation`, 'jsonFilePath'));
opJsonFilePath.parameters.push(parameter);
opJsonFilePath.details.default.dropBodyParameter = true;
}
}
createCommandVariant(action, subject, variant, model) {
const verb = this.getPowerShellVerb(action);
if (verb === 'Invoke') {
// if the 'operation' name was "post" -- it's kindof redundant.
// so, only include the operation name in the group name if it's anything else
if (action.toLowerCase() !== 'post') {
subject = [action, ...subject];
}
}
return {
alias: [],
subject: (0, codegen_1.pascalCase)([...(0, codegen_1.removeSequentialDuplicates)(subject.map(each => pluralizationService.singularize(each)))]),
variant: (0, codegen_1.pascalCase)(variant),
verb,
subjectPrefix: model.language.default.subjectPrefix,
action
};
}
getPowerShellVerb(action) {
const verb = this.verbMap[(0, codegen_1.pascalCase)(action)];
if (verb) {
return verb;
}
else {
return 'Invoke';
}
}
}
exports.Inferrer = Inferrer;
async function createCommandsV2(service) {
// return processCodeModel(commandCreator, service);
//const session = await startSession<PwshModel>(service, {}, codeModelSchema);
//const result = tweakModelV2(session);
const state = await new model_state_1.ModelState(service).init();
await service.writeFile({ filename: 'code-model-v4-createcommands-v2.yaml', content: (0, codegen_1.serialize)(await (await new Inferrer(state).init()).createCommands()), sourceMap: undefined, artifactType: 'code-model-v4' });
// return processCodeModel(async (state) => {
// return await (await new Inferrer(state).init()).createCommands();
// }, service, 'createCommands-v2');
}
exports.createCommandsV2 = createCommandsV2;
//# sourceMappingURL=create-commands-v2.js.map