/// <summary> /// Executes the target activity method. /// </summary> /// <param name="client">The associated Temporal client.</param> /// <param name="argBytes">The encoded activity arguments.</param> /// <returns>The encoded activity results.</returns> private async Task <byte[]> InvokeAsync(TemporalClient client, byte[] argBytes) { await SyncContext.Clear; var parameters = activityMethod.GetParameters(); var parameterTypes = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { parameterTypes[i] = parameters[i].ParameterType; } var resultType = activityMethod.ReturnType; var args = TemporalHelper.BytesToArgs(dataConverter, argBytes, parameterTypes); var serializedResult = Array.Empty <byte>(); if (resultType.IsGenericType) { // Activity method returns: Task<T> var result = await NeonHelper.GetTaskResultAsObjectAsync((Task)activityMethod.Invoke(this, args)); serializedResult = client.DataConverter.ToData(result); } else { // Activity method returns: Task await(Task) activityMethod.Invoke(this, args); } return(serializedResult); }
/// <summary> /// Default internal constructor. /// </summary> /// <param name="client">The associated client.</param> /// <param name="withinWorkflow"> /// Optionally indicates that the stub was created from within a workflow and that /// operations such as get result, query, signal, and cancel must be performed /// within local activities such that that can be replayed from history correctly. /// </param> internal WorkflowStub(TemporalClient client, bool withinWorkflow = false) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); this.client = client; this.withinWorkflow = withinWorkflow; }
/// <summary> /// Internal constructor. /// </summary> /// <param name="client">The parent client.</param> /// <param name="workerId">The ID of the worker as tracked by the <b>temporal-proxy</b>.</param> /// <param name="options">Specifies the worker options or <c>null</c>.</param> internal Worker(TemporalClient client, long workerId, WorkerOptions options) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); Covenant.Requires <ArgumentNullException>(options != null, nameof(options)); this.Client = client; this.WorkerId = workerId; this.options = options; }
/// <summary> /// Internal constructor for use outside of a workflow. /// </summary> /// <param name="client">Specifies the associated client.</param> /// <param name="execution">Specifies the target workflow execution.</param> /// <param name="namespace">Optionally specifies the target namespace (defaults to the client's default namespace).</param> internal ExternalWorkflowStub(TemporalClient client, WorkflowExecution execution, string @namespace = null) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); Covenant.Requires <ArgumentNullException>(execution != null, nameof(execution)); this.client = client; this.@namespace = client.ResolveNamespace(@namespace); this.Execution = execution; }
/// <summary> /// Internal constructor. /// </summary> /// <param name="parentWorkflow">The parent workflow.</param> /// <param name="workflowTypeName">The workflow type name.</param> /// <param name="options">Optional child workflow options.</param> internal ChildWorkflowFutureStub(Workflow parentWorkflow, string workflowTypeName, ChildWorkflowOptions options = null) { Covenant.Requires <ArgumentNullException>(parentWorkflow != null, nameof(parentWorkflow)); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(workflowTypeName), nameof(workflowTypeName)); this.parentWorkflow = parentWorkflow; this.client = parentWorkflow.Client; this.WorkflowTypeName = workflowTypeName; this.Options = ChildWorkflowOptions.Normalize(client, options); }
/// <summary> /// Internal constructor for use within a workflow. /// </summary> /// <param name="parentWorkflow">Specifies the parent workflow.</param> /// <param name="execution">Specifies the target workflow execution.</param> /// <param name="namespace">Optionally specifies the target namespace (defaults to the client's default namespace).</param> internal ExternalWorkflowStub(Workflow parentWorkflow, WorkflowExecution execution, string @namespace = null) { Covenant.Requires <ArgumentNullException>(parentWorkflow != null, nameof(parentWorkflow)); Covenant.Requires <ArgumentNullException>(execution != null, nameof(execution)); this.parentWorkflow = parentWorkflow; this.client = parentWorkflow.Client; this.@namespace = client.ResolveNamespace(@namespace); this.Execution = execution; }
/// <summary> /// Internal constructor. /// </summary> /// <param name="parentWorkflow">The associated parent workflow.</param> /// <param name="activityTypeName"> /// Specifies the target activity type name. /// </param> /// <param name="options">The activity options or <c>null</c>.</param> internal ActivityFutureStub(Workflow parentWorkflow, string activityTypeName, ActivityOptions options = null) { Covenant.Requires <ArgumentNullException>(parentWorkflow != null, nameof(parentWorkflow)); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(activityTypeName), nameof(activityTypeName)); this.parentWorkflow = parentWorkflow; this.client = parentWorkflow.Client; this.activityTypeName = activityTypeName; this.hasStarted = false; this.options = ActivityOptions.Normalize(client, options); }
/// <summary> /// Internal constructor. /// </summary> /// <param name="client">The associated client.</param> /// <param name="parentWorkflow">Identifies the parent workflow.</param> /// <param name="activityTypeName">Specifies the target activity type name.</param> /// <param name="options">Optionally specifies custom activity options.</param> /// <remarks> /// <para> /// <paramref name="activityTypeName"/> specifies the target activity implementation type name and optionally, /// the specific activity method to be called for activity interfaces that have multiple methods. For /// activity methods tagged by <c>ActivityMethod]</c>[ with specifying a name, the activity type name will default /// to the fully qualified interface type name or the custom type name specified by <see cref="ActivityAttribute.Name"/>. /// </para> /// <para> /// For activity methods with <see cref="ActivityMethodAttribute.Name"/> specified, the activity type will /// look like this by default: /// </para> /// <code> /// ACTIVITY-TYPE-NAME::METHOD-NAME /// </code> /// <note> /// You may need to customize activity type name when interoperating with activities written /// in other languages. See <a href="https://doc.neonkube.com/Neon.Temporal-CrossPlatform.htm">Temporal Cross-Platform</a> /// for more information. /// </note> /// </remarks> internal ActivityStub(TemporalClient client, Workflow parentWorkflow, string activityTypeName, ActivityOptions options = null) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); Covenant.Requires <ArgumentNullException>(parentWorkflow != null, nameof(parentWorkflow)); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(activityTypeName), nameof(activityTypeName)); this.client = client; this.parentWorkflow = parentWorkflow; this.activityTypeName = activityTypeName; this.options = options; }
/// <summary> /// Used to construct an untyped workflow stub that can be used to start an external workflow. /// </summary> /// <param name="client">The associated client.</param> /// <param name="workflowTypeName">The workflow type name.</param> /// <param name="execution">The workflow execution.</param> /// <param name="options">The workflow options.</param> internal WorkflowStub(TemporalClient client, string workflowTypeName, WorkflowExecution execution, StartWorkflowOptions options) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(workflowTypeName), nameof(workflowTypeName)); Covenant.Requires <ArgumentNullException>(execution != null, nameof(execution)); Covenant.Requires <ArgumentNullException>(options != null, nameof(options)); this.client = client; this.WorkflowTypeName = workflowTypeName; this.Execution = execution; this.Options = options; this.withinWorkflow = false; }
/// <summary> /// Internal constructor. /// </summary> /// <param name="parentWorkflow">The parent workflow.</param> /// <param name="queueId">The queue ID.</param> /// <param name="capacity">The maximum number of items allowed in the queue.</param> /// <exception cref="NotSupportedException">Thrown when this is called outside of a workflow entry point method.</exception> /// <remarks> /// <note> /// <see cref="WorkflowQueue{T}"/> instances may only be created within /// workflow entry point methods. /// </note> /// </remarks> internal WorkflowQueue(Workflow parentWorkflow, long queueId, int capacity) { Covenant.Requires <ArgumentNullException>(parentWorkflow != null, nameof(parentWorkflow)); Covenant.Requires <ArgumentException>(queueId > 0, nameof(queueId)); Covenant.Requires <ArgumentException>(capacity >= 2, nameof(capacity)); WorkflowBase.CheckCallContext(allowWorkflow: true); this.client = parentWorkflow.Client; this.contextId = parentWorkflow.ContextId; this.Capacity = capacity; this.queueId = queueId; this.isClosed = false; }
/// <summary> /// Internal constructor. /// </summary> /// <param name="client">The associated client.</param> /// <param name="methodName"> /// Optionally identifies the target workflow method by the name specified in /// the <c>[WorkflowMethod]</c> attribute tagging the method. Pass a <c>null</c> /// or empty string to target the default method. /// </param> /// <param name="options">Optional workflow options.</param> internal WorkflowFutureStub(TemporalClient client, string methodName = null, StartWorkflowOptions options = null) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); var workflowInterface = typeof(WorkflowInterface); var method = TemporalHelper.GetWorkflowMethod(workflowInterface, methodName); TemporalHelper.ValidateWorkflowInterface(workflowInterface); this.client = client; this.workflowTypeName = TemporalHelper.GetWorkflowTarget(workflowInterface, methodName).WorkflowTypeName; this.options = StartWorkflowOptions.Normalize(client, options, workflowInterface, method); }
/// <summary> /// Internal constructor. /// </summary> /// <param name="parentWorkflow">The associated parent workflow.</param> /// <param name="methodName"> /// Optionally identifies the target activity method by the name specified in /// the <c>[ActivityMethod]</c> attribute tagging the method. Pass a <c>null</c> /// or empty string to target the default method. /// </param> /// <param name="options">The activity options or <c>null</c>.</param> internal ActivityFutureStub(Workflow parentWorkflow, string methodName = null, ActivityOptions options = null) { Covenant.Requires <ArgumentNullException>(parentWorkflow != null, nameof(parentWorkflow)); var activityInterface = typeof(TActivityInterface); TemporalHelper.ValidateActivityInterface(activityInterface); this.parentWorkflow = parentWorkflow; this.client = parentWorkflow.Client; this.hasStarted = false; var activityTarget = TemporalHelper.GetActivityTarget(activityInterface, methodName); var methodAttribute = activityTarget.MethodAttribute; this.activityTypeName = activityTarget.ActivityTypeName; this.targetMethod = activityTarget.TargetMethod; this.options = ActivityOptions.Normalize(client, options, typeof(TActivityInterface), activityTarget.TargetMethod); }
//--------------------------------------------------------------------- // Static members /// <summary> /// Normalizes the options passed by creating or cloning a new instance as /// required and filling unset properties using default client settings. /// </summary> /// <param name="client">The associated Temporal client.</param> /// <param name="options">The input options or <c>null</c>.</param> /// <returns>The normalized options.</returns> internal static LocalActivityOptions Normalize(TemporalClient client, LocalActivityOptions options) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); if (options == null) { options = new LocalActivityOptions(); } else { options = options.Clone(); } if (options.ScheduleToCloseTimeout <= TimeSpan.Zero) { options.ScheduleToCloseTimeout = client.Settings.ActivityScheduleToCloseTimeout; } return(options); }
//--------------------------------------------------------------------- // Static members /// <summary> /// <b>INTERNAL USE ONLY:</b> Normalizes the options passed by creating or cloning a new /// instance as required and filling unset properties using default client settings. /// </summary> /// <param name="client">The associated Temporal client.</param> /// <param name="options">The input options or <c>null</c>.</param> /// <param name="workflowInterface">Optionally specifies the workflow interface definition.</param> /// /// <param name="method">Optionally specifies the target workflow method.</param> /// <returns>The normalized options.</returns> /// <exception cref="ArgumentNullException">Thrown if a valid task queue is not specified.</exception> public static ChildWorkflowOptions Normalize(TemporalClient client, ChildWorkflowOptions options, Type workflowInterface = null, MethodInfo method = null) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); WorkflowInterfaceAttribute interfaceAttribute = null; WorkflowMethodAttribute methodAttribute = null; if (options == null) { options = new ChildWorkflowOptions(); } else { options = options.Clone(); } if (workflowInterface != null) { TemporalHelper.ValidateWorkflowInterface(workflowInterface); interfaceAttribute = workflowInterface.GetCustomAttribute <WorkflowInterfaceAttribute>(); } if (method != null) { methodAttribute = method.GetCustomAttribute <WorkflowMethodAttribute>(); } if (string.IsNullOrEmpty(options.Namespace)) { if (!string.IsNullOrEmpty(methodAttribute?.Namespace)) { options.Namespace = methodAttribute.Namespace; } if (string.IsNullOrEmpty(options.Namespace) && !string.IsNullOrEmpty(interfaceAttribute?.Namespace)) { options.Namespace = interfaceAttribute.Namespace; } } if (string.IsNullOrEmpty(options.TaskQueue)) { if (!string.IsNullOrEmpty(methodAttribute?.TaskQueue)) { options.TaskQueue = methodAttribute.TaskQueue; } if (string.IsNullOrEmpty(options.TaskQueue) && !string.IsNullOrEmpty(interfaceAttribute?.TaskQueue)) { options.TaskQueue = interfaceAttribute.TaskQueue; } if (string.IsNullOrEmpty(options.TaskQueue)) { options.TaskQueue = client.Settings.TaskQueue; } } if (options.WorkflowExecutionTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.WorkflowExecutionTimeoutSeconds > 0) { options.WorkflowExecutionTimeout = TimeSpan.FromSeconds(methodAttribute.WorkflowExecutionTimeoutSeconds); } if (options.WorkflowExecutionTimeout <= TimeSpan.Zero) { options.WorkflowExecutionTimeout = client.Settings.WorkflowExecutionTimeout; } } if (options.WorkflowRunTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.WorkflowRunTimeoutSeconds > 0) { options.WorkflowRunTimeout = TimeSpan.FromSeconds(methodAttribute.WorkflowRunTimeoutSeconds); } if (options.WorkflowRunTimeout <= TimeSpan.Zero) { options.WorkflowRunTimeout = client.Settings.WorkflowRunTimeout; } } if (options.WorkflowTaskTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.WorkflowTaskTimeoutSeconds > 0) { options.WorkflowTaskTimeout = TimeSpan.FromSeconds(methodAttribute.WorkflowTaskTimeoutSeconds); } if (options.WorkflowTaskTimeout <= TimeSpan.Zero) { options.WorkflowTaskTimeout = client.Settings.WorkflowTaskTimeout; } } if (options.WorkflowIdReusePolicy == Temporal.WorkflowIdReusePolicy.UseDefault) { if (methodAttribute != null && methodAttribute.WorkflowIdReusePolicy != WorkflowIdReusePolicy.UseDefault) { options.WorkflowIdReusePolicy = methodAttribute.WorkflowIdReusePolicy; } if (options.WorkflowIdReusePolicy == Temporal.WorkflowIdReusePolicy.UseDefault) { options.WorkflowIdReusePolicy = client.Settings.WorkflowIdReusePolicy; } } return(options); }
/// <summary> /// Constructor. /// </summary> /// <param name="client">The associated client.</param> /// <param name="execution">The workflow execution.</param> /// /// <param name="namespace">Optionally specifies the target namespace. This defaults to the default client namespace.</param> internal ExternalWorkflowFuture(TemporalClient client, WorkflowExecution execution, string @namespace) { this.client = client; this.Execution = execution; this.@namespace = @namespace; }
//--------------------------------------------------------------------- // Static members /// <summary> /// Normalizes the options passed by creating or cloning a new instance as /// required and filling unset properties using default client settings. /// </summary> /// <param name="client">The associated Temporal client.</param> /// <param name="options">The input options or <c>null</c>.</param> /// <param name="workflowInterface">Optionally specifies the workflow interface definition.</param> /// /// <param name="method">Optionally specifies the target workflow method.</param> /// <returns>The normalized options.</returns> /// <exception cref="ArgumentNullException">Thrown if a valid task queue is not specified.</exception> internal static StartWorkflowOptions Normalize(TemporalClient client, StartWorkflowOptions options, Type workflowInterface = null, MethodInfo method = null) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); WorkflowInterfaceAttribute interfaceAttribute = null; WorkflowMethodAttribute methodAttribute = null; if (options == null) { options = new StartWorkflowOptions(); } else { options = options.Clone(); } if (workflowInterface != null) { TemporalHelper.ValidateWorkflowInterface(workflowInterface); interfaceAttribute = workflowInterface.GetCustomAttribute <WorkflowInterfaceAttribute>(); } if (method != null) { methodAttribute = method.GetCustomAttribute <WorkflowMethodAttribute>(); } if (string.IsNullOrEmpty(options.Namespace)) { if (!string.IsNullOrEmpty(methodAttribute?.Namespace)) { options.Namespace = methodAttribute.Namespace; } if (string.IsNullOrEmpty(options.Namespace) && !string.IsNullOrEmpty(interfaceAttribute?.Namespace)) { options.Namespace = interfaceAttribute.Namespace; } if (string.IsNullOrEmpty(options.Namespace)) { options.Namespace = client.Settings.Namespace; } if (string.IsNullOrEmpty(options.Namespace)) { throw new ArgumentNullException(nameof(options), "You must specify a valid namnespace explicitly in [TemporalSettings], [ActivityOptions] or via an [ActivityInterface] or [ActivityMethod] attribute on the target activity interface or method."); } } if (string.IsNullOrEmpty(options.TaskQueue)) { if (!string.IsNullOrEmpty(methodAttribute?.TaskQueue)) { options.TaskQueue = methodAttribute.TaskQueue; } if (string.IsNullOrEmpty(options.TaskQueue) && !string.IsNullOrEmpty(interfaceAttribute?.TaskQueue)) { options.TaskQueue = interfaceAttribute.TaskQueue; } if (string.IsNullOrEmpty(options.TaskQueue)) { options.TaskQueue = client.Settings.TaskQueue; } if (string.IsNullOrEmpty(options.TaskQueue)) { throw new ArgumentNullException(nameof(options), "You must specify a valid task queue explicitly via [StartWorkflowOptions] or using an [WorkflowInterface] or [WorkflowMethod] attribute on the target workflow interface or method."); } } if (options.WorkflowExecutionTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.WorkflowExecutionTimeoutSeconds > 0) { options.WorkflowExecutionTimeout = TimeSpan.FromSeconds(methodAttribute.WorkflowExecutionTimeoutSeconds); } if (options.WorkflowExecutionTimeout <= TimeSpan.Zero) { options.WorkflowExecutionTimeout = client.Settings.WorkflowExecutionTimeout; } } if (options.WorkflowRunTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.WorkflowRunTimeoutSeconds > 0) { options.WorkflowRunTimeout = TimeSpan.FromSeconds(methodAttribute.WorkflowRunTimeoutSeconds); } if (options.WorkflowRunTimeout <= TimeSpan.Zero) { options.WorkflowRunTimeout = client.Settings.WorkflowRunTimeout; } } if (options.WorkflowTaskTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.WorkflowTaskTimeoutSeconds > 0) { options.WorkflowTaskTimeout = TimeSpan.FromSeconds(methodAttribute.WorkflowTaskTimeoutSeconds); } if (options.WorkflowTaskTimeout <= TimeSpan.Zero) { options.WorkflowTaskTimeout = client.Settings.WorkflowTaskTimeout; } } if (options.WorkflowIdReusePolicy == Temporal.WorkflowIdReusePolicy.UseDefault) { if (methodAttribute != null && methodAttribute.WorkflowIdReusePolicy != WorkflowIdReusePolicy.UseDefault) { options.WorkflowIdReusePolicy = methodAttribute.WorkflowIdReusePolicy; } if (options.WorkflowIdReusePolicy == Temporal.WorkflowIdReusePolicy.UseDefault) { options.WorkflowIdReusePolicy = client.Settings.WorkflowIdReusePolicy; } } if (string.IsNullOrEmpty(options.CronSchedule) && !string.IsNullOrEmpty(methodAttribute?.CronSchedule)) { options.CronSchedule = methodAttribute.CronSchedule; } return(options); }
//--------------------------------------------------------------------- // Static members /// <summary> /// Normalizes the options passed by creating or cloning a new instance as /// required and filling unset properties using default client settings. /// </summary> /// <param name="client">The associated Temporal client.</param> /// <param name="options">The input options or <c>null</c>.</param> /// <param name="activityInterface">Optionally specifies the activity interface definition.</param> /// /// <param name="method">Optionally specifies the target workflow method.</param> /// <returns>The normalized options.</returns> /// <exception cref="ArgumentNullException">Thrown if a valid task queue is not specified.</exception> internal static ActivityOptions Normalize(TemporalClient client, ActivityOptions options, Type activityInterface = null, MethodInfo method = null) { Covenant.Requires <ArgumentNullException>(client != null, nameof(client)); options = options ?? new ActivityOptions(); ActivityInterfaceAttribute interfaceAttribute = null; ActivityMethodAttribute methodAttribute = null; if (activityInterface != null) { TemporalHelper.ValidateActivityInterface(activityInterface); interfaceAttribute = activityInterface.GetCustomAttribute <ActivityInterfaceAttribute>(); } if (method != null) { methodAttribute = method.GetCustomAttribute <ActivityMethodAttribute>(); } if (string.IsNullOrEmpty(options.Namespace)) { if (!string.IsNullOrEmpty(methodAttribute?.Namespace)) { options.Namespace = methodAttribute.Namespace; } if (string.IsNullOrEmpty(options.Namespace) && !string.IsNullOrEmpty(interfaceAttribute?.Namespace)) { options.Namespace = interfaceAttribute.Namespace; } } if (string.IsNullOrEmpty(options.TaskQueue)) { if (!string.IsNullOrEmpty(methodAttribute?.TaskQueue)) { options.TaskQueue = methodAttribute.TaskQueue; } if (string.IsNullOrEmpty(options.TaskQueue) && !string.IsNullOrEmpty(interfaceAttribute?.TaskQueue)) { options.TaskQueue = interfaceAttribute.TaskQueue; } } if (options.ScheduleToCloseTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.ScheduleToCloseTimeoutSeconds > 0) { options.ScheduleToCloseTimeout = TimeSpan.FromSeconds(methodAttribute.ScheduleToCloseTimeoutSeconds); } if (options.ScheduleToCloseTimeout <= TimeSpan.Zero) { options.ScheduleToCloseTimeout = client.Settings.ActivityScheduleToCloseTimeout; } } if (options.ScheduleToStartTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.ScheduleToStartTimeoutSeconds > 0) { options.ScheduleToStartTimeout = TimeSpan.FromSeconds(methodAttribute.ScheduleToStartTimeoutSeconds); } if (options.ScheduleToStartTimeout <= TimeSpan.Zero) { options.ScheduleToStartTimeout = client.Settings.ActivityScheduleToStartTimeout; } } if (options.StartToCloseTimeout <= TimeSpan.Zero) { if (methodAttribute != null && methodAttribute.StartToCloseTimeoutSeconds > 0) { options.StartToCloseTimeout = TimeSpan.FromSeconds(methodAttribute.StartToCloseTimeoutSeconds); } if (options.StartToCloseTimeout <= TimeSpan.Zero) { options.StartToCloseTimeout = client.Settings.ActivityStartToCloseTimeout; } } return(options); }