Merge pull request #6332 from elsa-workflows/bug/email-attachment-bytes
Add SMTP server configuration and refactor email activity for improved error handling and attachment processing
This commit is contained in:
commit
f9330dfd0e
|
|
@ -72,6 +72,18 @@
|
|||
environment:
|
||||
- PlantUml__RemoteUrl=
|
||||
- ConnectionStrings__DefaultConnection=USER ID=tracelens;PASSWORD=tracelenspass;HOST=postgres;PORT=5432;DATABASE=tracelens;POOLING=true;
|
||||
|
||||
smtp4dev: # Mock SMTP server
|
||||
image: rnwood/smtp4dev
|
||||
container_name: smtp4dev
|
||||
restart: always
|
||||
ports:
|
||||
- "3000:80" # Web interface
|
||||
- "2525:25" # SMTP port
|
||||
environment:
|
||||
- ASPNETCORE_URLS=http://+:80
|
||||
- Logging__LogLevel__Default=Information
|
||||
|
||||
|
||||
elsa-server:
|
||||
build:
|
||||
|
|
|
|||
|
|
@ -84,8 +84,7 @@ public class SendEmail : Activity
|
|||
/// <summary>
|
||||
/// The activity to execute when an error occurs while trying to send the email.
|
||||
/// </summary>
|
||||
[Port]
|
||||
public IActivity? Error { get; set; }
|
||||
[Port] public IActivity? Error { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
|
||||
|
|
@ -99,7 +98,10 @@ public class SendEmail : Activity
|
|||
message.From.Add(MailboxAddress.Parse(from));
|
||||
message.Subject = Subject.GetOrDefault(context) ?? "";
|
||||
|
||||
var bodyBuilder = new BodyBuilder { HtmlBody = Body.GetOrDefault(context) };
|
||||
var bodyBuilder = new BodyBuilder
|
||||
{
|
||||
HtmlBody = Body.GetOrDefault(context)
|
||||
};
|
||||
await AddAttachmentsAsync(context, bodyBuilder, cancellationToken);
|
||||
|
||||
message.Body = bodyBuilder.ToMessageBody();
|
||||
|
|
@ -119,7 +121,10 @@ public class SendEmail : Activity
|
|||
catch (Exception e)
|
||||
{
|
||||
logger.LogWarning(e, "Error while sending email message");
|
||||
context.AddExecutionLogEntry("Error", e.Message, payload: new { e.StackTrace });
|
||||
context.AddExecutionLogEntry("Error", e.Message, payload: new
|
||||
{
|
||||
e.StackTrace
|
||||
});
|
||||
await context.ScheduleActivityAsync(Error, OnErrorCompletedAsync);
|
||||
}
|
||||
}
|
||||
|
|
@ -155,38 +160,38 @@ public class SendEmail : Activity
|
|||
await AttachLocalFileAsync(bodyBuilder, path, cancellationToken);
|
||||
break;
|
||||
case byte[] bytes:
|
||||
{
|
||||
var fileName = $"Attachment-{++index}";
|
||||
bodyBuilder.Attachments.Add(fileName, bytes, ContentType.Parse("application/binary"));
|
||||
break;
|
||||
}
|
||||
{
|
||||
var fileName = $"Attachment-{++index}";
|
||||
bodyBuilder.Attachments.Add(fileName, bytes, ContentType.Parse("application/binary"));
|
||||
break;
|
||||
}
|
||||
case Stream stream:
|
||||
{
|
||||
var fileName = $"Attachment-{++index}";
|
||||
await bodyBuilder.Attachments.AddAsync(fileName, stream, ContentType.Parse("application/binary"), cancellationToken);
|
||||
break;
|
||||
}
|
||||
{
|
||||
var fileName = $"Attachment-{++index}";
|
||||
await bodyBuilder.Attachments.AddAsync(fileName, stream, ContentType.Parse("application/binary"), cancellationToken);
|
||||
break;
|
||||
}
|
||||
case EmailAttachment emailAttachment:
|
||||
{
|
||||
var fileName = emailAttachment.FileName ?? $"Attachment-{++index}";
|
||||
var contentType = emailAttachment.ContentType ?? "application/binary";
|
||||
var parsedContentType = ContentType.Parse(contentType);
|
||||
{
|
||||
var fileName = emailAttachment.FileName ?? $"Attachment-{++index}";
|
||||
var contentType = emailAttachment.ContentType ?? "application/binary";
|
||||
var parsedContentType = ContentType.Parse(contentType);
|
||||
|
||||
if (emailAttachment.Content is byte[] bytes)
|
||||
bodyBuilder.Attachments.Add(fileName, bytes, parsedContentType);
|
||||
if (emailAttachment.Content is byte[] bytes)
|
||||
bodyBuilder.Attachments.Add(fileName, bytes, parsedContentType);
|
||||
|
||||
else if (emailAttachment.Content is Stream stream)
|
||||
await bodyBuilder.Attachments.AddAsync(fileName, stream, parsedContentType, cancellationToken);
|
||||
else if (emailAttachment.Content is Stream stream)
|
||||
await bodyBuilder.Attachments.AddAsync(fileName, stream, parsedContentType, cancellationToken);
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
var json = JsonSerializer.Serialize(attachmentObject);
|
||||
var fileName = $"Attachment-{++index}";
|
||||
bodyBuilder.Attachments.Add(fileName, Encoding.UTF8.GetBytes(json), ContentType.Parse("application/json"));
|
||||
break;
|
||||
}
|
||||
{
|
||||
var json = JsonSerializer.Serialize(attachmentObject);
|
||||
var fileName = $"Attachment-{++index}";
|
||||
bodyBuilder.Attachments.Add(fileName, Encoding.UTF8.GetBytes(json), ContentType.Parse("application/json"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -203,7 +208,24 @@ public class SendEmail : Activity
|
|||
await bodyBuilder.Attachments.AddAsync(fileName, contentStream, ContentType.Parse(contentType), cancellationToken);
|
||||
}
|
||||
|
||||
private IEnumerable InterpretAttachmentsModel(object attachments) => attachments is string text ? new[] { text } : attachments is IEnumerable enumerable ? enumerable : new[] { attachments };
|
||||
private IEnumerable InterpretAttachmentsModel(object attachments)
|
||||
{
|
||||
if (attachments is byte[] bytes)
|
||||
return new[]
|
||||
{
|
||||
bytes
|
||||
};
|
||||
|
||||
return attachments is string text
|
||||
? new[]
|
||||
{
|
||||
text
|
||||
}
|
||||
: attachments as IEnumerable ?? new[]
|
||||
{
|
||||
attachments
|
||||
};
|
||||
}
|
||||
|
||||
private void SetRecipientsEmailAddresses(InternetAddressList list, IEnumerable<string>? addresses)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,10 +18,16 @@ public class VariableDefinitionMapper(IWellKnownTypeRegistry wellKnownTypeRegist
|
|||
/// </summary>
|
||||
public Variable? Map(VariableDefinition source)
|
||||
{
|
||||
if (!wellKnownTypeRegistry.TryGetTypeOrDefault(source.TypeName, out var type))
|
||||
var aliasedType = wellKnownTypeRegistry.TryGetType(source.TypeName, out var aliasedTypeValue) ? aliasedTypeValue : null;
|
||||
var type = aliasedType ?? Type.GetType(source.TypeName);
|
||||
|
||||
if(type == null)
|
||||
{
|
||||
logger.LogWarning("Failed to resolve the type {TypeName} of variable {VariableName}. Variable will not be mapped.", source.TypeName, source.Name);
|
||||
return null;
|
||||
}
|
||||
|
||||
var valueType = source.IsArray ? type.MakeArrayType() : type;
|
||||
var valueType = aliasedType ?? (source.IsArray ? type.MakeArrayType() : type);
|
||||
var variableGenericType = typeof(Variable<>).MakeGenericType(valueType);
|
||||
var variable = (Variable)Activator.CreateInstance(variableGenericType)!;
|
||||
|
||||
|
|
@ -58,15 +64,20 @@ public class VariableDefinitionMapper(IWellKnownTypeRegistry wellKnownTypeRegist
|
|||
{
|
||||
var variableType = source.GetType();
|
||||
var valueType = variableType.IsConstructedGenericType ? variableType.GetGenericArguments().FirstOrDefault() ?? typeof(object) : typeof(object);
|
||||
var valueTypeAlias = wellKnownTypeRegistry.TryGetAlias(valueType, out var alias) ? alias : null;
|
||||
var value = source.Value;
|
||||
var serializedValue = value.Format();
|
||||
var storageDriverTypeName = source.StorageDriverType?.GetSimpleAssemblyQualifiedName();
|
||||
|
||||
if(valueTypeAlias != null)
|
||||
return new(source.Id, source.Name, valueTypeAlias, false, serializedValue, storageDriverTypeName);
|
||||
|
||||
var isArray = valueType.IsArray;
|
||||
var isCollection = valueType.IsCollectionType();
|
||||
var elementValueType = isArray ? valueType.GetElementType() : isCollection ? valueType.GenericTypeArguments[0] : valueType;
|
||||
var value = source.Value;
|
||||
var valueTypeAlias = wellKnownTypeRegistry.GetAliasOrDefault(elementValueType);
|
||||
var storageDriverTypeName = source.StorageDriverType?.GetSimpleAssemblyQualifiedName();
|
||||
var serializedValue = value.Format();
|
||||
|
||||
return new(source.Id, source.Name, valueTypeAlias, isArray, serializedValue, storageDriverTypeName);
|
||||
var elementTypeAlias = wellKnownTypeRegistry.GetAliasOrDefault(elementValueType);
|
||||
|
||||
return new(source.Id, source.Name, elementTypeAlias, isArray, serializedValue, storageDriverTypeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
Loading…
Reference in a new issue