diff --git a/ChatbotBuilderApi.Application/Graphs/Nodes/Interaction/InteractionNodeDto.cs b/ChatbotBuilderApi.Application/Graphs/Nodes/Interaction/InteractionNodeDto.cs index 8b763c4..6029a5b 100644 --- a/ChatbotBuilderApi.Application/Graphs/Nodes/Interaction/InteractionNodeDto.cs +++ b/ChatbotBuilderApi.Application/Graphs/Nodes/Interaction/InteractionNodeDto.cs @@ -11,6 +11,7 @@ public sealed record InteractionNodeDto( InfoMeta Info, VisualMeta Visual, InputPortDto? TextInputPort, + IReadOnlyList ImageInputPorts, OutputPortDto? TextOutputPort, int? OutputEnumIdentifier, OutputPortDto? OptionOutputPort, @@ -20,9 +21,14 @@ public sealed record InteractionNodeDto( { public IEnumerable GetInputPortIds() { - if (TextOutputPort is not null) + if (TextInputPort is not null) { - yield return TextOutputPort.Info.Identifier; + yield return TextInputPort.Info.Identifier; + } + + foreach (var imageInputPort in ImageInputPorts) + { + yield return imageInputPort.Info.Identifier; } } diff --git a/ChatbotBuilderApi.Application/Graphs/Nodes/Interaction/InteractionNodeMapper.cs b/ChatbotBuilderApi.Application/Graphs/Nodes/Interaction/InteractionNodeMapper.cs index 285dc4a..ea1b4d2 100644 --- a/ChatbotBuilderApi.Application/Graphs/Nodes/Interaction/InteractionNodeMapper.cs +++ b/ChatbotBuilderApi.Application/Graphs/Nodes/Interaction/InteractionNodeMapper.cs @@ -46,6 +46,13 @@ public static InteractionNode ToDomain(this InteractionNodeDto dto, Enum? output dto.Info, dto.Visual, textInputPort, + dto.ImageInputPorts + .Select(i => InputPort.Create( + new InputPortId(Guid.NewGuid()), + i.Info, + i.Visual, + nodeId)) + .ToHashSet(), textOutputPort, outputEnum, optionOutputPort, @@ -82,6 +89,13 @@ public static InteractionNodeDto ToDto(this InteractionNode domain) domain.Info, domain.Visual, textInputPort, + domain.ImageInputPorts + .Select(i => new InputPortDto( + i.Info, + i.Visual, + domain.Info.Identifier, + DataType.Image)) + .ToList(), textOutputPort, domain.OutputEnum?.Info.Identifier, optionOutputPort, diff --git a/ChatbotBuilderApi.Application/Graphs/Nodes/Interaction/InteractionNodeValidator.cs b/ChatbotBuilderApi.Application/Graphs/Nodes/Interaction/InteractionNodeValidator.cs index b8d5f5f..c1f46ca 100644 --- a/ChatbotBuilderApi.Application/Graphs/Nodes/Interaction/InteractionNodeValidator.cs +++ b/ChatbotBuilderApi.Application/Graphs/Nodes/Interaction/InteractionNodeValidator.cs @@ -1,4 +1,5 @@ -using ChatbotBuilderApi.Application.Graphs.Ports.InputPorts; +using ChatbotBuilderApi.Application.Core.Extensions; +using ChatbotBuilderApi.Application.Graphs.Ports.InputPorts; using ChatbotBuilderApi.Application.Graphs.Ports.OutputPorts; using ChatbotBuilderApi.Application.Graphs.Shared.Data; using ChatbotBuilderApi.Application.Graphs.Shared.Interactions; @@ -24,6 +25,16 @@ public InteractionNodeValidator() .WithMessage("TextInputPort node identifier must match node identifier."); }); + RuleForEach(x => x.ImageInputPorts) + .SetValidator(new InputPortValidator(DataType.Image)); + + RuleFor(x => x.ImageInputPorts) + .MustBeUnique(); + + RuleFor(x => x) + .Must(x => x.ImageInputPorts.Count < 20) + .WithMessage("ImageInputPorts count must be less than 20."); + When(x => x.TextOutputPort is not null, () => { RuleFor(x => x.TextOutputPort) diff --git a/ChatbotBuilderApi.Domain/Graphs/Nodes/InteractionNode.cs b/ChatbotBuilderApi.Domain/Graphs/Nodes/InteractionNode.cs index a7453b8..1e83c87 100644 --- a/ChatbotBuilderApi.Domain/Graphs/Nodes/InteractionNode.cs +++ b/ChatbotBuilderApi.Domain/Graphs/Nodes/InteractionNode.cs @@ -12,8 +12,8 @@ public sealed class InteractionNode : Node, IInputNode, IEnumNode, IOutputNode { public InputPort? TextInputPort { get; } + public IReadOnlySet> ImageInputPorts { get; } = null!; public OutputPort? TextOutputPort { get; } - public Enum? OutputEnum { get; } public OutputPort? OptionOutputPort { get; } public IReadOnlyDictionary? OutputOptionMetas { get; } @@ -25,6 +25,7 @@ private InteractionNode( InfoMeta info, VisualMeta visual, InputPort? textInputPort, + IReadOnlySet> imageInputPorts, OutputPort? textOutputPort, Enum? outputEnum, OutputPort? optionOutputPort, @@ -32,6 +33,7 @@ private InteractionNode( : base(id, info, visual) { TextInputPort = textInputPort; + ImageInputPorts = imageInputPorts; TextOutputPort = textOutputPort; OutputEnum = outputEnum; OptionOutputPort = optionOutputPort; @@ -48,6 +50,7 @@ public static InteractionNode Create( InfoMeta info, VisualMeta visual, InputPort? textInputPort, + IReadOnlySet> imageInputPorts, OutputPort? textOutputPort, Enum? outputEnum, OutputPort? optionOutputPort, @@ -98,6 +101,7 @@ public static InteractionNode Create( info, visual, textInputPort, + imageInputPorts, textOutputPort, outputEnum, optionOutputPort, @@ -108,6 +112,7 @@ public InteractionOutput GetInteractionOutput() { return InteractionOutput.Create( TextInputPort?.GetData(), + ImageInputPorts.Select(i => i.GetData()).ToList(), TextOutputPort is not null, OptionOutputPort is not null, OutputOptionMetas); @@ -144,6 +149,11 @@ public IEnumerable> GetInputPorts() { yield return TextInputPort; } + + foreach (var imageInputPort in ImageInputPorts) + { + yield return imageInputPort; + } } public IEnumerable> GetOutputPorts() diff --git a/ChatbotBuilderApi.Domain/Graphs/ValueObjects/Interactions/InteractionOutput.cs b/ChatbotBuilderApi.Domain/Graphs/ValueObjects/Interactions/InteractionOutput.cs index 2e8939a..22defaa 100644 --- a/ChatbotBuilderApi.Domain/Graphs/ValueObjects/Interactions/InteractionOutput.cs +++ b/ChatbotBuilderApi.Domain/Graphs/ValueObjects/Interactions/InteractionOutput.cs @@ -10,17 +10,20 @@ namespace ChatbotBuilderApi.Domain.Graphs.ValueObjects.Interactions; public sealed class InteractionOutput : ValueObject { public TextData? TextOutput { get; } + public IReadOnlyList ImageOutputs { get; } = null!; public bool TextExpected { get; } public bool OptionExpected { get; } public IReadOnlyDictionary? ExpectedOptionMetas { get; } private InteractionOutput( TextData? textOutput, + IReadOnlyList imageOutputs, bool textExpected, bool optionExpected, IReadOnlyDictionary? expectedOptionMetas) { TextOutput = textOutput; + ImageOutputs = imageOutputs; TextExpected = textExpected; OptionExpected = optionExpected; ExpectedOptionMetas = expectedOptionMetas; @@ -33,16 +36,23 @@ private InteractionOutput() public static InteractionOutput Create( TextData? textOutput, + IReadOnlyList imageOutputs, bool textExpected, bool optionExpected, IReadOnlyDictionary? expectedOptionsMetas) { - return new InteractionOutput(textOutput, textExpected, optionExpected, expectedOptionsMetas); + return new InteractionOutput(textOutput, imageOutputs, textExpected, optionExpected, expectedOptionsMetas); } protected override IEnumerable GetAtomicValues() { yield return (object?)TextOutput ?? false; + + foreach (var imageOutput in ImageOutputs) + { + yield return imageOutput; + } + yield return TextExpected; yield return OptionExpected; yield return (object?)ExpectedOptionMetas ?? false; diff --git a/ChatbotBuilderApi.Presentation/Conversations/Messages/ViewModels/MessageViewModel.cs b/ChatbotBuilderApi.Presentation/Conversations/Messages/ViewModels/MessageViewModel.cs index 5a14d19..e1440b2 100644 --- a/ChatbotBuilderApi.Presentation/Conversations/Messages/ViewModels/MessageViewModel.cs +++ b/ChatbotBuilderApi.Presentation/Conversations/Messages/ViewModels/MessageViewModel.cs @@ -19,6 +19,7 @@ public sealed record InputMessageViewModel( /// /// Date and time the message was created. /// Text output data. If any. +/// A list of image output data. If any. /// Whether a text input is expected on the next user message. /// Whether an option input is expected on the next user message. /// If an option input is expected, @@ -26,6 +27,7 @@ public sealed record InputMessageViewModel( public sealed record OutputMessageViewModel( DateTime CreatedAt, TextDataModel? TextOutput, + IReadOnlyList ImageOutputs, bool TextExpected, bool OptionExpected, IReadOnlyDictionary? ExpectedOptionMetas); \ No newline at end of file diff --git a/ChatbotBuilderApi.Presentation/Conversations/Messages/ViewModels/MessageViewModelsMappers.cs b/ChatbotBuilderApi.Presentation/Conversations/Messages/ViewModels/MessageViewModelsMappers.cs index 887a89e..a8fe2c0 100644 --- a/ChatbotBuilderApi.Presentation/Conversations/Messages/ViewModels/MessageViewModelsMappers.cs +++ b/ChatbotBuilderApi.Presentation/Conversations/Messages/ViewModels/MessageViewModelsMappers.cs @@ -24,6 +24,7 @@ public static OutputMessageViewModel ToViewModel(this OutputMessage message) return new OutputMessageViewModel( message.CreatedAt, message.Output.TextOutput?.ToModel(), + message.Output.ImageOutputs.Select(i => i.ToModel()).ToList(), message.Output.TextExpected, message.Output.OptionExpected, message.Output.ExpectedOptionMetas?.ToDictionary( diff --git a/ChatbotBuilderApi.Presentation/Graphs/Nodes/Interaction/InteractionNodeMapper.cs b/ChatbotBuilderApi.Presentation/Graphs/Nodes/Interaction/InteractionNodeMapper.cs index 783489d..4678d83 100644 --- a/ChatbotBuilderApi.Presentation/Graphs/Nodes/Interaction/InteractionNodeMapper.cs +++ b/ChatbotBuilderApi.Presentation/Graphs/Nodes/Interaction/InteractionNodeMapper.cs @@ -16,6 +16,7 @@ public static InteractionNodeModel ToModel(this InteractionNodeDto dto) dto.Info.ToModel(), dto.Visual.ToModel(), dto.TextInputPort?.ToModel(), + dto.ImageInputPorts.Select(i => i.ToModel()).ToList(), dto.TextOutputPort?.ToModel(), dto.OutputEnumIdentifier, dto.OptionOutputPort?.ToModel(), @@ -30,6 +31,7 @@ public static InteractionNodeDto ToDto(this InteractionNodeModel model) model.Info.ToDomain(), model.Visual.ToDomain(), model.TextInputPort?.ToDto(), + model.ImageInputPorts.Select(i => i.ToDto()).ToList(), model.TextOutputPort?.ToDto(), model.OutputEnumId, model.OptionOutputPort?.ToDto(), diff --git a/ChatbotBuilderApi.Presentation/Graphs/Nodes/Interaction/InteractionNodeModel.cs b/ChatbotBuilderApi.Presentation/Graphs/Nodes/Interaction/InteractionNodeModel.cs index 41ea380..c9421ba 100644 --- a/ChatbotBuilderApi.Presentation/Graphs/Nodes/Interaction/InteractionNodeModel.cs +++ b/ChatbotBuilderApi.Presentation/Graphs/Nodes/Interaction/InteractionNodeModel.cs @@ -18,6 +18,8 @@ namespace ChatbotBuilderApi.Presentation.Graphs.Nodes.Interaction; /// Generic information for the node. /// Visual information for the node. /// Add this port if you want to display a text to the user. +/// (Max of 20) Add an Image port for each image you want to display to the user. +/// They will all be displayed to the user at once. /// Add this port if you want the user to input a text. /// Which enum to use for the OptionOutputPort. /// Add this port if you want the user to select an option. @@ -26,6 +28,7 @@ public sealed record InteractionNodeModel( InfoMetaModel Info, VisualMetaModel Visual, InputPortModel? TextInputPort, + IReadOnlyList ImageInputPorts, OutputPortModel? TextOutputPort, int? OutputEnumId, OutputPortModel? OptionOutputPort,