Skip to content

Commit

Permalink
Merge branch 'main' into feat/resourcesubject-mappings-sync
Browse files Browse the repository at this point in the history
  • Loading branch information
elsand authored Sep 4, 2024
2 parents 31d110c + dce6e56 commit 17d6544
Show file tree
Hide file tree
Showing 37 changed files with 1,958 additions and 164 deletions.
4 changes: 4 additions & 0 deletions .azure/applications/graphql/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ param location string
@minLength(3)
param apimIp string

@description('CPU and memory resources for the container app')
param resources object?

@description('The name of the container app environment')
@minLength(3)
@secure()
Expand Down Expand Up @@ -83,6 +86,7 @@ module containerApp '../../modules/containerApp/main.bicep' = {
containerAppEnvId: containerAppEnvironment.id
apimIp: apimIp
tags: tags
resources: resources
}
}

Expand Down
4 changes: 4 additions & 0 deletions .azure/applications/web-api-eu/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ param location string
@minLength(3)
param apimIp string

@description('CPU and memory resources for the container app')
param resources object?

@description('The name of the container app environment')
@minLength(3)
@secure()
Expand Down Expand Up @@ -86,6 +89,7 @@ module containerApp '../../modules/containerApp/main.bicep' = {
containerAppEnvId: containerAppEnvironment.id
apimIp: apimIp
tags: tags
resources: resources
}
}

Expand Down
4 changes: 4 additions & 0 deletions .azure/applications/web-api-so/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ param location string
@minLength(3)
param apimIp string

@description('CPU and memory resources for the container app')
param resources object?

@description('The name of the container app environment')
@minLength(3)
@secure()
Expand Down Expand Up @@ -90,6 +93,7 @@ module containerApp '../../modules/containerApp/main.bicep' = {
containerAppEnvId: containerAppEnvironment.id
apimIp: apimIp
tags: tags
resources: resources
}
}

Expand Down
4 changes: 4 additions & 0 deletions .azure/modules/containerApp/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ param containerAppEnvId string
@description('The tags to be applied to the container app')
param tags object

@description('CPU and memory resources for the container app')
param resources object?

