Unify managed identity practice across modules (#1289)

* wip

* wip

* wip

* wip

* wip

* get+put

* fix

* add preprocess method

* remove mi by setting identitytype as none

* move handler to a sperate method

* add directive

* remove mapping from new-object cmdlet

* remove patch for mi-suppported update cmdlet

* skip preprocess mi parameter if identitytype doesn't exist

* bug fix

* skip using getput to replace patch if no identity type exists

* bug fix

* remove hardcode for UserAssignedIdentityTypeDeclaration

* rush lint --fix

* rush lint --fix

* remove extra space

* support system assigned identity only scenario

* polish

* fix internal cmdlets

* get+put update identity type only

* add fallback logic if replacement is failed

* add tranform in last layer

* allow skip renaming identityuserassignedidentity to userassignedidentity

* wip

* Update powershell/cmdlets/class.ts

* handle identitytype case

* revert changes for psproxyoutput

* fix typo

* handle userassignedid case

* fix rush lint

* fix the case that variant is removed by directive so that operation contains identity type but variant doesn't contain identity type

* Calculate UserAssignedIdentity from string array

* if UserAssignedIdentity is a string array, use Length instead of Count to obtain amount

* calculate UserAssignedIdentity if contains UserAssignedIdentity in CommandType.Atomic case

* rush lint fix

* Fix UserAssignedIdentity is empty hashtable

* Fixed UserAssignedIdentity is renamed

* Fixed UserAssignedIdentity is renamed, read name from virtual property

* add get+put as OperationType.Update

* consider set command as operationType.create

* Should be required if getput for managedidentityupdate
This commit is contained in:
Beisi Zhou 2024-02-20 10:39:24 +08:00 коммит произвёл GitHub
Родитель 882e8e2a42
Коммит cc041291be
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
8 изменённых файлов: 244 добавлений и 48 удалений

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

@ -12,10 +12,10 @@ import { escapeString, docComment, serialize, pascalCase, DeepPartial, camelCase
import { items, values, Dictionary, length } from '@azure-tools/linq';
import {
Access, Attribute, BackedProperty, Catch, Class, ClassType, Constructor, dotnet, Else, Expression, Finally, ForEach, If, LambdaProperty, LiteralExpression, LocalVariable, Method, Modifier, Namespace, OneOrMoreStatements, Parameter, Property, Return, Statements, BlockStatement, StringExpression,
Switch, System, TerminalCase, toExpression, Try, Using, valueOf, Field, IsNull, Or, ExpressionOrLiteral, TerminalDefaultCase, xmlize, TypeDeclaration, And, IsNotNull, PartialMethod, Case, While, LiteralStatement
Switch, System, TerminalCase, toExpression, Try, Using, valueOf, Field, IsNull, Or, ExpressionOrLiteral, TerminalDefaultCase, xmlize, TypeDeclaration, And, IsNotNull, PartialMethod, Case, While, LiteralStatement, Not, ElseIf
} from '@azure-tools/codegen-csharp';
import { ClientRuntime, EventListener, Schema, ArrayOf, EnumImplementation } from '../llcsharp/exports';
import { Alias, ArgumentCompleterAttribute, PSArgumentCompleterAttribute, AsyncCommandRuntime, AsyncJob, CmdletAttribute, ErrorCategory, ErrorRecord, Events, InvocationInfo, OutputTypeAttribute, ParameterAttribute, PSCmdlet, PSCredential, SwitchParameter, ValidateNotNull, verbEnum, GeneratedAttribute, DescriptionAttribute, ExternalDocsAttribute, CategoryAttribute, ParameterCategory, ProfileAttribute, PSObject, InternalExportAttribute, ExportAsAttribute, DefaultRunspace, RunspaceFactory, AllowEmptyCollectionAttribute, DoNotExportAttribute, HttpPathAttribute, NotSuggestDefaultParameterSetAttribute } from '../internal/powershell-declarations';
import { NullableBoolean, Alias, ArgumentCompleterAttribute, PSArgumentCompleterAttribute, AsyncCommandRuntime, AsyncJob, CmdletAttribute, ErrorCategory, ErrorRecord, Events, InvocationInfo, OutputTypeAttribute, ParameterAttribute, PSCmdlet, PSCredential, SwitchParameter, ValidateNotNull, verbEnum, GeneratedAttribute, DescriptionAttribute, ExternalDocsAttribute, CategoryAttribute, ParameterCategory, ProfileAttribute, PSObject, InternalExportAttribute, ExportAsAttribute, DefaultRunspace, RunspaceFactory, AllowEmptyCollectionAttribute, DoNotExportAttribute, HttpPathAttribute, NotSuggestDefaultParameterSetAttribute } from '../internal/powershell-declarations';
import { State } from '../internal/state';
import { Channel } from '@autorest/extension-base';
import { IParameter } from '@azure-tools/codemodel-v3/dist/code-model/components';
@ -358,7 +358,7 @@ type operationParameter = {
};
type PreProcess = ((cmdlet: CmdletClass, pathParameters: Array<Expression>, nonPathParameters: Array<Expression | Property>, viaIdentity: boolean) => Statements) | undefined;
type ProcessGetResponse = ((cmdlet: CmdletClass) => Statements) | undefined;
export class CmdletClass extends Class {
private cancellationToken!: Expression;
public state: State;
@ -385,6 +385,8 @@ export class CmdletClass extends Class {
private responses: Array<Response>;
private callbackMethods: Array<LiteralExpression>;
private serializationMode: LiteralExpression | undefined;
private disableTransformIdentityType?: boolean;
private flattenUserAssignedIdentity?: boolean;
constructor(namespace: Namespace, operation: CommandOperation, state: State, objectInitializer?: DeepPartial<CmdletClass>) {
// generate the 'variant' part of the name
@ -422,6 +424,8 @@ export class CmdletClass extends Class {
this.addCommonStuff();
this.description = escapeString(this.operation.details.csharp.description);
const $this = this;
this.disableTransformIdentityType = await $this.state.getValue('disable-transform-identity-type', false);
this.flattenUserAssignedIdentity = await $this.state.getValue('flatten-userassignedidentity', true);
this.add(new Method('BeginProcessing', dotnet.Void, {
override: Modifier.Override,
@ -856,12 +860,18 @@ export class CmdletClass extends Class {
// make the call.
let preProcess: PreProcess;
switch ($this.operation.commandType) {
case CommandType.ManagedIdentityUpdate:
preProcess = $this.ContainsIdentityTypeParameter() ? $this.ManagedIdentityUpdateCmdletPreProcess : undefined;
break;
case CommandType.GetPut:
preProcess = $this.GetPutPreProcess;
break;
case CommandType.ManagedIdentityNew:
preProcess = $this.ContainsIdentityTypeParameter() && $this.ContainsUserAssignedIdentityParameter() ? $this.ManagedIdentityPreProcessForNewVerbCmdlet : undefined;
break;
case CommandType.Atomic:
default:
preProcess = undefined;
preProcess = $this.ContainsUserAssignedIdentityParameter() ? $this.ManagedUserAssignedIdentityPreProcess : undefined;
break;
}
const actualCall = function* () {
@ -1038,6 +1048,7 @@ export class CmdletClass extends Class {
if (serializationMode) {
parameters.push(serializationMode);
}
if (preProcess) {
yield preProcess($this, pathParameters, [...otherParams.map(each => toExpression(each.value)), dotnet.This, pipeline], true);
}
@ -1069,7 +1080,116 @@ export class CmdletClass extends Class {
}
}
private GetPutPreProcess(cmdlet: CmdletClass, pathParams: Array<Expression>, nonPathParams: Array<Expression>, viaIdentity: boolean): Statements {
private ContainsSpecifiedParameter(parameterName: string): boolean {
return this.operation.details.csharp.virtualParameters?.body?.map(p => p.name)?.includes(parameterName) ?? false;
}
private ContainsIdentityTypeParameter(): boolean {
return this.ContainsSpecifiedParameter('IdentityType');
}
private ContainsUserAssignedIdentityParameter(): boolean {
return this.ContainsSpecifiedParameter('IdentityUserAssignedIdentity') || this.ContainsSpecifiedParameter('UserAssignedIdentity');
}
private GetUserAssignedIdentityParameterElementType(): string | undefined {
return (<DictionarySchema>this.operation.details.csharp.virtualParameters?.body?.filter(p => p.name === 'UserAssignedIdentity' || p.name === 'IdentityUserAssignedIdentity')?.[0]?.schema)?.elementType?.language?.csharp?.fullname;
}
private GetUserAssignedIdentityPropertyTypeDeclaration(): string {
const userAssignedIdentityParameter = this.properties.filter(p => p.name === 'UserAssignedIdentity' || p.name === 'IdentityUserAssignedIdentity');
return userAssignedIdentityParameter?.[0]?.type?.declaration ?? undefined;
}
private GetUserAssignedIdentityPropertyName(): string {
const response = this.responses.filter(re => (<SchemaResponse>re)?.schema?.extensions?.['is-return-object'])?.[0];
const props = NewGetAllPublicVirtualProperties((<SchemaResponse>response).schema.language.csharp?.virtualProperties);
const userAssignedIdentityParameter = props?.filter(p => p.name === 'UserAssignedIdentity' || p.name === 'IdentityUserAssignedIdentity');
return userAssignedIdentityParameter?.[0]?.name ?? undefined;
}
private ManagedUserAssignedIdentityPreProcess(cmdlet: CmdletClass, pathParams: Array<Expression> = [], nonPathParams: Array<Expression> = [], viaIdentity = false): Statements {
const $this = cmdlet;
if ($this.ContainsUserAssignedIdentityParameter() && $this.flattenUserAssignedIdentity) {
return If('this.UserAssignedIdentity?.Length > 0',
function* () {
yield '// calculate UserAssignedIdentity';
yield ForEach('id', 'this.UserAssignedIdentity', `${$this.bodyParameter?.value}.${$this.GetUserAssignedIdentityPropertyName()}.Add(id, new ${$this.GetUserAssignedIdentityParameterElementType()}());`);
yield '';
}
);
}
return new Statements();
}
private ManagedIdentityPreProcessForNewVerbCmdlet(cmdlet: CmdletClass, pathParams: Array<Expression>, nonPathParams: Array<Expression>, viaIdentity: boolean): Statements {
const $this = cmdlet;
const preProcessManagedIdentityParametersMethod = new Method('PreProcessManagedIdentityParameters', dotnet.Void, {
access: Access.Private
});
const preProcessManagedIdentityParameters = function* () {
yield $this.ManagedUserAssignedIdentityPreProcess($this);
// if UserAssignedIdentity is a string array, use Length. Otherwise, use Count
yield If(`this.UserAssignedIdentity?.${$this.flattenUserAssignedIdentity ? 'Length' : 'Count'} > 0`,
function* () {
yield '// calculate IdentityType';
yield If(`"SystemAssigned".Equals(${$this.bodyParameter?.value}.IdentityType, StringComparison.InvariantCultureIgnoreCase)`, `${$this.bodyParameter?.value}.IdentityType = "SystemAssigned,UserAssigned";`);
yield Else(`${$this.bodyParameter?.value}.IdentityType = "UserAssigned";`);
});
};
if (!$this.hasMethodWithSameDeclaration(preProcessManagedIdentityParametersMethod)) {
preProcessManagedIdentityParametersMethod.add(preProcessManagedIdentityParameters);
$this.add(preProcessManagedIdentityParametersMethod);
}
return new Statements(function* () {
yield `this.${preProcessManagedIdentityParametersMethod.name}();`;
});
}
private ProcessGetResponseForManagedIdentityUpdateCmdlet(cmdlet: CmdletClass): Statements {
const $this = cmdlet;
const containsUserAssignedIdentity = $this.ContainsUserAssignedIdentityParameter();
const preProcessManagedIdentity = function* () {
yield new LocalVariable('supportsSystemAssignedIdentity', dotnet.Bool, { initializer: Or('true == this.EnableSystemAssignedIdentity', `null == this.EnableSystemAssignedIdentity && true == ${$this.bodyParameter?.value}?.IdentityType?.Contains("SystemAssigned")`) });
yield new LocalVariable('supportsUserAssignedIdentity', dotnet.Bool, { initializer: `${dotnet.False}` });
if (containsUserAssignedIdentity) {
yield $this.ManagedUserAssignedIdentityPreProcess($this);
yield `supportsUserAssignedIdentity = true == this.MyInvocation?.BoundParameters?.ContainsKey("UserAssignedIdentity") && ${$this.flattenUserAssignedIdentity ? 'this.UserAssignedIdentity?.Length' : `((${$this.GetUserAssignedIdentityPropertyTypeDeclaration()})this.MyInvocation?.BoundParameters["UserAssignedIdentity"])?.Count`} > 0 ||
true != this.MyInvocation?.BoundParameters?.ContainsKey("UserAssignedIdentity") && true == ${$this.bodyParameter?.value}.IdentityType?.Contains("UserAssigned");`;
yield If('!supportsUserAssignedIdentity', function* () {
yield `${$this.bodyParameter?.value}.${$this.GetUserAssignedIdentityPropertyName()} = null;`;
});
yield '';
}
yield '// calculate IdentityType';
yield If(And('supportsUserAssignedIdentity', 'supportsSystemAssignedIdentity'), `${$this.bodyParameter?.value}.IdentityType = "SystemAssigned,UserAssigned";`);
yield ElseIf(And('supportsUserAssignedIdentity', '!supportsSystemAssignedIdentity'), `${$this.bodyParameter?.value}.IdentityType = "UserAssigned";`);
yield ElseIf(And('!supportsUserAssignedIdentity', 'supportsSystemAssignedIdentity'), `${$this.bodyParameter?.value}.IdentityType = "SystemAssigned";`);
yield Else(`${$this.bodyParameter?.value}.IdentityType = "None";`);
};
const preProcessManagedIdentityMethod = new Method('PreProcessManagedIdentityParametersWithGetResult', dotnet.Void, {
access: Access.Private
});
if (!$this.hasMethodWithSameDeclaration(preProcessManagedIdentityMethod)) {
preProcessManagedIdentityMethod.add(preProcessManagedIdentity);
$this.add(preProcessManagedIdentityMethod);
}
return new Statements(function* () {
yield `this.${preProcessManagedIdentityMethod.name}();`;
});
}
private ManagedIdentityUpdateCmdletPreProcess(cmdlet: CmdletClass, pathParams: Array<Expression>, nonPathParams: Array<Expression>, viaIdentity: boolean): Statements {
return cmdlet.GetPutPreProcess(cmdlet, pathParams, nonPathParams, viaIdentity, cmdlet.ProcessGetResponseForManagedIdentityUpdateCmdlet);
}
private GetPutPreProcess(cmdlet: CmdletClass, pathParams: Array<Expression>, nonPathParams: Array<Expression>, viaIdentity: boolean, processGetResponse: ProcessGetResponse = undefined): Statements {
const $this = cmdlet;
const updateBodyMethod = new Method(`Update${$this.bodyParameter?.value}`, dotnet.Void, {
access: Access.Private
@ -1093,9 +1213,19 @@ export class CmdletClass extends Class {
});
$this.add(updateBodyMethod);
}
const getPut = function* () {
yield `${$this.bodyParameter?.value} = await this.${$this.$<Property>('Client').invokeMethod(httpOperationName, ...[...pathParams, ...nonPathParams]).implementation}`;
yield `${$this.bodyParameter?.value} = await this.${$this.$<Property>('Client').invokeMethod(httpOperationName, ...[...pathParams, ...nonPathParams]).implementation} `;
// PreProcess body parameter
if (processGetResponse) {
yield processGetResponse($this);
}
yield `this.${updateBodyMethod.name}();`;
/** Instance:
* _requestBodyParametersBody = await this.Client.GrafanaGetWithResult(SubscriptionId, ResourceGroupName, Name, this, Pipeline);
* this.Update_requestBodyParametersBody();
* */
};
return new Statements(getPut);
}
@ -1650,6 +1780,34 @@ export class CmdletClass extends Class {
const nullable = this.state.project.schemaDefinitionResolver.resolveTypeDeclaration(vSchema, !!(<NewVirtualProperty>vParam.origin).required, this.state).isNullable;
let cmdletParameter: Property;
if (propertyType.schema.type !== SchemaType.Array) {
if (vParam.name === 'IdentityType' && !this.disableTransformIdentityType) {
const enableSystemAssignedIdentity = new Property('EnableSystemAssignedIdentity', operation.details.csharp.verb.toLowerCase() === 'new' ? SwitchParameter : NullableBoolean, {
set: operation.details.csharp.verb.toLowerCase() === 'new' ? toExpression(`${expandedBodyParameter.value}.${getVirtualPropertyName((<any>vParam.origin)) || vParam.origin.name} = value.IsPresent ? "SystemAssigned": null `) : undefined
});
enableSystemAssignedIdentity.description = 'Decides if enable a system assigned identity for the resource.';
enableSystemAssignedIdentity.add(new Attribute(ParameterAttribute, { parameters: [new LiteralExpression(`Mandatory = ${vParam.required ? 'true' : 'false'}`), new LiteralExpression(`HelpMessage = "${escapeString(enableSystemAssignedIdentity.description || '.')}"`)] }));
if (length(vParam.alias) > 0) {
enableSystemAssignedIdentity.add(new Attribute(Alias, { parameters: vParam.alias.map(x => '"' + x + '"') }));
}
this.add(enableSystemAssignedIdentity);
continue;
}
if (vParam.name === 'IdentityUserAssignedIdentity' || vParam.name === 'UserAssignedIdentity') {
if (this.flattenUserAssignedIdentity) {
const userAssignedIdentity = new Property('UserAssignedIdentity', dotnet.StringArray);
userAssignedIdentity.description = 'The array of user assigned identities associated with the resource. The elements in array will be ARM resource ids in the form: \'/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}.\'';
userAssignedIdentity.add(new Attribute(ParameterAttribute, { parameters: [new LiteralExpression(`Mandatory = ${vParam.required ? 'true' : 'false'}`), new LiteralExpression(`HelpMessage = "${escapeString(userAssignedIdentity.description || '.')}"`)] }));
userAssignedIdentity.add(new Attribute(AllowEmptyCollectionAttribute));
if (length(vParam.alias) > 0) {
userAssignedIdentity.add(new Attribute(Alias, { parameters: vParam.alias.map(x => '"' + x + '"') }));
}
this.add(userAssignedIdentity);
continue;
} else {
vParam.name = 'UserAssignedIdentity';
}
}
cmdletParameter = new Property(vParam.name, propertyType, {
get: toExpression(`${expandedBodyParameter.value}.${getVirtualPropertyName((<any>vParam.origin)) || vParam.origin.name}${!nullable ? '' : ` ?? ${propertyType.defaultOfType}`}`), // /* ${inspect(vParam.origin)} */
// get: toExpression(`null == ${expandedBodyParameter.value}.${vParam.origin.name} ? ${propertyType.defaultOfType} : (${propertyType.declaration}) ${expandedBodyParameter.value}.${vParam.origin.name}`),
@ -1747,7 +1905,6 @@ export class CmdletClass extends Class {
if (length(vParam.alias) > 0) {
cmdletParameter.add(new Attribute(Alias, { parameters: vParam.alias.map(x => '"' + x + '"') }));
}
this.add(cmdletParameter);
}
const paramDictSchema = parameter.schema.type === SchemaType.Dictionary ? parameter.schema :
@ -2110,7 +2267,7 @@ export class CmdletClass extends Class {
if (operation.details.default.externalDocs) {
this.add(new Attribute(ExternalDocsAttribute, {
parameters: [`${new StringExpression(this.operation.details.default.externalDocs?.url ?? '')}`,
`${new StringExpression(this.operation.details.default.externalDocs?.description ?? '')}`]
`${new StringExpression(this.operation.details.default.externalDocs?.description ?? '')}`]
}));
}

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

@ -30,6 +30,7 @@ export const ResourceGroupCompleter: TypeDeclaration = new ClassType(sma, 'Resou
export const OutputTypeAttribute: TypeDeclaration = new ClassType(sma, 'OutputType');
export const ErrorRecord: TypeDeclaration = new ClassType(sma, 'ErrorRecord');
export const SwitchParameter: TypeDeclaration = new ClassType(sma, 'SwitchParameter');
export const NullableBoolean: TypeDeclaration = new ClassType(new Namespace('System'), 'Boolean?');
export const IArgumentCompleter: IInterface = { allProperties: [], declaration: 'System.Management.Automation.IArgumentCompleter' };
export const CompletionResult: TypeDeclaration = new ClassType(sma, 'CompletionResult');
export const CommandAst: TypeDeclaration = new ClassType(`${sma}.Language`, 'CommandAst');

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

@ -168,47 +168,60 @@ export /* @internal */ class Inferrer {
parameters: new Dictionary<any>(),
};
const disableGetPut = await this.state.getValue('disable-getput', false);
const disableTransformIdentityType = await this.state.getValue('disable-transform-identity-type', false);
this.state.message({ Channel: Channel.Debug, Text: 'detecting high level commands...' });
for (const operationGroup of values(model.operationGroups)) {
let hasPatch = false;
let commandType!: CommandType;
const getOperations: Array<Operation> = [];
let putOperation: Operation | undefined;
let patchOperation: Operation | undefined;
for (const operation of values(operationGroup.operations)) {
if (operation.requests?.[0]?.protocol?.http?.method.toLowerCase() === 'patch') {
hasPatch = true;
patchOperation = operation;
// bez: remove patch operation to avoid conflicts with replacements
if (!disableTransformIdentityType && this.IsManagedIdentityOperation(operation)) {
continue;
}
} else if (operation.requests?.[0]?.protocol?.http?.method.toLowerCase() === 'get') {
getOperations.push(operation);
} else if (operation.requests?.[0]?.protocol?.http?.method.toLowerCase() === 'put') {
putOperation = operation;
if (!disableTransformIdentityType && this.IsManagedIdentityOperation(operation)) {
commandType = CommandType.ManagedIdentityNew;
}
}
for (const variant of await this.inferCommandNames(operation, operationGroup.$key, this.state)) {
await this.addVariants(operation.parameters, operation, variant, '', this.state);
await this.addVariants(operation.parameters, operation, variant, '', this.state, undefined, commandType);
}
}
/*
generate variants for Update(Get+Put) for subjects only if:
- there is no patch operation
- 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 reqeust schema
- get operation response schema type is the same as put operation request schema type
*/
if (this.isAzure
&& !disableGetPut
&& !hasPatch
&& getOperations
&& putOperation
&& putOperation.requests?.length == 1) {
if (this.isAzure && getOperations && putOperation && putOperation.requests?.length == 1) {
const getOperation = getOperations.find(getOperation => getOperation.requests?.[0]?.protocol?.http?.path === putOperation?.requests?.[0]?.protocol?.http?.path);
const hasQueryParameter = getOperation?.parameters?.find(p => p.protocol.http?.in === 'query' && p.language.default.name !== 'apiVersion');
//parameter.protocal.http.in === 'body' probably only applies to open api 2.0
const schema = putOperation?.requests?.[0]?.parameters?.find(p => p.protocol.http?.in === 'body')?.schema;
if (getOperation && !hasQueryParameter && schema && [...values(getOperation?.responses)].filter(each => (<SchemaResponse>each).schema !== schema).length === 0) {
const supportsCombineGetPutOperation = getOperation && this.supportsGetPut(getOperation, putOperation);
if (!disableTransformIdentityType && supportsCombineGetPutOperation &&
(hasPatch && patchOperation && this.IsManagedIdentityOperation(patchOperation)
|| !hasPatch && putOperation && this.IsManagedIdentityOperation(putOperation))) {
await this.addVariants(putOperation.parameters, putOperation, this.createCommandVariant('create', [operationGroup.$key], [], this.state.model), '', this.state, [getOperation], CommandType.ManagedIdentityUpdate);
} else if (!disableTransformIdentityType && !supportsCombineGetPutOperation && hasPatch && patchOperation && this.IsManagedIdentityOperation(patchOperation)) {
// 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);
}
await this.state.setValue('disable-transform-identity-type', true);
} 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
*/
await this.addVariants(putOperation.parameters, putOperation, this.createCommandVariant('create', [operationGroup.$key], [], this.state.model), '', this.state, [getOperation], CommandType.GetPut);
}
}
}
// for (const operation of values(model.http.operations)) {
// for (const variant of await this.inferCommandNames(operation, this.state)) {
@ -219,6 +232,30 @@ export /* @internal */ class Inferrer {
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
*/
private supportsGetPut(getOperation: Operation, putOperation: Operation): boolean {
const hasQueryParameter = getOperation?.parameters?.find(p => p.protocol.http?.in === 'query' && p.language.default.name !== 'apiVersion');
//parameter.protocal.http.in === 'body' probably only applies to open api 2.0
const schema = putOperation?.requests?.[0]?.parameters?.find(p => p.protocol.http?.in === 'body')?.schema;
return (getOperation && !hasQueryParameter && schema && [...values(getOperation?.responses)].filter(each => (<SchemaResponse>each).schema !== schema).length === 0) ?? false;
}
private containsIdentityType(op: Operation): boolean {
const body = op.requests?.[0].parameters?.find((p) => !p.origin || p.origin.indexOf('modelerfour:synthesized') < 0) || null;
// property identity in the body parameter
const identityProperty = (body && body.schema && isObjectSchema(body.schema)) ? values(getAllProperties(body.schema)).where(property => !property.language.default.readOnly && isObjectSchema(property.schema) && property.language.default.name === 'identity')?.toArray()?.[0] : null;
const identityTypeProperty = (identityProperty && identityProperty.schema && isObjectSchema(identityProperty.schema)) ? values(getAllProperties(identityProperty.schema)).where(property => !property.language.default.readOnly && property.language.default.name === 'type')?.toArray()?.[0] : null;
return identityTypeProperty !== null && identityTypeProperty !== undefined;
}
private IsManagedIdentityOperation(op: Operation): boolean {
return this.containsIdentityType(op);
}
inferCommand(operation: Array<string>, group: string, suffix: Array<string> = []): Array<CommandVariant> {
operation = operation.filter(each => each !== 'all');
// no instant match
@ -441,9 +478,11 @@ export /* @internal */ class Inferrer {
// Add operation type to support x-ms-mutability
let operationType = OperationType.Other;
if (operation.requests) {
if (operation.requests[0].protocol.http?.method === 'put' && variant.action.toLowerCase() === 'create') {
if (operation.requests[0].protocol.http?.method === 'put' && (variant.action.toLowerCase() === 'create' || variant.action.toLowerCase() === 'update' && variant.verb.toLowerCase() === 'set')) {
// put create and put set
operationType = OperationType.Create;
} else if (operation.requests[0].protocol.http?.method === 'patch' && variant.action.toLowerCase() === 'update') {
} else if ((operation.requests[0].protocol.http?.method === 'patch' || operation.requests[0].protocol.http?.method === 'put') && variant.action.toLowerCase() === 'update') {
// patch update, get+put update and exclude set update
operationType = OperationType.Update;
}
}
@ -569,7 +608,8 @@ export /* @internal */ class Inferrer {
await this.addVariant(pascalCase([variant.action, vname, `via-identity${resourceName}`]), body, bodyParameterName, [...constants, ...otherParams, ...pathParams.slice(i + 1)], operation, variant, state, preOperations, commandType);
}
if (this.supportJsonInput && hasValidBodyParameters(operation) && !commandType) {
if (this.supportJsonInput && hasValidBodyParameters(operation) &&
commandType != CommandType.GetPut && commandType != CommandType.ManagedIdentityUpdate) {
const createStringParameter = (name: string, description: string, serializedName: string): IParameter => {
const schema = new SchemaModel(name, description, SchemaType.String);
const language = {

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

@ -213,7 +213,6 @@ function createVirtualProperties(schema: ObjectSchema, stack: Array<string>, thr
required: property.required || property.language.default.required,
};
virtualProperties.owned.push(privateProperty);
for (const inlinedProperty of [...virtualChildProperties.inherited, ...virtualChildProperties.owned]) {
// child properties are be inlined without prefixing the name with the property name
// unless there is a collision, in which case, we have to resolve
@ -376,7 +375,6 @@ function createVirtualParameters(operation: CommandOperation) {
} else if (operation.operationType === OperationType.Update && !(<VirtualProperty>virtualProperty).update) {
continue;
}
virtualParameters.body.push({
name: virtualProperty.name,
description: virtualProperty.property.language.default.description,
@ -411,7 +409,6 @@ function createVirtualParameters(operation: CommandOperation) {
operation.details.default.virtualParameters = virtualParameters;
}
async function createVirtuals(state: State): Promise<PwshModel> {
/*
A model class should provide inlined properties for anything in a property called properties
@ -422,7 +419,6 @@ async function createVirtuals(state: State): Promise<PwshModel> {
*/
const threshold = await state.getValue('inlining-threshold', 24);
const conflicts = new Array<string>();
for (const schema of values(state.model.schemas.objects)) {
// did we already inline this objecct
if (schema.language.default.inlined) {
@ -450,7 +446,7 @@ async function createVirtuals(state: State): Promise<PwshModel> {
}
function shouldBeRequired(operation: CommandOperation, virtualProperty: VirtualProperty): boolean {
const shouldBeOptional = operation.commandType === CommandType.GetPut;
const shouldBeOptional = operation.commandType === CommandType.GetPut || operation.commandType === CommandType.ManagedIdentityUpdate;
if (!shouldBeOptional) {
return virtualProperty.required;
}

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

@ -70,9 +70,8 @@ namespace Microsoft.Rest.ClientRuntime.PowerShell
var variantGroups = profileGroups.SelectMany(pg => pg.Variants
.GroupBy(v => new { v.CmdletName, v.IsInternal })
.Select(vg => new VariantGroup(ModuleName, vg.Key.CmdletName, vg.Select(v => v).ToArray(),
Path.Combine(vg.Key.IsInternal ? InternalFolder : ExportsFolder, pg.ProfileFolder), pg.ProfileName, isInternal: vg.Key.IsInternal)))
Path.Combine(vg.Key.IsInternal ? InternalFolder : ExportsFolder, pg.ProfileFolder), pg.ProfileName, isInternal: vg.Key.IsInternal)))
.ToArray();
var license = new StringBuilder();
license.Append(@"
# ----------------------------------------------------------------------------------
@ -103,6 +102,7 @@ ${$project.pwshCommentHeaderForCsharp}
sb.Append("param(");
sb.Append($"{(parameterGroups.Any() ? Environment.NewLine : String.Empty)}");
foreach (var parameterGroup in parameterGroups)
{
var parameters = parameterGroup.HasAllVariants ? parameterGroup.Parameters.Take(1) : parameterGroup.Parameters;

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

@ -188,6 +188,7 @@ namespace Microsoft.Rest.ClientRuntime.PowerShell
public VariantGroup VariantGroup { get; }
protected static readonly bool IsAzure = Convert.ToBoolean(@"${$project.azure}");
public BaseOutput(VariantGroup variantGroup)
{
VariantGroup = variantGroup;
@ -295,6 +296,7 @@ namespace Microsoft.Rest.ClientRuntime.PowerShell
}
return sb.ToString();
}
}
internal class ProcessOutput : BaseOutput

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

@ -31,7 +31,7 @@ namespace Microsoft.Rest.ClientRuntime.PowerShell
{
public string ModuleName { get; }
public string RootModuleName {get => @"${$project.rootModuleName}";}
public string RootModuleName { get => @"${$project.rootModuleName}"; }
public string CmdletName { get; }
public string CmdletVerb { get; }
public string CmdletNoun { get; }
@ -49,7 +49,6 @@ namespace Microsoft.Rest.ClientRuntime.PowerShell
public PsHelpInfo HelpInfo { get; }
public bool IsGenerated { get; }
public bool IsInternal { get; }
public string OutputFolder { get; }
public string FileName { get; }
public string FilePath { get; }
@ -84,7 +83,6 @@ namespace Microsoft.Rest.ClientRuntime.PowerShell
HelpInfo = Variants.Select(v => v.HelpInfo).FirstOrDefault() ?? new PsHelpInfo();
IsGenerated = Variants.All(v => v.Attributes.OfType<GeneratedAttribute>().Any());
IsInternal = isInternal;
OutputFolder = outputFolder;
FileName = $"{CmdletName}{(isTest ? ".Tests" : String.Empty)}.ps1";
FilePath = Path.Combine(OutputFolder, FileName);
@ -258,7 +256,6 @@ namespace Microsoft.Rest.ClientRuntime.PowerShell
public ParameterMetadata Metadata { get; }
public PsParameterHelpInfo HelpInfo { get; }
public Type ParameterType { get; }
public Attribute[] Attributes { get; }
public ParameterCategory[] Categories { get; }
public ParameterCategory OrderCategory { get; }
@ -334,7 +331,7 @@ namespace Microsoft.Rest.ClientRuntime.PowerShell
public bool Required { get; }
public bool ReadOnly { get; }
public string Description { get; }
public ComplexInterfaceInfo[] NestedInfos { get; }
public bool IsComplexInterface { get; }
@ -351,7 +348,7 @@ namespace Microsoft.Rest.ClientRuntime.PowerShell
var unwrappedType = Type.Unwrap();
var hasBeenSeen = seenTypes?.Contains(unwrappedType) ?? false;
(seenTypes ?? (seenTypes = new List<Type>())).Add(unwrappedType);
NestedInfos = hasBeenSeen ? new ComplexInterfaceInfo[]{} :
NestedInfos = hasBeenSeen ? new ComplexInterfaceInfo[] { } :
unwrappedType.GetInterfaces()
.Concat(InfoAttribute.PossibleTypes)
.SelectMany(pt => pt.GetProperties()
@ -440,7 +437,7 @@ namespace Microsoft.Rest.ClientRuntime.PowerShell
}
}
internal class PSArgumentCompleterInfo: CompleterInfo
internal class PSArgumentCompleterInfo : CompleterInfo
{
public string[] ResourceTypes { get; }
@ -511,7 +508,8 @@ namespace Microsoft.Rest.ClientRuntime.PowerShell
parameterHelp = parameterHelp.Where(ph => (!ph.ParameterSetNames.Any() || ph.ParameterSetNames.Any(psn => psn == variant.VariantName || psn == AllParameterSets)) && ph.Name != "IncludeTotalCount");
}
var result = parameters.Select(p => new Parameter(variant.VariantName, p.Key, p.Value, parameterHelp.FirstOrDefault(ph => ph.Name == p.Key)));
if (variant.SupportsPaging) {
if (variant.SupportsPaging)
{
// If supportsPaging is set, we will need to add First and Skip parameters since they are treated as common parameters which as not contained on Metadata>parameters
variant.Info.Parameters["First"].Attributes.OfType<ParameterAttribute>().FirstOrDefault(pa => pa.ParameterSetName == variant.VariantName || pa.ParameterSetName == AllParameterSets).HelpMessage = "Gets only the first 'n' objects.";
variant.Info.Parameters["Skip"].Attributes.OfType<ParameterAttribute>().FirstOrDefault(pa => pa.ParameterSetName == variant.VariantName || pa.ParameterSetName == AllParameterSets).HelpMessage = "Ignores the first 'n' objects and then gets the remaining objects.";

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

@ -62,7 +62,9 @@ export interface VirtualParameter {
export enum CommandType {
Atomic,
GetPut
GetPut,
ManagedIdentityUpdate,
ManagedIdentityNew
}
export class CommandOperation extends Extensions implements CommandOperation {