fix publishEvent() and remove sessionId in response

This commit is contained in:
Behzad Alizadeh
2026-07-23 11:52:04 +03:30
parent 6764991872
commit 54b068d8e5
5 changed files with 109 additions and 251 deletions
@@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="San.MongoLogger" />
</ItemGroup>
@@ -127,9 +127,9 @@ public static class DependencyInjection
// engine.Execute("function sayHelloWorld() { return greet('World'); }");
//});
})
// برای ثبت Activityهایی مانند Elsa.SendHttpRequest
.UseHttp()
.UseWorkflowContexts();
elsa.AddFastEndpointsAssembly<Program>();
});
// Optionally configure recurring tasks using alternative schedules.
@@ -171,8 +171,8 @@ public static class DependencyInjection
// Captures unhandled exceptions and returns a JSON response.
app.UseJsonSerializationErrorHandler();
//app.UseWorkflows();
app.UseWorkflows();
}
}
@@ -267,77 +267,122 @@ public class WorkflowService : IWorkflowService, ILoggable
#endregion
#region PublishEventAsync
/// <inheritdoc />
public async Task<WorkflowResponse<WorkflowModel>> PublishEventAsync(PublishEventModel input)
public async Task<WorkflowResponse<WorkflowModel>> PublishEventAsync(
PublishEventModel input)
{
var sw = Stopwatch.StartNew();
try
{
_logger.Log("Start of PublishEventAsync", WFInstanceId: input.WorkflowInstanceId, input);
_logger.Log(
"Start of PublishEventAsync",
WFInstanceId: input.WorkflowInstanceId,
input);
WorkflowResponse<WorkflowModel> response;
var instance = await _workflowInstanceManager.FindByIdAsync(input.WorkflowInstanceId);
var instance = await _workflowInstanceManager.FindByIdAsync(
input.WorkflowInstanceId);
if (instance == null)
{
var msg = $"Workflow instance with id '{input.WorkflowInstanceId}' not found";
response = new WorkflowResponse<WorkflowModel>()
var notFoundResponse = new WorkflowResponse<WorkflowModel>
{
Status = 1,
Message = msg,
Message =
$"Workflow instance with id " +
$"'{input.WorkflowInstanceId}' not found",
Data = null
};
sw.Stop();
_logger.Log("End of PublishEventAsync", WFInstanceId: input.WorkflowInstanceId, response, sw.ElapsedMilliseconds);
_logger.Log(
"End of PublishEventAsync",
WFInstanceId: input.WorkflowInstanceId,
notFoundResponse,
sw.ElapsedMilliseconds);
return response;
return notFoundResponse;
}
ExpandoObject payload;
/*
* مهم:
* ساخت Payload را مانند نسخه قبلی نگه می‌داریم:
*
* JsonElement
* ↓
* ExpandoObject
* ↓
* اضافه‌شدن SessionId
* ↓
* PublishEvent
*/
var payloadObject = new JObject();
ExpandoObject payload = new();
if (input.EventData.ValueKind is not JsonValueKind.Undefined
and not JsonValueKind.Null)
{
if (input.EventData.ValueKind != JsonValueKind.Object)
throw new InvalidOperationException("EventData must be a JSON object.");
{
throw new InvalidOperationException(
"EventData must be a JSON object.");
}
payloadObject = JObject.Parse(input.EventData.GetRawText());
var json = input.EventData.GetRawText();
var serializerSettings = new JsonSerializerSettings
{
Converters =
{
new ExpandoObjectConverter()
}
};
payload =
JsonConvert.DeserializeObject<ExpandoObject>(
json,
serializerSettings)
?? new ExpandoObject();
}
/*
* ExpandoObject پیاده‌سازی IDictionary است؛
* بنابراین بدون Serialize/Deserialize مجدد می‌توانیم
* SessionId را به Payload اضافه کنیم.
*/
if (!string.IsNullOrWhiteSpace(input.SessionId))
{
payloadObject["SessionId"] = input.SessionId;
var payloadDictionary =
(IDictionary<string, object?>)payload;
payloadDictionary["SessionId"] = input.SessionId;
}
var settings = new JsonSerializerSettings
{
Converters = { new ExpandoObjectConverter() }
};
await _eventPublisher.PublishAsync(
eventName: input.EventName,
correlationId: null,
workflowInstanceId: input.WorkflowInstanceId,
activityInstanceId: null,
payload: payload,
asynchronous: false);
payload = JsonConvert.DeserializeObject<ExpandoObject>(
payloadObject.ToString(Formatting.None),
settings
) ?? new ExpandoObject();
var variables =
await _workflowInstanceVariableManager.GetVariablesAsync(
input.WorkflowInstanceId);
//await _eventPublisher.PublishAsync(input.EventName, null, input.WorkflowInstanceId, null, input.EventData, asynchronous: true);
await _eventPublisher.PublishAsync(input.EventName, null, input.WorkflowInstanceId, null, payload);
var variables = await _workflowInstanceVariableManager.GetVariablesAsync(input.WorkflowInstanceId);
// تبدیل به دیکشنری
var dictionary = variables.ToDictionary(a => a.Variable.Name, a => a.Value);
var dictionary = variables.ToDictionary(
x => x.Variable.Name,
x => x.Value);
var workflowModel = dictionary.ConvertToWorkflowModel();
workflowModel.InstanceId = instance.Id;
workflowModel.UserId = instance.ExternalUserId;
workflowModel.InstanceMode = "exist";
response = new WorkflowResponse<WorkflowModel>()
var response = new WorkflowResponse<WorkflowModel>
{
Status = 0,
Message = "Success",
@@ -346,26 +391,36 @@ public class WorkflowService : IWorkflowService, ILoggable
sw.Stop();
_logger.Log("End of PublishEventAsync", WFInstanceId: input.WorkflowInstanceId, response, sw.ElapsedMilliseconds);
_logger.Log(
"End of PublishEventAsync",
WFInstanceId: input.WorkflowInstanceId,
response,
sw.ElapsedMilliseconds);
return response;
}
catch (Exception ex)
{
var response = new WorkflowResponse<WorkflowModel>()
sw.Stop();
var response = new WorkflowResponse<WorkflowModel>
{
Status = 100,
Message = ex.Message,
Data = null
};
sw.Stop();
_logger.Log("Exception occurs in PublishEventAsync", WFInstanceId: input.WorkflowInstanceId, response, sw.ElapsedMilliseconds, ex);
_logger.Log(
"Exception occurs in PublishEventAsync",
WFInstanceId: input.WorkflowInstanceId,
response,
sw.ElapsedMilliseconds,
ex);
return response;
}
}
#endregion
#region InquiryStateAsync
@@ -1,226 +1,28 @@
using System.Collections;
using System.Text.Json;
using System.Text.Json.Nodes;
using Newtonsoft.Json.Linq;
namespace Caspian.WorkflowFramework.Utility;
namespace Caspian.WorkflowFramework.Utility;
public static class WorkflowVariableExtensions
{
private const string SessionIdKey = "SessionId";
/// <summary>
/// sessionId را از دیکشنری اصلی و تمام مقادیر تو در تو حذف می‌کند.
/// SessionId را فقط از سطح اول دیکشنری متغیرهای Workflow حذف می‌کند.
/// مقایسه نام کلید به حروف بزرگ و کوچک حساس نیست.
/// </summary>
public static Dictionary<string, object?> RemoveSessionId(
this Dictionary<string, object?> variables)
{
if (variables == null || variables.Count == 0)
return variables;
if (variables is null || variables.Count == 0)
return variables!;
RemoveSessionIdKeys(variables);
var sessionIdKey = variables.Keys.FirstOrDefault(
key => string.Equals(
key,
SessionIdKey,
StringComparison.OrdinalIgnoreCase));
foreach (var key in variables.Keys.ToList())
variables[key] = RemoveSessionIdRecursive(variables[key]);
if (sessionIdKey is not null)
variables.Remove(sessionIdKey);
return variables;
}
private static object? RemoveSessionIdRecursive(object? value)
{
if (value == null)
return null;
switch (value)
{
// Dictionary و ExpandoObject
case IDictionary<string, object?> dictionary:
{
RemoveSessionIdKeys(dictionary);
foreach (var key in dictionary.Keys.ToList())
dictionary[key] = RemoveSessionIdRecursive(dictionary[key]);
return value;
}
// System.Text.Json.Nodes
case JsonObject jsonObject:
{
RemoveSessionIdKeys(jsonObject);
foreach (var property in jsonObject.ToList())
jsonObject[property.Key] =
RemoveSessionIdFromJsonNode(property.Value);
return jsonObject;
}
case JsonArray jsonArray:
{
for (var i = 0; i < jsonArray.Count; i++)
{
jsonArray[i] =
RemoveSessionIdFromJsonNode(jsonArray[i]);
}
return jsonArray;
}
// JsonElement تغییرپذیر نیست؛ پس نسخه پاک‌سازی‌شده می‌سازیم.
case JsonElement jsonElement:
{
var node = JsonNode.Parse(jsonElement.GetRawText());
if (node == null)
return jsonElement;
var cleanedNode = RemoveSessionIdFromJsonNode(node);
return cleanedNode == null
? jsonElement
: JsonSerializer.SerializeToElement(cleanedNode);
}
// Newtonsoft.Json
case JObject jObject:
{
RemoveSessionIdKeys(jObject);
foreach (var property in jObject.Properties().ToList())
property.Value = RemoveSessionIdFromJToken(property.Value);
return jObject;
}
case JArray jArray:
{
for (var i = 0; i < jArray.Count; i++)
jArray[i] = RemoveSessionIdFromJToken(jArray[i]);
return jArray;
}
// سایر لیست‌ها و آرایه‌ها
case IList list:
{
for (var i = 0; i < list.Count; i++)
list[i] = RemoveSessionIdRecursive(list[i]);
return list;
}
case IEnumerable enumerable when value is not string:
{
foreach (var child in enumerable)
RemoveSessionIdRecursive(child);
return value;
}
default:
return value;
}
}
private static JsonNode? RemoveSessionIdFromJsonNode(JsonNode? node)
{
if (node == null)
return null;
switch (node)
{
case JsonObject jsonObject:
{
RemoveSessionIdKeys(jsonObject);
foreach (var property in jsonObject.ToList())
{
jsonObject[property.Key] =
RemoveSessionIdFromJsonNode(property.Value);
}
break;
}
case JsonArray jsonArray:
{
for (var i = 0; i < jsonArray.Count; i++)
{
jsonArray[i] =
RemoveSessionIdFromJsonNode(jsonArray[i]);
}
break;
}
}
return node;
}
private static JToken RemoveSessionIdFromJToken(JToken token)
{
switch (token)
{
case JObject jObject:
{
RemoveSessionIdKeys(jObject);
foreach (var property in jObject.Properties().ToList())
property.Value = RemoveSessionIdFromJToken(property.Value);
break;
}
case JArray jArray:
{
for (var i = 0; i < jArray.Count; i++)
jArray[i] = RemoveSessionIdFromJToken(jArray[i]);
break;
}
}
return token;
}
private static void RemoveSessionIdKeys(
IDictionary<string, object?> dictionary)
{
var keys = dictionary.Keys
.Where(IsSessionId)
.ToList();
foreach (var key in keys)
dictionary.Remove(key);
}
private static void RemoveSessionIdKeys(JsonObject jsonObject)
{
var keys = jsonObject
.Select(x => x.Key)
.Where(IsSessionId)
.ToList();
foreach (var key in keys)
jsonObject.Remove(key);
}
private static void RemoveSessionIdKeys(JObject jObject)
{
var properties = jObject.Properties()
.Where(x => IsSessionId(x.Name))
.ToList();
foreach (var property in properties)
property.Remove();
}
private static bool IsSessionId(string key)
{
return string.Equals(
key,
SessionIdKey,
StringComparison.OrdinalIgnoreCase);
}
}
@@ -12,7 +12,7 @@
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5072",
"applicationUrl": "https://localhost:5002;http://localhost:5072",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}