var probes = [
{
periodSeconds: 5
Expand Down Expand Up @@ -82,6 +85,7 @@ resource containerApp 'Microsoft.App/containerApps@2024-03-01' = {
image: image
env: envVariables
probes: probes
resources: resources
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/action-delete-deployments.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Delete Previous deployments
uses: actions/github-script@v6
uses: actions/github-script@v7
env:
GITHUB_SHA_HEAD: ${{ github.event.pull_request.head.sha }}
with:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public static class Constants
public static readonly Uri UnauthorizedUri = new("urn:dialogporten:unauthorized");
public const string CorrespondenceScope = "digdir:dialogporten.correspondence";
public const string ServiceOwnerAdminScope = "digdir:dialogporten.serviceprovider.admin";
public const string LegacyHtmlScope = "digdir:dialogporten.serviceprovider.legacyhtml";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Digdir.Domain.Dialogporten.Application.Features.V1.Common.Actors;

public static class ActorValidationErrorMessages
{
public const string ActorIdActorNameExclusiveOr = "If 'ActorType' is 'ServiceOwner', both 'ActorId' and 'ActorName' must be null. " +
"For any other value of 'ActorType', 'ActorId' or 'ActorName' must be set, but not both simultaneously.";
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using Digdir.Domain.Dialogporten.Application.Common.Authorization;
using Digdir.Domain.Dialogporten.Application.Common.Extensions;
using Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation;
using Digdir.Domain.Dialogporten.Application.Externals.Presentation;
using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations;
using Digdir.Domain.Dialogporten.Domain;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents;
Expand All @@ -14,19 +17,19 @@ internal interface IIgnoreOnAssemblyScan;

internal sealed class ContentValueDtoValidator : AbstractValidator<ContentValueDto>, IIgnoreOnAssemblyScan
{
private const string LegacyHtmlMediaType = "text/html";

public ContentValueDtoValidator(DialogTransmissionContentType contentType)
{
RuleFor(x => x.MediaType)
.NotEmpty()
.Must(value => value is not null && contentType.AllowedMediaTypes.Contains(value))
.WithMessage($"{{PropertyName}} '{{PropertyValue}}' is not allowed for content type {contentType.Name}. " +
$"Allowed media types are {string.Join(", ", contentType.AllowedMediaTypes.Select(x => $"'{x}'"))}");
RuleForEach(x => x.Value)
.ContainsValidHtml()
.When(x => x.MediaType is not null and MediaTypes.Html);

RuleForEach(x => x.Value)
.ContainsValidMarkdown()
.When(x => x.MediaType is not null and MediaTypes.Markdown);
.When(x => x.MediaType is MediaTypes.Markdown);
RuleForEach(x => x.Value)
.Must(x => Uri.TryCreate(x.Value, UriKind.Absolute, out var uri) && uri.Scheme == Uri.UriSchemeHttps)
.When(x => x.MediaType is not null && x.MediaType.StartsWith(MediaTypes.EmbeddablePrefix, StringComparison.InvariantCultureIgnoreCase))
Expand All @@ -36,19 +39,20 @@ public ContentValueDtoValidator(DialogTransmissionContentType contentType)
.SetValidator(_ => new LocalizationDtosValidator(contentType.MaxLength));
}

public ContentValueDtoValidator(DialogContentType contentType)
public ContentValueDtoValidator(DialogContentType contentType, IUser? user = null)
{
var allowedMediaTypes = GetAllowedMediaTypes(contentType, user);
RuleFor(x => x.MediaType)
.NotEmpty()
.Must(value => value is not null && contentType.AllowedMediaTypes.Contains(value))
.Must(value => value is not null && allowedMediaTypes.Contains(value))
.WithMessage($"{{PropertyName}} '{{PropertyValue}}' is not allowed for content type {contentType.Name}. " +
$"Allowed media types are {string.Join(", ", contentType.AllowedMediaTypes.Select(x => $"'{x}'"))}");
$"Allowed media types are {string.Join(", ", allowedMediaTypes.Select(x => $"'{x}'"))}");
RuleForEach(x => x.Value)
.ContainsValidHtml()
.When(x => x.MediaType is not null and MediaTypes.Html);
.When(x => x.MediaType.Equals(LegacyHtmlMediaType, StringComparison.OrdinalIgnoreCase));
RuleForEach(x => x.Value)
.ContainsValidMarkdown()
.When(x => x.MediaType is not null and MediaTypes.Markdown);
.When(x => x.MediaType is MediaTypes.Markdown);
RuleForEach(x => x.Value)
.Must(x => Uri.TryCreate(x.Value, UriKind.Absolute, out var uri) && uri.Scheme == Uri.UriSchemeHttps)
.When(x => x.MediaType is not null && x.MediaType.StartsWith(MediaTypes.EmbeddablePrefix, StringComparison.InvariantCultureIgnoreCase))
Expand All @@ -57,4 +61,18 @@ public ContentValueDtoValidator(DialogContentType contentType)
.NotEmpty()
.SetValidator(_ => new LocalizationDtosValidator(contentType.MaxLength));
}

private static string[] GetAllowedMediaTypes(DialogContentType contentType, IUser? user)
{
if (user == null)
{
return contentType.AllowedMediaTypes;
}

var allowHtmlSupport = user.GetPrincipal().HasScope(Constants.LegacyHtmlScope);

return allowHtmlSupport
? contentType.AllowedMediaTypes.Append(LegacyHtmlMediaType).ToArray()
: contentType.AllowedMediaTypes;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using System.Reflection;
using Digdir.Domain.Dialogporten.Application.Common.Extensions.Enumerables;
using Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation;
using Digdir.Domain.Dialogporten.Application.Features.V1.Common;
using Digdir.Domain.Dialogporten.Application.Externals.Presentation;
using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Actors;
using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content;
using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations;
using Digdir.Domain.Dialogporten.Domain.Actors;
Expand Down Expand Up @@ -168,7 +169,7 @@ internal sealed class CreateDialogContentDtoValidator : AbstractValidator<Create
})
.ToDictionary(x => x.Property.Name, StringComparer.InvariantCultureIgnoreCase);

public CreateDialogContentDtoValidator()
public CreateDialogContentDtoValidator(IUser? user)
{
foreach (var (propertyName, propMetadata) in SourcePropertyMetaDataByName)
{
Expand All @@ -179,12 +180,12 @@ public CreateDialogContentDtoValidator()
.NotNull()
.WithMessage($"{propertyName} must not be empty.")
.SetValidator(new ContentValueDtoValidator(
DialogContentType.Parse(propertyName))!);
DialogContentType.Parse(propertyName), user)!);
break;
case NullabilityState.Nullable:
RuleFor(x => propMetadata.Property.GetValue(x) as ContentValueDto)
.SetValidator(new ContentValueDtoValidator(
DialogContentType.Parse(propertyName))!)
DialogContentType.Parse(propertyName), user)!)
.When(x => propMetadata.Property.GetValue(x) is not null);
break;
case NullabilityState.Unknown:
Expand Down Expand Up @@ -392,14 +393,11 @@ public CreateDialogDialogTransmissionActorDtoValidator()
RuleFor(x => x.ActorType)
.IsInEnum();

RuleFor(x => x.ActorId)
.Must((dto, value) => value is null || dto.ActorName is null)
.WithMessage("Only one of 'ActorId' or 'ActorName' can be set, but not both.");

RuleFor(x => x.ActorType)
.Must((dto, value) => (value == ActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) ||
(value != ActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null)))
.WithMessage("If 'ActorType' is 'ServiceOwner', both 'ActorId' and 'ActorName' must be null. Otherwise, one of them must be set.");
RuleFor(x => x)
.Must(dto => (dto.ActorId is null || dto.ActorName is null) &&
((dto.ActorType == ActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) ||
(dto.ActorType != ActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null))))
.WithMessage(ActorValidationErrorMessages.ActorIdActorNameExclusiveOr);

