diff --git a/docs/schema/V1/swagger.verified.json b/docs/schema/V1/swagger.verified.json
index b382f4cf1..5493bb968 100644
--- a/docs/schema/V1/swagger.verified.json
+++ b/docs/schema/V1/swagger.verified.json
@@ -70,7 +70,6 @@
"properties": {
"mediaType": {
"description": "Media type of the content (plaintext, Markdown). Can also indicate that the content is embeddable.",
- "example": "text/plain\ntext/markdown\napplication/vnd.dialogporten.frontchannelembed",
"type": "string"
},
"value": {
@@ -297,7 +296,7 @@
"additionalProperties": false,
"properties": {
"additionalInfo": {
- "description": "Additional information about the dialog, this may contain Markdown.",
+ "description": "Additional information about the dialog.\nSupported media types: text/plain, text/markdown",
"nullable": true,
"oneOf": [
{
@@ -306,7 +305,7 @@
]
},
"extendedStatus": {
- "description": "Used as the human-readable label used to describe the \u0022ExtendedStatus\u0022 field. Must be text/plain.",
+ "description": "Used as the human-readable label used to describe the \u0022ExtendedStatus\u0022 field.\nSupported media types: text/plain",
"nullable": true,
"oneOf": [
{
@@ -315,7 +314,7 @@
]
},
"mainContentReference": {
- "description": "Front-channel embedded content. Used to dynamically embed content in the frontend from an external URL.",
+ "description": "Front-channel embedded content. Used to dynamically embed content in the frontend from an external URL.\nSupported media types: application/vnd.dialogporten.frontchannelembed\u002Bjson;type=markdown",
"nullable": true,
"oneOf": [
{
@@ -324,7 +323,7 @@
]
},
"senderName": {
- "description": "Overridden sender name. If not supplied, assume \u0022org\u0022 as the sender name. Must be text/plain if supplied.",
+ "description": "Overridden sender name. If not supplied, assume \u0022org\u0022 as the sender name. Must be text/plain if supplied.\nSupported media types: text/plain",
"nullable": true,
"oneOf": [
{
@@ -333,7 +332,7 @@
]
},
"summary": {
- "description": "A short summary of the dialog and its current state. Must be text/plain.",
+ "description": "A short summary of the dialog and its current state.\nSupported media types: text/plain",
"oneOf": [
{
"$ref": "#/components/schemas/ContentValueDto"
@@ -341,7 +340,7 @@
]
},
"title": {
- "description": "The title of the dialog. Must be text/plain.",
+ "description": "The title of the dialog.\nSupported media types: text/plain",
"oneOf": [
{
"$ref": "#/components/schemas/ContentValueDto"
@@ -6474,4 +6473,4 @@
"url": "https://altinn-dev-api.azure-api.net/dialogporten"
}
]
-}
\ No newline at end of file
+}
diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/ContentValueDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/ContentValueDto.cs
index b322d74cf..2c6a9f77a 100644
--- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/ContentValueDto.cs
+++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/ContentValueDto.cs
@@ -13,10 +13,5 @@ public sealed class ContentValueDto
///
/// Media type of the content (plaintext, Markdown). Can also indicate that the content is embeddable.
///
- ///
- /// text/plain
- /// text/markdown
- /// application/vnd.dialogporten.frontchannelembed
- ///
public string MediaType { get; set; } = MediaTypes.PlainText;
}
diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/ContentValueDtoValidator.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/ContentValueDtoValidator.cs
index e3f77d40c..cffa0a34b 100644
--- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/ContentValueDtoValidator.cs
+++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/ContentValueDtoValidator.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
using Digdir.Domain.Dialogporten.Application.Common.Authorization;
using Digdir.Domain.Dialogporten.Application.Common.Extensions;
using Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation;
@@ -17,7 +18,6 @@ internal interface IIgnoreOnAssemblyScan;
internal sealed class ContentValueDtoValidator : AbstractValidator, IIgnoreOnAssemblyScan
{
- public const string LegacyHtmlMediaType = "text/html";
public ContentValueDtoValidator(DialogTransmissionContentType contentType)
{
@@ -68,22 +68,15 @@ x.MediaType is not null
.SetValidator(_ => new LocalizationDtosValidator(contentType.MaxLength));
}
+ [SuppressMessage("Style", "IDE0072:Add missing cases")]
private static string[] GetAllowedMediaTypes(DialogContentType contentType, IUser? user)
- {
- if (user == null)
- {
- return contentType.AllowedMediaTypes;
- }
-
- if (contentType.Id != DialogContentType.Values.AdditionalInfo)
+ => contentType.Id switch
{
- return contentType.AllowedMediaTypes;
- }
-
- var allowHtmlSupport = user.GetPrincipal().HasScope(Constants.LegacyHtmlScope);
-
- return allowHtmlSupport
- ? contentType.AllowedMediaTypes.Append(LegacyHtmlMediaType).ToArray()
- : contentType.AllowedMediaTypes;
- }
+ DialogContentType.Values.AdditionalInfo when UserHasLegacyHtmlScope(user)
+ => contentType.AllowedMediaTypes.Append(MediaTypes.LegacyHtml).ToArray(),
+ DialogContentType.Values.MainContentReference when UserHasLegacyHtmlScope(user)
+ => contentType.AllowedMediaTypes.Append(MediaTypes.LegacyEmbeddableHtml).ToArray(),
+ _ => contentType.AllowedMediaTypes
+ };
+ private static bool UserHasLegacyHtmlScope(IUser? user) => user is not null && user.GetPrincipal().HasScope(Constants.LegacyHtmlScope);
}
diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs
index 3d706fe9f..d59f59cda 100644
--- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs
+++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs
@@ -209,32 +209,38 @@ public sealed class CreateDialogDialogTransmissionDto
public sealed class CreateDialogContentDto
{
///
- /// The title of the dialog. Must be text/plain.
+ /// The title of the dialog.
+ /// Supported media types: text/plain
///
public ContentValueDto Title { get; set; } = null!;
///
- /// A short summary of the dialog and its current state. Must be text/plain.
+ /// A short summary of the dialog and its current state.
+ /// Supported media types: text/plain
///
public ContentValueDto Summary { get; set; } = null!;
///
/// Overridden sender name. If not supplied, assume "org" as the sender name. Must be text/plain if supplied.
+ /// Supported media types: text/plain
///
public ContentValueDto? SenderName { get; set; }
///
- /// Additional information about the dialog, this may contain Markdown.
+ /// Additional information about the dialog.
+ /// Supported media types: text/plain, text/markdown
///
public ContentValueDto? AdditionalInfo { get; set; }
///
- /// Used as the human-readable label used to describe the "ExtendedStatus" field. Must be text/plain.
+ /// Used as the human-readable label used to describe the "ExtendedStatus" field.
+ /// Supported media types: text/plain
///
public ContentValueDto? ExtendedStatus { get; set; }
///
/// Front-channel embedded content. Used to dynamically embed content in the frontend from an external URL.
+ /// Supported media types: application/vnd.dialogporten.frontchannelembed+json;type=markdown
///
public ContentValueDto? MainContentReference { get; set; }
}
diff --git a/src/Digdir.Domain.Dialogporten.Domain/MediaTypes.cs b/src/Digdir.Domain.Dialogporten.Domain/MediaTypes.cs
index e89f528c5..6e2c53f9e 100644
--- a/src/Digdir.Domain.Dialogporten.Domain/MediaTypes.cs
+++ b/src/Digdir.Domain.Dialogporten.Domain/MediaTypes.cs
@@ -4,7 +4,9 @@ public static class MediaTypes
{
public const string EmbeddablePrefix = "application/vnd.dialogporten.frontchannelembed";
public const string EmbeddableMarkdown = $"{EmbeddablePrefix}+json;type=markdown";
+ public const string LegacyEmbeddableHtml = $"{EmbeddablePrefix}+json;type=html";
+ public const string LegacyHtml = "text/html";
public const string Markdown = "text/markdown";
public const string PlainText = "text/plain";
}
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs
index 3b5fc68ac..54f08226b 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/CreateDialogTests.cs
@@ -4,6 +4,7 @@
using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations;
using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get;
using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common;
+using Digdir.Domain.Dialogporten.Domain;
using Digdir.Tool.Dialogporten.GenerateFakeData;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
@@ -276,7 +277,7 @@ public async Task Cannot_Create_Transmission_With_Empty_Content_Localization_Val
.Be(2);
}
- private const string LegacyHtmlMediaType = ContentValueDtoValidator.LegacyHtmlMediaType;
+ private const string LegacyHtmlMediaType = MediaTypes.LegacyHtml;
private static ContentValueDto CreateHtmlContentValueDto() => new()
{
@@ -350,4 +351,62 @@ public async Task Cannot_Create_Title_Content_With_Html_MediaType_With_Correct_S
.Should()
.Be(1);
}
+
+ [Fact]
+ public async Task Cannot_Create_Title_Content_With_Embeddable_Html_MediaType_With_Correct_Scope()
+ {
+ // Arrange
+ var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog();
+ createDialogCommand.Content.Title = new ContentValueDto
+ {
+ MediaType = MediaTypes.LegacyEmbeddableHtml,
+ Value = [new LocalizationDto { LanguageCode = "en", Value = "https://external.html" }]
+ };
+
+ var userWithLegacyScope = new IntegrationTestUser([new("scope", Constants.LegacyHtmlScope)]);
+ Application.ConfigureServiceCollection(services =>
+ {
+ services.RemoveAll();
+ services.AddSingleton(userWithLegacyScope);
+ });
+
+ // Act
+ var response = await Application.Send(createDialogCommand);
+
+ // Assert
+ response.TryPickT2(out var validationError, out _).Should().BeTrue();
+ validationError.Should().NotBeNull();
+ validationError.Errors
+ .Count(e => e.AttemptedValue.Equals(MediaTypes.LegacyEmbeddableHtml))
+ .Should()
+ .Be(1);
+ }
+
+ [Fact]
+ public async Task Can_Create_MainContentRef_Content_With_Embeddable_Html_MediaType_With_Correct_Scope()
+ {
+ // Arrange
+ var expectedDialogId = GenerateBigEndianUuidV7();
+ var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(id: expectedDialogId);
+ createDialogCommand.Content.MainContentReference = new ContentValueDto
+ {
+ MediaType = MediaTypes.LegacyEmbeddableHtml,
+ Value = [new LocalizationDto { LanguageCode = "en", Value = "https://external.html" }]
+ };
+
+ var userWithLegacyScope = new IntegrationTestUser([new("scope", Constants.LegacyHtmlScope)]);
+ Application.ConfigureServiceCollection(services =>
+ {
+ services.RemoveAll();
+ services.AddSingleton(userWithLegacyScope);
+ });
+
+ // Act
+ var response = await Application.Send(createDialogCommand);
+
+ // Assert
+ response.TryPickT0(out var success, out _).Should().BeTrue();
+ success.Should().NotBeNull();
+ success.Value.Should().Be(expectedDialogId);
+ }
}