diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 37a384a4681..1a5f5fcd071 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -11,11 +11,11 @@ import { getAllProperties as NewGetAllProperties, getAllPublicVirtualProperties import { escapeString, docComment, serialize, pascalCase, DeepPartial, camelCase } from '@azure-tools/codegen'; 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, + Access, Attribute, BackedProperty, Catch, Class, ClassType, Constructor, dotnet, Else, Expression, Finally, ForEach, If, LambdaMethod, 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, Not, ElseIf } from '@azure-tools/codegen-csharp'; import { ClientRuntime, EventListener, Schema, ArrayOf, EnumImplementation } from '../llcsharp/exports'; -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 { NullableBoolean, Alias, ArgumentCompleterAttribute, PSArgumentCompleterAttribute, AsyncCommandRuntime, AsyncJob, CmdletAttribute, ErrorCategory, ErrorRecord, Events, IDynamicParameters, 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'; @@ -460,6 +460,9 @@ export class CmdletClass extends Class { // implement part of the IContext this.implementIContext(); + // implement IDynamicParameters for write-verb cmdlets (Change Safety) + this.implementIDynamicParameters(); + // add constructors this.implementConstructors(); @@ -1622,6 +1625,23 @@ export class CmdletClass extends Class { extensibleParametersProp.get = toExpression(`${extensibleParameters.value} `); this.add(extensibleParametersProp); } + private implementIDynamicParameters() { + // Change Safety: only write-verb cmdlets (PUT/POST/DELETE/PATCH) participate in dynamic parameter discovery. + // The correlation id field only exists for azure projects, so gate on that as well. + if (!this.state.project.azure) { + return; + } + const httpMethod = (this.apiCall.requests?.[0]?.protocol.http?.method ?? '').toLowerCase(); + if (!['put', 'post', 'delete', 'patch'].includes(httpMethod)) { + return; + } + const $this = this; + this.interfaces.push(IDynamicParameters); + this.add(new LambdaMethod('GetDynamicParameters', dotnet.Object, new LiteralExpression(`${$this.state.project.serviceNamespace.moduleClass.declaration}.Instance.GetDynamicParameters(this.${$this.invocationInfo.value}, ${$this.correlationId.value})`), { + description: 'Gets the Change Safety dynamic parameters from a common module.', + returnsDescription: 'The RuntimeDefinedParameterDictionary of Change Safety parameters, or null.' + })); + } private implementIEventListener() { const $this = this; const cts = this.add(new Field('_cancellationTokenSource', System.Threading.CancellationTokenSource, { diff --git a/powershell/generators/psm1.ts b/powershell/generators/psm1.ts index 01985a80f31..582992cabe4 100644 --- a/powershell/generators/psm1.ts +++ b/powershell/generators/psm1.ts @@ -118,6 +118,9 @@ ${requestHandler} # Gets shared parameter values $instance.GetParameterValue = $VTable.GetParameterValue + # Delegate to get the Change Safety dynamic parameters + $instance.GetDynamicParametersValue = $VTable.GetDynamicParametersValue + # Allows shared module to listen to events from this module $instance.EventListener = $VTable.EventListener diff --git a/powershell/internal/powershell-declarations.ts b/powershell/internal/powershell-declarations.ts index 0cbb0c67b76..50e17598232 100644 --- a/powershell/internal/powershell-declarations.ts +++ b/powershell/internal/powershell-declarations.ts @@ -32,6 +32,7 @@ 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 IDynamicParameters: IInterface = { allProperties: [], declaration: 'System.Management.Automation.IDynamicParameters' }; export const CompletionResult: TypeDeclaration = new ClassType(sma, 'CompletionResult'); export const CommandAst: TypeDeclaration = new ClassType(`${sma}.Language`, 'CommandAst'); export const CompletionResultType: TypeDeclaration = new ClassType(sma, 'CompletionResultType'); diff --git a/powershell/module/module-class.ts b/powershell/module/module-class.ts index 1c552107e1b..87fd74ccee0 100644 --- a/powershell/module/module-class.ts +++ b/powershell/module/module-class.ts @@ -239,6 +239,13 @@ export class NewModuleClass extends Class { dotnet.String, /* parameterName */ /* returns */ dotnet.Object))); + const getDynamicParametersDelegate = namespace.add(new Alias('GetDynamicParametersDelegate', System.Func( + dotnet.String, /* resourceId */ + dotnet.String, /* moduleName */ + InvocationInfo, /* invocationInfo */ + dotnet.String, /* correlationId */ + /* returns */ dotnet.Object))); + const moduleLoadPipelineDelegate = namespace.add(new Alias('ModuleLoadPipelineDelegate', System.Action( dotnet.String, /* resourceId */ dotnet.String, /* moduleName */ @@ -331,6 +338,7 @@ export class NewModuleClass extends Class { this.add(OnNewRequest); } const GetParameterValue = this.add(new Property('GetParameterValue', getParameterDelegate, { description: 'The delegate to call to get parameter data from a common module.' })); + const GetDynamicParametersValue = this.add(new Property('GetDynamicParametersValue', getDynamicParametersDelegate, { description: 'The delegate to call to get the Change Safety dynamic parameters from a common module.' })); const EventListener = this.add(new Property('EventListener', eventListenerDelegate, { description: 'A delegate that gets called for each signalled event' })); const ArgumentCompleter = this.add(new Property('ArgumentCompleter', argumentCompleterDelegate, { description: 'Gets completion data for azure specific fields' })); const ProfileName = this.add(new Property('ProfileName', System.String, { description: 'The name of the currently selected Azure profile' })); @@ -346,6 +354,13 @@ export class NewModuleClass extends Class { returnsDescription: 'The parameter value from the common module. (Note: this should be type converted on the way back)' })); + /* get Change Safety dynamic parameters method (calls azAccounts) */ + this.add(new LambdaMethod('GetDynamicParameters', dotnet.Object, new LiteralExpression(`${GetDynamicParametersValue.value}?.Invoke( ${moduleResourceId.value}, ${moduleIdentity.value}, ${$this.pInvocationInfo.value}, ${$this.pCorrelationId.value} )`), { + parameters: [this.pInvocationInfo, this.pCorrelationId], + description: 'Gets the Change Safety dynamic parameters from a common module.', + returnsDescription: 'The RuntimeDefinedParameterDictionary of Change Safety parameters, or null.' + })); + /* signal method (calls azAccounts) */ const pSignal = new Parameter('signal', incomingSignalDelegate, { description: 'The callback for the event dispatcher ' }); const signalImpl = this.add(new Method('Signal', System.Threading.Tasks.Task(), {