RuleFor(x => x.ActorId!)
.IsValidPartyIdentifier()
Expand All @@ -414,14 +412,11 @@ public CreateDialogDialogActivityActorDtoValidator()
RuleFor(x => x.ActorType)
.IsInEnum();

RuleFor(x => x.ActorId)
.Must((dto, value) => value is null || dto.ActorName is null)
.WithMessage("Only one of 'ActorId' or 'ActorName' can be set, but not both.");

RuleFor(x => x.ActorType)
.Must((dto, value) => (value == ActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) ||
(value != ActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null)))
.WithMessage("If 'ActorType' is 'ServiceOwner', both 'ActorId' and 'ActorName' must be null. Otherwise, one of them must be set.");
RuleFor(x => x)
.Must(dto => (dto.ActorId is null || dto.ActorName is null) &&
((dto.ActorType == ActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) ||
(dto.ActorType != ActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null))))
.WithMessage(ActorValidationErrorMessages.ActorIdActorNameExclusiveOr);

RuleFor(x => x.ActorId!)
.IsValidPartyIdentifier()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using System.Reflection;
using Digdir.Domain.Dialogporten.Application.Common.Extensions.Enumerables;
using Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation;
using Digdir.Domain.Dialogporten.Application.Features.V1.Common;
using Digdir.Domain.Dialogporten.Application.Externals.Presentation;
using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Actors;
using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content;
using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations;
using Digdir.Domain.Dialogporten.Domain.Actors;
Expand Down Expand Up @@ -141,14 +142,11 @@ public UpdateDialogDialogTransmissionActorDtoValidator()
RuleFor(x => x.ActorType)
.IsInEnum();

RuleFor(x => x.ActorId)
.Must((dto, value) => value is null || dto.ActorName is null)
.WithMessage("Only one of 'ActorId' or 'ActorName' can be set, but not both.");

RuleFor(x => x.ActorType)
.Must((dto, value) => (value == ActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) ||
(value != ActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null)))
.WithMessage("If 'ActorType' is 'ServiceOwner', both 'ActorId' and 'ActorName' must be null. Otherwise, one of them must be set.");
RuleFor(x => x)
.Must(dto => (dto.ActorId is null || dto.ActorName is null) &&
((dto.ActorType == ActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) ||
(dto.ActorType != ActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null))))
.WithMessage(ActorValidationErrorMessages.ActorIdActorNameExclusiveOr);

