Skip to content

Emit Signal method and delegate to provide hook into events for Non-Azure scenarios. #622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions powershell/cmdlets/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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...
Expand Down
33 changes: 32 additions & 1 deletion powershell/module/module-class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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...';
Expand All @@ -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 <see cref="${System.Threading.Tasks.Task()}" /> 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<Task> 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) {
Expand Down