diff --git a/.azure/applications/graphql/main.bicep b/.azure/applications/graphql/main.bicep
index 2d63bebe9..f655664ac 100644
--- a/.azure/applications/graphql/main.bicep
+++ b/.azure/applications/graphql/main.bicep
@@ -61,6 +61,12 @@ resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2024-03-01'
name: containerAppEnvironmentName
}
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: '${namePrefix}-graphql-identity'
+ location: location
+ tags: tags
+}
+
var containerAppEnvVars = [
{
name: 'ASPNETCORE_ENVIRONMENT'
@@ -74,6 +80,10 @@ var containerAppEnvVars = [
name: 'AZURE_APPCONFIG_URI'
value: appConfiguration.properties.endpoint
}
+ {
+ name: 'AZURE_CLIENT_ID'
+ value: managedIdentity.properties.clientId
+ }
]
var port = 8080
@@ -157,6 +167,7 @@ module containerApp '../../modules/containerApp/main.bicep' = {
probes: probes
port: port
scale: scale
+ userAssignedIdentityId: managedIdentity.id
}
}
@@ -164,7 +175,7 @@ module keyVaultReaderAccessPolicy '../../modules/keyvault/addReaderRoles.bicep'
name: 'keyVaultReaderAccessPolicy-${containerAppName}'
params: {
keyvaultName: environmentKeyVaultResource.name
- principalIds: [containerApp.outputs.identityPrincipalId]
+ principalIds: [managedIdentity.properties.principalId]
}
}
@@ -172,7 +183,7 @@ module appConfigReaderAccessPolicy '../../modules/appConfiguration/addReaderRole
name: 'appConfigReaderAccessPolicy-${containerAppName}'
params: {
appConfigurationName: appConfigurationName
- principalIds: [containerApp.outputs.identityPrincipalId]
+ principalIds: [managedIdentity.properties.principalId]
}
}
diff --git a/.azure/applications/sync-resource-policy-information-job/main.bicep b/.azure/applications/sync-resource-policy-information-job/main.bicep
index 3c1b5f479..740691b41 100644
--- a/.azure/applications/sync-resource-policy-information-job/main.bicep
+++ b/.azure/applications/sync-resource-policy-information-job/main.bicep
@@ -46,6 +46,12 @@ resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2024-03-01'
name: containerAppEnvironmentName
}
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: '${namePrefix}-sync-rp-info-identity'
+ location: location
+ tags: tags
+}
+
var containerAppEnvVars = [
{
name: 'Infrastructure__DialogDbConnectionString'
@@ -63,6 +69,10 @@ var containerAppEnvVars = [
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: appInsightConnectionString
}
+ {
+ name: 'AZURE_CLIENT_ID'
+ value: managedIdentity.properties.clientId
+ }
]
// Base URL for accessing secrets in the Key Vault
@@ -94,6 +104,7 @@ module migrationJob '../../modules/containerAppJob/main.bicep' = {
tags: tags
cronExpression: jobSchedule
args: 'sync-resource-policy-information'
+ userAssignedIdentityId: managedIdentity.id
}
}
diff --git a/.azure/applications/sync-subject-resource-mappings-job/main.bicep b/.azure/applications/sync-subject-resource-mappings-job/main.bicep
index a2d6203f5..3279e4d7e 100644
--- a/.azure/applications/sync-subject-resource-mappings-job/main.bicep
+++ b/.azure/applications/sync-subject-resource-mappings-job/main.bicep
@@ -46,6 +46,12 @@ resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2024-03-01'
name: containerAppEnvironmentName
}
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: '${namePrefix}-sync-sr-mappings-identity'
+ location: location
+ tags: tags
+}
+
var containerAppEnvVars = [
{
name: 'Infrastructure__DialogDbConnectionString'
@@ -63,6 +69,10 @@ var containerAppEnvVars = [
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: appInsightConnectionString
}
+ {
+ name: 'AZURE_CLIENT_ID'
+ value: managedIdentity.properties.clientId
+ }
]
// Base URL for accessing secrets in the Key Vault
@@ -94,6 +104,7 @@ module migrationJob '../../modules/containerAppJob/main.bicep' = {
tags: tags
cronExpression: jobSchedule
args: 'sync-subject-resource-mappings'
+ userAssignedIdentityId: managedIdentity.id
}
}
@@ -101,9 +112,9 @@ module keyVaultReaderAccessPolicy '../../modules/keyvault/addReaderRoles.bicep'
name: 'keyVaultReaderAccessPolicy-${name}'
params: {
keyvaultName: environmentKeyVaultName
- principalIds: [migrationJob.outputs.identityPrincipalId]
+ principalIds: [managedIdentity.properties.principalId]
}
}
-output identityPrincipalId string = migrationJob.outputs.identityPrincipalId
+output identityPrincipalId string = managedIdentity.properties.principalId
output name string = migrationJob.outputs.name
diff --git a/.azure/applications/web-api-eu/main.bicep b/.azure/applications/web-api-eu/main.bicep
index 9f032afbf..b4cbd6592 100644
--- a/.azure/applications/web-api-eu/main.bicep
+++ b/.azure/applications/web-api-eu/main.bicep
@@ -60,6 +60,12 @@ resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2024-03-01'
name: containerAppEnvironmentName
}
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: '${namePrefix}-webapi-eu-identity'
+ location: location
+ tags: tags
+}
+
var containerAppEnvVars = [
{
name: 'ASPNETCORE_ENVIRONMENT'
@@ -77,6 +83,10 @@ var containerAppEnvVars = [
name: 'ASPNETCORE_URLS'
value: 'http://+:8080'
}
+ {
+ name: 'AZURE_CLIENT_ID'
+ value: managedIdentity.properties.clientId
+ }
]
@description('The scaling configuration for the container app')
@@ -159,6 +169,7 @@ module containerApp '../../modules/containerApp/main.bicep' = {
probes: probes
revisionSuffix: revisionSuffix
scale: scale
+ userAssignedIdentityId: managedIdentity.id
}
}
@@ -166,7 +177,7 @@ module keyVaultReaderAccessPolicy '../../modules/keyvault/addReaderRoles.bicep'
name: 'keyVaultReaderAccessPolicy-${containerAppName}'
params: {
keyvaultName: environmentKeyVaultResource.name
- principalIds: [containerApp.outputs.identityPrincipalId]
+ principalIds: [managedIdentity.properties.principalId]
}
}
@@ -174,7 +185,7 @@ module appConfigReaderAccessPolicy '../../modules/appConfiguration/addReaderRole
name: 'appConfigReaderAccessPolicy-${containerAppName}'
params: {
appConfigurationName: appConfigurationName
- principalIds: [containerApp.outputs.identityPrincipalId]
+ principalIds: [managedIdentity.properties.principalId]
}
}
diff --git a/.azure/applications/web-api-migration-job/main.bicep b/.azure/applications/web-api-migration-job/main.bicep
index 5df04957b..ca5857455 100644
--- a/.azure/applications/web-api-migration-job/main.bicep
+++ b/.azure/applications/web-api-migration-job/main.bicep
@@ -34,11 +34,21 @@ resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2024-03-01'
name: containerAppEnvironmentName
}
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: '${namePrefix}-migration-job-identity'
+ location: location
+ tags: tags
+}
+
var containerAppEnvVars = [
{
name: 'Infrastructure__DialogDbConnectionString'
secretRef: 'dbconnectionstring'
}
+ {
+ name: 'AZURE_CLIENT_ID'
+ value: managedIdentity.properties.clientId
+ }
]
// https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions-deployment#example-1
@@ -62,6 +72,7 @@ module migrationJob '../../modules/containerAppJob/main.bicep' = {
environmentVariables: containerAppEnvVars
secrets: secrets
tags: tags
+ userAssignedIdentityId: managedIdentity.id
}
}
@@ -69,9 +80,9 @@ module keyVaultReaderAccessPolicy '../../modules/keyvault/addReaderRoles.bicep'
name: 'keyVaultReaderAccessPolicy-${name}'
params: {
keyvaultName: environmentKeyVaultName
- principalIds: [migrationJob.outputs.identityPrincipalId]
+ principalIds: [managedIdentity.properties.principalId]
}
}
-output identityPrincipalId string = migrationJob.outputs.identityPrincipalId
+output identityPrincipalId string = managedIdentity.properties.principalId
output name string = migrationJob.outputs.name
diff --git a/.azure/applications/web-api-so/main.bicep b/.azure/applications/web-api-so/main.bicep
index d9085b77d..eb1feccc0 100644
--- a/.azure/applications/web-api-so/main.bicep
+++ b/.azure/applications/web-api-so/main.bicep
@@ -88,6 +88,12 @@ resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2024-03-01'
name: containerAppEnvironmentName
}
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
+ name: '${namePrefix}-webapi-so-identity'
+ location: location
+ tags: tags
+}
+
var containerAppEnvVars = [
{
name: 'ASPNETCORE_ENVIRONMENT'
@@ -105,6 +111,10 @@ var containerAppEnvVars = [
name: 'ASPNETCORE_URLS'
value: 'http://+:8080'
}
+ {
+ name: 'AZURE_CLIENT_ID'
+ value: managedIdentity.properties.clientId
+ }
]
resource environmentKeyVaultResource 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
@@ -160,6 +170,7 @@ module containerApp '../../modules/containerApp/main.bicep' = {
port: port
revisionSuffix: revisionSuffix
scale: scale
+ userAssignedIdentityId: managedIdentity.id
}
}
@@ -167,7 +178,7 @@ module keyVaultReaderAccessPolicy '../../modules/keyvault/addReaderRoles.bicep'
name: 'keyVaultReaderAccessPolicy-${containerAppName}'
params: {
keyvaultName: environmentKeyVaultResource.name
- principalIds: [containerApp.outputs.identityPrincipalId]
+ principalIds: [managedIdentity.properties.principalId]
}
}
@@ -175,7 +186,7 @@ module appConfigReaderAccessPolicy '../../modules/appConfiguration/addReaderRole
name: 'appConfigReaderAccessPolicy-${containerAppName}'
params: {
appConfigurationName: appConfigurationName
- principalIds: [containerApp.outputs.identityPrincipalId]
+ principalIds: [managedIdentity.properties.principalId]
}
}
diff --git a/.azure/infrastructure/main.bicep b/.azure/infrastructure/main.bicep
index 11de7911f..f304318e1 100644
--- a/.azure/infrastructure/main.bicep
+++ b/.azure/infrastructure/main.bicep
@@ -51,9 +51,6 @@ param appConfigurationSku AppConfigurationSku
import { Sku as AppInsightsSku } from '../modules/applicationInsights/create.bicep'
param appInsightsSku AppInsightsSku
-import { Sku as SlackNotifierSku } from '../modules/functionApp/slackNotifier.bicep'
-param slackNotifierSku SlackNotifierSku
-
import { Sku as PostgresSku } from '../modules/postgreSql/create.bicep'
import { StorageConfiguration as PostgresStorageConfig } from '../modules/postgreSql/create.bicep'
@@ -127,16 +124,6 @@ module appInsights '../modules/applicationInsights/create.bicep' = {
}
}
-module monitorWorkspace '../modules/monitor-workspace/main.bicep' = {
- scope: resourceGroup
- name: 'monitorWorkspace'
- params: {
- namePrefix: namePrefix
- location: location
- tags: tags
- }
-}
-
module apimAvailabilityTest '../modules/applicationInsights/availabilityTest.bicep' = {
scope: resourceGroup
name: 'apimAvailabilityTest'
@@ -272,19 +259,6 @@ module copyEnvironmentSecrets '../modules/keyvault/copySecrets.bicep' = {
}
}
-module slackNotifier '../modules/functionApp/slackNotifier.bicep' = {
- name: 'slackNotifier'
- scope: resourceGroup
- params: {
- location: location
- keyVaultName: environmentKeyVault.outputs.name
- namePrefix: namePrefix
- applicationInsightsName: appInsights.outputs.appInsightsName
- sku: slackNotifierSku
- tags: tags
- }
-}
-
module containerAppIdentity '../modules/managedIdentity/main.bicep' = {
scope: resourceGroup
name: 'containerAppIdentity'
@@ -303,31 +277,12 @@ module containerAppEnv '../modules/containerAppEnv/main.bicep' = {
location: location
appInsightWorkspaceName: appInsights.outputs.appInsightsWorkspaceName
appInsightsConnectionString: appInsights.outputs.connectionString
- monitorMetricsIngestionEndpoint: monitorWorkspace.outputs.containerAppEnvironmentMetricsIngestionEndpoint
userAssignedIdentityId: containerAppIdentity.outputs.managedIdentityId
subnetId: vnet.outputs.containerAppEnvironmentSubnetId
tags: tags
}
}
-module monitorMetricsPublisherRoles '../modules/monitor-workspace/addMetricsPublisherRoles.bicep' = {
- scope: resourceGroup
- name: 'monitorMetricsPublisherRoles'
- params: {
- monitorWorkspaceName: monitorWorkspace.outputs.monitorWorkspaceName
- principalIds: [containerAppIdentity.outputs.managedIdentityPrincipalId]
- }
-}
-
-module appInsightsReaderAccessPolicy '../modules/applicationInsights/addReaderRoles.bicep' = {
- scope: resourceGroup
- name: 'appInsightsReaderAccessPolicy'
- params: {
- appInsightsName: appInsights.outputs.appInsightsName
- principalIds: [slackNotifier.outputs.functionAppPrincipalId]
- }
-}
-
module postgresConnectionStringAppConfig '../modules/appConfiguration/upsertKeyValue.bicep' = {
scope: resourceGroup
name: 'AppConfig_Add_DialogDbConnectionString'
@@ -352,15 +307,6 @@ module redisConnectionStringAppConfig '../modules/appConfiguration/upsertKeyValu
}
}
-module keyVaultReaderAccessPolicy '../modules/keyvault/addReaderRoles.bicep' = {
- scope: resourceGroup
- name: 'keyVaultReaderAccessPolicyFunctions'
- params: {
- keyvaultName: environmentKeyVault.outputs.name
- principalIds: [slackNotifier.outputs.functionAppPrincipalId]
- }
-}
-
output resourceGroupName string = resourceGroup.name
output containerAppEnvId string = containerAppEnv.outputs.containerAppEnvId
output environmentKeyVaultName string = environmentKeyVault.outputs.name
diff --git a/.azure/infrastructure/prod.bicepparam b/.azure/infrastructure/prod.bicepparam
index 2b0a624e7..14699dd41 100644
--- a/.azure/infrastructure/prod.bicepparam
+++ b/.azure/infrastructure/prod.bicepparam
@@ -24,11 +24,6 @@ param appConfigurationSku = {
param appInsightsSku = {
name: 'PerGB2018'
}
-param slackNotifierSku = {
- storageAccountName: 'Standard_LRS'
- applicationServicePlanName: 'Y1'
- applicationServicePlanTier: 'Dynamic'
-}
param postgresConfiguration = {
sku: {
name: 'Standard_D8ads_v5'
diff --git a/.azure/infrastructure/staging.bicepparam b/.azure/infrastructure/staging.bicepparam
index c434a5469..e3230883c 100644
--- a/.azure/infrastructure/staging.bicepparam
+++ b/.azure/infrastructure/staging.bicepparam
@@ -24,11 +24,6 @@ param appConfigurationSku = {
param appInsightsSku = {
name: 'PerGB2018'
}
-param slackNotifierSku = {
- storageAccountName: 'Standard_LRS'
- applicationServicePlanName: 'Y1'
- applicationServicePlanTier: 'Dynamic'
-}
param postgresConfiguration = {
sku: {
name: 'Standard_D4ads_v5'
diff --git a/.azure/infrastructure/test.bicepparam b/.azure/infrastructure/test.bicepparam
index e4ca02594..9f2fe769f 100644
--- a/.azure/infrastructure/test.bicepparam
+++ b/.azure/infrastructure/test.bicepparam
@@ -24,11 +24,6 @@ param appConfigurationSku = {
param appInsightsSku = {
name: 'PerGB2018'
}
-param slackNotifierSku = {
- storageAccountName: 'Standard_LRS'
- applicationServicePlanName: 'Y1'
- applicationServicePlanTier: 'Dynamic'
-}
param postgresConfiguration = {
sku: {
name: 'Standard_B2s'
diff --git a/.azure/infrastructure/yt01.bicepparam b/.azure/infrastructure/yt01.bicepparam
index d37a727da..667c78ced 100644
--- a/.azure/infrastructure/yt01.bicepparam
+++ b/.azure/infrastructure/yt01.bicepparam
@@ -24,18 +24,13 @@ param appConfigurationSku = {
param appInsightsSku = {
name: 'PerGB2018'
}
-param slackNotifierSku = {
- storageAccountName: 'Standard_LRS'
- applicationServicePlanName: 'Y1'
- applicationServicePlanTier: 'Dynamic'
-}
param postgresConfiguration = {
sku: {
name: 'Standard_D8ads_v5'
tier: 'GeneralPurpose'
}
storage: {
- storageSizeGB: 256
+ storageSizeGB: 2048
autoGrow: 'Enabled'
type: 'Premium_LRS'
}
diff --git a/.azure/modules/containerApp/main.bicep b/.azure/modules/containerApp/main.bicep
index 9259e047e..c2f3c6979 100644
--- a/.azure/modules/containerApp/main.bicep
+++ b/.azure/modules/containerApp/main.bicep
@@ -58,9 +58,9 @@ param scale Scale = {
rules: []
}
-// TODO: Refactor to make userAssignedIdentityId a required parameter once all container apps use user-assigned identities
-@description('The ID of the user-assigned managed identity (optional)')
-param userAssignedIdentityId string = ''
+@description('The ID of the user-assigned managed identity')
+@minLength(1)
+param userAssignedIdentityId string
// Container app revision name does not allow '.' character
var cleanedRevisionSuffix = replace(revisionSuffix, '.', '-')
@@ -81,19 +81,19 @@ var ingress = {
ipSecurityRestrictions: ipSecurityRestrictions
}
-var identityConfig = empty(userAssignedIdentityId) ? {
- type: 'SystemAssigned'
-} : {
- type: 'UserAssigned'
- userAssignedIdentities: {
- '${userAssignedIdentityId}': {}
- }
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = {
+ name: last(split(userAssignedIdentityId, '/'))
}
resource containerApp 'Microsoft.App/containerApps@2024-03-01' = {
name: name
location: location
- identity: identityConfig
+ identity: {
+ type: 'UserAssigned'
+ userAssignedIdentities: {
+ '${userAssignedIdentityId}': {}
+ }
+ }
properties: {
configuration: {
ingress: ingress
@@ -116,10 +116,6 @@ resource containerApp 'Microsoft.App/containerApps@2024-03-01' = {
tags: tags
}
-resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(userAssignedIdentityId)) {
- name: last(split(userAssignedIdentityId, '/'))
-}
-
-output identityPrincipalId string = empty(userAssignedIdentityId) ? containerApp.identity.principalId : managedIdentity.properties.principalId
+output identityPrincipalId string = managedIdentity.properties.principalId
output name string = containerApp.name
output revisionName string = containerApp.properties.latestRevisionName
diff --git a/.azure/modules/containerAppEnv/main.bicep b/.azure/modules/containerAppEnv/main.bicep
index a05e598cb..c1fd26b5c 100644
--- a/.azure/modules/containerAppEnv/main.bicep
+++ b/.azure/modules/containerAppEnv/main.bicep
@@ -16,9 +16,6 @@ param appInsightWorkspaceName string
@description('The Application Insights connection string')
param appInsightsConnectionString string
-@description('The metrics ingestion endpoint of the Azure Monitor workspace')
-param monitorMetricsIngestionEndpoint string
-
@description('The ID of the user-assigned managed identity')
param userAssignedIdentityId string
@@ -57,18 +54,6 @@ resource containerAppEnv 'Microsoft.App/managedEnvironments@2024-02-02-preview'
logsConfiguration: {
destinations: ['appInsights']
}
- metricsConfiguration: {
- destinations: ['metrics-ingestion']
- }
- destinationsConfiguration: {
- otlpConfigurations: [
- {
- endpoint: monitorMetricsIngestionEndpoint
- name: 'metrics-ingestion'
- insecure: false
- }
- ]
- }
}
}
tags: tags
diff --git a/.azure/modules/containerAppJob/main.bicep b/.azure/modules/containerAppJob/main.bicep
index 8a1628018..836bbd250 100644
--- a/.azure/modules/containerAppJob/main.bicep
+++ b/.azure/modules/containerAppJob/main.bicep
@@ -25,6 +25,10 @@ param cronExpression string = ''
@description('The container args for the job (optional)')
param args string = ''
+@description('The ID of the user-assigned managed identity')
+@minLength(1)
+param userAssignedIdentityId string
+
var isScheduled = !empty(cronExpression)
var scheduledJobProperties = {
@@ -42,11 +46,18 @@ var manualJobProperties = {
}
}
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = {
+ name: last(split(userAssignedIdentityId, '/'))
+}
+
resource job 'Microsoft.App/jobs@2024-03-01' = {
name: name
location: location
identity: {
- type: 'SystemAssigned'
+ type: 'UserAssigned'
+ userAssignedIdentities: {
+ '${userAssignedIdentityId}': {}
+ }
}
properties: {
configuration: union(
@@ -72,5 +83,5 @@ resource job 'Microsoft.App/jobs@2024-03-01' = {
tags: tags
}
-output identityPrincipalId string = job.identity.principalId
+output identityPrincipalId string = managedIdentity.properties.principalId
output name string = job.name
diff --git a/.azure/modules/functionApp/appSettings.bicep b/.azure/modules/functionApp/appSettings.bicep
deleted file mode 100644
index eeca797f2..000000000
--- a/.azure/modules/functionApp/appSettings.bicep
+++ /dev/null
@@ -1,18 +0,0 @@
-@description('The name of the web application')
-param webAppName string
-
-@description('The new app settings to be applied')
-param appSettings object
-
-@description('The current app settings of the web application')
-param currentAppSettings object
-
-resource webApp 'Microsoft.Web/sites@2023-12-01' existing = {
- name: webAppName
-}
-
-resource siteconfig 'Microsoft.Web/sites/config@2023-12-01' = {
- parent: webApp
- name: 'appsettings'
- properties: union(currentAppSettings, appSettings)
-}
diff --git a/.azure/modules/functionApp/slackNotifier.bicep b/.azure/modules/functionApp/slackNotifier.bicep
deleted file mode 100644
index fbd0629eb..000000000
--- a/.azure/modules/functionApp/slackNotifier.bicep
+++ /dev/null
@@ -1,221 +0,0 @@
-import { uniqueStringBySubscriptionAndResourceGroup, uniqueResourceName } from '../../functions/resourceName.bicep'
-
-@description('The location where the resources will be deployed')
-param location string
-
-@description('The name of the Application Insights resource')
-param applicationInsightsName string
-
-@description('The prefix used for naming resources to ensure unique names')
-param namePrefix string
-
-@description('The name of the Key Vault')
-param keyVaultName string
-
-@description('Tags to apply to resources')
-param tags object
-
-@export()
-type Sku = {
- storageAccountName:
- | 'Standard_LRS'
- | 'Standard_GRS'
- | 'Standard_RAGRS'
- | 'Standard_ZRS'
- | 'Premium_LRS'
- | 'Premium_ZRS'
- applicationServicePlanName:
- | 'F1'
- | 'D1'
- | 'B1'
- | 'B2'
- | 'B3'
- | 'S1'
- | 'S2'
- | 'S3'
- | 'P1'
- | 'P2'
- | 'P3'
- | 'P1V2'
- | 'P2V2'
- | 'P3V2'
- | 'I1'
- | 'I2'
- | 'I3'
- | 'Y1'
- | 'Y2'
- | 'Y3'
- | 'Y1v2'
- | 'Y2v2'
- | 'Y3v2'
- | 'Y1v2Isolated'
- | 'Y2v2Isolated'
- | 'Y3v2Isolated'
- applicationServicePlanTier: 'Free' | 'Shared' | 'Basic' | 'Dynamic' | 'Standard' | 'Premium' | 'Isolated'
-}
-@description('The SKU of the Slack Notifier')
-param sku Sku
-
-// Storage account names only supports lower case and numbers
-// todo: add name of function as param and turn this into a reusable module
-// We use uniqueStringBySubscriptionAndResourceGroup directly here to avoid having too short storage account name.
-// This should be refactored to use one common storage account. Or one storage account for all app functions.
-var storageAccountName = take(
- replace('${'${namePrefix}-sn'}-${uniqueStringBySubscriptionAndResourceGroup()}', '-', ''),
- 24
-)
-
-resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' = {
- name: storageAccountName
- location: location
- sku: {
- name: sku.storageAccountName
- }
- kind: 'Storage'
- properties: {
- supportsHttpsTrafficOnly: true
- defaultToOAuthAuthentication: true
- minimumTlsVersion: 'TLS1_2'
- }
- tags: tags
-}
-
-resource applicationServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = {
- name: '${namePrefix}-slacknotifier-asp'
- location: location
- sku: {
- name: sku.applicationServicePlanName
- tier: sku.applicationServicePlanTier
- }
- properties: {}
- tags: tags
-}
-
-resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = {
- name: applicationInsightsName
-}
-
-var functionAppNameMaxLength = 40
-var functionAppName = uniqueResourceName('${namePrefix}-slacknotifier-fa', functionAppNameMaxLength)
-resource functionApp 'Microsoft.Web/sites@2023-12-01' = {
- name: functionAppName
- location: location
- kind: 'functionapp'
- identity: {
- type: 'SystemAssigned'
- }
- properties: {
- serverFarmId: applicationServicePlan.id
- publicNetworkAccess: 'Enabled'
- siteConfig: {
- // Setting/updating appSettings in separate module in order to not delete already deployed functions, see below
- }
- httpsOnly: true
- }
- tags: tags
-}
-
-var appSettings = {
- AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
- WEBSITE_CONTENTAZUREFILECONNECTIONSTRING: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
- WEBSITE_CONTENTSHARE: toLower(functionAppName)
- FUNCTIONS_EXTENSION_VERSION: '~4'
- APPINSIGHTS_INSTRUMENTATIONKEY: applicationInsights.properties.InstrumentationKey
- Slack__WebhookUrl: '@Microsoft.KeyVault(VaultName=${keyVaultName};SecretName=Slack--Webhook--Url)'
- FUNCTIONS_WORKER_RUNTIME: 'dotnet-isolated'
-}
-
-module updateAppSettings 'appSettings.bicep' = {
- name: '${functionAppName}-appsettings'
- params: {
- webAppName: functionAppName
- currentAppSettings: list(resourceId('Microsoft.Web/sites/config', functionAppName, 'appsettings'), '2023-01-01').properties
- appSettings: appSettings
- }
-}
-
-var defaultFunctionKey = listkeys('${functionApp.id}/host/default', '2023-01-01').functionKeys.default
-var forwardAlertToSlackTriggerUrl = 'https://${functionApp.properties.defaultHostName}/api/forwardalerttoslack?code=${defaultFunctionKey}'
-resource notifyDevTeam 'Microsoft.Insights/actionGroups@2023-01-01' = {
- name: '${namePrefix}-notify-devteam-ag'
- location: 'Global'
- properties: {
- enabled: true
- groupShortName: 'DevNotify'
- azureFunctionReceivers: [
- {
- name: functionApp.properties.defaultHostName
- functionName: 'ForwardAlertToSlack'
- functionAppResourceId: functionApp.id
- httpTriggerUrl: forwardAlertToSlackTriggerUrl
- useCommonAlertSchema: true
- }
- ]
- }
- tags: tags
-}
-
-var query = '''
- union
- (exceptions
- | where not(customDimensions.['Service Type'] == 'API Management')
- | where type != "ZiggyCreatures.Caching.Fusion.SyntheticTimeoutException"),
- (traces
- | where severityLevel >= 3 or (severityLevel >= 2 and customDimensions.SourceContext startswith "Digdir"))
- | where customDimensions.RequestPath !startswith "/health"
- | summarize Count = count()
- by
- Environment = tostring(customDimensions.EnvironmentName),
- Type = itemType,
- problemId,
- tostring(customDimensions.MessageTemplate),
- severityLevel
- | extend SeverityLevel = case(
- severityLevel == 0, "Verbose",
- severityLevel == 1, "Information",
- severityLevel == 2, "Warning",
- severityLevel == 3, "Error",
- severityLevel == 4, "Critical",
- tostring(severityLevel)
- )
- | extend Details = coalesce(problemId, customDimensions_MessageTemplate)
- | extend Details = replace_string(Details, "Digdir.Domain.Dialogporten.", "")
- | project Environment, Type, SeverityLevel, Count, Details
- '''
-resource exceptionOccuredAlertRule 'Microsoft.Insights/scheduledQueryRules@2023-03-15-preview' = {
- name: '${namePrefix}-exception-occured-sqr'
- location: location
- properties: {
- enabled: true
- severity: 1
- evaluationFrequency: 'PT5M'
- windowSize: 'PT5M'
- scopes: [applicationInsights.id]
- autoMitigate: false
- targetResourceTypes: [
- 'microsoft.insights/components'
- ]
- criteria: {
- allOf: [
- {
- query: query
- operator: 'GreaterThan'
- threshold: 0
- timeAggregation: 'Count'
- failingPeriods: {
- numberOfEvaluationPeriods: 1
- minFailingPeriodsToAlert: 1
- }
- }
- ]
- }
- actions: {
- actionGroups: [
- notifyDevTeam.id
- ]
- }
- }
- tags: tags
-}
-
-output functionAppPrincipalId string = functionApp.identity.principalId
diff --git a/.azure/modules/monitor-workspace/addMetricsPublisherRoles.bicep b/.azure/modules/monitor-workspace/addMetricsPublisherRoles.bicep
deleted file mode 100644
index 26ea44553..000000000
--- a/.azure/modules/monitor-workspace/addMetricsPublisherRoles.bicep
+++ /dev/null
@@ -1,25 +0,0 @@
-@description('The name of the Monitor workspace')
-param monitorWorkspaceName string
-
-@description('Array of principal IDs to assign the Monitoring Metrics Publisher role to')
-param principalIds array
-
-resource monitorWorkspace 'Microsoft.Monitor/accounts@2023-04-03' existing = {
- name: monitorWorkspaceName
-}
-
-@description('This is the built-in Monitoring Metrics Publisher role. See https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#monitoring-metrics-publisher')
-resource monitoringMetricsPublisherRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
- scope: subscription()
- name: '3913510d-42f4-4e42-8a64-420c390055eb'
-}
-
-resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for principalId in principalIds: {
- scope: monitorWorkspace
- name: guid(monitorWorkspace.id, principalId, monitoringMetricsPublisherRole.id)
- properties: {
- roleDefinitionId: monitoringMetricsPublisherRole.id
- principalId: principalId
- principalType: 'ServicePrincipal'
- }
-}]
diff --git a/.azure/modules/monitor-workspace/main.bicep b/.azure/modules/monitor-workspace/main.bicep
deleted file mode 100644
index b24e57d06..000000000
--- a/.azure/modules/monitor-workspace/main.bicep
+++ /dev/null
@@ -1,73 +0,0 @@
-@description('The prefix used for naming resources to ensure unique names')
-param namePrefix string
-
-@description('The location where the resources will be deployed')
-param location string
-
-@description('Tags to apply to resources')
-param tags object
-
-resource monitorWorkspace 'Microsoft.Monitor/accounts@2023-04-03' = {
- name: '${namePrefix}-monitor'
- location: location
- properties: {
- // todo: enable once we have ensured a connection to this monitor workspace https://github.com/digdir/dialogporten/issues/1462
- publicNetworkAccess: 'Enabled'
- }
- tags: tags
-}
-
-resource containerAppEnvironmentDataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2023-03-11' = {
- name: '${namePrefix}-cae-dce'
- location: location
- properties: {
- description: 'DCE for Container App Environment'
- networkAcls: {
- publicNetworkAccess: 'Enabled'
- }
- }
- tags: tags
-}
-
-resource containerAppEnvironmentDataCollectionRule 'Microsoft.Insights/dataCollectionRules@2023-03-11' = {
- name: '${namePrefix}-cae-dcr'
- location: location
- properties: {
- description: 'DCR for Container App Environment'
- dataCollectionEndpointId: containerAppEnvironmentDataCollectionEndpoint.id
- dataSources: {
- prometheusForwarder: [
- {
- streams: [
- 'Microsoft-PrometheusMetrics'
- ]
- name: 'PrometheusDataSource'
- }
- ]
- }
- destinations: {
- monitoringAccounts: [
- {
- accountResourceId: monitorWorkspace.id
- name: 'MonitoringAccountDestination'
- }
- ]
- }
- dataFlows: [
- {
- streams: [
- 'Microsoft-PrometheusMetrics'
- ]
- destinations: [
- 'MonitoringAccountDestination'
- ]
- }
- ]
- }
- tags: tags
-}
-
-output monitorWorkspaceId string = monitorWorkspace.id
-output monitorWorkspaceName string = monitorWorkspace.name
-output containerAppEnvironmentMetricsIngestionEndpoint string = containerAppEnvironmentDataCollectionEndpoint.properties.metricsIngestion.endpoint
-output containerAppEnvironmentLogsIngestionEndpoint string = containerAppEnvironmentDataCollectionEndpoint.properties.logsIngestion.endpoint
diff --git a/.azure/modules/vnet/main.bicep b/.azure/modules/vnet/main.bicep
index 8b8182897..c58bc06ee 100644
--- a/.azure/modules/vnet/main.bicep
+++ b/.azure/modules/vnet/main.bicep
@@ -263,44 +263,6 @@ resource serviceBusNSG 'Microsoft.Network/networkSecurityGroups@2024-05-01' = {
tags: tags
}
-resource monitorNSG 'Microsoft.Network/networkSecurityGroups@2024-05-01' = {
- name: '${namePrefix}-monitor-nsg'
- location: location
- properties: {
- securityRules: [
- {
- name: 'AllowAnyCustomAnyInbound'
- type: 'Microsoft.Network/networkSecurityGroups/securityRules'
- properties: {
- protocol: '*'
- sourcePortRange: '*'
- destinationPortRange: '*'
- sourceAddressPrefix: '*'
- destinationAddressPrefix: '*'
- access: 'Allow'
- priority: 100
- direction: 'Inbound'
- }
- }
- {
- name: 'AllowAnyCustomAnyOutbound'
- type: 'Microsoft.Network/networkSecurityGroups/securityRules'
- properties: {
- protocol: '*'
- sourcePortRange: '*'
- destinationPortRange: '*'
- sourceAddressPrefix: '*'
- destinationAddressPrefix: '*'
- access: 'Allow'
- priority: 100
- direction: 'Outbound'
- }
- }
- ]
- }
- tags: tags
-}
-
resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-05-01' = {
name: '${namePrefix}-vnet'
location: location
@@ -372,17 +334,6 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-05-01' = {
}
}
}
- {
- name: 'monitorSubnet'
- properties: {
- addressPrefix: '10.0.6.0/24'
- networkSecurityGroup: {
- id: monitorNSG.id
- }
- privateEndpointNetworkPolicies: 'Disabled'
- privateLinkServiceNetworkPolicies: 'Enabled'
- }
- }
]
}
tags: tags
@@ -411,8 +362,3 @@ output redisSubnetId string = resourceId(
virtualNetwork.name,
'redisSubnet'
)
-output monitorSubnetId string = resourceId(
- 'Microsoft.Network/virtualNetworks/subnets',
- virtualNetwork.name,
- 'monitorSubnet'
-)
diff --git a/.env b/.env
index 8300930d6..2309e6938 100644
--- a/.env
+++ b/.env
@@ -5,3 +5,8 @@ POSTGRES_DB=dialogporten
DB_CONNECTION_STRING=Server=dialogporten-postgres;Port=5432;Database=${POSTGRES_DB};User ID=${POSTGRES_USER};Password=${POSTGRES_PASSWORD};
COMPOSE_PROJECT_NAME=digdir
+
+# OTEL
+OTEL_NAMESPACE=dialogporten-local
+OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
+OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
\ No newline at end of file
diff --git a/.github/slack-templates/pipeline-failed.json b/.github/slack-templates/pipeline-failed.json
index cd02b10a0..43c41db3c 100644
--- a/.github/slack-templates/pipeline-failed.json
+++ b/.github/slack-templates/pipeline-failed.json
@@ -23,7 +23,7 @@
"type": "section",
"text": {
"type": "mrkdwn",
- "text": "*Job Status:*\n• Infrastructure: ${{ env.INFRA_STATUS }}\n• Apps: ${{ env.APPS_STATUS }}\n• Slack Notifier: ${{ env.SLACK_NOTIFIER_STATUS }}\n• E2E Tests: ${{ env.E2E_TESTS_STATUS }}\n• Performance Tests: ${{ env.PERFORMANCE_TESTS_STATUS }}\n• Schema NPM: ${{ env.SCHEMA_NPM_STATUS }}\n• Publish: ${{ env.PUBLISH_STATUS }}"
+ "text": "*Job Status:*\n• Infrastructure: ${{ env.INFRA_STATUS }}\n• Apps: ${{ env.APPS_STATUS }}\n• E2E Tests: ${{ env.E2E_TESTS_STATUS }}\n• Performance Tests: ${{ env.PERFORMANCE_TESTS_STATUS }}\n• Schema NPM: ${{ env.SCHEMA_NPM_STATUS }}\n• Publish: ${{ env.PUBLISH_STATUS }}"
}
},
{
diff --git a/.github/workflows/ci-cd-main.yml b/.github/workflows/ci-cd-main.yml
index 52a8bd597..d77025508 100644
--- a/.github/workflows/ci-cd-main.yml
+++ b/.github/workflows/ci-cd-main.yml
@@ -108,22 +108,6 @@ jobs:
version: ${{ needs.get-current-version.outputs.version }}-${{ needs.generate-git-short-sha.outputs.gitShortSha }}
runMigration: ${{ github.event_name == 'workflow_dispatch' || needs.check-for-changes.outputs.hasMigrationChanges == 'true' }}
- deploy-slack-notifier:
- name: Deploy slack notifier (test)
- needs: [check-for-changes]
- if: ${{ github.event_name == 'workflow_dispatch' || needs.check-for-changes.outputs.hasSlackNotifierChanges == 'true' }}
- uses: ./.github/workflows/workflow-deploy-function.yml
- secrets:
- AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
- AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
- AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- # todo: resolve this automatically, or use tags
- AZURE_FUNCTION_APP_NAME: ${{ secrets.AZURE_SLACK_NOTIFIER_FUNCTION_APP_NAME }}
- with:
- function-app-name: "slack-notifier"
- function-project-path: "./src/Digdir.Tool.Dialogporten.SlackNotifier"
- environment: test
-
publish-schema-npm:
name: Deploy schema npm package
needs: [check-for-changes, get-current-version, generate-git-short-sha, deploy-apps]
@@ -156,7 +140,6 @@ jobs:
needs: [
deploy-infra,
deploy-apps,
- deploy-slack-notifier,
run-e2e-tests,
publish-schema-npm,
publish,
@@ -168,7 +151,6 @@ jobs:
environment: test
infra_status: ${{ needs.deploy-infra.result }}
apps_status: ${{ needs.deploy-apps.result }}
- slack_notifier_status: ${{ needs.deploy-slack-notifier.result }}
e2e_tests_status: ${{ needs.run-e2e-tests.result }}
schema_npm_status: ${{ needs.publish-schema-npm.result }}
publish_status: ${{ needs.publish.result }}
diff --git a/.github/workflows/ci-cd-prod.yml b/.github/workflows/ci-cd-prod.yml
index 4313c0110..7cf4ca86c 100644
--- a/.github/workflows/ci-cd-prod.yml
+++ b/.github/workflows/ci-cd-prod.yml
@@ -139,22 +139,6 @@ jobs:
secrets:
GH_TOKEN: ${{ secrets.RELEASE_VERSION_STORAGE_PAT }}
- deploy-slack-notifier:
- name: Deploy slack notifier (prod)
- needs: [check-for-changes]
- if: ${{ github.event_name == 'workflow_dispatch' || needs.check-for-changes.outputs.hasSlackNotifierChanges == 'true' }}
- uses: ./.github/workflows/workflow-deploy-function.yml
- secrets:
- AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
- AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
- AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- # todo: resolve this automatically, or use tags
- AZURE_FUNCTION_APP_NAME: ${{ secrets.AZURE_SLACK_NOTIFIER_FUNCTION_APP_NAME }}
- with:
- function-app-name: "slack-notifier"
- function-project-path: "./src/Digdir.Tool.Dialogporten.SlackNotifier"
- environment: prod
-
# run-e2e-tests:
# name: "Run K6 functional end-to-end tests"
# # we want the end-to-end tests to be dependent on deployment of infrastructure and apps, but if infrastructure is skipped, we still want to run the tests
@@ -174,14 +158,13 @@ jobs:
send-slack-message-on-failure:
name: Send Slack message on failure
- needs: [deploy-infra, deploy-apps, deploy-slack-notifier]
+ needs: [deploy-infra, deploy-apps]
if: ${{ always() && failure() && !cancelled() }}
uses: ./.github/workflows/workflow-send-ci-cd-status-slack-message.yml
with:
environment: prod
infra_status: ${{ needs.deploy-infra.result }}
apps_status: ${{ needs.deploy-apps.result }}
- slack_notifier_status: ${{ needs.deploy-slack-notifier.result }}
secrets:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID_FOR_CI_CD_STATUS }}
diff --git a/.github/workflows/ci-cd-staging.yml b/.github/workflows/ci-cd-staging.yml
index 5091b5238..611839543 100644
--- a/.github/workflows/ci-cd-staging.yml
+++ b/.github/workflows/ci-cd-staging.yml
@@ -105,22 +105,6 @@ jobs:
secrets:
GH_TOKEN: ${{ secrets.RELEASE_VERSION_STORAGE_PAT }}
- deploy-slack-notifier:
- name: Deploy slack notifier (staging)
- needs: [check-for-changes]
- if: ${{ github.event_name == 'workflow_dispatch' || needs.check-for-changes.outputs.hasSlackNotifierChanges == 'true' }}
- uses: ./.github/workflows/workflow-deploy-function.yml
- secrets:
- AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
- AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
- AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- # todo: resolve this automatically, or use tags
- AZURE_FUNCTION_APP_NAME: ${{ secrets.AZURE_SLACK_NOTIFIER_FUNCTION_APP_NAME }}
- with:
- function-app-name: "slack-notifier"
- function-project-path: "./src/Digdir.Tool.Dialogporten.SlackNotifier"
- environment: staging
-
publish-schema-npm:
name: Publish schema npm package
needs: [check-for-changes, get-current-version, deploy-apps]
@@ -150,14 +134,13 @@ jobs:
send-slack-message-on-failure:
name: Send Slack message on failure
- needs: [deploy-infra, deploy-apps, deploy-slack-notifier, run-e2e-tests, publish-schema-npm, publish]
+ needs: [deploy-infra, deploy-apps, run-e2e-tests, publish-schema-npm, publish]
if: ${{ always() && failure() && !cancelled() }}
uses: ./.github/workflows/workflow-send-ci-cd-status-slack-message.yml
with:
environment: staging
infra_status: ${{ needs.deploy-infra.result }}
apps_status: ${{ needs.deploy-apps.result }}
- slack_notifier_status: ${{ needs.deploy-slack-notifier.result }}
e2e_tests_status: ${{ needs.run-e2e-tests.result }}
schema_npm_status: ${{ needs.publish-schema-npm.result }}
publish_status: ${{ needs.publish.result }}
diff --git a/.github/workflows/ci-cd-yt01.yml b/.github/workflows/ci-cd-yt01.yml
index 74a8f5df3..7cf989443 100644
--- a/.github/workflows/ci-cd-yt01.yml
+++ b/.github/workflows/ci-cd-yt01.yml
@@ -107,22 +107,6 @@ jobs:
secrets:
GH_TOKEN: ${{ secrets.RELEASE_VERSION_STORAGE_PAT }}
- deploy-slack-notifier:
- name: Deploy slack notifier (yt01)
- needs: [check-for-changes]
- if: ${{ github.event_name == 'workflow_dispatch' || needs.check-for-changes.outputs.hasSlackNotifierChanges == 'true' }}
- uses: ./.github/workflows/workflow-deploy-function.yml
- secrets:
- AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
- AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
- AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- # todo: resolve this automatically, or use tags
- AZURE_FUNCTION_APP_NAME: ${{ secrets.AZURE_SLACK_NOTIFIER_FUNCTION_APP_NAME }}
- with:
- function-app-name: "slack-notifier"
- function-project-path: "./src/Digdir.Tool.Dialogporten.SlackNotifier"
- environment: yt01
-
run-e2e-tests:
name: "Run K6 functional end-to-end tests"
# we want the end-to-end tests to be dependent on deployment of infrastructure and apps, but if infrastructure is skipped, we still want to run the tests
@@ -150,15 +134,14 @@ jobs:
secrets:
TOKEN_GENERATOR_USERNAME: ${{ secrets.TOKEN_GENERATOR_USERNAME }}
TOKEN_GENERATOR_PASSWORD: ${{ secrets.TOKEN_GENERATOR_PASSWORD }}
-
strategy:
max-parallel: 1
matrix:
- files:
+ files:
- tests/k6/tests/serviceowner/serviceOwnerSearchWithThresholds.js
- tests/k6/tests/serviceowner/createDialogWithThresholds.js
- tests/k6/tests/enduser/enduserSearchWithThresholds.js
- fail-fast: false
+ fail-fast: false
with:
environment: yt01
apiVersion: v1
@@ -171,14 +154,13 @@ jobs:
send-slack-message-on-failure:
name: Send Slack message on failure
- needs: [deploy-infra, deploy-apps, deploy-slack-notifier, run-e2e-tests, publish, run-performance-tests]
+ needs: [deploy-infra, deploy-apps, run-e2e-tests, publish, run-performance-tests]
if: ${{ always() && failure() && !cancelled() }}
uses: ./.github/workflows/workflow-send-ci-cd-status-slack-message.yml
with:
environment: yt01
infra_status: ${{ needs.deploy-infra.result }}
apps_status: ${{ needs.deploy-apps.result }}
- slack_notifier_status: ${{ needs.deploy-slack-notifier.result }}
e2e_tests_status: ${{ needs.run-e2e-tests.result }}
performance_tests_status: ${{ needs.run-performance-tests.result }}
publish_status: ${{ needs.publish.result }}
diff --git a/.github/workflows/workflow-deploy-function.yml b/.github/workflows/workflow-deploy-function.yml
deleted file mode 100644
index e64632b22..000000000
--- a/.github/workflows/workflow-deploy-function.yml
+++ /dev/null
@@ -1,67 +0,0 @@
-name: Deploy DotNet project to Azure Function App
-
-on:
- workflow_call:
- secrets:
- AZURE_CLIENT_ID:
- required: true
- AZURE_TENANT_ID:
- required: true
- AZURE_SUBSCRIPTION_ID:
- required: true
- AZURE_FUNCTION_APP_NAME:
- required: true
-
- inputs:
- function-app-name:
- type: string
- required: true
- function-project-path:
- type: string
- required: true
- environment:
- type: string
- required: true
-
-concurrency:
- # If multiple merges to main are performed simultaneously, they will just be queued up.
- group: ${{ github.workflow }}-${{ inputs.environment }}-${{ github.ref_name }}
-
-jobs:
- build-and-deploy:
- name: Build and deploy ${{ inputs.function-app-name }} to ${{ inputs.environment }}
- runs-on: ubuntu-latest
- environment: ${{ inputs.environment }}
- permissions:
- id-token: write
- contents: read
- steps:
- - name: "Checkout GitHub Action"
- uses: actions/checkout@v4
-
- - name: OIDC Login to Azure Public Cloud
- uses: azure/login@v2
- with:
- client-id: ${{ secrets.AZURE_CLIENT_ID }}
- tenant-id: ${{ secrets.AZURE_TENANT_ID }}
- subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
-
- - name: Setup DotNet Environment
- uses: actions/setup-dotnet@v4
- with:
- global-json-file: ./global.json
-
- - name: "Resolve Project Dependencies Using Dotnet"
- shell: bash
- run: |
- pushd './${{ inputs.function-project-path }}'
- dotnet build -c Release -o ./output
- popd
-
- - name: "Run Azure Functions Action"
- uses: Azure/functions-action@v1
- id: fa
- with:
- app-name: ${{ secrets.AZURE_FUNCTION_APP_NAME }}
- package: "${{ inputs.function-project-path }}/output"
- publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }}
diff --git a/.github/workflows/workflow-send-ci-cd-status-slack-message.yml b/.github/workflows/workflow-send-ci-cd-status-slack-message.yml
index 557f5d1e6..ef613e320 100644
--- a/.github/workflows/workflow-send-ci-cd-status-slack-message.yml
+++ b/.github/workflows/workflow-send-ci-cd-status-slack-message.yml
@@ -14,10 +14,6 @@ on:
type: string
description: "Status of the apps deployment job"
default: "skipped"
- slack_notifier_status:
- type: string
- description: "Status of the Slack notifier deployment job"
- default: "skipped"
e2e_tests_status:
type: string
description: "Status of the end-to-end tests job"
@@ -68,7 +64,6 @@ jobs:
{
echo "INFRA_EMOJI=$(determine_emoji "${{ inputs.infra_status }}")"
echo "APPS_EMOJI=$(determine_emoji "${{ inputs.apps_status }}")"
- echo "SLACK_NOTIFIER_EMOJI=$(determine_emoji "${{ inputs.slack_notifier_status }}")"
echo "E2E_TESTS_EMOJI=$(determine_emoji "${{ inputs.e2e_tests_status }}")"
echo "SCHEMA_NPM_EMOJI=$(determine_emoji "${{ inputs.schema_npm_status }}")"
echo "PUBLISH_EMOJI=$(determine_emoji "${{ inputs.publish_status }}")"
@@ -85,7 +80,6 @@ jobs:
# statuses
INFRA_STATUS: "${{ steps.status-emojis.outputs.INFRA_EMOJI }}"
APPS_STATUS: "${{ steps.status-emojis.outputs.APPS_EMOJI }}"
- SLACK_NOTIFIER_STATUS: "${{ steps.status-emojis.outputs.SLACK_NOTIFIER_EMOJI }}"
E2E_TESTS_STATUS: "${{ steps.status-emojis.outputs.E2E_TESTS_EMOJI }}"
SCHEMA_NPM_STATUS: "${{ steps.status-emojis.outputs.SCHEMA_NPM_EMOJI }}"
PUBLISH_STATUS: "${{ steps.status-emojis.outputs.PUBLISH_EMOJI }}"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f1c23c3bb..fe7fae707 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,51 @@
# Changelog
+## [1.44.1](https://github.com/digdir/dialogporten/compare/v1.44.0...v1.44.1) (2025-01-07)
+
+
+### Bug Fixes
+
+* **ci:** Use correct size for yt01 db ([#1658](https://github.com/digdir/dialogporten/issues/1658)) ([e18e5f7](https://github.com/digdir/dialogporten/commit/e18e5f7e44556e1a4173906303ccef14aeb9de13))
+
+## [1.44.0](https://github.com/digdir/dialogporten/compare/v1.43.0...v1.44.0) (2025-01-07)
+
+
+### Features
+
+* **webapi:** Add ETag to response headers ([#1645](https://github.com/digdir/dialogporten/issues/1645)) ([7a32e60](https://github.com/digdir/dialogporten/commit/7a32e601061b42400aa1c94b61be69ff7c9d0ec9))
+
+
+### Bug Fixes
+
+* disable slack notifier ([#1655](https://github.com/digdir/dialogporten/issues/1655)) ([554fc8b](https://github.com/digdir/dialogporten/commit/554fc8b3294c125b0e8561ebcbfe254e75fede1c))
+
+## [1.43.0](https://github.com/digdir/dialogporten/compare/v1.42.1...v1.43.0) (2025-01-07)
+
+
+### Features
+
+* Add additional types to DialogActivity ([#1629](https://github.com/digdir/dialogporten/issues/1629)) ([feb1347](https://github.com/digdir/dialogporten/commit/feb1347c0a79406e0a8f6bb312faad42c8db7eec))
+
+
+### Bug Fixes
+
+* **app:** Add dedicated scope and dbcontext to GetSubjectResources ([#1648](https://github.com/digdir/dialogporten/issues/1648)) ([d1040e4](https://github.com/digdir/dialogporten/commit/d1040e41e2b09d1b8e3388ada4790ab1d63c738b))
+* revert azure monitor workspace ([#1624](https://github.com/digdir/dialogporten/issues/1624)) ([d66b155](https://github.com/digdir/dialogporten/commit/d66b155f3e6749466c344ee9aa9319810f65cf6c))
+
+## [1.42.1](https://github.com/digdir/dialogporten/compare/v1.42.0...v1.42.1) (2024-12-25)
+
+
+### Bug Fixes
+
+* **webapi:** Only allow transmissionId on TransmissionOpened activities ([#1631](https://github.com/digdir/dialogporten/issues/1631)) ([80261d1](https://github.com/digdir/dialogporten/commit/80261d18af159acd000c2fa06d6ae351aa681d7d))
+
+## [1.42.0](https://github.com/digdir/dialogporten/compare/v1.41.3...v1.42.0) (2024-12-16)
+
+
+### Features
+
+* **apps:** add otel exporter for graphql, service and web-api ([#1528](https://github.com/digdir/dialogporten/issues/1528)) ([cb9238e](https://github.com/digdir/dialogporten/commit/cb9238ef76188b4dde371e08b7ce597645bcd8b7))
+
## [1.41.3](https://github.com/digdir/dialogporten/compare/v1.41.2...v1.41.3) (2024-12-13)
diff --git a/README.md b/README.md
index 0135ecec0..2b85f2a90 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
#### Prerequisites
-- [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) (see [global.json](global.json) for the currently required version)
+- [.NET 9 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) (see [global.json](global.json) for the currently required version)
#### Installing Podman (Mac)
@@ -34,14 +34,14 @@ brew install docker-compose
#### Prerequisites
- [Git](https://git-scm.com/download/win)
-- [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
+- [.NET 9 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/9.0)
- [WSL2](https://docs.microsoft.com/en-us/windows/wsl/install) (To install, open a PowerShell admin window and run `wsl --install`)
- [Virtual Machine Platform](https://support.microsoft.com/en-us/windows/enable-virtualization-on-windows-11-pcs-c5578302-6e43-4b4b-a449-8ced115f58e1) (Installs with WSL2, see the link above)
#### Installing Podman (Windows)
1. Install [Podman Desktop](https://podman.io/getting-started/installation).
-.
+
2. Start Podman Desktop and follow instructions to install Podman.
3. Follow instructions in Podman Desktop to create and start a Podman machine.
@@ -113,6 +113,68 @@ Besides ordinary unit and integration tests, there are test suites for both func
See `tests/k6/README.md` for more information.
+## Health Checks
+
+The project includes integrated health checks that are exposed through standard endpoints:
+- `/health/startup` - Dependency checks
+- `/health/liveness` - Self checks
+- `/health/readiness` - Critical service checks
+- `/health` - General health status
+- `/health/deep` - Comprehensive health check including external services
+
+These health checks are integrated with Azure Container Apps' health probe system and are used to monitor the application's health status.
+
+## Observability with OpenTelemetry
+
+This project uses OpenTelemetry for distributed tracing and metrics collection. The setup includes:
+
+### Core Features
+- Distributed tracing across services
+- Runtime and application metrics
+- Integration with Azure Monitor/Application Insights
+- Support for both OTLP and Azure Monitor exporters
+- Automatic instrumentation for:
+ - ASP.NET Core
+ - HTTP clients
+ - Entity Framework Core
+ - PostgreSQL
+ - FusionCache
+
+### Configuration
+
+OpenTelemetry is configured through environment variables that are automatically provided by Azure Container Apps in production environments:
+
+```json
+{
+ "OTEL_SERVICE_NAME": "your-service-name",
+ "OTEL_EXPORTER_OTLP_ENDPOINT": "http://your-collector:4317",
+ "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
+ "OTEL_RESOURCE_ATTRIBUTES": "key1=value1,key2=value2",
+ "APPLICATIONINSIGHTS_CONNECTION_STRING": "your-connection-string"
+}
+```
+
+### Local Development
+
+For local development, the project includes a docker-compose setup with:
+- OpenTelemetry Collector
+- Grafana
+- Other supporting services
+
+To run the local observability stack:
+```bash
+podman compose -f docker-compose-otel.yml up
+```
+
+### Request Filtering
+
+The telemetry setup includes smart filtering to:
+- Exclude health check endpoints from tracing
+- Filter out duplicate traces from Azure SDK clients
+- Only record relevant HTTP client calls
+
+For more details about the OpenTelemetry setup, see the `ConfigureTelemetry` method in `AspNetUtilitiesExtensions.cs`.
+
## Updating the SDK in global.json
When RenovateBot updates `global.json` or base image versions in Dockerfiles, make sure they match.
The `global.json` file should always have the same SDK version as the base image in the Dockerfiles.
@@ -329,7 +391,7 @@ There is a `ssh-jumper` virtual machine deployed with the infrastructure. This c
2. Using the forwarding utility script:
- See [scripts/forward-bash/README.md](scripts/forward-bash/README.md) for a more user-friendly way to establish database connections through SSH.
+ See [scripts/database-forwarder/README.md](scripts/database-forwarder/README.md) for a more user-friendly way to establish database connections through SSH.
### Applications
diff --git a/docker-compose-otel.yml b/docker-compose-otel.yml
new file mode 100644
index 000000000..cb8f38aad
--- /dev/null
+++ b/docker-compose-otel.yml
@@ -0,0 +1,45 @@
+services:
+ # OpenTelemetry Collector
+ otel-collector:
+ image: otel/opentelemetry-collector-contrib:0.116.1
+ command: ["--config=/etc/otel-collector-config.yaml"]
+ volumes:
+ - ./local-otel-configuration/otel-collector-config.yaml:/etc/otel-collector-config.yaml
+ ports:
+ - "4317:4317" # OTLP gRPC receiver
+ - "4318:4318" # OTLP http receiver
+ - "8888:8888" # Prometheus metrics exposed by the collector
+ - "8889:8889" # Prometheus exporter metrics
+ depends_on:
+ jaeger:
+ condition: service_healthy
+
+ # Jaeger for trace visualization
+ jaeger:
+ image: jaegertracing/all-in-one:1.64.0
+ ports:
+ - "16686:16686" # Jaeger UI
+ - "14250:14250" # Model used by collector
+ environment:
+ - COLLECTOR_OTLP_ENABLED=true
+
+ # Prometheus for metrics
+ prometheus:
+ image: prom/prometheus:v3.0.1
+ volumes:
+ - ./local-otel-configuration/prometheus.yml:/etc/prometheus/prometheus.yml
+ ports:
+ - "9090:9090"
+
+ # Grafana for metrics visualization
+ grafana:
+ image: grafana/grafana:11.4.0
+ ports:
+ - "3000:3000"
+ environment:
+ - GF_AUTH_ANONYMOUS_ENABLED=true
+ - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
+ volumes:
+ - ./local-otel-configuration/grafana-datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml
+ - ./local-otel-configuration/grafana-dashboards.yml:/etc/grafana/provisioning/dashboards/dashboards.yml
+ - ./local-otel-configuration/dashboards:/etc/grafana/provisioning/dashboards
diff --git a/docker-compose.yml b/docker-compose.yml
index f2a7ec59f..4519148b5 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,6 +1,7 @@
include:
- docker-compose-db-redis.yml
- docker-compose-cdc.yml
+ - docker-compose-otel.yml
services:
dialogporten-webapi-ingress:
@@ -14,7 +15,7 @@ services:
restart: always
dialogporten-webapi:
- scale: 2
+ scale: 1
build:
context: .
dockerfile: src/Digdir.Domain.Dialogporten.WebApi/Dockerfile
@@ -34,6 +35,10 @@ services:
- Serilog__MinimumLevel__Default=Debug
- ASPNETCORE_URLS=http://+:8080
- ASPNETCORE_ENVIRONMENT=Development
+ - OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT}
+ - OTEL_EXPORTER_OTLP_PROTOCOL=${OTEL_EXPORTER_OTLP_PROTOCOL}
+ - OTEL_SERVICE_NAME=dialogporten-webapi
+ - OTEL_RESOURCE_ATTRIBUTES=service.instance.id=dialogporten-webapi,service.namespace=${OTEL_NAMESPACE}
volumes:
- ./.aspnet/https:/https
@@ -70,5 +75,9 @@ services:
- Serilog__WriteTo__0__Name=Console
- Serilog__MinimumLevel__Default=Debug
- ASPNETCORE_ENVIRONMENT=Development
+ - OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT}
+ - OTEL_EXPORTER_OTLP_PROTOCOL=${OTEL_EXPORTER_OTLP_PROTOCOL}
+ - OTEL_SERVICE_NAME=dialogporten-graphql
+ - OTEL_RESOURCE_ATTRIBUTES=service.instance.id=dialogporten-graphql,service.namespace=${OTEL_NAMESPACE}
volumes:
- ./.aspnet/https:/https
diff --git a/docs/schema/V1/package-lock.json b/docs/schema/V1/package-lock.json
index 4aaec0128..a6fefd9ea 100644
--- a/docs/schema/V1/package-lock.json
+++ b/docs/schema/V1/package-lock.json
@@ -11,10 +11,10 @@
"devDependencies": {
"glob": "11.0.0",
"graphql-tag": "2.12.6",
- "vitest": "2.1.3"
+ "vitest": "2.1.8"
},
"engines": {
- "node": "20"
+ "node": "22"
}
},
"node_modules/@esbuild/aix-ppc64": {
@@ -406,7 +406,8 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
@@ -633,15 +634,15 @@
"dev": true
},
"node_modules/@vitest/expect": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.3.tgz",
- "integrity": "sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ==",
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz",
+ "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/spy": "2.1.3",
- "@vitest/utils": "2.1.3",
- "chai": "^5.1.1",
+ "@vitest/spy": "2.1.8",
+ "@vitest/utils": "2.1.8",
+ "chai": "^5.1.2",
"tinyrainbow": "^1.2.0"
},
"funding": {
@@ -649,22 +650,21 @@
}
},
"node_modules/@vitest/mocker": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.3.tgz",
- "integrity": "sha512-eSpdY/eJDuOvuTA3ASzCjdithHa+GIF1L4PqtEELl6Qa3XafdMLBpBlZCIUCX2J+Q6sNmjmxtosAG62fK4BlqQ==",
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz",
+ "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/spy": "2.1.3",
+ "@vitest/spy": "2.1.8",
"estree-walker": "^3.0.3",
- "magic-string": "^0.30.11"
+ "magic-string": "^0.30.12"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
- "@vitest/spy": "2.1.3",
- "msw": "^2.3.5",
+ "msw": "^2.4.9",
"vite": "^5.0.0"
},
"peerDependenciesMeta": {
@@ -677,9 +677,9 @@
}
},
"node_modules/@vitest/pretty-format": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.3.tgz",
- "integrity": "sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==",
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz",
+ "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -690,13 +690,13 @@
}
},
"node_modules/@vitest/runner": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.3.tgz",
- "integrity": "sha512-JGzpWqmFJ4fq5ZKHtVO3Xuy1iF2rHGV4d/pdzgkYHm1+gOzNZtqjvyiaDGJytRyMU54qkxpNzCx+PErzJ1/JqQ==",
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz",
+ "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/utils": "2.1.3",
+ "@vitest/utils": "2.1.8",
"pathe": "^1.1.2"
},
"funding": {
@@ -704,14 +704,14 @@
}
},
"node_modules/@vitest/snapshot": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.3.tgz",
- "integrity": "sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg==",
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz",
+ "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "2.1.3",
- "magic-string": "^0.30.11",
+ "@vitest/pretty-format": "2.1.8",
+ "magic-string": "^0.30.12",
"pathe": "^1.1.2"
},
"funding": {
@@ -719,27 +719,27 @@
}
},
"node_modules/@vitest/spy": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.3.tgz",
- "integrity": "sha512-Nb2UzbcUswzeSP7JksMDaqsI43Sj5+Kry6ry6jQJT4b5gAK+NS9NED6mDb8FlMRCX8m5guaHCDZmqYMMWRy5nQ==",
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz",
+ "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "tinyspy": "^3.0.0"
+ "tinyspy": "^3.0.2"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/utils": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.3.tgz",
- "integrity": "sha512-xpiVfDSg1RrYT0tX6czgerkpcKFmFOF/gCr30+Mve5V2kewCy4Prn1/NDMSRwaSmT7PRaOF83wu+bEtsY1wrvA==",
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz",
+ "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "2.1.3",
- "loupe": "^3.1.1",
+ "@vitest/pretty-format": "2.1.8",
+ "loupe": "^3.1.2",
"tinyrainbow": "^1.2.0"
},
"funding": {
@@ -775,6 +775,7 @@
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=12"
}
@@ -804,10 +805,11 @@
}
},
"node_modules/chai": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz",
- "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==",
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz",
+ "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"assertion-error": "^2.0.1",
"check-error": "^2.1.1",
@@ -824,6 +826,7 @@
"resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
"integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">= 16"
}
@@ -884,6 +887,7 @@
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
"integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=6"
}
@@ -900,6 +904,13 @@
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"dev": true
},
+ "node_modules/es-module-lexer": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
+ "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/esbuild": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
@@ -947,6 +958,16 @@
"@types/estree": "^1.0.0"
}
},
+ "node_modules/expect-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz",
+ "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/foreground-child": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz",
@@ -978,15 +999,6 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
- "node_modules/get-func-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
- "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
- "dev": true,
- "engines": {
- "node": "*"
- }
- },
"node_modules/glob": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz",
@@ -1072,13 +1084,11 @@
}
},
"node_modules/loupe": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz",
- "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
+ "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==",
"dev": true,
- "dependencies": {
- "get-func-name": "^2.0.1"
- }
+ "license": "MIT"
},
"node_modules/lru-cache": {
"version": "11.0.0",
@@ -1090,9 +1100,9 @@
}
},
"node_modules/magic-string": {
- "version": "0.30.11",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
- "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==",
+ "version": "0.30.17",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+ "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1192,6 +1202,7 @@
"resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
"integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">= 14.16"
}
@@ -1325,9 +1336,9 @@
"license": "MIT"
},
"node_modules/std-env": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz",
- "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==",
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz",
+ "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==",
"dev": true,
"license": "MIT"
},
@@ -1435,17 +1446,18 @@
"license": "MIT"
},
"node_modules/tinyexec": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz",
- "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==",
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+ "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
"dev": true,
"license": "MIT"
},
"node_modules/tinypool": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.0.tgz",
- "integrity": "sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz",
+ "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": "^18.0.0 || >=20.0.0"
}
@@ -1536,14 +1548,15 @@
}
},
"node_modules/vite-node": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.3.tgz",
- "integrity": "sha512-I1JadzO+xYX887S39Do+paRePCKoiDrWRRjp9kkG5he0t7RXNvPAJPCQSJqbGN4uCrFFeS3Kj3sLqY8NMYBEdA==",
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz",
+ "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==",
"dev": true,
"license": "MIT",
"dependencies": {
"cac": "^6.7.14",
- "debug": "^4.3.6",
+ "debug": "^4.3.7",
+ "es-module-lexer": "^1.5.4",
"pathe": "^1.1.2",
"vite": "^5.0.0"
},
@@ -1558,30 +1571,31 @@
}
},
"node_modules/vitest": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.3.tgz",
- "integrity": "sha512-Zrxbg/WiIvUP2uEzelDNTXmEMJXuzJ1kCpbDvaKByFA9MNeO95V+7r/3ti0qzJzrxdyuUw5VduN7k+D3VmVOSA==",
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz",
+ "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/expect": "2.1.3",
- "@vitest/mocker": "2.1.3",
- "@vitest/pretty-format": "^2.1.3",
- "@vitest/runner": "2.1.3",
- "@vitest/snapshot": "2.1.3",
- "@vitest/spy": "2.1.3",
- "@vitest/utils": "2.1.3",
- "chai": "^5.1.1",
- "debug": "^4.3.6",
- "magic-string": "^0.30.11",
+ "@vitest/expect": "2.1.8",
+ "@vitest/mocker": "2.1.8",
+ "@vitest/pretty-format": "^2.1.8",
+ "@vitest/runner": "2.1.8",
+ "@vitest/snapshot": "2.1.8",
+ "@vitest/spy": "2.1.8",
+ "@vitest/utils": "2.1.8",
+ "chai": "^5.1.2",
+ "debug": "^4.3.7",
+ "expect-type": "^1.1.0",
+ "magic-string": "^0.30.12",
"pathe": "^1.1.2",
- "std-env": "^3.7.0",
+ "std-env": "^3.8.0",
"tinybench": "^2.9.0",
- "tinyexec": "^0.3.0",
- "tinypool": "^1.0.0",
+ "tinyexec": "^0.3.1",
+ "tinypool": "^1.0.1",
"tinyrainbow": "^1.2.0",
"vite": "^5.0.0",
- "vite-node": "2.1.3",
+ "vite-node": "2.1.8",
"why-is-node-running": "^2.3.0"
},
"bin": {
@@ -1596,8 +1610,8 @@
"peerDependencies": {
"@edge-runtime/vm": "*",
"@types/node": "^18.0.0 || >=20.0.0",
- "@vitest/browser": "2.1.3",
- "@vitest/ui": "2.1.3",
+ "@vitest/browser": "2.1.8",
+ "@vitest/ui": "2.1.8",
"happy-dom": "*",
"jsdom": "*"
},
diff --git a/docs/schema/V1/package.json b/docs/schema/V1/package.json
index b65a007fe..4704869d8 100644
--- a/docs/schema/V1/package.json
+++ b/docs/schema/V1/package.json
@@ -3,7 +3,7 @@
"version": "1.0.11",
"description": "GraphQl schema and OpenAPI spec for Dialogporten",
"engines": {
- "node": "20"
+ "node": "22"
},
"author": "DigDir",
"main": "src/index.js",
@@ -16,7 +16,7 @@
"test": "npm run build && vitest run --test-timeout=10000"
},
"devDependencies": {
- "vitest": "2.1.3",
+ "vitest": "2.1.8",
"glob": "11.0.0",
"graphql-tag": "2.12.6"
},
diff --git a/docs/schema/V1/swagger.verified.json b/docs/schema/V1/swagger.verified.json
index e617e216e..375c25de3 100644
--- a/docs/schema/V1/swagger.verified.json
+++ b/docs/schema/V1/swagger.verified.json
@@ -83,7 +83,13 @@
"TransmissionOpened",
"PaymentMade",
"SignatureProvided",
- "DialogOpened"
+ "DialogOpened",
+ "DialogDeleted",
+ "DialogRestored",
+ "SentToSigning",
+ "SentToFormFill",
+ "SentToSendIn",
+ "SentToPayment"
],
"type": "string",
"x-enumNames": [
@@ -93,7 +99,13 @@
"TransmissionOpened",
"PaymentMade",
"SignatureProvided",
- "DialogOpened"
+ "DialogOpened",
+ "DialogDeleted",
+ "DialogRestored",
+ "SentToSigning",
+ "SentToFormFill",
+ "SentToSendIn",
+ "SentToPayment"
]
},
"DialogsEntitiesTransmissions_DialogTransmissionType": {
@@ -315,7 +327,7 @@
"additionalProperties": false,
"properties": {
"mediaType": {
- "description": "Media type of the content (plaintext, Markdown). Can also indicate that the content is embeddable.",
+ "description": "Media type of the content, this can also indicate that the content is embeddable.\nFor a list of supported media types, see (link TBD).",
"type": "string"
},
"value": {
@@ -5851,7 +5863,12 @@
}
}
},
- "description": "The UUID of the created the dialog aggregate. A relative URL to the newly created activity is set in the \u0022Location\u0022 header."
+ "description": "The UUID of the created dialog aggregate. A relative URL to the newly created activity is set in the \u0022Location\u0022 header.",
+ "headers": {
+ "Etag": {
+ "description": "The new UUID ETag of the dialog"
+ }
+ }
},
"204": {
"description": "No Content"
@@ -5920,7 +5937,12 @@
],
"responses": {
"204": {
- "description": "The dialog aggregate was deleted successfully."
+ "description": "The dialog aggregate was deleted successfully.",
+ "headers": {
+ "Etag": {
+ "description": "The new UUID ETag of the dialog"
+ }
+ }
},
"400": {
"content": {
@@ -6076,7 +6098,12 @@
},
"responses": {
"204": {
- "description": "Patch was successfully applied."
+ "description": "Patch was successfully applied.",
+ "headers": {
+ "Etag": {
+ "description": "The new UUID ETag of the dialog"
+ }
+ }
},
"400": {
"content": {
@@ -6171,7 +6198,12 @@
},
"responses": {
"204": {
- "description": "The dialog aggregate was updated successfully."
+ "description": "The dialog aggregate was updated successfully.",
+ "headers": {
+ "Etag": {
+ "description": "The new UUID ETag of the dialog"
+ }
+ }
},
"400": {
"content": {
@@ -6469,7 +6501,12 @@
}
}
},
- "description": "The UUID of the created the dialog activity. A relative URL to the newly created activity is set in the \u0022Location\u0022 header."
+ "description": "The UUID of the created dialog activity. A relative URL to the newly created activity is set in the \u0022Location\u0022 header.",
+ "headers": {
+ "Etag": {
+ "description": "The new UUID ETag of the dialog"
+ }
+ }
},
"204": {
"description": "No Content"
@@ -6832,7 +6869,12 @@
}
}
},
- "description": "The UUID of the created the dialog transmission. A relative URL to the newly created activity is set in the \u0022Location\u0022 header."
+ "description": "The UUID of the created dialog transmission. A relative URL to the newly created activity is set in the \u0022Location\u0022 header.",
+ "headers": {
+ "Etag": {
+ "description": "The new UUID ETag of the dialog"
+ }
+ }
},
"204": {
"description": "No Content"
diff --git a/local-otel-configuration/dashboards/aspnet-core-metrics.json b/local-otel-configuration/dashboards/aspnet-core-metrics.json
new file mode 100644
index 000000000..f60749a2f
--- /dev/null
+++ b/local-otel-configuration/dashboards/aspnet-core-metrics.json
@@ -0,0 +1,176 @@
+{
+ "annotations": {
+ "list": []
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 0
+ },
+ "id": 1,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "title": "HTTP Request Duration",
+ "type": "timeseries",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "expr": "sum(rate(dialogporten_http_server_request_duration_seconds_bucket[$__rate_interval])) by (le)",
+ "legendFormat": "{{le}}",
+ "refId": "A"
+ }
+ ]
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 0
+ },
+ "id": 2,
+ "options": {
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showThresholdLabels": false,
+ "showThresholdMarkers": true
+ },
+ "pluginVersion": "10.2.0",
+ "title": "Active Requests",
+ "type": "gauge",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "expr": "dialogporten_http_server_active_requests",
+ "refId": "A"
+ }
+ ]
+ }
+ ],
+ "refresh": "5s",
+ "schemaVersion": 38,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-15m",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "",
+ "title": "ASP.NET Core Metrics",
+ "uid": "aspnet-core-metrics",
+ "version": 1,
+ "weekStart": ""
+}
\ No newline at end of file
diff --git a/local-otel-configuration/dashboards/dashboards.yml b/local-otel-configuration/dashboards/dashboards.yml
new file mode 100755
index 000000000..e69de29bb
diff --git a/local-otel-configuration/dashboards/http-client-metrics.json b/local-otel-configuration/dashboards/http-client-metrics.json
new file mode 100644
index 000000000..56e2f687e
--- /dev/null
+++ b/local-otel-configuration/dashboards/http-client-metrics.json
@@ -0,0 +1,296 @@
+{
+ "annotations": {
+ "list": []
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 0
+ },
+ "id": 1,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "title": "HTTP Client Active Requests & Connections",
+ "type": "timeseries",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "expr": "dialogporten_http_client_active_requests",
+ "legendFormat": "Active Requests",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "expr": "dialogporten_http_client_open_connections",
+ "legendFormat": "Open Connections",
+ "refId": "B"
+ }
+ ]
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 0
+ },
+ "id": 2,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "title": "Request Queue Time",
+ "type": "timeseries",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "expr": "rate(dialogporten_http_client_request_time_in_queue_seconds_sum[$__rate_interval]) / rate(dialogporten_http_client_request_time_in_queue_seconds_count[$__rate_interval])",
+ "legendFormat": "Average Queue Time",
+ "refId": "A"
+ }
+ ]
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 8
+ },
+ "id": 3,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "title": "DNS Lookup Duration",
+ "type": "timeseries",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "expr": "rate(dialogporten_dns_lookup_duration_seconds_sum[$__rate_interval]) / rate(dialogporten_dns_lookup_duration_seconds_count[$__rate_interval])",
+ "legendFormat": "Average DNS Lookup Time",
+ "refId": "A"
+ }
+ ]
+ }
+ ],
+ "refresh": "5s",
+ "schemaVersion": 38,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-15m",
+ "to": "now"
+ },
+ "title": "HTTP Client Metrics",
+ "uid": "http-client-metrics",
+ "version": 1,
+ "weekStart": ""
+}
\ No newline at end of file
diff --git a/local-otel-configuration/dashboards/runtime-metrics.json b/local-otel-configuration/dashboards/runtime-metrics.json
new file mode 100644
index 000000000..eed1ef0c6
--- /dev/null
+++ b/local-otel-configuration/dashboards/runtime-metrics.json
@@ -0,0 +1,390 @@
+{
+ "annotations": {
+ "list": []
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "bytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 0
+ },
+ "id": 1,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "title": "Memory Usage",
+ "type": "timeseries",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "expr": "dialogporten_process_runtime_dotnet_gc_heap_size_bytes",
+ "legendFormat": "Heap Size",
+ "refId": "A"
+ }
+ ]
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 0
+ },
+ "id": 2,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "title": "GC Collections",
+ "type": "timeseries",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "expr": "rate(dialogporten_process_runtime_dotnet_gc_collections_count_total[5m])",
+ "legendFormat": "Gen {{generation}}",
+ "refId": "A"
+ }
+ ]
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 16
+ },
+ "id": 3,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "title": "Thread Pool",
+ "type": "timeseries",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "expr": "dialogporten_process_runtime_dotnet_thread_pool_queue_length",
+ "legendFormat": "Queue Length",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "expr": "dialogporten_process_runtime_dotnet_thread_pool_threads_count",
+ "legendFormat": "Thread Count",
+ "refId": "B"
+ }
+ ]
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 16
+ },
+ "id": 4,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "title": "Exceptions & Lock Contentions",
+ "type": "timeseries",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "expr": "rate(dialogporten_process_runtime_dotnet_exceptions_count_total[$__rate_interval])",
+ "legendFormat": "Exceptions/sec",
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "Prometheus"
+ },
+ "expr": "rate(dialogporten_process_runtime_dotnet_monitor_lock_contention_count_total[$__rate_interval])",
+ "legendFormat": "Lock Contentions/sec",
+ "refId": "B"
+ }
+ ]
+ }
+ ],
+ "refresh": "5s",
+ "schemaVersion": 38,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-15m",
+ "to": "now"
+ },
+ "title": "Runtime Metrics",
+ "uid": "runtime-metrics",
+ "version": 1,
+ "weekStart": ""
+}
\ No newline at end of file
diff --git a/local-otel-configuration/grafana-dashboards.yml b/local-otel-configuration/grafana-dashboards.yml
new file mode 100644
index 000000000..3712f1164
--- /dev/null
+++ b/local-otel-configuration/grafana-dashboards.yml
@@ -0,0 +1,11 @@
+apiVersion: 1
+
+providers:
+ - name: 'Default'
+ orgId: 1
+ folder: ''
+ type: file
+ disableDeletion: false
+ editable: true
+ options:
+ path: /etc/grafana/provisioning/dashboards
\ No newline at end of file
diff --git a/local-otel-configuration/grafana-datasources.yml b/local-otel-configuration/grafana-datasources.yml
new file mode 100644
index 000000000..4139ccba6
--- /dev/null
+++ b/local-otel-configuration/grafana-datasources.yml
@@ -0,0 +1,8 @@
+apiVersion: 1
+
+datasources:
+ - name: Prometheus
+ type: prometheus
+ access: proxy
+ url: http://prometheus:9090
+ isDefault: true
\ No newline at end of file
diff --git a/local-otel-configuration/otel-collector-config.yaml b/local-otel-configuration/otel-collector-config.yaml
new file mode 100644
index 000000000..97bc0c2b6
--- /dev/null
+++ b/local-otel-configuration/otel-collector-config.yaml
@@ -0,0 +1,52 @@
+receivers:
+ otlp:
+ protocols:
+ http:
+ endpoint: 0.0.0.0:4318
+ cors:
+ allowed_origins: ["*"]
+ allowed_headers: ["*"]
+ include_metadata: true
+
+processors:
+ batch:
+ timeout: 1s
+ send_batch_size: 1024
+
+exporters:
+ prometheus:
+ endpoint: "0.0.0.0:8889"
+ namespace: "dialogporten"
+ otlp:
+ endpoint: "jaeger:4317"
+ tls:
+ insecure: true
+ debug:
+ verbosity: detailed
+ sampling_initial: 5
+ sampling_thereafter: 200
+
+extensions:
+ health_check:
+ pprof:
+ zpages:
+
+service:
+ extensions: [health_check, pprof, zpages]
+ telemetry:
+ logs:
+ level: debug
+ development: true
+ pipelines:
+ traces:
+ receivers: [otlp]
+ processors: [batch]
+ exporters: [otlp, debug]
+ metrics:
+ receivers: [otlp]
+ processors: [batch]
+ exporters: [prometheus, debug]
+ logs:
+ receivers: [otlp]
+ processors: [batch]
+ exporters: [debug]
diff --git a/local-otel-configuration/prometheus.yml b/local-otel-configuration/prometheus.yml
new file mode 100644
index 000000000..58650dae4
--- /dev/null
+++ b/local-otel-configuration/prometheus.yml
@@ -0,0 +1,8 @@
+global:
+ scrape_interval: 15s
+ evaluation_interval: 15s
+
+scrape_configs:
+ - job_name: 'otel-collector'
+ static_configs:
+ - targets: ['otel-collector:8889']
\ No newline at end of file
diff --git a/renovate.json b/renovate.json
index 74b545b9a..026d030e0 100644
--- a/renovate.json
+++ b/renovate.json
@@ -17,6 +17,24 @@
"schedule": [
"every 3 months"
]
+ },
+ {
+ "packagePatterns": [
+ "^Npgsql"
+ ],
+ "groupName": "Npgsql dependencies"
+ },
+ {
+ "packagePatterns": [
+ "^Microsoft"
+ ],
+ "groupName": "Microsoft dependencies"
+ },
+ {
+ "packagePatterns": [
+ "^Serilog"
+ ],
+ "groupName": "Serilog dependencies"
}
]
}
diff --git a/src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj b/src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj
index 55cec0c47..3c661b369 100644
--- a/src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj
+++ b/src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj
@@ -18,7 +18,7 @@
-
+
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 c3db7d6a5..58f5bf96c 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
@@ -11,7 +11,8 @@ public sealed class ContentValueDto
public List Value { get; set; } = [];
///
- /// Media type of the content (plaintext, Markdown). Can also indicate that the content is embeddable.
+ /// Media type of the content, this can also indicate that the content is embeddable.
+ /// For a list of supported media types, see (link TBD).
///
public string MediaType { get; set; } = MediaTypes.PlainText;
}
diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Events/CloudEventTypes.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Events/CloudEventTypes.cs
index 71a28d03f..3aa06d588 100644
--- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Events/CloudEventTypes.cs
+++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Events/CloudEventTypes.cs
@@ -21,6 +21,12 @@ internal static class CloudEventTypes
nameof(DialogActivityType.Values.PaymentMade) => "dialogporten.dialog.activity.payment-made.v1",
nameof(DialogActivityType.Values.SignatureProvided) => "dialogporten.dialog.activity.signature-provided.v1",
nameof(DialogActivityType.Values.DialogOpened) => "dialogporten.dialog.activity.dialog-opened.v1",
+ nameof(DialogActivityType.Values.DialogDeleted) => "dialogporten.dialog.activity.dialog-deleted.v1",
+ nameof(DialogActivityType.Values.DialogRestored) => "dialogporten.dialog.activity.dialog-restored.v1",
+ nameof(DialogActivityType.Values.SentToSigning) => "dialogporten.dialog.activity.sent-to-signing.v1",
+ nameof(DialogActivityType.Values.SentToFormFill) => "dialogporten.dialog.activity.sent-to-form-fill.v1",
+ nameof(DialogActivityType.Values.SentToSendIn) => "dialogporten.dialog.activity.sent-to-send-in.v1",
+ nameof(DialogActivityType.Values.SentToPayment) => "dialogporten.dialog.activity.sent-to-payment.v1",
_ => throw new ArgumentOutOfRangeException(nameof(eventName), eventName, null)
};
diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
index 2fee13aa3..09374e635 100644
--- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
+++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommand.cs
@@ -21,8 +21,10 @@ namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialog
public sealed class CreateDialogCommand : CreateDialogDto, IRequest;
+public sealed record CreateDialogSuccess(Guid DialogId, Guid Revision);
+
[GenerateOneOf]
-public sealed partial class CreateDialogResult : OneOfBase, DomainError, ValidationError, Forbidden>;
+public sealed partial class CreateDialogResult : OneOfBase;
internal sealed class CreateDialogCommandHandler : IRequestHandler
{
@@ -93,7 +95,7 @@ public async Task Handle(CreateDialogCommand request, Cancel
await _db.Dialogs.AddAsync(dialog, cancellationToken);
var saveResult = await _unitOfWork.SaveChangesAsync(cancellationToken);
return saveResult.Match(
- success => new Success(dialog.Id),
+ success => new CreateDialogSuccess(dialog.Id, dialog.Revision),
domainError => domainError,
concurrencyError => throw new UnreachableException("Should never get a concurrency error when creating a new dialog"));
}
diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommandValidator.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommandValidator.cs
index 42a867469..e35be4053 100644
--- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommandValidator.cs
+++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommandValidator.cs
@@ -434,11 +434,11 @@ public CreateDialogDialogActivityDtoValidator(
.When(x => x.Type != DialogActivityType.Values.Information);
RuleFor(x => x.TransmissionId)
.Null()
- .WithMessage($"A {nameof(DialogActivityType.Values.DialogOpened)} activity cannot reference a transmission.")
- .When(x => x.Type == DialogActivityType.Values.DialogOpened);
+ .WithMessage($"Only activities of type {nameof(DialogActivityType.Values.TransmissionOpened)} can reference a transmission.")
+ .When(x => x.Type != DialogActivityType.Values.TransmissionOpened);
RuleFor(x => x.TransmissionId)
.NotEmpty()
- .WithMessage($"A {nameof(DialogActivityType.Values.TransmissionOpened)} needs to reference a transmission.")
+ .WithMessage($"An activity of type {nameof(DialogActivityType.Values.TransmissionOpened)} needs to reference a transmission.")
.When(x => x.Type == DialogActivityType.Values.TransmissionOpened);
}
}
diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Delete/DeleteDialogCommand.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Delete/DeleteDialogCommand.cs
index c64e816d1..9b9250ed6 100644
--- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Delete/DeleteDialogCommand.cs
+++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Delete/DeleteDialogCommand.cs
@@ -19,7 +19,9 @@ public sealed class DeleteDialogCommand : IRequest
}
[GenerateOneOf]
-public sealed partial class DeleteDialogResult : OneOfBase;
+public sealed partial class DeleteDialogResult : OneOfBase;
+
+public sealed record DeleteDialogSuccess(Guid Revision);
internal sealed class DeleteDialogCommandHandler : IRequestHandler
{
@@ -70,7 +72,7 @@ public async Task Handle(DeleteDialogCommand request, Cancel
.SaveChangesAsync(cancellationToken);
return saveResult.Match(
- success => success,
+ success => new DeleteDialogSuccess(dialog.Revision),
domainError => throw new UnreachableException("Should never get a domain error when deleting a dialog"),
concurrencyError => concurrencyError);
}
diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
index fb160b026..4b11eb5aa 100644
--- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
+++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommand.cs
@@ -19,7 +19,6 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
using OneOf;
-using OneOf.Types;
namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update;
@@ -31,7 +30,9 @@ public sealed class UpdateDialogCommand : IRequest
}
[GenerateOneOf]
-public sealed partial class UpdateDialogResult : OneOfBase;
+public sealed partial class UpdateDialogResult : OneOfBase;
+
+public sealed record UpdateDialogSuccess(Guid Revision);
internal sealed class UpdateDialogCommandHandler : IRequestHandler
{
@@ -167,7 +168,7 @@ public async Task Handle(UpdateDialogCommand request, Cancel
.SaveChangesAsync(cancellationToken);
return saveResult.Match(
- success => success,
+ success => new UpdateDialogSuccess(dialog.Revision),
domainError => domainError,
concurrencyError => concurrencyError);
}
diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommandValidator.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommandValidator.cs
index 909d47e7a..f7ea74dff 100644
--- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommandValidator.cs
+++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommandValidator.cs
@@ -400,11 +400,11 @@ public UpdateDialogDialogActivityDtoValidator(
.When(x => x.Type != DialogActivityType.Values.Information);
RuleFor(x => x.TransmissionId)
.Null()
- .WithMessage($"A {nameof(DialogActivityType.Values.DialogOpened)} activity cannot reference a transmission.")
- .When(x => x.Type == DialogActivityType.Values.DialogOpened);
+ .WithMessage($"Only activities of type {nameof(DialogActivityType.Values.TransmissionOpened)} can reference a transmission.")
+ .When(x => x.Type != DialogActivityType.Values.TransmissionOpened);
RuleFor(x => x.TransmissionId)
.NotEmpty()
- .WithMessage($"A {nameof(DialogActivityType.Values.TransmissionOpened)} needs to reference a transmission.")
+ .WithMessage($"An activity of type {nameof(DialogActivityType.Values.TransmissionOpened)} needs to reference a transmission.")
.When(x => x.Type == DialogActivityType.Values.TransmissionOpened);
}
}
diff --git a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Digdir.Domain.Dialogporten.ChangeDataCapture.csproj b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Digdir.Domain.Dialogporten.ChangeDataCapture.csproj
index 824248115..1bb247ea4 100644
--- a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Digdir.Domain.Dialogporten.ChangeDataCapture.csproj
+++ b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Digdir.Domain.Dialogporten.ChangeDataCapture.csproj
@@ -2,12 +2,12 @@
-
+
-
+
diff --git a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Dockerfile b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Dockerfile
index 9f7e6e9f4..c3d76f09c 100644
--- a/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Dockerfile
+++ b/src/Digdir.Domain.Dialogporten.ChangeDataCapture/Dockerfile
@@ -1,7 +1,7 @@
-FROM mcr.microsoft.com/dotnet/aspnet:9.0.0@sha256:d8f01f752bf9bd3ff630319181a2ccfbeecea4080a1912095a34002f61bfa345 AS base
+FROM mcr.microsoft.com/dotnet/aspnet:9.0.0@sha256:b4bea3a52a0a77317fa93c5bbdb076623f81e3e2f201078d89914da71318b5d8 AS base
WORKDIR /app
-FROM mcr.microsoft.com/dotnet/sdk:9.0.101@sha256:fe8ceeca5ee197deba95419e3b85c32744970b730ae11645e13f1cb74a848d98 AS build
+FROM mcr.microsoft.com/dotnet/sdk:9.0.101@sha256:3fcf6f1e809c0553f9feb222369f58749af314af6f063f389cbd2f913b4ad556 AS build
WORKDIR /src
COPY [".editorconfig", "."]
diff --git a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivityType.cs b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivityType.cs
index 344be95c0..41b95e447 100644
--- a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivityType.cs
+++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Activities/DialogActivityType.cs
@@ -10,12 +10,12 @@ public DialogActivityType(Values id) : base(id) { }
public enum Values
{
///
- /// Refers to a dialog that has been created.
+ /// Indicates that a dialog has been created.
///
DialogCreated = 1,
///
- /// Refers to a dialog that has been closed.
+ /// Indicates that a dialog has been closed.
///
DialogClosed = 2,
@@ -25,7 +25,7 @@ public enum Values
Information = 3,
///
- /// Refers to a transmission that has been opened.
+ /// Indicates that a transmission has been opened.
///
TransmissionOpened = 4,
@@ -40,8 +40,38 @@ public enum Values
SignatureProvided = 6,
///
- /// Refers to a dialog that has been opened.
+ /// Indicates that a dialog has been opened.
///
DialogOpened = 7,
+
+ ///
+ /// Indicates that a dialog has been deleted.
+ ///
+ DialogDeleted = 8,
+
+ ///
+ /// Indicates that a dialog has been restored.
+ ///
+ DialogRestored = 9,
+
+ ///
+ /// Indicates that a dialog has been sent to signing.
+ ///
+ SentToSigning = 10,
+
+ ///
+ /// Indicates that a dialog has been sent to form fill.
+ ///
+ SentToFormFill = 11,
+
+ ///
+ /// Indicates that a dialog has been sent to send in.
+ ///
+ SentToSendIn = 12,
+
+ ///
+ /// Indicates that a dialog has been sent to payment.
+ ///
+ SentToPayment = 13,
}
}
diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/Digdir.Domain.Dialogporten.GraphQL.csproj b/src/Digdir.Domain.Dialogporten.GraphQL/Digdir.Domain.Dialogporten.GraphQL.csproj
index 47b62ee5d..5eddd36b8 100644
--- a/src/Digdir.Domain.Dialogporten.GraphQL/Digdir.Domain.Dialogporten.GraphQL.csproj
+++ b/src/Digdir.Domain.Dialogporten.GraphQL/Digdir.Domain.Dialogporten.GraphQL.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/Dockerfile b/src/Digdir.Domain.Dialogporten.GraphQL/Dockerfile
index 52dc82647..119952117 100644
--- a/src/Digdir.Domain.Dialogporten.GraphQL/Dockerfile
+++ b/src/Digdir.Domain.Dialogporten.GraphQL/Dockerfile
@@ -1,8 +1,8 @@
-FROM mcr.microsoft.com/dotnet/aspnet:9.0.0@sha256:d8f01f752bf9bd3ff630319181a2ccfbeecea4080a1912095a34002f61bfa345 AS base
+FROM mcr.microsoft.com/dotnet/aspnet:9.0.0@sha256:b4bea3a52a0a77317fa93c5bbdb076623f81e3e2f201078d89914da71318b5d8 AS base
WORKDIR /app
EXPOSE 8080
-FROM mcr.microsoft.com/dotnet/sdk:9.0.101@sha256:fe8ceeca5ee197deba95419e3b85c32744970b730ae11645e13f1cb74a848d98 AS build
+FROM mcr.microsoft.com/dotnet/sdk:9.0.101@sha256:3fcf6f1e809c0553f9feb222369f58749af314af6f063f389cbd2f913b4ad556 AS build
WORKDIR /src
COPY [".editorconfig", "."]
diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/OpenTelemetryEventListener.cs b/src/Digdir.Domain.Dialogporten.GraphQL/OpenTelemetryEventListener.cs
index 67beed8bb..b851f63f6 100644
--- a/src/Digdir.Domain.Dialogporten.GraphQL/OpenTelemetryEventListener.cs
+++ b/src/Digdir.Domain.Dialogporten.GraphQL/OpenTelemetryEventListener.cs
@@ -2,7 +2,6 @@
using HotChocolate.Execution;
using HotChocolate.Execution.Instrumentation;
using Microsoft.AspNetCore.Http.Extensions;
-using OpenTelemetry.Trace;
namespace Digdir.Domain.Dialogporten.GraphQL;
diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs b/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs
index 7753936ac..5808a8408 100644
--- a/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs
+++ b/src/Digdir.Domain.Dialogporten.GraphQL/Program.cs
@@ -68,12 +68,15 @@ static void BuildAndRun(string[] args, TelemetryConfiguration telemetryConfigura
var thisAssembly = Assembly.GetExecutingAssembly();
- builder.ConfigureTelemetry();
- builder.Services.AddOpenTelemetry()
- .WithTracing(tracerProviderBuilder =>
- {
- tracerProviderBuilder.AddSource(DialogportenGraphQLSource);
- });
+ builder.ConfigureTelemetry((settings, configuration) =>
+ {
+ settings.ServiceName = configuration["OTEL_SERVICE_NAME"] ?? builder.Environment.ApplicationName;
+ settings.Endpoint = configuration["OTEL_EXPORTER_OTLP_ENDPOINT"];
+ settings.Protocol = configuration["OTEL_EXPORTER_OTLP_PROTOCOL"];
+ settings.AppInsightsConnectionString = configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"];
+ settings.ResourceAttributes = configuration["OTEL_RESOURCE_ATTRIBUTES"];
+ settings.TraceSources.Add(DialogportenGraphQLSource);
+ });
builder.Services
// Options setup
diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/AltinnAuthorizationClient.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/AltinnAuthorizationClient.cs
index 4ae95cb71..2f46d07ae 100644
--- a/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/AltinnAuthorizationClient.cs
+++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Altinn/Authorization/AltinnAuthorizationClient.cs
@@ -10,7 +10,9 @@
using Digdir.Domain.Dialogporten.Domain.Parties.Abstractions;
using Digdir.Domain.Dialogporten.Domain.SubjectResources;
using Digdir.Domain.Dialogporten.Infrastructure.Common.Exceptions;
+using Digdir.Domain.Dialogporten.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ZiggyCreatures.Caching.Fusion;
@@ -25,8 +27,8 @@ internal sealed class AltinnAuthorizationClient : IAltinnAuthorization
private readonly IFusionCache _partiesCache;
private readonly IFusionCache _subjectResourcesCache;
private readonly IUser _user;
- private readonly IDialogDbContext _dialogDbContext;
private readonly ILogger _logger;
+ private readonly IServiceScopeFactory _serviceScopeFactory;
private static readonly JsonSerializerOptions SerializerOptions = new()
{
@@ -38,16 +40,16 @@ public AltinnAuthorizationClient(
HttpClient client,
IFusionCacheProvider cacheProvider,
IUser user,
- IDialogDbContext dialogDbContext,
- ILogger logger)
+ ILogger logger,
+ IServiceScopeFactory serviceScopeFactory)
{
_httpClient = client ?? throw new ArgumentNullException(nameof(client));
_pdpCache = cacheProvider.GetCache(nameof(Authorization)) ?? throw new ArgumentNullException(nameof(cacheProvider));
_partiesCache = cacheProvider.GetCache(nameof(AuthorizedPartiesResult)) ?? throw new ArgumentNullException(nameof(cacheProvider));
_subjectResourcesCache = cacheProvider.GetCache(nameof(SubjectResource)) ?? throw new ArgumentNullException(nameof(cacheProvider));
_user = user ?? throw new ArgumentNullException(nameof(user));
- _dialogDbContext = dialogDbContext ?? throw new ArgumentNullException(nameof(dialogDbContext));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
}
public async Task GetDialogDetailsAuthorization(
@@ -180,10 +182,13 @@ await AuthorizationHelper.CollapseSubjectResources(
return dialogSearchAuthorizationResult;
}
-
private async Task> GetAllSubjectResources(CancellationToken cancellationToken) =>
- await _subjectResourcesCache.GetOrSetAsync(nameof(SubjectResource), async ct
- => await _dialogDbContext.SubjectResources.ToListAsync(cancellationToken: ct),
+ await _subjectResourcesCache.GetOrSetAsync(nameof(SubjectResource), async ct =>
+ {
+ using var scope = _serviceScopeFactory.CreateScope();
+ var dbContext = scope.ServiceProvider.GetRequiredService();
+ return await dbContext.SubjectResources.AsNoTracking().ToListAsync(cancellationToken: ct);
+ },
token: cancellationToken);
private async Task PerformDialogDetailsAuthorization(
diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Digdir.Domain.Dialogporten.Infrastructure.csproj b/src/Digdir.Domain.Dialogporten.Infrastructure/Digdir.Domain.Dialogporten.Infrastructure.csproj
index 847c03471..311f64dea 100644
--- a/src/Digdir.Domain.Dialogporten.Infrastructure/Digdir.Domain.Dialogporten.Infrastructure.csproj
+++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Digdir.Domain.Dialogporten.Infrastructure.csproj
@@ -3,8 +3,8 @@
-
-
+
+
diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/MigrationBundle.dockerfile b/src/Digdir.Domain.Dialogporten.Infrastructure/MigrationBundle.dockerfile
index 633bd3398..1cddc7c40 100644
--- a/src/Digdir.Domain.Dialogporten.Infrastructure/MigrationBundle.dockerfile
+++ b/src/Digdir.Domain.Dialogporten.Infrastructure/MigrationBundle.dockerfile
@@ -1,7 +1,7 @@
-FROM mcr.microsoft.com/dotnet/aspnet:9.0.0@sha256:d8f01f752bf9bd3ff630319181a2ccfbeecea4080a1912095a34002f61bfa345 AS base
+FROM mcr.microsoft.com/dotnet/aspnet:9.0.0@sha256:b4bea3a52a0a77317fa93c5bbdb076623f81e3e2f201078d89914da71318b5d8 AS base
WORKDIR /app
-FROM mcr.microsoft.com/dotnet/sdk:9.0.101@sha256:fe8ceeca5ee197deba95419e3b85c32744970b730ae11645e13f1cb74a848d98 AS build
+FROM mcr.microsoft.com/dotnet/sdk:9.0.101@sha256:3fcf6f1e809c0553f9feb222369f58749af314af6f063f389cbd2f913b4ad556 AS build
WORKDIR /src
RUN dotnet tool install --global dotnet-ef
diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20241222195253_AddAdditionalActivityTypes.Designer.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20241222195253_AddAdditionalActivityTypes.Designer.cs
new file mode 100644
index 000000000..523a8413b
--- /dev/null
+++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20241222195253_AddAdditionalActivityTypes.Designer.cs
@@ -0,0 +1,2194 @@
+//
+using System;
+using Digdir.Domain.Dialogporten.Infrastructure.Persistence;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Migrations
+{
+ [DbContext(typeof(DialogDbContext))]
+ [Migration("20241222195253_AddAdditionalActivityTypes")]
+ partial class AddAdditionalActivityTypes
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "9.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Actors.Actor", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("ActorId")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("ActorName")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("ActorTypeId")
+ .HasColumnType("integer");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("Discriminator")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("UpdatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ActorTypeId");
+
+ b.ToTable("Actor");
+
+ b.HasDiscriminator().HasValue("Actor");
+
+ b.UseTphMappingStrategy();
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Actors.ActorType", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("Id");
+
+ b.ToTable("ActorType");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ Name = "PartyRepresentative"
+ },
+ new
+ {
+ Id = 2,
+ Name = "ServiceOwner"
+ });
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Attachments.Attachment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("Discriminator")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("UpdatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.HasKey("Id");
+
+ b.ToTable("Attachment");
+
+ b.HasDiscriminator().HasValue("Attachment");
+
+ b.UseTphMappingStrategy();
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Attachments.AttachmentUrl", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("AttachmentId")
+ .HasColumnType("uuid");
+
+ b.Property("ConsumerTypeId")
+ .HasColumnType("integer");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("MediaType")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("UpdatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasMaxLength(1023)
+ .HasColumnType("character varying(1023)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AttachmentId");
+
+ b.HasIndex("ConsumerTypeId");
+
+ b.ToTable("AttachmentUrl");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Attachments.AttachmentUrlConsumerType", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("Id");
+
+ b.ToTable("AttachmentUrlConsumerType");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ Name = "Gui"
+ },
+ new
+ {
+ Id = 2,
+ Name = "Api"
+ });
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.DialogEndUserContext", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("DialogId")
+ .HasColumnType("uuid");
+
+ b.Property("Revision")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("SystemLabelId")
+ .HasColumnType("integer");
+
+ b.Property("UpdatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DialogId")
+ .IsUnique();
+
+ b.HasIndex("SystemLabelId");
+
+ b.ToTable("DialogEndUserContext");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.LabelAssignmentLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("Action")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("ContextId")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ContextId");
+
+ b.ToTable("LabelAssignmentLog");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.SystemLabel", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("Id");
+
+ b.ToTable("SystemLabel");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ Name = "Default"
+ },
+ new
+ {
+ Id = 2,
+ Name = "Bin"
+ },
+ new
+ {
+ Id = 3,
+ Name = "Archive"
+ });
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("Action")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("AuthorizationAttribute")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("DialogId")
+ .HasColumnType("uuid");
+
+ b.Property("UpdatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DialogId");
+
+ b.ToTable("DialogApiAction");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiActionEndpoint", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("ActionId")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("Deprecated")
+ .HasColumnType("boolean");
+
+ b.Property("DocumentationUrl")
+ .HasMaxLength(1023)
+ .HasColumnType("character varying(1023)");
+
+ b.Property("HttpMethodId")
+ .HasColumnType("integer");
+
+ b.Property("RequestSchema")
+ .HasMaxLength(1023)
+ .HasColumnType("character varying(1023)");
+
+ b.Property("ResponseSchema")
+ .HasMaxLength(1023)
+ .HasColumnType("character varying(1023)");
+
+ b.Property("SunsetAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UpdatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasMaxLength(1023)
+ .HasColumnType("character varying(1023)");
+
+ b.Property("Version")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ActionId");
+
+ b.HasIndex("HttpMethodId");
+
+ b.ToTable("DialogApiActionEndpoint");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("Action")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("AuthorizationAttribute")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("DialogId")
+ .HasColumnType("uuid");
+
+ b.Property("HttpMethodId")
+ .HasColumnType("integer");
+
+ b.Property("IsDeleteDialogAction")
+ .HasColumnType("boolean");
+
+ b.Property("PriorityId")
+ .HasColumnType("integer");
+
+ b.Property("UpdatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasMaxLength(1023)
+ .HasColumnType("character varying(1023)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DialogId");
+
+ b.HasIndex("HttpMethodId");
+
+ b.HasIndex("PriorityId");
+
+ b.ToTable("DialogGuiAction");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPriority", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("Id");
+
+ b.ToTable("DialogGuiActionPriority");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ Name = "Primary"
+ },
+ new
+ {
+ Id = 2,
+ Name = "Secondary"
+ },
+ new
+ {
+ Id = 3,
+ Name = "Tertiary"
+ });
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("DialogId")
+ .HasColumnType("uuid");
+
+ b.Property("ExtendedType")
+ .HasMaxLength(1023)
+ .HasColumnType("character varying(1023)");
+
+ b.Property("TransmissionId")
+ .HasColumnType("uuid");
+
+ b.Property("TypeId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DialogId");
+
+ b.HasIndex("TransmissionId");
+
+ b.HasIndex("TypeId");
+
+ b.ToTable("DialogActivity");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityType", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("Id");
+
+ b.ToTable("DialogActivityType");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ Name = "DialogCreated"
+ },
+ new
+ {
+ Id = 2,
+ Name = "DialogClosed"
+ },
+ new
+ {
+ Id = 3,
+ Name = "Information"
+ },
+ new
+ {
+ Id = 4,
+ Name = "TransmissionOpened"
+ },
+ new
+ {
+ Id = 5,
+ Name = "PaymentMade"
+ },
+ new
+ {
+ Id = 6,
+ Name = "SignatureProvided"
+ },
+ new
+ {
+ Id = 7,
+ Name = "DialogOpened"
+ },
+ new
+ {
+ Id = 8,
+ Name = "DialogDeleted"
+ },
+ new
+ {
+ Id = 9,
+ Name = "DialogRestored"
+ },
+ new
+ {
+ Id = 10,
+ Name = "SentToSigning"
+ },
+ new
+ {
+ Id = 11,
+ Name = "SentToFormFill"
+ },
+ new
+ {
+ Id = 12,
+ Name = "SentToSendIn"
+ },
+ new
+ {
+ Id = 13,
+ Name = "SentToPayment"
+ });
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents.DialogContent", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("DialogId")
+ .HasColumnType("uuid");
+
+ b.Property("MediaType")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("TypeId")
+ .HasColumnType("integer");
+
+ b.Property("UpdatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TypeId");
+
+ b.HasIndex("DialogId", "TypeId")
+ .IsUnique();
+
+ b.ToTable("DialogContent");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents.DialogContentType", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.PrimitiveCollection("AllowedMediaTypes")
+ .IsRequired()
+ .HasColumnType("text[]");
+
+ b.Property("MaxLength")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("OutputInList")
+ .HasColumnType("boolean");
+
+ b.Property("Required")
+ .HasColumnType("boolean");
+
+ b.HasKey("Id");
+
+ b.ToTable("DialogContentType");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ AllowedMediaTypes = new[] { "text/plain" },
+ MaxLength = 255,
+ Name = "Title",
+ OutputInList = true,
+ Required = true
+ },
+ new
+ {
+ Id = 2,
+ AllowedMediaTypes = new[] { "text/plain" },
+ MaxLength = 255,
+ Name = "SenderName",
+ OutputInList = true,
+ Required = false
+ },
+ new
+ {
+ Id = 3,
+ AllowedMediaTypes = new[] { "text/plain" },
+ MaxLength = 255,
+ Name = "Summary",
+ OutputInList = true,
+ Required = true
+ },
+ new
+ {
+ Id = 4,
+ AllowedMediaTypes = new[] { "text/plain", "text/markdown" },
+ MaxLength = 1023,
+ Name = "AdditionalInfo",
+ OutputInList = false,
+ Required = false
+ },
+ new
+ {
+ Id = 5,
+ AllowedMediaTypes = new[] { "text/plain" },
+ MaxLength = 20,
+ Name = "ExtendedStatus",
+ OutputInList = true,
+ Required = false
+ },
+ new
+ {
+ Id = 6,
+ AllowedMediaTypes = new[] { "application/vnd.dialogporten.frontchannelembed+json;type=markdown" },
+ MaxLength = 1023,
+ Name = "MainContentReference",
+ OutputInList = false,
+ Required = false
+ });
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("Deleted")
+ .HasColumnType("boolean");
+
+ b.Property("DeletedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DueAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ExpiresAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ExtendedStatus")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("ExternalReference")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Org")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)")
+ .UseCollation("C");
+
+ b.Property("Party")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)")
+ .UseCollation("C");
+
+ b.Property("PrecedingProcess")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Process")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Progress")
+ .HasColumnType("integer");
+
+ b.Property("Revision")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("ServiceResource")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)")
+ .UseCollation("C");
+
+ b.Property("ServiceResourceType")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("StatusId")
+ .HasColumnType("integer");
+
+ b.Property("UpdatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("VisibleFrom")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedAt");
+
+ b.HasIndex("DueAt");
+
+ b.HasIndex("Org");
+
+ b.HasIndex("Party");
+
+ b.HasIndex("Process");
+
+ b.HasIndex("ServiceResource");
+
+ b.HasIndex("StatusId");
+
+ b.HasIndex("UpdatedAt");
+
+ b.ToTable("Dialog", (string)null);
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSearchTag", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("DialogId")
+ .HasColumnType("uuid");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasMaxLength(63)
+ .HasColumnType("character varying(63)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DialogId", "Value")
+ .IsUnique();
+
+ b.ToTable("DialogSearchTag");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("DialogId")
+ .HasColumnType("uuid");
+
+ b.Property("EndUserTypeId")
+ .HasColumnType("integer");
+
+ b.Property("IsViaServiceOwner")
+ .HasColumnType("boolean");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DialogId");
+
+ b.HasIndex("EndUserTypeId");
+
+ b.ToTable("DialogSeenLog");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogStatus", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("Id");
+
+ b.ToTable("DialogStatus");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ Name = "New"
+ },
+ new
+ {
+ Id = 2,
+ Name = "InProgress"
+ },
+ new
+ {
+ Id = 3,
+ Name = "Draft"
+ },
+ new
+ {
+ Id = 4,
+ Name = "Sent"
+ },
+ new
+ {
+ Id = 5,
+ Name = "RequiresAttention"
+ },
+ new
+ {
+ Id = 6,
+ Name = "Completed"
+ });
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogUserType", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("Id");
+
+ b.ToTable("DialogUserType");
+
+ b.HasData(
+ new
+ {
+ Id = 0,
+ Name = "Unknown"
+ },
+ new
+ {
+ Id = 1,
+ Name = "Person"
+ },
+ new
+ {
+ Id = 2,
+ Name = "SystemUser"
+ },
+ new
+ {
+ Id = 3,
+ Name = "ServiceOwner"
+ },
+ new
+ {
+ Id = 4,
+ Name = "ServiceOwnerOnBehalfOfPerson"
+ });
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.Contents.DialogTransmissionContent", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("MediaType")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("TransmissionId")
+ .HasColumnType("uuid");
+
+ b.Property("TypeId")
+ .HasColumnType("integer");
+
+ b.Property("UpdatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TypeId");
+
+ b.HasIndex("TransmissionId", "TypeId")
+ .IsUnique();
+
+ b.ToTable("DialogTransmissionContent");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.Contents.DialogTransmissionContentType", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.PrimitiveCollection("AllowedMediaTypes")
+ .IsRequired()
+ .HasColumnType("text[]");
+
+ b.Property("MaxLength")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Required")
+ .HasColumnType("boolean");
+
+ b.HasKey("Id");
+
+ b.ToTable("DialogTransmissionContentType");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ AllowedMediaTypes = new[] { "text/plain" },
+ MaxLength = 255,
+ Name = "Title",
+ Required = true
+ },
+ new
+ {
+ Id = 2,
+ AllowedMediaTypes = new[] { "text/plain" },
+ MaxLength = 255,
+ Name = "Summary",
+ Required = true
+ },
+ new
+ {
+ Id = 3,
+ AllowedMediaTypes = new[] { "application/vnd.dialogporten.frontchannelembed+json;type=markdown" },
+ MaxLength = 1023,
+ Name = "ContentReference",
+ Required = false
+ });
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmission", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("AuthorizationAttribute")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("DialogId")
+ .HasColumnType("uuid");
+
+ b.Property("ExtendedType")
+ .HasMaxLength(1023)
+ .HasColumnType("character varying(1023)");
+
+ b.Property("RelatedTransmissionId")
+ .HasColumnType("uuid");
+
+ b.Property("TypeId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DialogId");
+
+ b.HasIndex("RelatedTransmissionId");
+
+ b.HasIndex("TypeId");
+
+ b.ToTable("DialogTransmission");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmissionType", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("Id");
+
+ b.ToTable("DialogTransmissionType");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ Name = "Information"
+ },
+ new
+ {
+ Id = 2,
+ Name = "Acceptance"
+ },
+ new
+ {
+ Id = 3,
+ Name = "Rejection"
+ },
+ new
+ {
+ Id = 4,
+ Name = "Request"
+ },
+ new
+ {
+ Id = 5,
+ Name = "Alert"
+ },
+ new
+ {
+ Id = 6,
+ Name = "Decision"
+ },
+ new
+ {
+ Id = 7,
+ Name = "Submission"
+ },
+ new
+ {
+ Id = 8,
+ Name = "Correction"
+ });
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Http.HttpVerb", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("Id");
+
+ b.ToTable("HttpVerb");
+
+ b.HasData(
+ new
+ {
+ Id = 1,
+ Name = "GET"
+ },
+ new
+ {
+ Id = 2,
+ Name = "POST"
+ },
+ new
+ {
+ Id = 3,
+ Name = "PUT"
+ },
+ new
+ {
+ Id = 4,
+ Name = "PATCH"
+ },
+ new
+ {
+ Id = 5,
+ Name = "DELETE"
+ },
+ new
+ {
+ Id = 6,
+ Name = "HEAD"
+ },
+ new
+ {
+ Id = 7,
+ Name = "OPTIONS"
+ },
+ new
+ {
+ Id = 8,
+ Name = "TRACE"
+ },
+ new
+ {
+ Id = 9,
+ Name = "CONNECT"
+ });
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.Localization", b =>
+ {
+ b.Property("LocalizationSetId")
+ .HasColumnType("uuid");
+
+ b.Property("LanguageCode")
+ .HasMaxLength(15)
+ .HasColumnType("character varying(15)");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("UpdatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasMaxLength(4095)
+ .HasColumnType("character varying(4095)");
+
+ b.HasKey("LocalizationSetId", "LanguageCode");
+
+ b.ToTable("Localization");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("Discriminator")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("Id");
+
+ b.ToTable("LocalizationSet");
+
+ b.HasDiscriminator().HasValue("LocalizationSet");
+
+ b.UseTphMappingStrategy();
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.ResourcePolicyInformation.ResourcePolicyInformation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("MinimumAuthenticationLevel")
+ .HasColumnType("integer");
+
+ b.Property("Resource")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("UpdatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Resource")
+ .IsUnique();
+
+ b.ToTable("ResourcePolicyInformation");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.SubjectResources.SubjectResource", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasDefaultValueSql("gen_random_uuid()");
+
+ b.Property("CreatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.Property("Resource")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Subject")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("UpdatedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Resource", "Subject")
+ .IsUnique();
+
+ b.ToTable("SubjectResource");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Infrastructure.Persistence.IdempotentNotifications.NotificationAcknowledgement", b =>
+ {
+ b.Property("EventId")
+ .HasColumnType("uuid");
+
+ b.Property("NotificationHandler")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("AcknowledgedAt")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp with time zone")
+ .HasDefaultValueSql("current_timestamp at time zone 'utc'");
+
+ b.HasKey("EventId", "NotificationHandler");
+
+ b.HasIndex("EventId");
+
+ b.ToTable("NotificationAcknowledgement");
+ });
+
+ modelBuilder.Entity("MassTransit.EntityFrameworkCoreIntegration.InboxState", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Consumed")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ConsumerId")
+ .HasColumnType("uuid");
+
+ b.Property("Delivered")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ExpirationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LastSequenceNumber")
+ .HasColumnType("bigint");
+
+ b.Property("LockId")
+ .HasColumnType("uuid");
+
+ b.Property("MessageId")
+ .HasColumnType("uuid");
+
+ b.Property("ReceiveCount")
+ .HasColumnType("integer");
+
+ b.Property("Received")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("bytea");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Delivered");
+
+ b.ToTable("MassTransitInboxState", (string)null);
+ });
+
+ modelBuilder.Entity("MassTransit.EntityFrameworkCoreIntegration.OutboxMessage", b =>
+ {
+ b.Property("SequenceNumber")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("SequenceNumber"));
+
+ b.Property("Body")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ContentType")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("ConversationId")
+ .HasColumnType("uuid");
+
+ b.Property("CorrelationId")
+ .HasColumnType("uuid");
+
+ b.Property("DestinationAddress")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("EnqueueTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ExpirationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("FaultAddress")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("Headers")
+ .HasColumnType("text");
+
+ b.Property("InboxConsumerId")
+ .HasColumnType("uuid");
+
+ b.Property("InboxMessageId")
+ .HasColumnType("uuid");
+
+ b.Property("InitiatorId")
+ .HasColumnType("uuid");
+
+ b.Property("MessageId")
+ .HasColumnType("uuid");
+
+ b.Property("MessageType")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OutboxId")
+ .HasColumnType("uuid");
+
+ b.Property("Properties")
+ .HasColumnType("text");
+
+ b.Property("RequestId")
+ .HasColumnType("uuid");
+
+ b.Property("ResponseAddress")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("SentTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("SourceAddress")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.HasKey("SequenceNumber");
+
+ b.HasIndex("EnqueueTime");
+
+ b.HasIndex("ExpirationTime");
+
+ b.HasIndex("OutboxId", "SequenceNumber")
+ .IsUnique();
+
+ b.HasIndex("InboxMessageId", "InboxConsumerId", "SequenceNumber")
+ .IsUnique();
+
+ b.ToTable("MassTransitOutboxMessage", (string)null);
+ });
+
+ modelBuilder.Entity("MassTransit.EntityFrameworkCoreIntegration.OutboxState", b =>
+ {
+ b.Property("OutboxId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Created")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Delivered")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LastSequenceNumber")
+ .HasColumnType("bigint");
+
+ b.Property("LockId")
+ .HasColumnType("uuid");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .ValueGeneratedOnAddOrUpdate()
+ .HasColumnType("bytea");
+
+ b.HasKey("OutboxId");
+
+ b.HasIndex("Created");
+
+ b.ToTable("MassTransitOutboxState", (string)null);
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.LabelAssignmentLogActor", b =>
+ {
+ b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Actors.Actor");
+
+ b.Property("LabelAssignmentLogId")
+ .HasColumnType("uuid");
+
+ b.HasIndex("LabelAssignmentLogId")
+ .IsUnique();
+
+ b.HasDiscriminator().HasValue("LabelAssignmentLogActor");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityPerformedByActor", b =>
+ {
+ b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Actors.Actor");
+
+ b.Property("ActivityId")
+ .HasColumnType("uuid");
+
+ b.HasIndex("ActivityId")
+ .IsUnique();
+
+ b.HasDiscriminator().HasValue("DialogActivityPerformedByActor");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLogSeenByActor", b =>
+ {
+ b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Actors.Actor");
+
+ b.Property("DialogSeenLogId")
+ .HasColumnType("uuid");
+
+ b.HasIndex("DialogSeenLogId")
+ .IsUnique();
+
+ b.HasDiscriminator().HasValue("DialogSeenLogSeenByActor");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmissionSenderActor", b =>
+ {
+ b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Actors.Actor");
+
+ b.Property("TransmissionId")
+ .HasColumnType("uuid");
+
+ b.HasIndex("TransmissionId")
+ .IsUnique();
+
+ b.HasDiscriminator().HasValue("DialogTransmissionSenderActor");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogAttachment", b =>
+ {
+ b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Attachments.Attachment");
+
+ b.Property("DialogId")
+ .HasColumnType("uuid");
+
+ b.HasIndex("DialogId");
+
+ b.HasDiscriminator().HasValue("DialogAttachment");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmissionAttachment", b =>
+ {
+ b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Attachments.Attachment");
+
+ b.Property("TransmissionId")
+ .HasColumnType("uuid");
+
+ b.HasIndex("TransmissionId");
+
+ b.HasDiscriminator().HasValue("DialogTransmissionAttachment");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Attachments.AttachmentDisplayName", b =>
+ {
+ b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet");
+
+ b.Property("AttachmentId")
+ .HasColumnType("uuid");
+
+ b.HasIndex("AttachmentId")
+ .IsUnique();
+
+ b.HasDiscriminator().HasValue("AttachmentDisplayName");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPrompt", b =>
+ {
+ b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet");
+
+ b.Property("GuiActionId")
+ .HasColumnType("uuid");
+
+ b.HasIndex("GuiActionId")
+ .IsUnique();
+
+ b.ToTable("LocalizationSet", t =>
+ {
+ t.Property("GuiActionId")
+ .HasColumnName("DialogGuiActionPrompt_GuiActionId");
+ });
+
+ b.HasDiscriminator().HasValue("DialogGuiActionPrompt");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionTitle", b =>
+ {
+ b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet");
+
+ b.Property("GuiActionId")
+ .HasColumnType("uuid");
+
+ b.HasIndex("GuiActionId")
+ .IsUnique();
+
+ b.HasDiscriminator().HasValue("DialogGuiActionTitle");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityDescription", b =>
+ {
+ b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet");
+
+ b.Property("ActivityId")
+ .HasColumnType("uuid");
+
+ b.HasIndex("ActivityId")
+ .IsUnique();
+
+ b.HasDiscriminator().HasValue("DialogActivityDescription");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents.DialogContentValue", b =>
+ {
+ b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet");
+
+ b.Property("DialogContentId")
+ .HasColumnType("uuid");
+
+ b.HasIndex("DialogContentId")
+ .IsUnique();
+
+ b.HasDiscriminator().HasValue("DialogContentValue");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.Contents.DialogTransmissionContentValue", b =>
+ {
+ b.HasBaseType("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet");
+
+ b.Property("TransmissionContentId")
+ .HasColumnType("uuid");
+
+ b.HasIndex("TransmissionContentId")
+ .IsUnique();
+
+ b.HasDiscriminator().HasValue("DialogTransmissionContentValue");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Actors.Actor", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Actors.ActorType", "ActorType")
+ .WithMany()
+ .HasForeignKey("ActorTypeId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("ActorType");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Attachments.AttachmentUrl", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Attachments.Attachment", "Attachment")
+ .WithMany("Urls")
+ .HasForeignKey("AttachmentId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Attachments.AttachmentUrlConsumerType", "ConsumerType")
+ .WithMany()
+ .HasForeignKey("ConsumerTypeId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Attachment");
+
+ b.Navigation("ConsumerType");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.DialogEndUserContext", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog")
+ .WithOne("DialogEndUserContext")
+ .HasForeignKey("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.DialogEndUserContext", "DialogId")
+ .OnDelete(DeleteBehavior.SetNull);
+
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.SystemLabel", "SystemLabel")
+ .WithMany()
+ .HasForeignKey("SystemLabelId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Dialog");
+
+ b.Navigation("SystemLabel");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.LabelAssignmentLog", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.DialogEndUserContext", "Context")
+ .WithMany("LabelAssignmentLogs")
+ .HasForeignKey("ContextId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Context");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog")
+ .WithMany("ApiActions")
+ .HasForeignKey("DialogId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Dialog");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiActionEndpoint", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", "Action")
+ .WithMany("Endpoints")
+ .HasForeignKey("ActionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Http.HttpVerb", "HttpMethod")
+ .WithMany()
+ .HasForeignKey("HttpMethodId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Action");
+
+ b.Navigation("HttpMethod");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog")
+ .WithMany("GuiActions")
+ .HasForeignKey("DialogId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Http.HttpVerb", "HttpMethod")
+ .WithMany()
+ .HasForeignKey("HttpMethodId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPriority", "Priority")
+ .WithMany()
+ .HasForeignKey("PriorityId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Dialog");
+
+ b.Navigation("HttpMethod");
+
+ b.Navigation("Priority");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog")
+ .WithMany("Activities")
+ .HasForeignKey("DialogId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmission", "Transmission")
+ .WithMany("Activities")
+ .HasForeignKey("TransmissionId")
+ .OnDelete(DeleteBehavior.SetNull);
+
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityType", "Type")
+ .WithMany()
+ .HasForeignKey("TypeId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Dialog");
+
+ b.Navigation("Transmission");
+
+ b.Navigation("Type");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents.DialogContent", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog")
+ .WithMany("Content")
+ .HasForeignKey("DialogId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents.DialogContentType", "Type")
+ .WithMany()
+ .HasForeignKey("TypeId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Dialog");
+
+ b.Navigation("Type");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogStatus", "Status")
+ .WithMany()
+ .HasForeignKey("StatusId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Status");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSearchTag", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog")
+ .WithMany("SearchTags")
+ .HasForeignKey("DialogId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Dialog");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLog", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog")
+ .WithMany("SeenLog")
+ .HasForeignKey("DialogId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogUserType", "EndUserType")
+ .WithMany()
+ .HasForeignKey("EndUserTypeId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Dialog");
+
+ b.Navigation("EndUserType");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.Contents.DialogTransmissionContent", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmission", "Transmission")
+ .WithMany("Content")
+ .HasForeignKey("TransmissionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.Contents.DialogTransmissionContentType", "Type")
+ .WithMany()
+ .HasForeignKey("TypeId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Transmission");
+
+ b.Navigation("Type");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmission", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog")
+ .WithMany("Transmissions")
+ .HasForeignKey("DialogId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmission", "RelatedTransmission")
+ .WithMany("RelatedTransmissions")
+ .HasForeignKey("RelatedTransmissionId")
+ .OnDelete(DeleteBehavior.SetNull);
+
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmissionType", "Type")
+ .WithMany()
+ .HasForeignKey("TypeId")
+ .OnDelete(DeleteBehavior.Restrict)
+ .IsRequired();
+
+ b.Navigation("Dialog");
+
+ b.Navigation("RelatedTransmission");
+
+ b.Navigation("Type");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.Localization", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", "LocalizationSet")
+ .WithMany("Localizations")
+ .HasForeignKey("LocalizationSetId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("LocalizationSet");
+ });
+
+ modelBuilder.Entity("MassTransit.EntityFrameworkCoreIntegration.OutboxMessage", b =>
+ {
+ b.HasOne("MassTransit.EntityFrameworkCoreIntegration.OutboxState", null)
+ .WithMany()
+ .HasForeignKey("OutboxId");
+
+ b.HasOne("MassTransit.EntityFrameworkCoreIntegration.InboxState", null)
+ .WithMany()
+ .HasForeignKey("InboxMessageId", "InboxConsumerId")
+ .HasPrincipalKey("MessageId", "ConsumerId");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.LabelAssignmentLogActor", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.LabelAssignmentLog", "LabelAssignmentLog")
+ .WithOne("PerformedBy")
+ .HasForeignKey("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.LabelAssignmentLogActor", "LabelAssignmentLogId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("LabelAssignmentLog");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityPerformedByActor", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", "Activity")
+ .WithOne("PerformedBy")
+ .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityPerformedByActor", "ActivityId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Activity");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLogSeenByActor", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLog", "DialogSeenLog")
+ .WithOne("SeenBy")
+ .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLogSeenByActor", "DialogSeenLogId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("DialogSeenLog");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmissionSenderActor", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmission", "Transmission")
+ .WithOne("Sender")
+ .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmissionSenderActor", "TransmissionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Transmission");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogAttachment", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", "Dialog")
+ .WithMany("Attachments")
+ .HasForeignKey("DialogId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Dialog");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmissionAttachment", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmission", "Transmission")
+ .WithMany("Attachments")
+ .HasForeignKey("TransmissionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Transmission");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Attachments.AttachmentDisplayName", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Attachments.Attachment", "Attachment")
+ .WithOne("DisplayName")
+ .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Attachments.AttachmentDisplayName", "AttachmentId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Attachment");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPrompt", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", "GuiAction")
+ .WithOne("Prompt")
+ .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionPrompt", "GuiActionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("GuiAction");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionTitle", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", "GuiAction")
+ .WithOne("Title")
+ .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiActionTitle", "GuiActionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("GuiAction");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityDescription", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", "Activity")
+ .WithOne("Description")
+ .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivityDescription", "ActivityId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Activity");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents.DialogContentValue", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents.DialogContent", "DialogContent")
+ .WithOne("Value")
+ .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents.DialogContentValue", "DialogContentId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("DialogContent");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.Contents.DialogTransmissionContentValue", b =>
+ {
+ b.HasOne("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.Contents.DialogTransmissionContent", "TransmissionContent")
+ .WithOne("Value")
+ .HasForeignKey("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.Contents.DialogTransmissionContentValue", "TransmissionContentId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("TransmissionContent");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Attachments.Attachment", b =>
+ {
+ b.Navigation("DisplayName");
+
+ b.Navigation("Urls");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.DialogEndUserContext", b =>
+ {
+ b.Navigation("LabelAssignmentLogs");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities.LabelAssignmentLog", b =>
+ {
+ b.Navigation("PerformedBy")
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogApiAction", b =>
+ {
+ b.Navigation("Endpoints");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions.DialogGuiAction", b =>
+ {
+ b.Navigation("Prompt");
+
+ b.Navigation("Title");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities.DialogActivity", b =>
+ {
+ b.Navigation("Description");
+
+ b.Navigation("PerformedBy")
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents.DialogContent", b =>
+ {
+ b.Navigation("Value")
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogEntity", b =>
+ {
+ b.Navigation("Activities");
+
+ b.Navigation("ApiActions");
+
+ b.Navigation("Attachments");
+
+ b.Navigation("Content");
+
+ b.Navigation("DialogEndUserContext")
+ .IsRequired();
+
+ b.Navigation("GuiActions");
+
+ b.Navigation("SearchTags");
+
+ b.Navigation("SeenLog");
+
+ b.Navigation("Transmissions");
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.DialogSeenLog", b =>
+ {
+ b.Navigation("SeenBy")
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.Contents.DialogTransmissionContent", b =>
+ {
+ b.Navigation("Value")
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.DialogTransmission", b =>
+ {
+ b.Navigation("Activities");
+
+ b.Navigation("Attachments");
+
+ b.Navigation("Content");
+
+ b.Navigation("RelatedTransmissions");
+
+ b.Navigation("Sender")
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Digdir.Domain.Dialogporten.Domain.Localizations.LocalizationSet", b =>
+ {
+ b.Navigation("Localizations");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20241222195253_AddAdditionalActivityTypes.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20241222195253_AddAdditionalActivityTypes.cs
new file mode 100644
index 000000000..7b2663816
--- /dev/null
+++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/20241222195253_AddAdditionalActivityTypes.cs
@@ -0,0 +1,63 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
+
+namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Migrations
+{
+ ///
+ public partial class AddAdditionalActivityTypes : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.InsertData(
+ table: "DialogActivityType",
+ columns: new[] { "Id", "Name" },
+ values: new object[,]
+ {
+ { 8, "DialogDeleted" },
+ { 9, "DialogRestored" },
+ { 10, "SentToSigning" },
+ { 11, "SentToFormFill" },
+ { 12, "SentToSendIn" },
+ { 13, "SentToPayment" }
+ });
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DeleteData(
+ table: "DialogActivityType",
+ keyColumn: "Id",
+ keyValue: 8);
+
+ migrationBuilder.DeleteData(
+ table: "DialogActivityType",
+ keyColumn: "Id",
+ keyValue: 9);
+
+ migrationBuilder.DeleteData(
+ table: "DialogActivityType",
+ keyColumn: "Id",
+ keyValue: 10);
+
+ migrationBuilder.DeleteData(
+ table: "DialogActivityType",
+ keyColumn: "Id",
+ keyValue: 11);
+
+ migrationBuilder.DeleteData(
+ table: "DialogActivityType",
+ keyColumn: "Id",
+ keyValue: 12);
+
+ migrationBuilder.DeleteData(
+ table: "DialogActivityType",
+ keyColumn: "Id",
+ keyValue: 13);
+ }
+ }
+}
diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs
index 78f3166d4..c1495c887 100644
--- a/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs
+++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/DialogDbContextModelSnapshot.cs
@@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
- .HasAnnotation("ProductVersion", "8.0.10")
+ .HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@@ -561,6 +561,36 @@ protected override void BuildModel(ModelBuilder modelBuilder)
{
Id = 7,
Name = "DialogOpened"
+ },
+ new
+ {
+ Id = 8,
+ Name = "DialogDeleted"
+ },
+ new
+ {
+ Id = 9,
+ Name = "DialogRestored"
+ },
+ new
+ {
+ Id = 10,
+ Name = "SentToSigning"
+ },
+ new
+ {
+ Id = 11,
+ Name = "SentToFormFill"
+ },
+ new
+ {
+ Id = 12,
+ Name = "SentToSendIn"
+ },
+ new
+ {
+ Id = 13,
+ Name = "SentToPayment"
});
});
@@ -607,7 +637,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("Id")
.HasColumnType("integer");
- b.Property("AllowedMediaTypes")
+ b.PrimitiveCollection("AllowedMediaTypes")
.IsRequired()
.HasColumnType("text[]");
@@ -980,7 +1010,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("Id")
.HasColumnType("integer");
- b.Property("AllowedMediaTypes")
+ b.PrimitiveCollection("AllowedMediaTypes")
.IsRequired()
.HasColumnType("text[]");
diff --git a/src/Digdir.Domain.Dialogporten.Janitor/Digdir.Domain.Dialogporten.Janitor.csproj b/src/Digdir.Domain.Dialogporten.Janitor/Digdir.Domain.Dialogporten.Janitor.csproj
index 41b558ee8..1b929c63a 100644
--- a/src/Digdir.Domain.Dialogporten.Janitor/Digdir.Domain.Dialogporten.Janitor.csproj
+++ b/src/Digdir.Domain.Dialogporten.Janitor/Digdir.Domain.Dialogporten.Janitor.csproj
@@ -13,8 +13,8 @@
-
-
+
+
diff --git a/src/Digdir.Domain.Dialogporten.Janitor/Dockerfile b/src/Digdir.Domain.Dialogporten.Janitor/Dockerfile
index 8b0dbc453..0ba540bbd 100644
--- a/src/Digdir.Domain.Dialogporten.Janitor/Dockerfile
+++ b/src/Digdir.Domain.Dialogporten.Janitor/Dockerfile
@@ -1,7 +1,7 @@
-FROM mcr.microsoft.com/dotnet/aspnet:9.0.0@sha256:d8f01f752bf9bd3ff630319181a2ccfbeecea4080a1912095a34002f61bfa345 AS base
+FROM mcr.microsoft.com/dotnet/aspnet:9.0.0@sha256:b4bea3a52a0a77317fa93c5bbdb076623f81e3e2f201078d89914da71318b5d8 AS base
WORKDIR /app
-FROM mcr.microsoft.com/dotnet/sdk:9.0.101@sha256:fe8ceeca5ee197deba95419e3b85c32744970b730ae11645e13f1cb74a848d98 AS build
+FROM mcr.microsoft.com/dotnet/sdk:9.0.101@sha256:3fcf6f1e809c0553f9feb222369f58749af314af6f063f389cbd2f913b4ad556 AS build
WORKDIR /src
COPY [".editorconfig", "."]
diff --git a/src/Digdir.Domain.Dialogporten.Service/Digdir.Domain.Dialogporten.Service.csproj b/src/Digdir.Domain.Dialogporten.Service/Digdir.Domain.Dialogporten.Service.csproj
index 5edb017d4..a4b65ddb0 100644
--- a/src/Digdir.Domain.Dialogporten.Service/Digdir.Domain.Dialogporten.Service.csproj
+++ b/src/Digdir.Domain.Dialogporten.Service/Digdir.Domain.Dialogporten.Service.csproj
@@ -1,7 +1,7 @@
-
+
diff --git a/src/Digdir.Domain.Dialogporten.Service/Dockerfile b/src/Digdir.Domain.Dialogporten.Service/Dockerfile
index 79d760639..dfe7c1653 100644
--- a/src/Digdir.Domain.Dialogporten.Service/Dockerfile
+++ b/src/Digdir.Domain.Dialogporten.Service/Dockerfile
@@ -1,7 +1,7 @@
-FROM mcr.microsoft.com/dotnet/aspnet:9.0.0@sha256:d8f01f752bf9bd3ff630319181a2ccfbeecea4080a1912095a34002f61bfa345 AS base
+FROM mcr.microsoft.com/dotnet/aspnet:9.0.0@sha256:b4bea3a52a0a77317fa93c5bbdb076623f81e3e2f201078d89914da71318b5d8 AS base
WORKDIR /app
-FROM mcr.microsoft.com/dotnet/sdk:9.0.101@sha256:fe8ceeca5ee197deba95419e3b85c32744970b730ae11645e13f1cb74a848d98 AS build
+FROM mcr.microsoft.com/dotnet/sdk:9.0.101@sha256:3fcf6f1e809c0553f9feb222369f58749af314af6f063f389cbd2f913b4ad556 AS build
WORKDIR /src
COPY [".editorconfig", "."]
diff --git a/src/Digdir.Domain.Dialogporten.Service/Program.cs b/src/Digdir.Domain.Dialogporten.Service/Program.cs
index 3fffde09d..28a1c3a39 100644
--- a/src/Digdir.Domain.Dialogporten.Service/Program.cs
+++ b/src/Digdir.Domain.Dialogporten.Service/Program.cs
@@ -51,7 +51,14 @@ static void BuildAndRun(string[] args, TelemetryConfiguration telemetryConfigura
.AddAzureConfiguration(builder.Environment.EnvironmentName)
.AddLocalConfiguration(builder.Environment);
- builder.ConfigureTelemetry();
+ builder.ConfigureTelemetry((settings, configuration) =>
+ {
+ settings.ServiceName = configuration["OTEL_SERVICE_NAME"] ?? builder.Environment.ApplicationName;
+ settings.Endpoint = configuration["OTEL_EXPORTER_OTLP_ENDPOINT"];
+ settings.Protocol = configuration["OTEL_EXPORTER_OTLP_PROTOCOL"];
+ settings.AppInsightsConnectionString = configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"];
+ settings.ResourceAttributes = configuration["OTEL_RESOURCE_ATTRIBUTES"];
+ });
builder.Services
.AddAzureAppConfiguration()
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Constants.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Constants.cs
index 17daba2ee..27a652350 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Common/Constants.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Constants.cs
@@ -3,6 +3,7 @@
internal static class Constants
{
internal const string IfMatch = "If-Match";
+ internal const string ETag = "Etag";
internal const string Authorization = "Authorization";
internal const string CurrentTokenIssuer = "CurrentIssuer";
internal const int MaxRequestBodySize = 100_000;
@@ -10,7 +11,7 @@ internal static class Constants
internal static class SwaggerSummary
{
internal const string ReturnedResult = "Successfully returned the dialog {0}.";
- internal const string Created = "The UUID of the created the dialog {0}. A relative URL to the newly created activity is set in the \"Location\" header.";
+ internal const string Created = "The UUID of the created dialog {0}. A relative URL to the newly created activity is set in the \"Location\" header.";
internal const string Deleted = "The dialog {0} was deleted successfully.";
internal const string Updated = "The dialog {0} was updated successfully.";
internal const string ValidationError = "Validation error occured. See problem details for a list of errors.";
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Digdir.Domain.Dialogporten.WebApi.csproj b/src/Digdir.Domain.Dialogporten.WebApi/Digdir.Domain.Dialogporten.WebApi.csproj
index 590d00b8d..4d3a6285c 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Digdir.Domain.Dialogporten.WebApi.csproj
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Digdir.Domain.Dialogporten.WebApi.csproj
@@ -15,16 +15,16 @@
-
+
-
+
-
-
-
-
+
+
+
+
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Dockerfile b/src/Digdir.Domain.Dialogporten.WebApi/Dockerfile
index 1c82387b4..fe5608db5 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Dockerfile
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Dockerfile
@@ -1,8 +1,8 @@
-FROM mcr.microsoft.com/dotnet/aspnet:9.0.0@sha256:d8f01f752bf9bd3ff630319181a2ccfbeecea4080a1912095a34002f61bfa345 AS base
+FROM mcr.microsoft.com/dotnet/aspnet:9.0.0@sha256:b4bea3a52a0a77317fa93c5bbdb076623f81e3e2f201078d89914da71318b5d8 AS base
WORKDIR /app
EXPOSE 8080
-FROM mcr.microsoft.com/dotnet/sdk:9.0.101@sha256:fe8ceeca5ee197deba95419e3b85c32744970b730ae11645e13f1cb74a848d98 AS build
+FROM mcr.microsoft.com/dotnet/sdk:9.0.101@sha256:3fcf6f1e809c0553f9feb222369f58749af314af6f063f389cbd2f913b4ad556 AS build
WORKDIR /src
COPY [".editorconfig", "."]
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/Common/Headers/HttpResponseHeaderExamples.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/Common/Headers/HttpResponseHeaderExamples.cs
new file mode 100644
index 000000000..288ed510d
--- /dev/null
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/Common/Headers/HttpResponseHeaderExamples.cs
@@ -0,0 +1,13 @@
+using Digdir.Domain.Dialogporten.WebApi.Common;
+using FastEndpoints;
+
+namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Headers;
+
+public static class HttpResponseHeaderExamples
+{
+ public static ResponseHeader NewDialogETagHeader(int statusCode)
+ => new(statusCode, Constants.ETag)
+ {
+ Description = "The new UUID ETag of the dialog",
+ };
+}
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogActivities/Create/CreateDialogActivityEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogActivities/Create/CreateDialogActivityEndpoint.cs
index 85484c662..6f1e9040a 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogActivities/Create/CreateDialogActivityEndpoint.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogActivities/Create/CreateDialogActivityEndpoint.cs
@@ -65,7 +65,16 @@ await errors.Match(
var result = await _sender.Send(updateDialogCommand, ct);
await result.Match(
- success => SendCreatedAtAsync(new GetActivityQuery { DialogId = dialog.Id, ActivityId = req.Id.Value }, req.Id, cancellation: ct),
+ success =>
+ {
+ HttpContext.Response.Headers.Append(Constants.ETag, success.Revision.ToString());
+ return SendCreatedAtAsync(
+ new GetActivityQuery
+ {
+ DialogId = dialog.Id,
+ ActivityId = req.Id.Value
+ }, req.Id, cancellation: ct);
+ },
notFound => this.NotFoundAsync(notFound, ct),
gone => this.GoneAsync(gone, ct),
validationError => this.BadRequestAsync(validationError, ct),
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogActivities/Create/CreateDialogActivityEndpointSummary.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogActivities/Create/CreateDialogActivityEndpointSummary.cs
index 8d005c557..b8f7f8057 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogActivities/Create/CreateDialogActivityEndpointSummary.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogActivities/Create/CreateDialogActivityEndpointSummary.cs
@@ -1,6 +1,7 @@
using Digdir.Domain.Dialogporten.WebApi.Common;
using Digdir.Domain.Dialogporten.WebApi.Common.Authorization;
using Digdir.Domain.Dialogporten.WebApi.Common.Extensions;
+using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Headers;
using FastEndpoints;
namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.DialogActivities.Create;
@@ -18,6 +19,7 @@ The activity is created with the given configuration. For more information see t
ResponseExamples[StatusCodes.Status201Created] = "018bb8e5-d9d0-7434-8ec5-569a6c8e01fc";
+ ResponseHeaders = [HttpResponseHeaderExamples.NewDialogETagHeader(StatusCodes.Status201Created)];
Responses[StatusCodes.Status201Created] = Constants.SwaggerSummary.Created.FormatInvariant("activity");
Responses[StatusCodes.Status400BadRequest] = Constants.SwaggerSummary.ValidationError;
Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.ServiceOwnerAuthenticationFailure.FormatInvariant(AuthorizationScope.ServiceProvider);
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionEndpoint.cs
index 2018e2d3b..27623c061 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionEndpoint.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionEndpoint.cs
@@ -66,7 +66,13 @@ await errors.Match(
var result = await _sender.Send(updateDialogCommand, ct);
await result.Match(
- success => SendCreatedAtAsync(new GetTransmissionQuery { DialogId = dialog.Id, TransmissionId = req.Id.Value }, req.Id, cancellation: ct),
+ success =>
+ {
+ HttpContext.Response.Headers.Append(Constants.ETag, success.Revision.ToString());
+ return SendCreatedAtAsync(
+ new GetTransmissionQuery { DialogId = dialog.Id, TransmissionId = req.Id.Value }, req.Id,
+ cancellation: ct);
+ },
notFound => this.NotFoundAsync(notFound, ct),
gone => this.GoneAsync(gone, ct),
validationError => this.BadRequestAsync(validationError, ct),
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionEndpointSummary.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionEndpointSummary.cs
index ecb0430cd..4af3789e3 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionEndpointSummary.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionEndpointSummary.cs
@@ -1,6 +1,7 @@
using Digdir.Domain.Dialogporten.WebApi.Common;
using Digdir.Domain.Dialogporten.WebApi.Common.Authorization;
using Digdir.Domain.Dialogporten.WebApi.Common.Extensions;
+using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Headers;
using FastEndpoints;
namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.DialogTransmissions.Create;
@@ -18,6 +19,7 @@ The transmission is created with the given configuration. For more information s
ResponseExamples[StatusCodes.Status201Created] = "018bb8e5-d9d0-7434-8ec5-569a6c8e01fc";
+ ResponseHeaders = [HttpResponseHeaderExamples.NewDialogETagHeader(StatusCodes.Status201Created)];
Responses[StatusCodes.Status201Created] = Constants.SwaggerSummary.Created.FormatInvariant("transmission");
Responses[StatusCodes.Status400BadRequest] = Constants.SwaggerSummary.ValidationError;
Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.ServiceOwnerAuthenticationFailure.FormatInvariant(AuthorizationScope.ServiceProvider);
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Create/CreateDialogEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Create/CreateDialogEndpoint.cs
index cbc9938e7..cb6f33e38 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Create/CreateDialogEndpoint.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Create/CreateDialogEndpoint.cs
@@ -1,5 +1,6 @@
using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create;
using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get;
+using Digdir.Domain.Dialogporten.WebApi.Common;
using Digdir.Domain.Dialogporten.WebApi.Common.Authorization;
using Digdir.Domain.Dialogporten.WebApi.Common.Extensions;
using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Extensions;
@@ -34,7 +35,12 @@ public override async Task HandleAsync(CreateDialogCommand req, CancellationToke
{
var result = await _sender.Send(req, ct);
await result.Match(
- success => SendCreatedAtAsync(new GetDialogQuery { DialogId = success.Value }, success.Value, cancellation: ct),
+ success =>
+ {
+ HttpContext.Response.Headers.Append(Constants.ETag, success.Revision.ToString());
+ return SendCreatedAtAsync(new GetDialogQuery { DialogId = success.DialogId },
+ success.DialogId, cancellation: ct);
+ },
domainError => this.UnprocessableEntityAsync(domainError, ct),
validationError => this.BadRequestAsync(validationError, ct),
forbidden => this.ForbiddenAsync(forbidden, ct));
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Create/CreateDialogEndpointSummary.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Create/CreateDialogEndpointSummary.cs
index ae324eb51..82f7efb89 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Create/CreateDialogEndpointSummary.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Create/CreateDialogEndpointSummary.cs
@@ -1,6 +1,7 @@
using Digdir.Domain.Dialogporten.WebApi.Common;
using Digdir.Domain.Dialogporten.WebApi.Common.Authorization;
using Digdir.Domain.Dialogporten.WebApi.Common.Extensions;
+using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Headers;
using FastEndpoints;
namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.Dialogs.Create;
@@ -18,6 +19,7 @@ The dialog is created with the given configuration. For more information see the
ResponseExamples[StatusCodes.Status201Created] = "018bb8e5-d9d0-7434-8ec5-569a6c8e01fc";
+ ResponseHeaders = [HttpResponseHeaderExamples.NewDialogETagHeader(StatusCodes.Status201Created)];
Responses[StatusCodes.Status201Created] = Constants.SwaggerSummary.Created.FormatInvariant("aggregate");
Responses[StatusCodes.Status400BadRequest] = Constants.SwaggerSummary.ValidationError;
Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.ServiceOwnerAuthenticationFailure.FormatInvariant(AuthorizationScope.ServiceProvider);
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Delete/DeleteDialogEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Delete/DeleteDialogEndpoint.cs
index 40f91368d..d8c6445de 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Delete/DeleteDialogEndpoint.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Delete/DeleteDialogEndpoint.cs
@@ -36,7 +36,11 @@ public override async Task HandleAsync(DeleteDialogRequest req, CancellationToke
var command = new DeleteDialogCommand { Id = req.DialogId, IfMatchDialogRevision = req.IfMatchDialogRevision };
var result = await _sender.Send(command, ct);
await result.Match(
- success => SendNoContentAsync(ct),
+ success =>
+ {
+ HttpContext.Response.Headers.Append(Constants.ETag, success.Revision.ToString());
+ return SendNoContentAsync(ct);
+ },
notFound => this.NotFoundAsync(notFound, ct),
gone => this.GoneAsync(gone, ct),
forbidden => this.ForbiddenAsync(forbidden, ct),
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Delete/DeleteDialogEndpointSummary.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Delete/DeleteDialogEndpointSummary.cs
index 9125a68de..973b9d1bb 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Delete/DeleteDialogEndpointSummary.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Delete/DeleteDialogEndpointSummary.cs
@@ -1,6 +1,7 @@
using Digdir.Domain.Dialogporten.WebApi.Common;
using Digdir.Domain.Dialogporten.WebApi.Common.Authorization;
using Digdir.Domain.Dialogporten.WebApi.Common.Extensions;
+using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Headers;
using FastEndpoints;
namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.Dialogs.Delete;
@@ -18,6 +19,7 @@ Deletes a given dialog (soft delete). For more information see the documentation
Optimistic concurrency control is implemented using the If-Match header. Supply the Revision value from the GetDialog endpoint to ensure that the dialog is not deleted by another request in the meantime.
""";
+ ResponseHeaders = [HttpResponseHeaderExamples.NewDialogETagHeader(StatusCodes.Status204NoContent)];
Responses[StatusCodes.Status204NoContent] = Constants.SwaggerSummary.Deleted.FormatInvariant("aggregate");
Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.ServiceOwnerAuthenticationFailure.FormatInvariant(AuthorizationScope.ServiceProvider);
Responses[StatusCodes.Status403Forbidden] = Constants.SwaggerSummary.AccessDeniedToDialog.FormatInvariant("delete");
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs
index b439574cb..f56fbb7ef 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs
@@ -60,6 +60,7 @@ public PatchDialogsController(ISender sender, IMapper mapper)
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status412PreconditionFailed)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status422UnprocessableEntity)]
+ [ProducesResponseHeader(StatusCodes.Status204NoContent, Constants.ETag, "The new UUID ETag of the dialog")]
public async Task Patch(
[FromRoute] Guid dialogId,
[FromHeader(Name = Constants.IfMatch)] Guid? etag,
@@ -87,7 +88,11 @@ public async Task Patch(
var command = new UpdateDialogCommand { Id = dialogId, IfMatchDialogRevision = etag, Dto = updateDialogDto };
var result = await _sender.Send(command, ct);
return result.Match(
- success => (IActionResult)NoContent(),
+ success =>
+ {
+ HttpContext.Response.Headers.Append(Constants.ETag, success.Revision.ToString());
+ return (IActionResult)NoContent();
+ },
notFound => NotFound(HttpContext.GetResponseOrDefault(StatusCodes.Status404NotFound, notFound.ToValidationResults())),
badRequest => BadRequest(HttpContext.GetResponseOrDefault(StatusCodes.Status400BadRequest, badRequest.ToValidationResults())),
validationFailed => BadRequest(HttpContext.GetResponseOrDefault(StatusCodes.Status400BadRequest, validationFailed.Errors.ToList())),
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/ProducesResponseHeaderAttribute.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/ProducesResponseHeaderAttribute.cs
new file mode 100644
index 000000000..77e2d3e12
--- /dev/null
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/ProducesResponseHeaderAttribute.cs
@@ -0,0 +1,16 @@
+namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.Dialogs.Patch;
+
+[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
+public sealed class ProducesResponseHeaderAttribute : Attribute
+{
+ public ProducesResponseHeaderAttribute(int statusCode, string headerName, string description)
+ {
+ HeaderName = headerName;
+ StatusCode = statusCode;
+ Description = description;
+ }
+
+ public string HeaderName { get; }
+ public int StatusCode { get; }
+ public string Description { get; }
+}
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/ProducesResponseHeaderOperationProcessor.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/ProducesResponseHeaderOperationProcessor.cs
new file mode 100644
index 000000000..26ec4387e
--- /dev/null
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/ProducesResponseHeaderOperationProcessor.cs
@@ -0,0 +1,29 @@
+using System.Globalization;
+using System.Reflection;
+using NSwag;
+using NSwag.Generation.Processors;
+using NSwag.Generation.Processors.Contexts;
+
+namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.Dialogs.Patch;
+
+public sealed class ProducesResponseHeaderOperationProcessor : IOperationProcessor
+{
+ public bool Process(OperationProcessorContext context)
+ {
+ var headerAttribute = context.MethodInfo.GetCustomAttribute();
+ if (headerAttribute == null)
+ {
+ return true;
+ }
+
+ var statusCode = headerAttribute.StatusCode.ToString(CultureInfo.InvariantCulture);
+ var response = context.OperationDescription.Operation.Responses[statusCode];
+ var header = new OpenApiHeader
+ {
+ Description = headerAttribute.Description,
+ };
+
+ response.Headers.Add(headerAttribute.HeaderName, header);
+ return true;
+ }
+}
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogEndpoint.cs
index d75486f68..e8b866cca 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogEndpoint.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogEndpoint.cs
@@ -43,7 +43,11 @@ public override async Task HandleAsync(UpdateDialogRequest req, CancellationToke
var updateDialogResult = await _sender.Send(command, ct);
await updateDialogResult.Match(
- success => SendNoContentAsync(ct),
+ success =>
+ {
+ HttpContext.Response.Headers.Append(Constants.ETag, success.Revision.ToString());
+ return SendNoContentAsync(ct);
+ },
notFound => this.NotFoundAsync(notFound, ct),
gone => this.GoneAsync(gone, ct),
validationFailed => this.BadRequestAsync(validationFailed, ct),
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogEndpointSummary.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogEndpointSummary.cs
index 055cc3eb7..d6874cf58 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogEndpointSummary.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogEndpointSummary.cs
@@ -1,6 +1,7 @@
using Digdir.Domain.Dialogporten.WebApi.Common;
using Digdir.Domain.Dialogporten.WebApi.Common.Authorization;
using Digdir.Domain.Dialogporten.WebApi.Common.Extensions;
+using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Headers;
using FastEndpoints;
namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.Dialogs.Update;
@@ -15,6 +16,7 @@ Replaces a given dialog with the supplied model. For more information see the do
{Constants.SwaggerSummary.OptimisticConcurrencyNote}
""";
+ ResponseHeaders = [HttpResponseHeaderExamples.NewDialogETagHeader(StatusCodes.Status204NoContent)];
Responses[StatusCodes.Status204NoContent] = Constants.SwaggerSummary.Updated.FormatInvariant("aggregate");
Responses[StatusCodes.Status400BadRequest] = Constants.SwaggerSummary.ValidationError;
Responses[StatusCodes.Status401Unauthorized] =
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/OpenApiDocumentExtensions.cs b/src/Digdir.Domain.Dialogporten.WebApi/OpenApiDocumentExtensions.cs
index 9dc187e41..a9d53de3f 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/OpenApiDocumentExtensions.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/OpenApiDocumentExtensions.cs
@@ -1,4 +1,3 @@
-using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.Dialogs.Update;
using NJsonSchema;
using NSwag;
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Program.cs b/src/Digdir.Domain.Dialogporten.WebApi/Program.cs
index c02e9f007..7674052f5 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/Program.cs
+++ b/src/Digdir.Domain.Dialogporten.WebApi/Program.cs
@@ -15,6 +15,7 @@
using Digdir.Domain.Dialogporten.WebApi.Common.Authorization;
using Digdir.Domain.Dialogporten.WebApi.Common.Json;
using Digdir.Domain.Dialogporten.WebApi.Common.Swagger;
+using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.Dialogs.Patch;
using Digdir.Library.Utils.AspNet;
using FastEndpoints;
using FastEndpoints.Swagger;
@@ -78,7 +79,14 @@ static void BuildAndRun(string[] args, TelemetryConfiguration telemetryConfigura
var thisAssembly = Assembly.GetExecutingAssembly();
- builder.ConfigureTelemetry();
+ builder.ConfigureTelemetry((settings, configuration) =>
+ {
+ settings.ServiceName = configuration["OTEL_SERVICE_NAME"] ?? builder.Environment.ApplicationName;
+ settings.Endpoint = configuration["OTEL_EXPORTER_OTLP_ENDPOINT"];
+ settings.Protocol = configuration["OTEL_EXPORTER_OTLP_PROTOCOL"];
+ settings.AppInsightsConnectionString = configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"];
+ settings.ResourceAttributes = configuration["OTEL_RESOURCE_ATTRIBUTES"];
+ });
builder.Services
// Options setup
@@ -118,6 +126,9 @@ static void BuildAndRun(string[] args, TelemetryConfiguration telemetryConfigura
// generic "2" suffix duplicate names get, so we add a "SO" suffix to the serviceowner specific schemas.
// This should match the operationIds used for service owners.
s.AddServiceOwnerSuffixToSchemas();
+
+ // Adding ResponseHeaders for PATCH MVC controller
+ s.OperationProcessors.Add(new ProducesResponseHeaderOperationProcessor());
};
})
.AddControllers(options => options.InputFormatters.Insert(0, JsonPatchInputFormatter.Get()))
diff --git a/src/Digdir.Domain.Dialogporten.WebApi/appsettings.json b/src/Digdir.Domain.Dialogporten.WebApi/appsettings.json
index ffd6358a4..938115f5a 100644
--- a/src/Digdir.Domain.Dialogporten.WebApi/appsettings.json
+++ b/src/Digdir.Domain.Dialogporten.WebApi/appsettings.json
@@ -19,4 +19,4 @@
}
},
"AllowedHosts": "*"
-}
+}
\ No newline at end of file
diff --git a/src/Digdir.Library.Entity.Abstractions/Digdir.Library.Entity.Abstractions.csproj b/src/Digdir.Library.Entity.Abstractions/Digdir.Library.Entity.Abstractions.csproj
index cb9c284f0..d9961bfe5 100644
--- a/src/Digdir.Library.Entity.Abstractions/Digdir.Library.Entity.Abstractions.csproj
+++ b/src/Digdir.Library.Entity.Abstractions/Digdir.Library.Entity.Abstractions.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/src/Digdir.Library.Entity.Abstractions/Features/Identifiable/IdentifiableExtensions.cs b/src/Digdir.Library.Entity.Abstractions/Features/Identifiable/IdentifiableExtensions.cs
index 51db679dd..5c647cc26 100644
--- a/src/Digdir.Library.Entity.Abstractions/Features/Identifiable/IdentifiableExtensions.cs
+++ b/src/Digdir.Library.Entity.Abstractions/Features/Identifiable/IdentifiableExtensions.cs
@@ -32,11 +32,15 @@ public static Guid CreateVersion7IfDefault(this Guid value) =>
///
/// Creates a new version 7 UUID.
+ ///
///
/// A new version 7 UUID in big endian format.
- public static Guid CreateVersion7() =>
- // We want Guids in big endian format. The default behavior of Medo is big endian,
- // however, the implicit conversion from Medo.Uuid7 to Guid is little endian.
- // "matchGuidEndianness" is set to true to ensure big endian.
- Uuid7.NewUuid7().ToGuid(matchGuidEndianness: true);
+ // We want Guids in big endian text representation.
+ // The default behavior of Medo is bigEndian text representation,
+ // Setting bigEndian to false for two reasons:
+ // 1. Use the parameter name explicitly in case the Medo API changes
+ // 2. Make it clear that we want big endian text representation, which means little endian byte order
+ public static Guid CreateVersion7(DateTimeOffset? timeStamp = null) => timeStamp is null
+ ? Uuid7.NewUuid7().ToGuid(bigEndian: false)
+ : Uuid7.NewUuid7(timeStamp.Value).ToGuid(bigEndian: false);
}
diff --git a/src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs b/src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs
index c47413c8f..c1388ec0d 100644
--- a/src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs
+++ b/src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs
@@ -1,8 +1,8 @@
-using Azure.Monitor.OpenTelemetry.AspNetCore;
using Digdir.Library.Utils.AspNet.HealthChecks;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
@@ -10,13 +10,15 @@
using OpenTelemetry.Trace;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
+using OpenTelemetry;
+using OpenTelemetry.Exporter;
+using System.Diagnostics;
+using Azure.Monitor.OpenTelemetry.Exporter;
namespace Digdir.Library.Utils.AspNet;
public static class AspNetUtilitiesExtensions
{
- private const string MassTransitSource = "MassTransit";
-
public static IServiceCollection AddAspNetHealthChecks(this IServiceCollection services, Action? configure = null)
{
var optionsBuilder = services.AddOptions();
@@ -49,41 +51,115 @@ private static WebApplication MapHealthCheckEndpoint(this WebApplication app, st
return app;
}
- public static WebApplicationBuilder ConfigureTelemetry(this WebApplicationBuilder builder)
+ public static WebApplicationBuilder ConfigureTelemetry(
+ this WebApplicationBuilder builder,
+ Action? configure = null)
{
- builder.Services.AddOpenTelemetry()
- .ConfigureResource(resource => resource
- .AddService(serviceName: builder.Environment.ApplicationName))
- .WithTracing(tracing =>
+ var settings = new TelemetrySettings();
+ configure?.Invoke(settings, builder.Configuration);
+
+ Console.WriteLine($"[OpenTelemetry] Configuring telemetry for service: {settings.ServiceName}");
+
+ var telemetryBuilder = builder.Services.AddOpenTelemetry()
+ .ConfigureResource(resource =>
{
- if (builder.Environment.IsDevelopment())
+ var resourceBuilder = resource.AddService(serviceName: settings.ServiceName ?? builder.Environment.ApplicationName);
+
+ var resourceAttributes = settings.ResourceAttributes;
+ if (string.IsNullOrEmpty(resourceAttributes)) return;
+
+ try
{
- tracing.SetSampler(new AlwaysOnSampler());
+ var attributes = resourceAttributes
+ .Split(',', StringSplitOptions.RemoveEmptyEntries)
+ .Select(pair => pair.Split('=', 2))
+ .Where(parts => parts.Length == 2 && !string.IsNullOrEmpty(parts[0]))
+ .Select(parts => new KeyValuePair(parts[0].Trim(), parts[1].Trim()));
+
+ foreach (var attribute in attributes)
+ {
+ resourceBuilder.AddAttributes([attribute]);
+ }
}
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException(
+ "Failed to parse OTEL_RESOURCE_ATTRIBUTES. Expected format: key1=value1,key2=value2",
+ ex
+ );
+ }
+ });
+
+ if (!string.IsNullOrEmpty(settings.Endpoint) && !string.IsNullOrEmpty(settings.Protocol))
+ {
+ Console.WriteLine($"[OpenTelemetry] Using endpoint: {settings.Endpoint}");
+ Console.WriteLine($"[OpenTelemetry] Using protocol: {settings.Protocol}");
- tracing.AddAspNetCoreInstrumentation(options =>
+ var otlpProtocol = settings.Protocol.ToLowerInvariant() switch
+ {
+ "grpc" => OtlpExportProtocol.Grpc,
+ "http/protobuf" => OtlpExportProtocol.HttpProtobuf,
+ "http" => OtlpExportProtocol.HttpProtobuf,
+ _ => throw new ArgumentException($"Unsupported protocol: {settings.Protocol}")
+ };
+
+ telemetryBuilder.UseOtlpExporter(otlpProtocol, new Uri(settings.Endpoint));
+
+ telemetryBuilder
+ .WithTracing(tracing =>
{
- options.Filter = httpContext =>
- !httpContext.Request.Path.StartsWithSegments("/health");
+ if (builder.Environment.IsDevelopment())
+ {
+ tracing.SetSampler(new AlwaysOnSampler());
+ }
+
+ foreach (var source in settings.TraceSources)
+ {
+ tracing.AddSource(source);
+ }
+
+ tracing
+ .AddAspNetCoreInstrumentation(opts =>
+ {
+ opts.RecordException = true;
+ opts.Filter = httpContext => !httpContext.Request.Path.StartsWithSegments("/health");
+ })
+ .AddHttpClientInstrumentation(o =>
+ {
+ o.RecordException = true;
+ o.FilterHttpRequestMessage = _ =>
+ {
+ var parentActivity = Activity.Current?.Parent;
+ if (parentActivity != null && parentActivity.Source.Name.Equals("Azure.Core.Http", StringComparison.Ordinal))
+ {
+ return false;
+ }
+ return true;
+ };
+ })
+ .AddEntityFrameworkCoreInstrumentation()
+ .AddNpgsql()
+ .AddFusionCacheInstrumentation();
});
- tracing.AddHttpClientInstrumentation();
- tracing.AddNpgsql();
- tracing.AddSource(MassTransitSource); // MassTransit ActivitySource
- })
- .WithMetrics(metrics =>
+ telemetryBuilder.WithMetrics(metrics =>
{
- metrics.AddRuntimeInstrumentation();
- });
+ metrics.AddRuntimeInstrumentation()
+ .AddAspNetCoreInstrumentation()
+ .AddHttpClientInstrumentation();
- if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING")))
- {
- builder.Services.AddOpenTelemetry().UseAzureMonitor();
+ if (!string.IsNullOrEmpty(settings.AppInsightsConnectionString))
+ {
+ metrics.AddAzureMonitorMetricExporter(options =>
+ {
+ options.ConnectionString = settings.AppInsightsConnectionString;
+ });
+ }
+ });
}
else
{
- // Use Application Insights SDK for local development
- builder.Services.AddApplicationInsightsTelemetry();
+ Console.WriteLine("[OpenTelemetry] OTLP exporter not configured - skipping");
}
return builder;
diff --git a/src/Digdir.Library.Utils.AspNet/AspNetUtilitiesSettings.cs b/src/Digdir.Library.Utils.AspNet/AspNetUtilitiesSettings.cs
index f8e2f7fdb..7cb14248c 100644
--- a/src/Digdir.Library.Utils.AspNet/AspNetUtilitiesSettings.cs
+++ b/src/Digdir.Library.Utils.AspNet/AspNetUtilitiesSettings.cs
@@ -9,3 +9,21 @@ public sealed class HealthCheckSettings
{
public List HttpGetEndpointsToCheck { get; set; } = [];
}
+
+public sealed class TelemetrySettings
+{
+ private const string MassTransitSource = "MassTransit";
+ private const string AzureSource = "Azure.*";
+
+ public string? ServiceName { get; set; }
+ public string? Endpoint { get; set; }
+ public string? Protocol { get; set; }
+ public string? AppInsightsConnectionString { get; set; }
+ // Expected format: key1=value1,key2=value2
+ public string? ResourceAttributes { get; set; }
+ public HashSet TraceSources { get; set; } = new()
+ {
+ AzureSource,
+ MassTransitSource
+ };
+}
\ No newline at end of file
diff --git a/src/Digdir.Library.Utils.AspNet/Digdir.Library.Utils.AspNet.csproj b/src/Digdir.Library.Utils.AspNet/Digdir.Library.Utils.AspNet.csproj
index cab4e5a23..36a45f2a5 100644
--- a/src/Digdir.Library.Utils.AspNet/Digdir.Library.Utils.AspNet.csproj
+++ b/src/Digdir.Library.Utils.AspNet/Digdir.Library.Utils.AspNet.csproj
@@ -10,13 +10,16 @@
-
+
+
+
+
diff --git a/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs b/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs
index 7b0af6a26..4cbd05a9d 100644
--- a/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs
+++ b/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs
@@ -242,7 +242,7 @@ public static List GenerateFakeDialogTransmissions(int? count =
DialogTransmissionType.Values? type = null)
{
return new Faker()
- .RuleFor(o => o.Id, _ => Uuid7.NewUuid7().ToGuid(true))
+ .RuleFor(o => o.Id, _ => IdentifiableExtensions.CreateVersion7())
.RuleFor(o => o.CreatedAt, f => f.Date.Past())
.RuleFor(o => o.Type, f => type ?? f.PickRandom())
.RuleFor(o => o.Sender, _ => new() { ActorType = ActorType.Values.ServiceOwner })
@@ -262,7 +262,7 @@ public static List GenerateFakeDialogActivities(int? count = null,
.Where(x => x != DialogActivityType.Values.TransmissionOpened).ToList();
return new Faker()
- .RuleFor(o => o.Id, () => Uuid7.NewUuid7().ToGuid(true))
+ .RuleFor(o => o.Id, () => IdentifiableExtensions.CreateVersion7())
.RuleFor(o => o.CreatedAt, f => f.Date.Past())
.RuleFor(o => o.ExtendedType, f => new Uri(f.Internet.UrlWithPath(Uri.UriSchemeHttps)))
.RuleFor(o => o.Type, f => type ?? f.PickRandom(activityTypes))
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Digdir.Domain.Dialogporten.Application.Integration.Tests.csproj b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Digdir.Domain.Dialogporten.Application.Integration.Tests.csproj
index 942a0cc2e..bcd82360f 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Digdir.Domain.Dialogporten.Application.Integration.Tests.csproj
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Digdir.Domain.Dialogporten.Application.Integration.Tests.csproj
@@ -14,9 +14,9 @@
-
+
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/Common/Events/DomainEventsTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/Common/Events/DomainEventsTests.cs
index 115184cb9..3ac416221 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/Common/Events/DomainEventsTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/Common/Events/DomainEventsTests.cs
@@ -11,9 +11,9 @@
using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Delete;
using Digdir.Domain.Dialogporten.Domain.Attachments;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Events.Activities;
+using Digdir.Library.Entity.Abstractions.Features.Identifiable;
using MassTransit.Internals;
using MassTransit.Testing;
-using static Digdir.Domain.Dialogporten.Application.Integration.Tests.UuiDv7Utils;
namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.Common.Events;
@@ -32,6 +32,20 @@ static DomainEventsTests()
Mapper = mapperConfiguration.CreateMapper();
}
+ [Fact]
+ public void All_DialogActivityTypes_Must_Have_A_Mapping_In_CloudEventTypes()
+ {
+ // Arrange
+ var allActivityTypes = Enum.GetValues().ToList();
+
+ // Act/Assert
+ allActivityTypes.ForEach(activityType =>
+ {
+ Action act = () => CloudEventTypes.Get(activityType.ToString());
+ act.Should().NotThrow($"all activity types must have a mapping in {nameof(CloudEventTypes)} ({activityType} is missing)");
+ });
+ }
+
[Fact]
public async Task Creates_CloudEvents_When_Dialog_Created()
{
@@ -126,7 +140,7 @@ public async Task Creates_CloudEvent_When_Attachments_Updates()
{
// Arrange
var harness = await Application.ConfigureServicesWithMassTransitTestHarness();
- var dialogId = GenerateBigEndianUuidV7();
+ var dialogId = IdentifiableExtensions.CreateVersion7();
var createDialogCommand = DialogGenerator.GenerateFakeDialog(
id: dialogId,
attachments: []);
@@ -174,7 +188,7 @@ public async Task Creates_CloudEvents_When_Dialog_Deleted()
{
// Arrange
var harness = await Application.ConfigureServicesWithMassTransitTestHarness();
- var dialogId = GenerateBigEndianUuidV7();
+ var dialogId = IdentifiableExtensions.CreateVersion7();
var createDialogCommand = DialogGenerator.GenerateFakeDialog(id: dialogId, attachments: [], activities: []);
await Application.Send(createDialogCommand);
@@ -204,7 +218,7 @@ public async Task Creates_DialogDeletedEvent_When_Dialog_Purged()
{
// Arrange
var harness = await Application.ConfigureServicesWithMassTransitTestHarness();
- var dialogId = GenerateBigEndianUuidV7();
+ var dialogId = IdentifiableExtensions.CreateVersion7();
var createDialogCommand = DialogGenerator.GenerateFakeDialog(id: dialogId, attachments: [], activities: []);
await Application.Send(createDialogCommand);
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/ActivityLogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/ActivityLogTests.cs
index d5495680e..6f8ed9bc5 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/ActivityLogTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/ActivityLogTests.cs
@@ -19,7 +19,7 @@ public async Task Get_Dialog_ActivityLog_Should_Not_Return_User_Ids_Unhashed()
var (_, createCommandResponse) = await GenerateDialogWithActivity();
// Act
- var response = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value });
+ var response = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.DialogId });
// Assert
response.TryPickT0(out var result, out _).Should().BeTrue();
@@ -63,13 +63,17 @@ public async Task Get_ActivityLog_Should_Not_Return_User_Ids_Unhashed()
// Arrange
var (_, createCommandResponse) = await GenerateDialogWithActivity();
- var getDialogResult = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value });
+ var getDialogResult = await Application.Send(new GetDialogQuery
+ {
+ DialogId = createCommandResponse.AsT0.DialogId
+ });
+
var activityId = getDialogResult.AsT0.Activities.First().Id;
// Act
var response = await Application.Send(new GetActivityQuery
{
- DialogId = createCommandResponse.AsT0.Value,
+ DialogId = createCommandResponse.AsT0.DialogId,
ActivityId = activityId
});
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/DeletedDialogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/DeletedDialogTests.cs
index 30e3613cc..c1be07e25 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/DeletedDialogTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/DeletedDialogTests.cs
@@ -16,7 +16,7 @@ public async Task Fetching_Deleted_Dialog_Should_Return_Gone()
var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog();
var createDialogResponse = await Application.Send(createDialogCommand);
- var dialogId = createDialogResponse.AsT0.Value;
+ var dialogId = createDialogResponse.AsT0.DialogId;
var deleteDialogCommand = new DeleteDialogCommand { Id = dialogId };
await Application.Send(deleteDialogCommand);
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs
index ab9cb0bfe..872a2356e 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/EndUser/Dialogs/Queries/SeenLogTests.cs
@@ -20,7 +20,7 @@ public async Task Get_Dialog_SeenLog_Should_Not_Return_User_Ids_Unhashed()
var createCommandResponse = await Application.Send(createDialogCommand);
// Act
- var response = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value });
+ var response = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.DialogId });
// Assert
response.TryPickT0(out var result, out _).Should().BeTrue();
@@ -42,7 +42,7 @@ public async Task Search_Dialog_SeenLog_Should_Not_Return_User_Ids_Unhashed()
var createCommandResponse = await Application.Send(createDialogCommand);
// Trigger SeenLog
- await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value });
+ await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.DialogId });
// Act
var response = await Application.Send(new SearchDialogQuery
@@ -70,13 +70,17 @@ public async Task Get_SeenLog_Should_Not_Return_User_Ids_Unhashed()
var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog();
var createCommandResponse = await Application.Send(createDialogCommand);
- var triggerSeenLogResponse = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value });
+ var triggerSeenLogResponse = await Application.Send(new GetDialogQuery
+ {
+ DialogId = createCommandResponse.AsT0.DialogId
+ });
+
var seenLogId = triggerSeenLogResponse.AsT0.SeenSinceLastUpdate.Single().Id;
// Act
var response = await Application.Send(new GetSeenLogQuery
{
- DialogId = createCommandResponse.AsT0.Value,
+ DialogId = createCommandResponse.AsT0.DialogId,
SeenLogId = seenLogId
});
@@ -97,12 +101,12 @@ public async Task Search_SeenLog_Should_Not_Return_User_Ids_Unhashed()
var createCommandResponse = await Application.Send(createDialogCommand);
// Trigger SeenLog
- await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value });
+ await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.DialogId });
// Act
var response = await Application.Send(new SearchSeenLogQuery
{
- DialogId = createCommandResponse.AsT0.Value
+ DialogId = createCommandResponse.AsT0.DialogId
});
// Assert
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 9e7323f2c..7fc55434d 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
@@ -5,11 +5,11 @@
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.Library.Entity.Abstractions.Features.Identifiable;
using Digdir.Tool.Dialogporten.GenerateFakeData;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
-using static Digdir.Domain.Dialogporten.Application.Integration.Tests.UuiDv7Utils;
namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.ServiceOwner.Dialogs.Commands;
@@ -38,8 +38,8 @@ public async Task Cant_Create_Dialog_With_UUIDv4_format()
public async Task Cant_Create_Dialog_With_UUIDv7_In_Little_Endian_Format()
{
// Arrange
- // Guid created with Medo, Uuid7.NewUuid7().ToGuid()
- var invalidDialogId = Guid.Parse("638e9101-6bc7-7975-b392-ba5c5a528c23");
+ // Guid created with Medo, Uuid7.NewUuid7().ToGuid(bigEndian: true)
+ var invalidDialogId = Guid.Parse("b2ca9301-c371-ab74-a87b-4ee1416b9655");
var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(id: invalidDialogId);
@@ -56,7 +56,7 @@ public async Task Cant_Create_Dialog_With_ID_With_Timestamp_In_The_Future()
{
// Arrange
var timestamp = DateTimeOffset.UtcNow.AddSeconds(1);
- var invalidDialogId = GenerateBigEndianUuidV7(timestamp);
+ var invalidDialogId = IdentifiableExtensions.CreateVersion7(timestamp);
var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(id: invalidDialogId);
@@ -73,7 +73,7 @@ public async Task Create_Dialog_With_ID_With_Timestamp_In_The_Past()
{
// Arrange
var timestamp = DateTimeOffset.UtcNow.AddSeconds(-1);
- var validDialogId = GenerateBigEndianUuidV7(timestamp);
+ var validDialogId = IdentifiableExtensions.CreateVersion7(timestamp);
var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(id: validDialogId);
@@ -83,14 +83,14 @@ public async Task Create_Dialog_With_ID_With_Timestamp_In_The_Past()
// Assert
response.TryPickT0(out var success, out _).Should().BeTrue();
success.Should().NotBeNull();
- success.Value.Should().Be(validDialogId);
+ success.DialogId.Should().Be(validDialogId);
}
[Fact]
public async Task Create_CreatesDialog_WhenDialogIsSimple()
{
// Arrange
- var expectedDialogId = GenerateBigEndianUuidV7();
+ var expectedDialogId = IdentifiableExtensions.CreateVersion7();
var createCommand = DialogGenerator.GenerateSimpleFakeDialog(id: expectedDialogId);
// Act
@@ -98,14 +98,14 @@ public async Task Create_CreatesDialog_WhenDialogIsSimple()
// Assert
response.TryPickT0(out var success, out _).Should().BeTrue();
- success.Value.Should().Be(expectedDialogId);
+ success.DialogId.Should().Be(expectedDialogId);
}
[Fact]
public async Task Create_CreateDialog_WhenDialogIsComplex()
{
// Arrange
- var expectedDialogId = GenerateBigEndianUuidV7();
+ var expectedDialogId = IdentifiableExtensions.CreateVersion7();
var createDialogCommand = DialogGenerator.GenerateFakeDialog(id: expectedDialogId);
// Act
@@ -113,14 +113,14 @@ public async Task Create_CreateDialog_WhenDialogIsComplex()
// Assert
result.TryPickT0(out var success, out _).Should().BeTrue();
- success.Value.Should().Be(expectedDialogId);
+ success.DialogId.Should().Be(expectedDialogId);
}
[Fact]
public async Task Can_Create_Dialog_With_UpdatedAt_Supplied()
{
// Arrange
- var dialogId = GenerateBigEndianUuidV7();
+ var dialogId = IdentifiableExtensions.CreateVersion7();
var createdAt = DateTimeOffset.UtcNow.AddYears(-20);
var updatedAt = DateTimeOffset.UtcNow.AddYears(-15);
var createDialogCommand = DialogGenerator.GenerateFakeDialog(id: dialogId, updatedAt: updatedAt, createdAt: createdAt);
@@ -136,7 +136,7 @@ public async Task Can_Create_Dialog_With_UpdatedAt_Supplied()
// Assert
createDialogResult.TryPickT0(out var dialogCreatedSuccess, out _).Should().BeTrue();
- dialogCreatedSuccess.Value.Should().Be(dialogId);
+ dialogCreatedSuccess.DialogId.Should().Be(dialogId);
getDialogQuery.Should().NotBeNull();
getDialogResponse.TryPickT0(out var dialog, out _).Should().BeTrue();
@@ -387,7 +387,7 @@ public async Task Cannot_Create_Title_Content_With_Embeddable_Html_MediaType_Wit
public async Task Can_Create_MainContentRef_Content_With_Embeddable_Html_MediaType_With_Correct_Scope()
{
// Arrange
- var expectedDialogId = GenerateBigEndianUuidV7();
+ var expectedDialogId = IdentifiableExtensions.CreateVersion7();
var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(id: expectedDialogId);
createDialogCommand.Content.MainContentReference = new ContentValueDto
{
@@ -408,6 +408,20 @@ public async Task Can_Create_MainContentRef_Content_With_Embeddable_Html_MediaTy
// Assert
response.TryPickT0(out var success, out _).Should().BeTrue();
success.Should().NotBeNull();
- success.Value.Should().Be(expectedDialogId);
+ success.DialogId.Should().Be(expectedDialogId);
+ }
+
+ [Fact]
+ public async Task CreateDialogCommand_Should_Return_Revision()
+ {
+ // Arrange
+ var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog();
+
+ // Act
+ var response = await Application.Send(createDialogCommand);
+
+ // Assert
+ response.TryPickT0(out var success, out _).Should().BeTrue();
+ success.Revision.Should().NotBeEmpty();
}
}
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/DeleteDialogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/DeleteDialogTests.cs
index a225fc9a3..459033a6b 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/DeleteDialogTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/DeleteDialogTests.cs
@@ -18,7 +18,7 @@ public async Task Deleting_Dialog_Should_Set_DeletedAt()
var createDialogResponse = await Application.Send(createDialogCommand);
// Act
- var dialogId = createDialogResponse.AsT0.Value;
+ var dialogId = createDialogResponse.AsT0.DialogId;
var deleteDialogCommand = new DeleteDialogCommand { Id = dialogId };
await Application.Send(deleteDialogCommand);
@@ -38,7 +38,7 @@ public async Task Updating_Deleted_Dialog_Should_Return_EntityDeleted()
var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog();
var createDialogResponse = await Application.Send(createDialogCommand);
- var dialogId = createDialogResponse.AsT0.Value;
+ var dialogId = createDialogResponse.AsT0.DialogId;
var deleteDialogCommand = new DeleteDialogCommand { Id = dialogId };
await Application.Send(deleteDialogCommand);
@@ -58,4 +58,26 @@ public async Task Updating_Deleted_Dialog_Should_Return_EntityDeleted()
entityDeleted.Should().NotBeNull();
entityDeleted.Message.Should().Contain(dialogId.ToString());
}
+
+ [Fact]
+ public async Task DeleteDialogCommand_Should_Return_New_Revision()
+ {
+ // Arrange
+ var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog();
+ var createDialogResponse = await Application.Send(createDialogCommand);
+
+ var dialogId = createDialogResponse.AsT0.DialogId;
+ var oldRevision = createDialogResponse.AsT0.Revision;
+
+ // Act
+ var deleteDialogCommand = new DeleteDialogCommand { Id = dialogId };
+ var deleteDialogResponse = await Application.Send(deleteDialogCommand);
+
+ // Assert
+ deleteDialogResponse.TryPickT0(out var success, out _).Should().BeTrue();
+ success.Should().NotBeNull();
+ success.Revision.Should().NotBeEmpty();
+ success.Revision.Should().NotBe(oldRevision);
+ }
+
}
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/PurgeDialogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/PurgeDialogTests.cs
index f1da5c248..22adc5938 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/PurgeDialogTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/PurgeDialogTests.cs
@@ -2,9 +2,9 @@
using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
+using Digdir.Library.Entity.Abstractions.Features.Identifiable;
using Digdir.Tool.Dialogporten.GenerateFakeData;
using FluentAssertions;
-using static Digdir.Domain.Dialogporten.Application.Integration.Tests.UuiDv7Utils;
namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.ServiceOwner.Dialogs.Commands;
@@ -15,7 +15,7 @@ public class PurgeDialogTests(DialogApplication application) : ApplicationCollec
public async Task Purge_RemovesDialog_FromDatabase()
{
// Arrange
- var expectedDialogId = GenerateBigEndianUuidV7();
+ var expectedDialogId = IdentifiableExtensions.CreateVersion7();
var createCommand = DialogGenerator.GenerateFakeDialog(id: expectedDialogId);
var createResponse = await Application.Send(createCommand);
createResponse.TryPickT0(out _, out _).Should().BeTrue();
@@ -41,7 +41,7 @@ public async Task Purge_RemovesDialog_FromDatabase()
public async Task Purge_ReturnsConcurrencyError_OnIfMatchDialogRevisionMismatch()
{
// Arrange
- var expectedDialogId = GenerateBigEndianUuidV7();
+ var expectedDialogId = IdentifiableExtensions.CreateVersion7();
var createCommand = DialogGenerator.GenerateFakeDialog(id: expectedDialogId);
var createResponse = await Application.Send(createCommand);
createResponse.TryPickT0(out _, out _).Should().BeTrue();
@@ -58,7 +58,7 @@ public async Task Purge_ReturnsConcurrencyError_OnIfMatchDialogRevisionMismatch(
public async Task Purge_ReturnsNotFound_OnNonExistingDialog()
{
// Arrange
- var expectedDialogId = GenerateBigEndianUuidV7();
+ var expectedDialogId = IdentifiableExtensions.CreateVersion7();
var createCommand = DialogGenerator.GenerateFakeDialog(id: expectedDialogId);
await Application.Send(createCommand);
var purgeCommand = new PurgeDialogCommand { DialogId = expectedDialogId };
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/UpdateDialogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/UpdateDialogTests.cs
index d8e752f60..debac9c8c 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/UpdateDialogTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/UpdateDialogTests.cs
@@ -16,12 +16,42 @@ namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.S
[Collection(nameof(DialogCqrsCollectionFixture))]
public class UpdateDialogTests(DialogApplication application) : ApplicationCollectionFixture(application)
{
+ [Fact]
+ public async Task UpdateDialogCommand_Should_Return_New_Revision()
+ {
+ // Arrange
+ var createCommandResponse = await Application.Send(DialogGenerator.GenerateSimpleFakeDialog());
+
+ var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.DialogId };
+ var getDialogDto = await Application.Send(getDialogQuery);
+ var oldRevision = getDialogDto.AsT0.Revision;
+
+ var mapper = Application.GetMapper();
+ var updateDialogDto = mapper.Map(getDialogDto.AsT0);
+
+ // Update something
+ updateDialogDto.Progress++;
+
+ // Act
+ var updateResponse = await Application.Send(new UpdateDialogCommand
+ {
+ Id = createCommandResponse.AsT0.DialogId,
+ Dto = updateDialogDto
+ });
+
+ // Assert
+ updateResponse.TryPickT0(out var success, out _).Should().BeTrue();
+ success.Should().NotBeNull();
+ success.Revision.Should().NotBeEmpty();
+ success.Revision.Should().NotBe(oldRevision);
+ }
+
[Fact]
public async Task Cannot_Include_Old_Activities_To_UpdateCommand()
{
// Arrange
var (_, createCommandResponse) = await GenerateDialogWithActivity();
- var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value };
+ var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.DialogId };
var getDialogDto = await Application.Send(getDialogQuery);
var mapper = Application.GetMapper();
@@ -39,7 +69,11 @@ public async Task Cannot_Include_Old_Activities_To_UpdateCommand()
});
// Act
- var updateResponse = await Application.Send(new UpdateDialogCommand { Id = createCommandResponse.AsT0.Value, Dto = updateDialogDto });
+ var updateResponse = await Application.Send(new UpdateDialogCommand
+ {
+ Id = createCommandResponse.AsT0.DialogId,
+ Dto = updateDialogDto
+ });
// Assert
updateResponse.TryPickT5(out var domainError, out _).Should().BeTrue();
@@ -56,7 +90,7 @@ public async Task Cannot_Include_Old_Transmissions_In_UpdateCommand()
createDialogCommand.Transmissions.Add(existingTransmission);
var createCommandResponse = await Application.Send(createDialogCommand);
- var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value };
+ var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.DialogId };
var getDialogDto = await Application.Send(getDialogQuery);
var mapper = Application.GetMapper();
@@ -76,7 +110,11 @@ public async Task Cannot_Include_Old_Transmissions_In_UpdateCommand()
});
// Act
- var updateResponse = await Application.Send(new UpdateDialogCommand { Id = createCommandResponse.AsT0.Value, Dto = updateDialogDto });
+ var updateResponse = await Application.Send(new UpdateDialogCommand
+ {
+ Id = createCommandResponse.AsT0.DialogId,
+ Dto = updateDialogDto
+ });
// Assert
updateResponse.TryPickT5(out var domainError, out _).Should().BeTrue();
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/ActivityLogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/ActivityLogTests.cs
index 6b899cff3..a509ec024 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/ActivityLogTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/ActivityLogTests.cs
@@ -20,7 +20,7 @@ public async Task Get_Dialog_ActivityLog_Should_Return_User_Ids_Unhashed()
var (_, createCommandResponse) = await GenerateDialogWithActivity();
// Act
- var response = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value });
+ var response = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.DialogId });
// Assert
response.TryPickT0(out var result, out _).Should().BeTrue();
@@ -64,13 +64,17 @@ public async Task Get_ActivityLog_Should_Return_User_Ids_Unhashed()
// Arrange
var (_, createCommandResponse) = await GenerateDialogWithActivity();
- var getDialogResult = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value });
+ var getDialogResult = await Application.Send(new GetDialogQuery
+ {
+ DialogId = createCommandResponse.AsT0.DialogId
+ });
+
var activityId = getDialogResult.AsT0.Activities.First().Id;
// Act
var response = await Application.Send(new GetActivityQuery
{
- DialogId = createCommandResponse.AsT0.Value,
+ DialogId = createCommandResponse.AsT0.DialogId,
ActivityId = activityId
});
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/GetDialogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/GetDialogTests.cs
index 2493b064f..6bd67650a 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/GetDialogTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/GetDialogTests.cs
@@ -1,8 +1,8 @@
using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get;
using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common;
+using Digdir.Library.Entity.Abstractions.Features.Identifiable;
using Digdir.Tool.Dialogporten.GenerateFakeData;
using FluentAssertions;
-using static Digdir.Domain.Dialogporten.Application.Integration.Tests.UuiDv7Utils;
namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.ServiceOwner.Dialogs.Queries;
@@ -15,13 +15,13 @@ public GetDialogTests(DialogApplication application) : base(application) { }
public async Task Get_ReturnsSimpleDialog_WhenDialogExists()
{
// Arrange
- var expectedDialogId = GenerateBigEndianUuidV7();
+ var expectedDialogId = IdentifiableExtensions.CreateVersion7();
var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog(id: expectedDialogId);
var createCommandResponse = await Application.Send(createDialogCommand);
// Act
- var response = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value });
+ var response = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.DialogId });
// Assert
response.TryPickT0(out var result, out _).Should().BeTrue();
@@ -36,12 +36,12 @@ public async Task Get_ReturnsSimpleDialog_WhenDialogExists()
public async Task Get_ReturnsDialog_WhenDialogExists()
{
// Arrange
- var expectedDialogId = GenerateBigEndianUuidV7();
+ var expectedDialogId = IdentifiableExtensions.CreateVersion7();
var createCommand = DialogGenerator.GenerateFakeDialog(id: expectedDialogId);
var createCommandResponse = await Application.Send(createCommand);
// Act
- var response = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value });
+ var response = await Application.Send(new GetDialogQuery { DialogId = createCommandResponse.AsT0.DialogId });
// Assert
response.TryPickT0(out var result, out _).Should().BeTrue();
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/SeenLogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/SeenLogTests.cs
index 49502e2a0..0068177f9 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/SeenLogTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/SeenLogTests.cs
@@ -21,10 +21,13 @@ public async Task Get_Dialog_SeenLog_Should_Return_User_Ids_Unhashed()
var createCommandResponse = await Application.Send(createDialogCommand);
// Call EndUser API to trigger SeenLog
- await Application.Send(new GetDialogQueryEndUser { DialogId = createCommandResponse.AsT0.Value });
+ await Application.Send(new GetDialogQueryEndUser { DialogId = createCommandResponse.AsT0.DialogId });
// Act
- var response = await Application.Send(new GetDialogQueryServiceOwner { DialogId = createCommandResponse.AsT0.Value });
+ var response = await Application.Send(new GetDialogQueryServiceOwner
+ {
+ DialogId = createCommandResponse.AsT0.DialogId
+ });
// Assert
response.TryPickT0(out var result, out _).Should().BeTrue();
@@ -45,7 +48,7 @@ public async Task Search_Dialog_SeenLog_Should_Return_User_Ids_Unhashed()
var createCommandResponse = await Application.Send(createDialogCommand);
// Trigger SeenLog
- await Application.Send(new GetDialogQueryEndUser { DialogId = createCommandResponse.AsT0.Value });
+ await Application.Send(new GetDialogQueryEndUser { DialogId = createCommandResponse.AsT0.DialogId });
// Act
var response = await Application.Send(new SearchDialogQuery
@@ -73,13 +76,17 @@ public async Task Get_SeenLog_Should_Return_User_Ids_Unhashed()
var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog();
var createCommandResponse = await Application.Send(createDialogCommand);
- var triggerSeenLogResponse = await Application.Send(new GetDialogQueryEndUser { DialogId = createCommandResponse.AsT0.Value });
+ var triggerSeenLogResponse = await Application.Send(new GetDialogQueryEndUser
+ {
+ DialogId = createCommandResponse.AsT0.DialogId
+ });
+
var seenLogId = triggerSeenLogResponse.AsT0.SeenSinceLastUpdate.Single().Id;
// Act
var response = await Application.Send(new GetSeenLogQuery
{
- DialogId = createCommandResponse.AsT0.Value,
+ DialogId = createCommandResponse.AsT0.DialogId,
SeenLogId = seenLogId
});
@@ -100,12 +107,12 @@ public async Task Search_SeenLog_Should_Return_User_Ids_Unhashed()
var createCommandResponse = await Application.Send(createDialogCommand);
// Trigger SeenLog
- await Application.Send(new GetDialogQueryEndUser { DialogId = createCommandResponse.AsT0.Value });
+ await Application.Send(new GetDialogQueryEndUser { DialogId = createCommandResponse.AsT0.DialogId });
// Act
var response = await Application.Send(new SearchSeenLogQuery
{
- DialogId = createCommandResponse.AsT0.Value
+ DialogId = createCommandResponse.AsT0.DialogId
});
// Assert
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/NotificationCondition/NotificationConditionTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/NotificationCondition/NotificationConditionTests.cs
index 9c13b255c..90aa57c76 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/NotificationCondition/NotificationConditionTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/NotificationCondition/NotificationConditionTests.cs
@@ -40,7 +40,7 @@ public async Task SendNotification_Should_Be_True_When_Conditions_Are_Met(
var response = await Application.Send(createDialogCommand);
response.TryPickT0(out var dialogId, out _);
- var notificationConditionQuery = CreateNotificationConditionQuery(dialogId.Value, activityType, conditionType);
+ var notificationConditionQuery = CreateNotificationConditionQuery(dialogId.DialogId, activityType, conditionType);
if (activityType is DialogActivityType.Values.TransmissionOpened)
{
@@ -93,11 +93,11 @@ public async Task Gone_Should_Be_Returned_When_Dialog_Is_Deleted()
var createDialogCommand = DialogGenerator.GenerateSimpleFakeDialog();
var response = await Application.Send(createDialogCommand);
- response.TryPickT0(out var dialogId, out _);
+ response.TryPickT0(out var success, out _);
- await Application.Send(new DeleteDialogCommand { Id = dialogId.Value });
+ await Application.Send(new DeleteDialogCommand { Id = success.DialogId });
- var notificationConditionQuery = CreateNotificationConditionQuery(dialogId.Value);
+ var notificationConditionQuery = CreateNotificationConditionQuery(success.DialogId);
// Act
var queryResult = await Application.Send(notificationConditionQuery);
@@ -106,7 +106,7 @@ public async Task Gone_Should_Be_Returned_When_Dialog_Is_Deleted()
queryResult.TryPickT3(out var deleted, out _);
queryResult.IsT3.Should().BeTrue();
deleted.Should().NotBeNull();
- deleted.Message.Should().Contain(dialogId.Value.ToString());
+ deleted.Message.Should().Contain(success.DialogId.ToString());
}
private static NotificationConditionQuery CreateNotificationConditionQuery(Guid dialogId,
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/CreateTransmissionTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/CreateTransmissionTests.cs
index 27cfeb4a9..0b52be4c7 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/CreateTransmissionTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/CreateTransmissionTests.cs
@@ -4,9 +4,9 @@
using Digdir.Domain.Dialogporten.Domain;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.Contents;
+using Digdir.Library.Entity.Abstractions.Features.Identifiable;
using Digdir.Tool.Dialogporten.GenerateFakeData;
using FluentAssertions;
-using static Digdir.Domain.Dialogporten.Application.Integration.Tests.UuiDv7Utils;
namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.ServiceOwner.Transmissions.Commands;
@@ -19,7 +19,7 @@ public CreateTransmissionTests(DialogApplication application) : base(application
public async Task Can_Create_Simple_Transmission()
{
// Arrange
- var dialogId = GenerateBigEndianUuidV7();
+ var dialogId = IdentifiableExtensions.CreateVersion7();
var createCommand = DialogGenerator.GenerateSimpleFakeDialog(id: dialogId);
var transmission = DialogGenerator.GenerateFakeDialogTransmissions(1)[0];
@@ -30,7 +30,7 @@ public async Task Can_Create_Simple_Transmission()
// Assert
response.TryPickT0(out var success, out _).Should().BeTrue();
- success.Value.Should().Be(dialogId);
+ success.DialogId.Should().Be(dialogId);
var transmissionEntities = await Application.GetDbEntities();
transmissionEntities.Should().HaveCount(1);
transmissionEntities.First().DialogId.Should().Be(dialogId);
@@ -41,10 +41,10 @@ public async Task Can_Create_Simple_Transmission()
public async Task Can_Create_Transmission_With_Embeddable_Content()
{
// Arrange
- var dialogId = GenerateBigEndianUuidV7();
+ var dialogId = IdentifiableExtensions.CreateVersion7();
var createCommand = DialogGenerator.GenerateSimpleFakeDialog(id: dialogId);
- var transmissionId = GenerateBigEndianUuidV7();
+ var transmissionId = IdentifiableExtensions.CreateVersion7();
var transmission = DialogGenerator.GenerateFakeDialogTransmissions(1)[0];
const string contentUrl = "https://example.com/transmission";
@@ -62,7 +62,7 @@ public async Task Can_Create_Transmission_With_Embeddable_Content()
// Assert
response.TryPickT0(out var success, out _).Should().BeTrue();
- success.Value.Should().Be(dialogId);
+ success.DialogId.Should().Be(dialogId);
var transmissionEntities = await Application.GetDbEntities();
transmissionEntities.Should().HaveCount(1);
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/UpdateTransmissionTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/UpdateTransmissionTests.cs
index 261e36056..3e2c51000 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/UpdateTransmissionTests.cs
+++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Transmissions/Commands/UpdateTransmissionTests.cs
@@ -3,9 +3,9 @@
using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common;
using Digdir.Domain.Dialogporten.Domain.Actors;
using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions;
+using Digdir.Library.Entity.Abstractions.Features.Identifiable;
using Digdir.Tool.Dialogporten.GenerateFakeData;
using FluentAssertions;
-using static Digdir.Domain.Dialogporten.Application.Integration.Tests.UuiDv7Utils;
namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.ServiceOwner.Transmissions.Commands;
@@ -24,7 +24,7 @@ public async Task Can_Create_Simple_Transmission_In_Update()
createDialogCommand.Transmissions.Add(existingTransmission);
var createCommandResponse = await Application.Send(createDialogCommand);
- var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value };
+ var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.DialogId };
var getDialogDto = await Application.Send(getDialogQuery);
var mapper = Application.GetMapper();
@@ -36,7 +36,7 @@ public async Task Can_Create_Simple_Transmission_In_Update()
// Act
var updateResponse = await Application.Send(new UpdateDialogCommand
{
- Id = createCommandResponse.AsT0.Value,
+ Id = createCommandResponse.AsT0.DialogId,
Dto = updateDialogDto
});
@@ -58,7 +58,7 @@ public async Task Can_Update_Related_Transmission_With_Null_Id()
createDialogCommand.Transmissions.Add(existingTransmission);
var createCommandResponse = await Application.Send(createDialogCommand);
- var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value };
+ var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.DialogId };
var getDialogDto = await Application.Send(getDialogQuery);
var mapper = Application.GetMapper();
@@ -76,7 +76,7 @@ public async Task Can_Update_Related_Transmission_With_Null_Id()
// Act
var updateResponse = await Application.Send(new UpdateDialogCommand
{
- Id = createCommandResponse.AsT0.Value,
+ Id = createCommandResponse.AsT0.DialogId,
Dto = updateDialogDto
});
@@ -97,7 +97,7 @@ public async Task Cannot_Include_Old_Transmissions_In_UpdateCommand()
createDialogCommand.Transmissions.Add(existingTransmission);
var createCommandResponse = await Application.Send(createDialogCommand);
- var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.Value };
+ var getDialogQuery = new GetDialogQuery { DialogId = createCommandResponse.AsT0.DialogId };
var getDialogDto = await Application.Send(getDialogQuery);
var mapper = Application.GetMapper();
@@ -110,7 +110,7 @@ public async Task Cannot_Include_Old_Transmissions_In_UpdateCommand()
// Act
var updateResponse = await Application.Send(new UpdateDialogCommand
{
- Id = createCommandResponse.AsT0.Value,
+ Id = createCommandResponse.AsT0.DialogId,
Dto = updateDialogDto
});
@@ -122,7 +122,7 @@ public async Task Cannot_Include_Old_Transmissions_In_UpdateCommand()
private static TransmissionDto UpdateDialogDialogTransmissionDto() => new()
{
- Id = GenerateBigEndianUuidV7(),
+ Id = IdentifiableExtensions.CreateVersion7(),
Type = DialogTransmissionType.Values.Information,
Sender = new() { ActorType = ActorType.Values.ServiceOwner },
Content = new()
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/UUIDv7Utils.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/UUIDv7Utils.cs
deleted file mode 100644
index 040fac197..000000000
--- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/UUIDv7Utils.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Medo;
-
-namespace Digdir.Domain.Dialogporten.Application.Integration.Tests;
-
-public static class UuiDv7Utils
-{
- public static Guid GenerateBigEndianUuidV7(DateTimeOffset? timeStamp = null) => timeStamp is null ?
- Uuid7.NewUuid7().ToGuid(matchGuidEndianness: true)
- : Uuid7.NewUuid7(timeStamp.Value).ToGuid(matchGuidEndianness: true);
-}
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Digdir.Domain.Dialogporten.Application.Unit.Tests.csproj b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Digdir.Domain.Dialogporten.Application.Unit.Tests.csproj
index 002f7ea39..3302f4db8 100644
--- a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Digdir.Domain.Dialogporten.Application.Unit.Tests.csproj
+++ b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Digdir.Domain.Dialogporten.Application.Unit.Tests.csproj
@@ -12,10 +12,10 @@
-
+
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Activities/ActivityValidatorTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Activities/ActivityValidatorTests.cs
new file mode 100644
index 000000000..03e5bcb68
--- /dev/null
+++ b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Activities/ActivityValidatorTests.cs
@@ -0,0 +1,62 @@
+using AutoMapper;
+using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations;
+using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors;
+using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create;
+using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update;
+using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
+using Digdir.Library.Entity.Abstractions.Features.Identifiable;
+using Digdir.Tool.Dialogporten.GenerateFakeData;
+using FluentAssertions;
+
+using UpdateActivityDto =
+ Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update.ActivityDto;
+using CreateActivityDto =
+ Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create.ActivityDto;
+
+namespace Digdir.Domain.Dialogporten.Application.Unit.Tests.Features.V1.ServiceOwner.Activities;
+
+public class ActivityValidatorTests
+{
+ public static IEnumerable