RuleFor(x => x.ActorId!)
.IsValidPartyIdentifier()
Expand Down Expand Up @@ -222,7 +220,7 @@ internal sealed class UpdateDialogContentDtoValidator : AbstractValidator<Update
})
.ToDictionary(x => x.Property.Name, StringComparer.InvariantCultureIgnoreCase);

public UpdateDialogContentDtoValidator()
public UpdateDialogContentDtoValidator(IUser? user)
{
foreach (var (propertyName, propMetadata) in SourcePropertyMetaDataByName)
{
Expand All @@ -233,12 +231,12 @@ public UpdateDialogContentDtoValidator()
.NotNull()
.WithMessage($"{propertyName} must not be empty.")
.SetValidator(
new ContentValueDtoValidator(DialogContentType.Parse(propertyName))!);
new ContentValueDtoValidator(DialogContentType.Parse(propertyName), user)!);
break;
case NullabilityState.Nullable:
RuleFor(x => propMetadata.Property.GetValue(x) as ContentValueDto)
.SetValidator(
new ContentValueDtoValidator(DialogContentType.Parse(propertyName))!)
new ContentValueDtoValidator(DialogContentType.Parse(propertyName), user)!)
.When(x => propMetadata.Property.GetValue(x) is not null);
break;
case NullabilityState.Unknown:
Expand Down Expand Up @@ -408,14 +406,11 @@ public UpdateDialogDialogActivityActorDtoValidator()
RuleFor(x => x.ActorType)
.IsInEnum();

RuleFor(x => x.ActorId)
.Must((dto, value) => value is null || dto.ActorName is null)
.WithMessage("Only one of 'ActorId' or 'ActorName' can be set, but not both.");

RuleFor(x => x.ActorType)
.Must((dto, value) => (value == ActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) ||
(value != ActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null)))
.WithMessage("If 'ActorType' is 'ServiceOwner', both 'ActorId' and 'ActorName' must be null. Otherwise, one of them must be set.");
RuleFor(x => x)
.Must(dto => (dto.ActorId is null || dto.ActorName is null) &&
((dto.ActorType == ActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) ||
(dto.ActorType != ActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null))))
.WithMessage(ActorValidationErrorMessages.ActorIdActorNameExclusiveOr);

RuleFor(x => x.ActorId!)
.IsValidPartyIdentifier()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.12.0"/>
<PackageReference Include="MassTransit" Version="8.2.4"/>
<PackageReference Include="MassTransit" Version="8.2.5"/>
<PackageReference Include="Npgsql" Version="8.0.3"/>
<PackageReference Include="Microsoft.Azure.AppConfiguration.AspNetCore" Version="7.3.0"/>
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public enum Values
Required = false,
MaxLength = 1023,
OutputInList = false,
AllowedMediaTypes = [MediaTypes.Html, MediaTypes.PlainText, MediaTypes.Markdown]
AllowedMediaTypes = [MediaTypes.PlainText, MediaTypes.Markdown]
},
Values.ExtendedStatus => new(id)
{
Expand Down
1 change: 0 additions & 1 deletion src/Digdir.Domain.Dialogporten.Domain/MediaTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@ public static class MediaTypes

public const string Markdown = "text/markdown";
public const string PlainText = "text/plain";
public const string Html = "text/html";
}
1 change: 0 additions & 1 deletion src/Digdir.Domain.Dialogporten.GraphQL/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ static void BuildAndRun(string[] args)

builder.Host.UseSerilog((context, services, configuration) => configuration
.MinimumLevel.Warning()
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", Serilog.Events.LogEventLevel.Fatal)
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Infrastructure": {
"Redis": {
"Enabled": true,
Expand Down
14 changes: 10 additions & 4 deletions src/Digdir.Domain.Dialogporten.GraphQL/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "None",
"System.Net.Http.HttpClient": "Information"
"Default": "Warning"
}
},
"Serilog": {
"MinimumLevel": {
"Default": "Warning",
"Override": {
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "Fatal"
}
}
},
"Infrastructure": {
Expand Down
Loading

0 comments on commit 17d6544

Please sign in to comment.