diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 7b08dff6287..d27c3254b7c 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -1022,6 +1022,10 @@ export class CmdletClass extends Class { // in azure mode, we signal the AzAccount module with every event that makes it here. yield `await ${$this.state.project.serviceNamespace.moduleClass.declaration}.Instance.Signal(${id.value}, ${token.value}, ${messageData.value}, (i,t,m) => ((${ClientRuntime.IEventListener})this).Signal(i,t,()=> ${ClientRuntime.EventDataConverter}.ConvertFrom( m() ) as ${ClientRuntime.EventData} ), ${$this.invocationInfo.value}, this.ParameterSetName, ${$this.correlationId.value}, ${$this.processRecordId.value}, null );`; yield If(`${token.value}.IsCancellationRequested`, Return()); + } else { + // In Non-Azure Modes, emit the Signal method without coorelation and processrecordid + yield `await ${$this.state.project.serviceNamespace.moduleClass.declaration}.Instance.Signal(${id.value}, ${token.value}, ${messageData.value}, (i,t,m) => ((${ClientRuntime.IEventListener})this).Signal(i,t,()=> ${ClientRuntime.EventDataConverter}.ConvertFrom( m() ) as ${ClientRuntime.EventData} ), ${$this.invocationInfo.value}, this.ParameterSetName, null );`; + yield If(`${token.value}.IsCancellationRequested`, Return()); } yield `WriteDebug($"{id}: {(messageData().Message ?? ${System.String.Empty})}");`; // any handling of the signal on our side... diff --git a/powershell/module/module-class.ts b/powershell/module/module-class.ts index 1bed829add9..18d20a5f45d 100644 --- a/powershell/module/module-class.ts +++ b/powershell/module/module-class.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Access, Alias, Class, ClassType, Constructor, dotnet, Field, LambdaMethod, LambdaProperty, LazyProperty, LiteralExpression, LocalVariable, MemberVariable, Method, Modifier, Namespace, Parameter, ParameterModifier, PartialMethod, Property, Return, Statements, StringExpression, System, TypeDeclaration, Using, valueOf, Variable } from '@azure-tools/codegen-csharp'; +import { Access, Alias, Class, ClassType, Constructor, dotnet, Field, LambdaMethod, LambdaProperty, LazyProperty, LiteralExpression, LocalVariable, MemberVariable, Method, Modifier, Namespace, Parameter, ParameterModifier, PartialMethod, Property, Return, Statements, StringExpression, System, TypeDeclaration, Using, valueOf, Variable, If } from '@azure-tools/codegen-csharp'; import { InvocationInfo, PSCredential, IArgumentCompleter, CompletionResult, CommandAst, CompletionResultType, } from '../internal/powershell-declarations'; import { State } from '../internal/state'; @@ -129,6 +129,21 @@ export class ModuleClass extends Class { createInitAndPipeline(namespace: Namespace) { const $this = this; + // Custom Event Listener without Azure Spefic concepts. (ProcessId and CorelationId) + const customEventListenerFunc = System.Func( + dotnet.String, + System.Threading.CancellationToken, + System.Func(System.EventArgs), + this.incomingSignalFunc, + InvocationInfo, + dotnet.String, + System.Exception, + /* returns */ System.Threading.Tasks.Task()); + + const incomingSignalDelegate = namespace.add(new Alias('SignalDelegate', this.incomingSignalFunc)); + const eventListenerDelegate = namespace.add(new Alias('EventListenerDelegate', customEventListenerFunc)); + const EventListener = this.add(new Property('EventListener', eventListenerDelegate, { description: 'A delegate that gets called for each signalled event' })); + // non-azure init method this.initMethod.add(function* () { yield '// called at module init time...'; @@ -152,6 +167,22 @@ export class ModuleClass extends Class { }); this.add(new LambdaProperty('Name', dotnet.String, new StringExpression(this.state.project.moduleName), { description: 'The Name of this module ' })); + + // Add Signal extensibility point + const pSignal = new Parameter('signal', incomingSignalDelegate, { description: 'The callback for the event dispatcher ' }); + // Emit signal extensibility points that called EventListenerDelegate, allowing us to handle Signals emitted by the Pipeline in the Auth Module + const signalImpl = this.add(new Method('Signal', System.Threading.Tasks.Task(), { + parameters: [this.pId, this.pToken, this.pGetEventData, pSignal, this.pInvocationInfo, this.pParameterSetName, this.pException], async: Modifier.Async, + description: 'Called to dispatch events to the common module listener', + returnsDescription: `A that will be complete when handling of the event is completed.` + })); + + signalImpl.push(Using('NoSynchronizationContext', '')); + signalImpl.add(function* () { + // Emit call to EventListener after explicit null check. + // Not using Null-Conditional operator causes Null Reference exception when Func is null, due to awaiting null Task. + yield If(`${EventListener.value} != null`, `await ${EventListener.value}.Invoke(${$this.pId.value},${$this.pToken.value},${$this.pGetEventData.value}, ${pSignal.value}, ${$this.pInvocationInfo}, ${$this.pParameterSetName},${$this.pException});`) + }); } createAzureInitAndPipeline(namespace: Namespace) {