├── version.txt
├── docs
├── schema
│ └── V1
│ │ ├── src
│ │ └── .gitkeep
│ │ ├── .npmignore
│ │ ├── .gitignore
│ │ ├── package.json
│ │ └── gql-to-js.sh
└── deploy-process.png
├── local-otel-configuration
├── dashboards
│ └── dashboards.yml
├── prometheus.yml
├── grafana-dashboards.yml
└── grafana-datasources.yml
├── .release-please-manifest.json
├── tests
├── Digdir.Domain.Dialogporten.Application.Integration.Tests
│ ├── Usings.cs
│ ├── Common
│ │ ├── ApplicationClassFixture.cs
│ │ ├── ApplicationCollectionFixture.cs
│ │ ├── IntegrationTestCloudBus.cs
│ │ ├── TestDomainEventConsumer.cs
│ │ ├── AddingEndUserTransmissionSentLabelTestData.cs
│ │ └── TestClock.cs
│ └── Features
│ │ └── V1
│ │ ├── DialogCqrsCollectionFixture.cs
│ │ └── Common
│ │ └── UpdateDialogServiceOwnerContextCommandExtensions.cs
├── k6
│ ├── .gitignore
│ ├── tests
│ │ ├── performancetest_data
│ │ │ ├── endusers-staging.csv
│ │ │ ├── serviceowners-staging.csv
│ │ │ └── serviceowners-yt01.csv
│ │ ├── enduser-tests.js
│ │ ├── serviceowner-tests.js
│ │ ├── all-tests.js
│ │ ├── enduser
│ │ │ ├── parties.js
│ │ │ └── all-tests.js
│ │ ├── graphql
│ │ │ └── performance
│ │ │ │ ├── graphqlGetParties.js
│ │ │ │ ├── graphqlGetAllDialogsForEnduser.js
│ │ │ │ ├── graphqlGetAllDialogsForEnduserFTS.js
│ │ │ │ ├── graphqlGetAllDialogsForEnduserCount.js
│ │ │ │ ├── graphqlGetAllDialogsForParties.js
│ │ │ │ ├── graphqlGetAllDialogsForPartiesFTS.js
│ │ │ │ ├── graphqlGetAllDialogsForRandomParty.js
│ │ │ │ ├── graphqlGetAllDialogsForPartiesCount.js
│ │ │ │ ├── graphqlGetAllDialogsForRandomPartyCount.js
│ │ │ │ └── graphqlSearchWithThresholds.js
│ │ └── serviceowner
│ │ │ ├── dialogCreateInvalidProcess.js
│ │ │ └── performance
│ │ │ └── createDialogWithThresholds.js
│ ├── suites
│ │ ├── sentinel.js
│ │ ├── createremove-perf-10-vus-1-min.js
│ │ └── all-single-pass.js
│ └── common
│ │ ├── k6-utils.js
│ │ ├── summary.js
│ │ └── console.js
├── .editorconfig
├── Digdir.Domain.Dialogporten.Application.Unit.Tests
│ ├── Common
│ │ └── IOptionsMock.cs
│ └── Features
│ │ └── V1
│ │ └── Common
│ │ └── Utils
│ │ ├── ApplicationEventHandlerUtilsTests.Developer_Should_Use_Caution_When_Modifying_Endpoints.verified.txt
│ │ └── ApplicationEventHandlerUtilsTests.Developer_Should_Use_Caution_When_Modifying_Events.verified.txt
├── Digdir.Domain.Dialogporten.WebApi.Unit.Tests
│ └── Utils.cs
├── Digdir.Domain.Dialogporten.GraphQl.Integration.Tests
│ └── Utils.cs
└── Digdir.Domain.Dialogporten.Infrastructure.Unit.Tests
│ └── Digdir.Domain.Dialogporten.Infrastructure.Unit.Tests.csproj
├── global.json
├── src
├── Digdir.Domain.Dialogporten.Infrastructure
│ ├── Persistence
│ │ ├── Migrations
│ │ │ └── .editorconfig
│ │ ├── Constants.cs
│ │ ├── ValueConverters
│ │ │ └── DateTimeOffsetConverter.cs
│ │ ├── Configurations
│ │ │ ├── Actors
│ │ │ │ └── ActorNameConfiguration.cs
│ │ │ ├── DialogServiceOwnerContexts
│ │ │ │ └── DialogServiceOwnerContextConfiguration.cs
│ │ │ ├── SubjectResources
│ │ │ │ └── SubjectResourceConfiguration.cs
│ │ │ ├── DialogEndUserContexts
│ │ │ │ ├── DialogEndUserContextSystemLabelConfiguration.cs
│ │ │ │ └── DialogEndUserContextConfiguration.cs
│ │ │ ├── ResourcePolicyInformation
│ │ │ │ └── ResourcePolicyInformationConfiguration.cs
│ │ │ └── Dialogs
│ │ │ │ ├── Activities
│ │ │ │ └── DialogActivityConfiguration.cs
│ │ │ │ ├── Transmissions
│ │ │ │ └── DialogTransmissionConfiguration.cs
│ │ │ │ └── DialogSearchTagConfiguration.cs
│ │ └── Sql
│ │ │ └── Dialog
│ │ │ └── Search
│ │ │ ├── Function.UpsertDialogSearchOne.sql
│ │ │ ├── View.DialogSearchRebuildProgress.sql
│ │ │ ├── Function.UpsertDialogSearchOne.V2.sql
│ │ │ ├── Function.SeedDialogSearchQueueFull.sql
│ │ │ ├── View.VDialogDocument.sql
│ │ │ ├── View.VDialogDocument.V2.sql
│ │ │ └── Function.ClaimDialogsStandard.sql
│ ├── Common
│ │ ├── Exceptions
│ │ │ ├── OptimisticConcurrencyTimeoutException.cs
│ │ │ └── UpstreamServiceException.cs
│ │ └── PollyPolicy.cs
│ ├── InfrastructureAssemblyMarker.cs
│ ├── GraphQL
│ │ ├── GraphQlSubscriptionConstants.cs
│ │ ├── DialogEventPayload.cs
│ │ └── DummyRequestExecutorBuilder.cs
│ └── IEnumerableExtensions.cs
├── Digdir.Tool.Dialogporten.LargeDataSetGenerator
│ ├── .editorconfig
│ ├── .gitignore
│ ├── Parties.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── CopyTaskDto.cs
│ ├── PersonNames.cs
│ ├── EntityGenerators
│ │ ├── SeenLog.cs
│ │ └── EndUserContext.cs
│ ├── service_resources
│ └── Words.cs
├── Digdir.Library.Dialogporten.WebApiClient
│ ├── Directory.Build.props
│ ├── AssemblyMarker.cs
│ ├── Common
│ │ ├── Exceptions
│ │ │ └── IncompleteDialogportenClientInitializationException.cs
│ │ └── IClock.cs
│ ├── .refitter
│ ├── Services
│ │ └── DefaultValidationResult.cs
│ └── Infrastructure
│ │ └── IInternalDialogportenApi.cs
├── Digdir.Domain.Dialogporten.Application
│ ├── Common
│ │ ├── ReturnTypes
│ │ │ ├── ConcurrencyError.cs
│ │ │ ├── ValidationError.cs
│ │ │ ├── Conflict.cs
│ │ │ ├── BadRequest.cs
│ │ │ ├── DomainError.cs
│ │ │ ├── EntityNotVisible.cs
│ │ │ └── Forbidden.cs
│ │ ├── Pagination
│ │ │ ├── Order
│ │ │ │ ├── OrderDirection.cs
│ │ │ │ └── Order.cs
│ │ │ ├── OrderOption
│ │ │ │ ├── IOrderDefinition.cs
│ │ │ │ └── OrderSelector.cs
│ │ │ ├── PaginationConstants.cs
│ │ │ └── Continuation
│ │ │ │ └── ContinuationToken.cs
│ │ ├── ResourceRegistry
│ │ │ └── Constants.cs
│ │ ├── IIgnoreOnAssemblyScan.cs
│ │ ├── Extensions
│ │ │ ├── TypeExtensions.cs
│ │ │ ├── QueryableExtensions.cs
│ │ │ ├── Enumerables
│ │ │ │ ├── General.cs
│ │ │ │ └── AsyncEnumerableExtensions.cs
│ │ │ └── OptionExtensions
│ │ │ │ └── OptionsBuilderFluentValidationExtensions.cs
│ │ ├── Behaviours
│ │ │ └── FeatureMetric
│ │ │ │ └── IFeatureMetricDeliveryContext.cs
│ │ ├── Context
│ │ │ ├── ApplicationContext.cs
│ │ │ └── IApplicationContext.cs
│ │ ├── IClock.cs
│ │ └── ITransactionTime.cs
│ ├── Features
│ │ └── V1
│ │ │ ├── Common
│ │ │ ├── DeletedFilter.cs
│ │ │ ├── Authorization
│ │ │ │ └── Constants.cs
│ │ │ ├── Content
│ │ │ │ ├── PropertyInfoWithNullability.cs
│ │ │ │ ├── ContentValueDto.cs
│ │ │ │ └── MappingProfile.cs
│ │ │ ├── Actors
│ │ │ │ └── ActorValidationErrorMessages.cs
│ │ │ ├── Extensions
│ │ │ │ └── TransmissionEntityExtensions.cs
│ │ │ ├── Localizations
│ │ │ │ └── LocalizationDto.cs
│ │ │ └── ValidationErrorStrings.cs
│ │ │ ├── ServiceOwner
│ │ │ ├── Dialogs
│ │ │ │ ├── Queries
│ │ │ │ │ ├── NotificationCondition
│ │ │ │ │ │ └── NotificationConditionDto.cs
│ │ │ │ │ ├── GetSeenLog
│ │ │ │ │ │ ├── MappingProfile.cs
│ │ │ │ │ │ └── SeenLogDto.cs
│ │ │ │ │ ├── SearchSeenLogs
│ │ │ │ │ │ ├── MappingProfile.cs
│ │ │ │ │ │ └── SeenLogDto.cs
│ │ │ │ │ ├── GetActivity
│ │ │ │ │ │ └── MappingProfile.cs
│ │ │ │ │ ├── SearchActivities
│ │ │ │ │ │ ├── ActivityDto.cs
│ │ │ │ │ │ └── MappingProfile.cs
│ │ │ │ │ └── Get
│ │ │ │ │ │ └── GetDialogDataLoader.cs
│ │ │ │ └── Commands
│ │ │ │ │ ├── Purge
│ │ │ │ │ └── PurgeDialogCommandValidator.cs
│ │ │ │ │ ├── Create
│ │ │ │ │ └── Validators
│ │ │ │ │ │ ├── CreateDialogSearchTagDtoValidator.cs
│ │ │ │ │ │ ├── CreateDialogServiceOwnerLabelDtoValidator.cs
│ │ │ │ │ │ ├── CreateDialogDialogAttachmentUrlDtoValidator.cs
│ │ │ │ │ │ └── CreateDialogTransmissionAttachmentUrlDtoValidator.cs
│ │ │ │ │ ├── Update
│ │ │ │ │ ├── Validators
│ │ │ │ │ │ ├── UpdateDialogSearchTagDtoValidator.cs
│ │ │ │ │ │ ├── UpdateDialogDialogAttachmentUrlDtoValidator.cs
│ │ │ │ │ │ └── UpdateDialogTransmissionAttachmentUrlDtoValidator.cs
│ │ │ │ │ └── UpdateDialogDataLoader.cs
│ │ │ │ │ └── UpdateFormSavedActivityTime
│ │ │ │ │ └── UpdateFormSavedActivityTimeCommandValidator.cs
│ │ │ ├── ServiceOwnerContext
│ │ │ │ ├── Commands
│ │ │ │ │ └── Update
│ │ │ │ │ │ ├── MappingProfile.cs
│ │ │ │ │ │ └── UpdateServiceOwnerContextDto.cs
│ │ │ │ └── Queries
│ │ │ │ │ └── GetServiceOwnerLabels
│ │ │ │ │ ├── MappingProfile.cs
│ │ │ │ │ └── ServiceOwnerLabelDto.cs
│ │ │ └── Common
│ │ │ │ └── Actors
│ │ │ │ └── ActorDto.cs
│ │ │ ├── ResourceRegistry
│ │ │ └── Commands
│ │ │ │ ├── SyncPolicy
│ │ │ │ └── SyncPolicyCommandValidator.cs
│ │ │ │ └── SyncSubjectMap
│ │ │ │ └── SyncSubjectMapCommandValidator.cs
│ │ │ ├── WellKnown
│ │ │ ├── OauthAuthorizationServer
│ │ │ │ └── Queries
│ │ │ │ │ └── Get
│ │ │ │ │ └── GetOauthAuthorizationServerDto.cs
│ │ │ └── Jwks
│ │ │ │ └── Queries
│ │ │ │ └── Get
│ │ │ │ └── GetJwksDto.cs
│ │ │ ├── EndUser
│ │ │ ├── EndUserContext
│ │ │ │ └── Queries
│ │ │ │ │ └── SearchLabelAssignmentLog
│ │ │ │ │ ├── MappingProfile.cs
│ │ │ │ │ └── LabelAssignmentLogDto.cs
│ │ │ ├── Dialogs
│ │ │ │ └── Queries
│ │ │ │ │ ├── GetSeenLog
│ │ │ │ │ ├── MappingProfile.cs
│ │ │ │ │ └── SeenLogDto.cs
│ │ │ │ │ ├── SearchSeenLogs
│ │ │ │ │ ├── MappingProfile.cs
│ │ │ │ │ └── SeenLogDto.cs
│ │ │ │ │ ├── GetActivity
│ │ │ │ │ ├── MappingProfile.cs
│ │ │ │ │ └── ActivityDto.cs
│ │ │ │ │ └── SearchActivities
│ │ │ │ │ ├── MappingProfile.cs
│ │ │ │ │ └── ActivityDto.cs
│ │ │ └── Common
│ │ │ │ └── Actors
│ │ │ │ └── ActorDto.cs
│ │ │ └── AccessManagement
│ │ │ └── Queries
│ │ │ └── GetParties
│ │ │ └── MappingProfile.cs
│ ├── Externals
│ │ ├── Presentation
│ │ │ └── IUser.cs
│ │ ├── IPartyNameRegistry.cs
│ │ ├── IServiceOwnerNameRegistry.cs
│ │ ├── AltinnAuthorization
│ │ │ └── ActionResource.cs
│ │ └── ICloudEventBus.cs
│ └── ApplicationAssemblyMarker.cs
├── Digdir.Domain.Dialogporten.Domain
│ ├── Dialogs
│ │ ├── Events
│ │ │ ├── IProcessEvent.cs
│ │ │ ├── DialogSeenDomainEvent.cs
│ │ │ ├── DialogCreatedDomainEvent.cs
│ │ │ ├── DialogDeletedDomainEvent.cs
│ │ │ ├── DialogUpdatedDomainEvent.cs
│ │ │ ├── DialogRestoredDomainEvent.cs
│ │ │ ├── DialogTransmissionCreatedDomainEvent.cs
│ │ │ └── Activities
│ │ │ │ └── DialogActivityCreatedDomainEvent.cs
│ │ └── Entities
│ │ │ ├── Actions
│ │ │ ├── DialogGuiActionPriority.cs
│ │ │ └── DialogApiAction.cs
│ │ │ └── DialogSearchTag.cs
│ ├── Common
│ │ ├── Exceptions
│ │ │ └── ForbiddenException.cs
│ │ ├── DomainEvents
│ │ │ ├── DomainEventExtensions.cs
│ │ │ └── DomainEvent.cs
│ │ ├── EventPublisher
│ │ │ ├── IEventPublisher.cs
│ │ │ └── IDomainEvent.cs
│ │ ├── Constants.cs
│ │ └── DomainFailure.cs
│ ├── DomainAssemblyMarker.cs
│ ├── Outboxes
│ │ └── OutboxMessageConsumer.cs
│ ├── Digdir.Domain.Dialogporten.Domain.csproj
│ ├── DomainExtensions.cs
│ ├── SubjectResources
│ │ └── SubjectResource.cs
│ ├── Actors
│ │ ├── ActorType.cs
│ │ ├── Actor.cs
│ │ └── ActorName.cs
│ ├── Parties
│ │ └── Abstractions
│ │ │ └── IPartyIdentifier.cs
│ ├── ResourcePolicyInformation
│ │ └── ResourcePolicyInformation.cs
│ ├── Attachments
│ │ ├── AttachmentUrlConsumerType.cs
│ │ └── AttachmentUrl.cs
│ ├── Localizations
│ │ └── LocalizationSet.cs
│ ├── Http
│ │ └── HttpVerb.cs
│ ├── DialogEndUserContexts
│ │ └── Entities
│ │ │ └── DialogEndUserContextSystemLabel.cs
│ └── DialogServiceOwnerContexts
│ │ └── Entities
│ │ └── DialogServiceOwnerLabel.cs
├── Digdir.Domain.Dialogporten.WebApi
│ ├── WebApiAssemblyMarker.cs
│ ├── Common
│ │ ├── Json
│ │ │ └── ShortNameGenerator.cs
│ │ ├── Extensions
│ │ │ └── StringExtensions.cs
│ │ ├── Authentication
│ │ │ └── AuthenticationOptions.cs
│ │ ├── Authorization
│ │ │ ├── AuthorizationPolicy.cs
│ │ │ └── AllowAnonymousHandler.cs
│ │ ├── FeatureMetric
│ │ │ └── FeatureMetricOptions.cs
│ │ ├── WebApiSettings.cs
│ │ └── ApplicationUser.cs
│ └── Endpoints
│ │ └── V1
│ │ ├── EndUser
│ │ ├── EndUserGroup.cs
│ │ └── AccessManagement
│ │ │ └── Queries
│ │ │ └── GetParties
│ │ │ └── GetPartiesEndpointSummary.cs
│ │ ├── ServiceOwner
│ │ └── ServiceOwnerGroup.cs
│ │ ├── MetadataGroup.cs
│ │ ├── WellKnown
│ │ └── Jwks
│ │ │ └── Get
│ │ │ └── GetJwksEndpointSummary.cs
│ │ └── Common
│ │ └── Headers
│ │ └── HttpResponseHeaderExamples.cs
├── Digdir.Domain.Dialogporten.GraphQL
│ ├── GraphQLAssemblyMarker.cs
│ ├── EndUser
│ │ ├── Queries.cs
│ │ ├── Parties
│ │ │ └── MappingProfile.cs
│ │ └── MutationTypes
│ │ │ └── MappingProfile.cs
│ ├── Common
│ │ ├── Constants.cs
│ │ ├── Authentication
│ │ │ └── AuthenticationOptions.cs
│ │ ├── Authorization
│ │ │ ├── AuthorizationPolicy.cs
│ │ │ └── AllowAnonymousHandler.cs
│ │ └── ApplicationUser.cs
│ ├── Properties
│ │ └── launchSettings.json
│ └── appsettings.json
├── Digdir.Domain.Dialogporten.Janitor
│ ├── JanitorAssemblyMarker.cs
│ ├── appsettings.json
│ ├── CostManagementAggregation
│ │ ├── cost-coefficients.json
│ │ └── MetricsAggregationOptions.cs
│ └── ConsoleUser.cs
├── Digdir.Domain.Dialogporten.Service
│ ├── ServiceAssemblyMarker.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── appsettings.json
│ ├── ServiceUser.cs
│ └── Digdir.Domain.Dialogporten.Service.csproj
├── Digdir.Library.Entity.Abstractions
│ ├── Features
│ │ ├── Immutable
│ │ │ └── IImmutableEntity.cs
│ │ ├── Aggregate
│ │ │ └── AggregateParentAttribute.cs
│ │ ├── Identifiable
│ │ │ └── IIdentifiableEntity.cs
│ │ ├── Versionable
│ │ │ ├── IVersionableEntity.cs
│ │ │ └── VersionableExtensions.cs
│ │ ├── Creatable
│ │ │ ├── ICreatableEntity.cs
│ │ │ └── CreatableExtensions.cs
│ │ └── Updatable
│ │ │ └── IUpdateableEntity.cs
│ ├── Digdir.Library.Entity.Abstractions.csproj
│ ├── LibraryEntityAbstractionsAssemblyMarker.cs
│ ├── IEntity.cs
│ └── IJoinEntity.cs
├── Digdir.Tool.Dialogporten.Ed25519KeyPairGenerator
│ └── Digdir.Tool.Dialogporten.Ed25519KeyPairGenerator.csproj
├── Digdir.Library.Dialogporten.WebApiClient.WebApiSample
│ ├── WebApiSample.http
│ ├── Digdir.Library.Dialogporten.WebApiClient.WebApiSample.csproj
│ ├── appsettings.json
│ └── Properties
│ │ └── launchSettings.json
├── Digdir.Library.Utils.AspNet
│ ├── WebHostCommonSettings.cs
│ ├── ServiceCollectionExtensions.cs
│ └── FusionCacheFilter.cs
├── Digdir.Library.Entity.EntityFrameworkCore
│ ├── LibraryEntityFrameworkCoreAssemblyMarker.cs
│ └── Digdir.Library.Entity.EntityFrameworkCore.csproj
└── Digdir.Tool.Dialogporten.GenerateFakeData
│ └── Digdir.Tool.Dialogporten.GenerateFakeData.csproj
├── sonar-project.properties
├── .github
├── CODEOWNERS
├── tools
│ └── README.md
├── pr-title-checker-config.json
├── pull_request_template.md
├── ISSUE_TEMPLATE
│ └── bug.md
└── workflows
│ ├── workflow-get-current-version.yml
│ └── ci-cd-pull-request-title.yml
├── .dockerignore
├── .azure
├── modules
│ ├── keyvault
│ │ └── upsertSecret.bicep
│ ├── managedIdentity
│ │ └── main.bicep
│ └── privateDnsZoneGroup
│ │ └── main.bicep
├── applications
│ ├── web-api-migration-job
│ │ ├── prod.bicepparam
│ │ ├── test.bicepparam
│ │ ├── yt01.bicepparam
│ │ └── staging.bicepparam
│ ├── reindex-dialogsearch-job
│ │ ├── test.bicepparam
│ │ ├── prod.bicepparam
│ │ ├── staging.bicepparam
│ │ └── yt01.bicepparam
│ ├── sync-resource-policy-information-job
│ │ ├── prod.bicepparam
│ │ ├── test.bicepparam
│ │ ├── yt01.bicepparam
│ │ └── staging.bicepparam
│ ├── sync-subject-resource-mappings-job
│ │ ├── prod.bicepparam
│ │ ├── test.bicepparam
│ │ ├── yt01.bicepparam
│ │ └── staging.bicepparam
│ ├── graphql
│ │ ├── test.bicepparam
│ │ ├── staging.bicepparam
│ │ └── yt01.bicepparam
│ ├── web-api-eu
│ │ ├── test.bicepparam
│ │ ├── staging.bicepparam
│ │ └── yt01.bicepparam
│ ├── web-api-so
│ │ ├── test.bicepparam
│ │ ├── staging.bicepparam
│ │ └── yt01.bicepparam
│ ├── aggregate-cost-metrics-job
│ │ ├── prod.bicepparam
│ │ └── staging.bicepparam
│ └── service
│ │ ├── test.bicepparam
│ │ ├── staging.bicepparam
│ │ ├── yt01.bicepparam
│ │ └── prod.bicepparam
└── bicepconfig.json
├── .env
├── docker-compose-cdc.yml
├── nginx-webapi.conf
├── nginx-graphql.conf
├── docker-compose-jobs.yml
├── Directory.Build.props
└── release-please-config.json
/version.txt:
--------------------------------------------------------------------------------
1 | 1.97.0
2 |
--------------------------------------------------------------------------------
/docs/schema/V1/src/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/local-otel-configuration/dashboards/dashboards.yml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.release-please-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | ".": "1.97.0"
3 | }
--------------------------------------------------------------------------------
/docs/schema/V1/.npmignore:
--------------------------------------------------------------------------------
1 | swagger.received.json
2 | schema.received.graphql
3 | .gitkeep
4 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Usings.cs:
--------------------------------------------------------------------------------
1 | global using Xunit;
--------------------------------------------------------------------------------
/docs/deploy-process.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/altinn/dialogporten/main/docs/deploy-process.png
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "9.0.308",
4 | "rollForward": "disable"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Migrations/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | generated_code = true
--------------------------------------------------------------------------------
/tests/k6/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore summary files generated by running some suites
2 | junit.xml
3 | summary.json
4 |
--------------------------------------------------------------------------------
/src/Digdir.Tool.Dialogporten.LargeDataSetGenerator/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 | dotnet_diagnostic.CA1305.severity = none
--------------------------------------------------------------------------------
/src/Digdir.Tool.Dialogporten.LargeDataSetGenerator/.gitignore:
--------------------------------------------------------------------------------
1 | person_names
2 | parties
3 | wordlist_en
4 | wordlist_no
5 |
--------------------------------------------------------------------------------
/tests/k6/tests/performancetest_data/endusers-staging.csv:
--------------------------------------------------------------------------------
1 | ssn,resource,scopes
2 | 08895699684,super-simple-service,digdir:dialogporten
3 |
--------------------------------------------------------------------------------
/tests/k6/suites/sentinel.js:
--------------------------------------------------------------------------------
1 | import { default as sentinelCheck } from '../common/sentinel.js';
2 |
3 | export default function () {
4 | sentinelCheck();
5 | };
6 |
--------------------------------------------------------------------------------
/tests/k6/tests/enduser-tests.js:
--------------------------------------------------------------------------------
1 | import { default as endUserTests } from './enduser/all-tests.js';
2 |
3 | export function runAllTests() {
4 | endUserTests();
5 | };
--------------------------------------------------------------------------------
/src/Digdir.Library.Dialogporten.WebApiClient/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/tests/k6/tests/serviceowner-tests.js:
--------------------------------------------------------------------------------
1 | import { default as serviceOwnerTests } from './serviceowner/all-tests.js';
2 |
3 | export function runAllTests() {
4 | serviceOwnerTests();
5 | };
--------------------------------------------------------------------------------
/tests/k6/common/k6-utils.js:
--------------------------------------------------------------------------------
1 | export { uuidv4, randomItem, randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js';
2 | export { URL } from 'https://jslib.k6.io/url/1.0.0/index.js';
3 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/ReturnTypes/ConcurrencyError.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Common.ReturnTypes;
2 |
3 | public sealed record ConcurrencyError;
4 |
--------------------------------------------------------------------------------
/tests/k6/tests/performancetest_data/serviceowners-staging.csv:
--------------------------------------------------------------------------------
1 | org,orgno,scopes,resource
2 | digdir,991825827,digdir:dialogporten.serviceprovider digdir:dialogporten.serviceprovider.search,super-simple-service
3 |
--------------------------------------------------------------------------------
/tests/k6/tests/performancetest_data/serviceowners-yt01.csv:
--------------------------------------------------------------------------------
1 | org,orgno,scopes,resource
2 | ttd,713431400,digdir:dialogporten.serviceprovider digdir:dialogporten.serviceprovider.search,ttd-dialogporten-performance-test-01
3 |
--------------------------------------------------------------------------------
/sonar-project.properties:
--------------------------------------------------------------------------------
1 | # Disable code duplication detection for C# and k6 files
2 | sonar.cpd.exclusions=**/*.cs,**/k6/**/*
3 |
4 | # Disable all rules for auto-generated migration files
5 | sonar.exclusions=**/Migrations/**/*
6 |
--------------------------------------------------------------------------------
/tests/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | # CA1707: Identifiers should not contain underscores
3 | dotnet_diagnostic.CA1707.severity = none
4 |
5 | # IDE0010: Add missing cases to switch statements
6 | dotnet_diagnostic.IDE0010.severity = none
--------------------------------------------------------------------------------
/local-otel-configuration/prometheus.yml:
--------------------------------------------------------------------------------
1 | global:
2 | scrape_interval: 15s
3 | evaluation_interval: 15s
4 |
5 | scrape_configs:
6 | - job_name: 'otel-collector'
7 | static_configs:
8 | - targets: ['otel-collector:8889']
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/Order/OrderDirection.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Common.Pagination.Order;
2 |
3 | public enum OrderDirection
4 | {
5 | Asc = 1,
6 | Desc = 2
7 | }
8 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/DeletedFilter.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.Common;
2 |
3 | public enum DeletedFilter
4 | {
5 | Exclude = 1,
6 | Include = 2,
7 | Only = 3
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Events/IProcessEvent.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Events;
2 |
3 | public interface IProcessEvent
4 | {
5 | string? Process { get; }
6 | string? PrecedingProcess { get; }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Common/Exceptions/OptimisticConcurrencyTimeoutException.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Infrastructure.Common.Exceptions;
2 |
3 | internal sealed class OptimisticConcurrencyTimeoutException : Exception;
4 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Common/PollyPolicy.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Infrastructure.Common;
2 |
3 | internal static class PollyPolicy
4 | {
5 | public const string DefaultHttpRetryPolicy = "DefaultHttpRetryPolicy";
6 | }
7 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Externals/Presentation/IUser.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Externals.Presentation;
4 |
5 | public interface IUser
6 | {
7 | ClaimsPrincipal GetPrincipal();
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Tool.Dialogporten.LargeDataSetGenerator/Parties.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Tool.Dialogporten.LargeDataSetGenerator;
2 |
3 | internal static class Parties
4 | {
5 | internal static readonly string[] List = File.ReadAllLines("./parties").Distinct().ToArray();
6 | }
7 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/ResourceRegistry/Constants.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Common.ResourceRegistry;
2 |
3 | public sealed class Constants
4 | {
5 | public const string CorrespondenceService = "correspondenceservice";
6 | }
7 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Dialogporten.WebApiClient/AssemblyMarker.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace Altinn.ApiClients.Dialogporten;
4 |
5 | internal sealed class AssemblyMarker
6 | {
7 | public static readonly Assembly Assembly = typeof(AssemblyMarker).Assembly;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Authorization/Constants.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.Common.Authorization;
2 |
3 | public static class Constants
4 | {
5 | public const string DialogTokenIssuerVersion = "/api/v1";
6 | }
7 |
--------------------------------------------------------------------------------
/local-otel-configuration/grafana-dashboards.yml:
--------------------------------------------------------------------------------
1 | apiVersion: 1
2 |
3 | providers:
4 | - name: 'Default'
5 | orgId: 1
6 | folder: ''
7 | type: file
8 | disableDeletion: false
9 | editable: true
10 | options:
11 | path: /etc/grafana/provisioning/dashboards
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Common/Exceptions/ForbiddenException.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Domain.Common.Exceptions;
2 |
3 | public sealed class ForbiddenException : ApplicationException
4 | {
5 | public ForbiddenException(string message) : base(message) { }
6 | }
7 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/DomainAssemblyMarker.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain;
4 |
5 | public sealed class DomainAssemblyMarker
6 | {
7 | public static readonly Assembly Assembly = typeof(DomainAssemblyMarker).Assembly;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/WebApiAssemblyMarker.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace Digdir.Domain.Dialogporten.WebApi;
4 |
5 | public sealed class WebApiAssemblyMarker
6 | {
7 | public static readonly Assembly Assembly = typeof(WebApiAssemblyMarker).Assembly;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.GraphQL/GraphQLAssemblyMarker.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace Digdir.Domain.Dialogporten.GraphQL;
4 |
5 | public sealed class GraphQLAssemblyMarker
6 | {
7 | public static readonly Assembly Assembly = typeof(GraphQLAssemblyMarker).Assembly;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Janitor/JanitorAssemblyMarker.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace Digdir.Domain.Dialogporten.Janitor;
4 |
5 | public sealed class JanitorAssemblyMarker
6 | {
7 | public static readonly Assembly Assembly = typeof(JanitorAssemblyMarker).Assembly;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Service/ServiceAssemblyMarker.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace Digdir.Domain.Dialogporten.Service;
4 |
5 | public sealed class ServiceAssemblyMarker
6 | {
7 | public static readonly Assembly Assembly = typeof(ServiceAssemblyMarker).Assembly;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Dialogporten.WebApiClient/Common/Exceptions/IncompleteDialogportenClientInitializationException.cs:
--------------------------------------------------------------------------------
1 | namespace Altinn.ApiClients.Dialogporten.Common.Exceptions;
2 |
3 | public sealed class IncompleteDialogportenClientInitializationException(string message) : Exception(message);
4 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @altinn/team-dialogporten-backend
2 | .azure/** @altinn/team-dialogporten-backend @altinn/team-dialogporten-infra
3 | .github/** @altinn/team-dialogporten-backend @altinn/team-dialogporten-infra
4 | .github/CODEOWNERS @altinn/team-dialogporten-backend
--------------------------------------------------------------------------------
/src/Digdir.Tool.Dialogporten.LargeDataSetGenerator/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "LargeDataSetGenerator": {
4 | "commandName": "Project",
5 | "environmentVariables": {
6 | "ASPNETCORE_ENVIRONMENT": "Development"
7 | }
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/IIgnoreOnAssemblyScan.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Common;
2 |
3 | ///
4 | /// Marker interface to indicate that a class should be ignored during assembly scanning.
5 | ///
6 | public interface IIgnoreOnAssemblyScan;
7 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/ApplicationAssemblyMarker.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application;
4 |
5 | public sealed class ApplicationAssemblyMarker
6 | {
7 | public static readonly Assembly Assembly = typeof(ApplicationAssemblyMarker).Assembly;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Queries.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.GraphQL.Common.Authorization;
2 | using HotChocolate.Authorization;
3 |
4 | namespace Digdir.Domain.Dialogporten.GraphQL.EndUser;
5 |
6 | [Authorize(Policy = AuthorizationPolicy.EndUser)]
7 | public sealed partial class Queries;
8 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/PropertyInfoWithNullability.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content;
4 |
5 | internal sealed record PropertyInfoWithNullability(PropertyInfo Property, NullabilityInfo NullabilityInfo);
6 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Dialogporten.WebApiClient/Common/IClock.cs:
--------------------------------------------------------------------------------
1 | namespace Altinn.ApiClients.Dialogporten.Common;
2 |
3 | internal interface IClock
4 | {
5 | DateTimeOffset UtcNow { get; }
6 | }
7 |
8 | internal class DefaultClock : IClock
9 | {
10 | public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
11 | }
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/OrderOption/IOrderDefinition.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Common.Pagination.OrderOption;
2 |
3 | public interface IOrderDefinition
4 | {
5 | static abstract IOrderOptions Configure(IOrderOptionsBuilder options);
6 | }
7 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/InfrastructureAssemblyMarker.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace Digdir.Domain.Dialogporten.Infrastructure;
4 |
5 | public sealed class InfrastructureAssemblyMarker
6 | {
7 | public static readonly Assembly Assembly = typeof(InfrastructureAssemblyMarker).Assembly;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Tool.Dialogporten.LargeDataSetGenerator/CopyTaskDto.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Tool.Dialogporten.LargeDataSetGenerator;
2 |
3 | public record struct CopyTaskDto(
4 | Func Generator,
5 | string EntityName,
6 | string CopyCommand,
7 | bool SingleLinePerTimestamp = false,
8 | int NumberOfTasks = 1);
9 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/TypeExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Common.Extensions;
2 |
3 | public static class TypeExtensions
4 | {
5 | public static bool IsNullableType(this Type type)
6 | => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
7 | }
8 |
--------------------------------------------------------------------------------
/local-otel-configuration/grafana-datasources.yml:
--------------------------------------------------------------------------------
1 | apiVersion: 1
2 |
3 | datasources:
4 | - name: Prometheus
5 | type: prometheus
6 | access: proxy
7 | url: http://prometheus:9090
8 | isDefault: true
9 |
10 | - name: Loki
11 | type: loki
12 | access: proxy
13 | url: http://loki:3100
14 | jsonData:
15 | maxLines: 1000
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/ReturnTypes/ValidationError.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation.Results;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Common.ReturnTypes;
4 |
5 | public sealed record ValidationError(IEnumerable Errors)
6 | {
7 | public ValidationError(ValidationFailure error) : this([error]) { }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/NotificationCondition/NotificationConditionDto.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.NotificationCondition;
2 |
3 | public sealed class NotificationConditionDto
4 | {
5 | public bool SendNotification { get; set; }
6 | }
7 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/GraphQL/GraphQlSubscriptionConstants.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Infrastructure.GraphQL;
2 |
3 | public static class GraphQlSubscriptionConstants
4 | {
5 | public const string SubscriptionTopicPrefix = "graphql_subscriptions_";
6 | public const string DialogEventsTopic = "dialogEvents/";
7 | }
8 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Behaviours/FeatureMetric/IFeatureMetricDeliveryContext.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Common.Behaviours.FeatureMetric;
2 |
3 | public interface IFeatureMetricDeliveryContext
4 | {
5 | void ReportOutcome(string presentationTag, params IEnumerable> additionalTags);
6 | }
7 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.Abstractions/Features/Immutable/IImmutableEntity.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Library.Entity.Abstractions.Features.Immutable;
2 |
3 | ///
4 | /// Interface to mark an entity as immutable.
5 | /// See Digdir.Library.Entity.EntityFrameworkCore.Features.Immutable.HandleImmutableEntities.
6 | ///
7 | public interface IImmutableEntity;
8 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Context/ApplicationContext.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Common.Context;
2 |
3 | public sealed class ApplicationContext : IApplicationContext
4 | {
5 | public Dictionary Metadata { get; } = [];
6 | public void AddMetadata(string key, string value) => Metadata[key] = value;
7 | }
8 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/ReturnTypes/Conflict.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation.Results;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Common.ReturnTypes;
4 |
5 | public sealed record Conflict(string PropertyName, string ErrorMessage)
6 | {
7 | public List ToValidationResults() => [new(PropertyName, ErrorMessage)];
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Externals/IPartyNameRegistry.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Externals;
2 |
3 | public interface IPartyNameRegistry
4 | {
5 | Task GetName(string externalIdWithPrefix, CancellationToken cancellationToken);
6 | Task GetOrgName(string orgNumber, CancellationToken cancellationToken);
7 | }
8 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Outboxes/OutboxMessageConsumer.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Domain.Outboxes;
2 |
3 | public sealed class OutboxMessageConsumer
4 | {
5 | public Guid EventId { get; set; }
6 | public string ConsumerName { get; set; } = string.Empty;
7 |
8 | public OutboxMessage OutboxMessage { get; set; } = null!;
9 | }
10 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.Abstractions/Features/Aggregate/AggregateParentAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Library.Entity.Abstractions.Features.Aggregate;
2 |
3 | ///
4 | /// Used to define a relationship from parent to child in the aggregate tree
5 | ///
6 | [AttributeUsage(AttributeTargets.Property)]
7 | public sealed class AggregateChildAttribute : Attribute;
8 |
--------------------------------------------------------------------------------
/docs/schema/V1/.gitignore:
--------------------------------------------------------------------------------
1 | # in `npm prepublishOnly` we'll genenrate files for src/
2 | # So here we gitignore all of the files since they're generated
3 | # But we add an exception for `src/.gitkeep` so that we can keep
4 | # the folder around. (Git only commits files, not folders, so
5 | # `.gitkeep` is just an empty file to force git to commit the folder)
6 | src/*
7 | !src/.gitkeep
8 |
--------------------------------------------------------------------------------
/src/Digdir.Tool.Dialogporten.Ed25519KeyPairGenerator/Digdir.Tool.Dialogporten.Ed25519KeyPairGenerator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/GraphQL/DialogEventPayload.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Infrastructure.GraphQL;
2 |
3 | internal struct DialogEventPayload
4 | {
5 | public Guid Id { get; set; }
6 | public DialogEventType Type { get; set; }
7 | }
8 |
9 | internal enum DialogEventType
10 | {
11 | DialogUpdated = 1,
12 | DialogDeleted = 2
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/IEnumerableExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | namespace Digdir.Domain.Dialogporten.Infrastructure;
4 |
5 | internal static class IEnumerableExtensions
6 | {
7 | internal static bool IsNullOrEmpty([NotNullWhen(false)] this IEnumerable? enumerable) =>
8 | enumerable is null || !enumerable.Any();
9 | }
10 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Common/Json/ShortNameGenerator.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.WebApi.Common.Swagger;
2 | using NJsonSchema.Generation;
3 |
4 | namespace Digdir.Domain.Dialogporten.WebApi.Common.Json;
5 |
6 | internal sealed class ShortNameGenerator : ISchemaNameGenerator
7 | {
8 | public string Generate(Type type) => TypeNameConverter.ToShortName(type);
9 | }
10 |
--------------------------------------------------------------------------------
/src/Digdir.Tool.Dialogporten.LargeDataSetGenerator/PersonNames.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Tool.Dialogporten.LargeDataSetGenerator;
2 |
3 | internal static class PersonNames
4 | {
5 | internal static readonly string[]
6 | List = File.Exists("./person_names")
7 | ? File.ReadAllLines("./person_names")
8 | : throw new FileNotFoundException("./person_names");
9 | }
10 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/ApplicationClassFixture.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Common;
2 |
3 | public abstract class ApplicationClassFixture : ApplicationCollectionFixture, IClassFixture
4 | {
5 | protected ApplicationClassFixture(DialogApplication application) : base(application) { }
6 | }
7 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 |
3 | namespace Digdir.Domain.Dialogporten.WebApi.Common.Extensions;
4 |
5 | internal static class StringExtensions
6 | {
7 | public static string FormatInvariant(this string pattern, params object[] args)
8 | => string.Format(CultureInfo.InvariantCulture, pattern, args);
9 | }
10 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Digdir.Domain.Dialogporten.Domain.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.GraphQL/Common/Constants.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.GraphQL.Common;
2 |
3 | internal static class Constants
4 | {
5 | internal const string Authorization = "Authorization";
6 | internal const string CurrentTokenIssuer = "CurrentIssuer";
7 | internal const string AcceptLanguage = "Accept-Language";
8 | internal const string ETag = "Etag";
9 | }
10 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Constants.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence;
2 |
3 | internal static class Constants
4 | {
5 | internal const string Gin = "gin";
6 | internal const string GinTrgmOps = "gin_trgm_ops";
7 | internal const string PostgreSqlTrigram = "pg_trgm";
8 | internal const string BtreeGin = "btree_gin";
9 | }
10 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Common/IOptionsMock.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Options;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Unit.Tests.Common;
4 |
5 | internal sealed class OptionsMock : IOptions where T : class
6 | {
7 | public T Value { get; set; }
8 |
9 | public OptionsMock(T value)
10 | {
11 | Value = value;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Service/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Digdir.Domain.Dialogporten.Service": {
4 | "commandName": "Project",
5 | "launchBrowser": false,
6 | "environmentVariables": {
7 | "DOTNET_ENVIRONMENT": "Development"
8 | },
9 | "applicationUrl": "https://localhost:56841;http://localhost:56842"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Events/DialogSeenDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common.DomainEvents;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Events;
4 |
5 | public sealed record DialogSeenDomainEvent(
6 | Guid DialogId,
7 | string ServiceResource,
8 | string Party,
9 | string? Process,
10 | string? PrecedingProcess) : DomainEvent, IProcessEvent;
11 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.dockerignore
2 | **/.env
3 | **/.git
4 | **/.gitignore
5 | **/.project
6 | **/.settings
7 | **/.toolstarget
8 | **/.vs
9 | **/.vscode
10 | **/.idea
11 | **/*.*proj.user
12 | **/*.dbmdl
13 | **/*.jfm
14 | **/azds.yaml
15 | **/bin
16 | **/charts
17 | **/docker-compose*
18 | **/Dockerfile*
19 | **/node_modules
20 | **/npm-debug.log
21 | **/obj
22 | **/secrets.dev.yaml
23 | **/values.dev.yaml
24 | LICENSE
25 | README.md
--------------------------------------------------------------------------------
/docs/schema/V1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@digdir/dialogporten-schema",
3 | "version": "1.0.11",
4 | "description": "GraphQl schema and OpenAPI spec for Dialogporten",
5 | "engines": {
6 | "node": "22"
7 | },
8 | "author": "DigDir",
9 | "main": "src/index.js",
10 | "repository": {
11 | "url": "git+https://github.com/Altinn/dialogporten.git"
12 | },
13 | "license": "ISC",
14 | "type": "module"
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Events/DialogCreatedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common.DomainEvents;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Events;
4 |
5 | public sealed record DialogCreatedDomainEvent(
6 | Guid DialogId,
7 | string ServiceResource,
8 | string Party,
9 | string? Process,
10 | string? PrecedingProcess) : DomainEvent, IProcessEvent;
11 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Events/DialogDeletedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common.DomainEvents;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Events;
4 |
5 | public sealed record DialogDeletedDomainEvent(
6 | Guid DialogId,
7 | string ServiceResource,
8 | string Party,
9 | string? Process,
10 | string? PrecedingProcess) : DomainEvent, IProcessEvent;
11 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Events/DialogUpdatedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common.DomainEvents;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Events;
4 |
5 | public sealed record DialogUpdatedDomainEvent(
6 | Guid DialogId,
7 | string ServiceResource,
8 | string Party,
9 | string? Process,
10 | string? PrecedingProcess) : DomainEvent, IProcessEvent;
11 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.Abstractions/Features/Identifiable/IIdentifiableEntity.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Library.Entity.Abstractions.Features.Identifiable;
2 |
3 | ///
4 | /// Abstraction implemented by entities that can be identified.
5 | ///
6 | public interface IIdentifiableEntity
7 | {
8 | ///
9 | /// The entity identification.
10 | ///
11 | Guid Id { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/.azure/modules/keyvault/upsertSecret.bicep:
--------------------------------------------------------------------------------
1 | param destKeyVaultName string
2 | param secretName string
3 | param tags object
4 | @secure()
5 | param secretValue string
6 |
7 | resource secret 'Microsoft.KeyVault/vaults/secrets@2024-11-01' = {
8 | name: '${destKeyVaultName}/${secretName}'
9 | properties: {
10 | value: secretValue
11 | }
12 | tags: tags
13 | }
14 |
15 | output secretUri string = secret.properties.secretUri
16 |
--------------------------------------------------------------------------------
/.github/tools/README.md:
--------------------------------------------------------------------------------
1 | # Tools
2 |
3 | `.github/tools/containerAppJobVerifier.sh` is used to verify the status of a container application job. It checks if the job has completed successfully or not.
4 |
5 | `.github/tools/pwdGenerator.ps1` is used to generate passwords.
6 |
7 | `.github/tools/revisionVerifier.sh` is used to verify the revision of the deployed container app. It ensures that the current revision has been deployed successfully.
8 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Events/DialogRestoredDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common.DomainEvents;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Events;
4 |
5 | public sealed record DialogRestoredDomainEvent(
6 | Guid DialogId,
7 | string ServiceResource,
8 | string Party,
9 | string? Process,
10 | string? PrecedingProcess) : DomainEvent, IProcessEvent;
11 |
--------------------------------------------------------------------------------
/tests/k6/common/summary.js:
--------------------------------------------------------------------------------
1 | import { generateJUnitXML, textSummary } from "./report.js";
2 |
3 | export default function (data) {
4 | return {
5 | 'stdout': textSummary(data, { indent: ' ', enableColors: true }) + "\n",
6 | //'summary.txt': textSummary(data, { indent: ' ', enableColors: false }) + "\n",
7 | //'summary.json': JSON.stringify(data),
8 | 'junit.xml': generateJUnitXML(data)
9 | };
10 | }
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/EndUserGroup.cs:
--------------------------------------------------------------------------------
1 | using FastEndpoints;
2 |
3 | namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.EndUser;
4 |
5 | public sealed class EndUserGroup : Group
6 | {
7 | public const string RoutePrefix = "enduser";
8 | public EndUserGroup()
9 | {
10 | Configure(RoutePrefix, ep =>
11 | {
12 | ep.EndpointVersion(1);
13 | });
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Dialogporten.WebApiClient.WebApiSample/WebApiSample.http:
--------------------------------------------------------------------------------
1 | @HostAddress = http://localhost:5006
2 | @dialogId = DIALOG_ID_HERE
3 | @dialogToken = "DIALOG_TOKEN_HERE"
4 |
5 | ### Verify DialogToken
6 |
7 | POST {{HostAddress}}/dialogTokenVerify/
8 | Accept: application/json
9 |
10 | {
11 | "token": {{dialogToken}}
12 | }
13 |
14 | ### Get dialog by id
15 | GET {{HostAddress}}/dialog/{{dialogId}}
16 | Accept: application/json
17 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Utils.AspNet/WebHostCommonSettings.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Library.Utils.AspNet;
2 |
3 | public sealed class WebHostCommonSettings
4 | {
5 | public const string SectionName = "WebHostCommon";
6 |
7 | public MaintenanceMode MaintenanceMode { get; init; } = new();
8 | }
9 |
10 | public sealed class MaintenanceMode
11 | {
12 | public bool Enabled { get; init; }
13 | public DateTimeOffset? RetryAt { get; init; }
14 | }
15 |
--------------------------------------------------------------------------------
/.azure/applications/web-api-migration-job/prod.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'prod'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param replicaTimeOutInSeconds = 86400
7 |
8 | //secrets
9 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
10 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
11 |
--------------------------------------------------------------------------------
/.azure/applications/web-api-migration-job/test.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'test'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param replicaTimeOutInSeconds = 86400
7 |
8 | //secrets
9 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
10 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
11 |
--------------------------------------------------------------------------------
/.azure/applications/web-api-migration-job/yt01.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'yt01'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param replicaTimeOutInSeconds = 86400
7 |
8 | //secrets
9 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
10 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
11 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | # ENV variables for docker-compose
2 | POSTGRES_USER=postgres
3 | POSTGRES_PASSWORD=supersecret
4 | POSTGRES_DB=dialogporten
5 | DB_CONNECTION_STRING=Server=dialogporten-postgres;Port=5432;Database=${POSTGRES_DB};User ID=${POSTGRES_USER};Password=${POSTGRES_PASSWORD};
6 |
7 | COMPOSE_PROJECT_NAME=digdir
8 |
9 | # OTEL
10 | OTEL_NAMESPACE=dialogporten-local
11 | OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
12 | OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.Abstractions/Features/Versionable/IVersionableEntity.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Library.Entity.Abstractions.Features.Versionable;
2 |
3 | ///
4 | /// Abstraction implemented by entities to keep track of their version.
5 | ///
6 | public interface IVersionableEntity
7 | {
8 | ///
9 | /// The entity tag unique to each version of the entity.
10 | ///
11 | Guid Revision { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/tests/k6/tests/all-tests.js:
--------------------------------------------------------------------------------
1 | import { default as serviceOwnerTests } from './serviceowner/all-tests.js';
2 | import { default as enduserTests } from './enduser/all-tests.js';
3 | import { default as sentinelCheck } from '../common/sentinel.js';
4 |
5 | export function runAllTests() {
6 | serviceOwnerTests();
7 | enduserTests();
8 |
9 | // Run sentinel check last, which will warn about and purge any leftover dialogs
10 | sentinelCheck();
11 | };
12 |
--------------------------------------------------------------------------------
/.azure/applications/web-api-migration-job/staging.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'staging'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param replicaTimeOutInSeconds = 86400
7 |
8 | //secrets
9 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
10 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
11 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/QueryableExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Linq.Expressions;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Common.Extensions;
4 |
5 | public static class QueryableExtensions
6 | {
7 | public static IQueryable WhereIf(this IQueryable source, bool predicate, Expression> queryPredicate)
8 | => predicate ? source.Where(queryPredicate) : source;
9 | }
10 |
--------------------------------------------------------------------------------
/tests/k6/tests/enduser/parties.js:
--------------------------------------------------------------------------------
1 | import { describe, expect, expectStatusFor, getEU } from '../../common/testimports.js'
2 |
3 | export default function () {
4 | describe('Check if we get any parties', () => {
5 | let r = getEU("parties");
6 | expectStatusFor(r).to.equal(200);
7 | expect(r, 'response').to.have.validJsonBody();
8 | expect(r.json(), 'response json').to.have.property("authorizedParties").with.lengthOf.at.least(2);
9 | });
10 | }
--------------------------------------------------------------------------------
/.github/pr-title-checker-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "LABEL": {
3 | "name": "title needs formatting",
4 | "color": "EEEEEE"
5 | },
6 | "CHECKS": {
7 | "regexp": "^(?!.*[\"'`])(feat|fix|docs|test|ci|chore|trivial)!?(\\(.*\\))?!?:.*"
8 | },
9 | "MESSAGES": {
10 | "success": "PR title is valid",
11 | "failure": "PR title is invalid",
12 | "notice": "PR Title needs to pass regex '^(?!.*[\"'`])(feat|fix|docs|test|ci|chore|trivial)!?(\\(.*\\))?!?:.*'"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Externals/IServiceOwnerNameRegistry.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Externals;
2 |
3 | public interface IServiceOwnerNameRegistry
4 | {
5 | Task GetServiceOwnerInfo(string orgNumber, CancellationToken cancellationToken);
6 | }
7 |
8 | public sealed class ServiceOwnerInfo
9 | {
10 | public required string OrgNumber { get; init; }
11 | public required string ShortName { get; init; }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Events/DialogTransmissionCreatedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common.DomainEvents;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Events;
4 |
5 | public sealed record DialogTransmissionCreatedDomainEvent(
6 | Guid DialogId,
7 | Guid TransmissionId,
8 | string ServiceResource,
9 | string Party,
10 | string? Process,
11 | string? PrecedingProcess) : DomainEvent, IProcessEvent;
12 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.Abstractions/Features/Creatable/ICreatableEntity.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Library.Entity.Abstractions.Features.Creatable;
2 |
3 | ///
4 | /// Abstraction implemented by entities that can be created during the application lifetime.
5 | ///
6 | public interface ICreatableEntity
7 | {
8 | ///
9 | /// Time at which the entity was first created in UTC.
10 | ///
11 | DateTimeOffset CreatedAt { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.GraphQL/Common/Authentication/AuthenticationOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.GraphQL.Common.Authentication;
2 |
3 | public sealed class AuthenticationOptions
4 | {
5 | public required List JwtBearerTokenSchemas { get; init; }
6 | }
7 |
8 | public sealed class JwtBearerTokenSchemasOptions
9 | {
10 | public required string Name { get; init; }
11 | public required string WellKnown { get; init; }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Common/Exceptions/UpstreamServiceException.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Infrastructure.Common.Exceptions;
2 |
3 | public interface IUpstreamServiceError;
4 | internal sealed class UpstreamServiceException : Exception, IUpstreamServiceError
5 | {
6 | public UpstreamServiceException(Exception innerException) : base(innerException.Message, innerException) { }
7 | public UpstreamServiceException(string message) : base(message) { }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/AuthenticationOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.WebApi.Common.Authentication;
2 |
3 | public sealed class AuthenticationOptions
4 | {
5 | public required List JwtBearerTokenSchemas { get; init; }
6 | }
7 |
8 | public sealed class JwtBearerTokenSchemasOptions
9 | {
10 | public required string Name { get; init; }
11 | public required string WellKnown { get; init; }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.Abstractions/Features/Updatable/IUpdateableEntity.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Library.Entity.Abstractions.Features.Updatable;
2 |
3 | ///
4 | /// Abstraction implemented by entities that can be updated during the application lifetime.
5 | ///
6 | public interface IUpdateableEntity
7 | {
8 | ///
9 | /// Time at which the entity was last updated in UTC.
10 | ///
11 | DateTimeOffset UpdatedAt { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/ApplicationCollectionFixture.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Common;
2 |
3 | public abstract class ApplicationCollectionFixture(DialogApplication application) : IAsyncLifetime
4 | {
5 | protected DialogApplication Application { get; } = application;
6 |
7 | public Task DisposeAsync() => Task.CompletedTask;
8 | public Task InitializeAsync() => Application.ResetState();
9 | }
10 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Common/DomainEvents/DomainEventExtensions.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common.EventPublisher;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.Common.DomainEvents;
4 |
5 | public static class DomainEventExtensions
6 | {
7 | public static bool ShouldNotBeSentToAltinnEvents(this IDomainEvent domainEvent)
8 | => domainEvent.Metadata.TryGetValue(Constants.IsSilentUpdate, out var value)
9 | && value == bool.TrueString;
10 | }
11 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/DomainExtensions.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common.EventPublisher;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain;
4 |
5 | public static class DomainExtensions
6 | {
7 | public static IEnumerable GetDomainEventTypes()
8 | => DomainAssemblyMarker.Assembly.DefinedTypes
9 | .Where(x => !x.IsAbstract && !x.IsInterface && !x.IsGenericType)
10 | .Where(x => x.IsAssignableTo(typeof(IDomainEvent)));
11 | }
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/SubjectResources/SubjectResource.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.SubjectResources;
4 |
5 | public class SubjectResource : IEntity
6 | {
7 | public Guid Id { get; set; }
8 | public string Subject { get; set; } = null!;
9 | public string Resource { get; set; } = null!;
10 | public DateTimeOffset UpdatedAt { get; set; }
11 | public DateTimeOffset CreatedAt { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/ServiceOwnerGroup.cs:
--------------------------------------------------------------------------------
1 | using FastEndpoints;
2 |
3 | namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner;
4 |
5 | public sealed class ServiceOwnerGroup : Group
6 | {
7 | public const string RoutePrefix = "Serviceowner";
8 | public ServiceOwnerGroup()
9 | {
10 | Configure(RoutePrefix.ToLowerInvariant(), ep =>
11 | {
12 | ep.EndpointVersion(1);
13 | });
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ResourceRegistry/Commands/SyncPolicy/SyncPolicyCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ResourceRegistry.Commands.SyncPolicy;
4 |
5 | internal sealed class SyncPolicyCommandValidator : AbstractValidator
6 | {
7 | public SyncPolicyCommandValidator()
8 | {
9 | RuleFor(x => x.NumberOfConcurrentRequests).InclusiveBetween(1, 50);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Parties/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Application.Features.V1.AccessManagement.Queries.GetParties;
3 |
4 | namespace Digdir.Domain.Dialogporten.GraphQL.EndUser.Parties;
5 |
6 | public sealed class MappingProfile : Profile
7 | {
8 | public MappingProfile()
9 | {
10 | CreateMap();
11 | CreateMap();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/WellKnown/OauthAuthorizationServer/Queries/Get/GetOauthAuthorizationServerDto.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.WellKnown.OauthAuthorizationServer.Queries.Get;
4 |
5 | public sealed class GetOauthAuthorizationServerDto
6 | {
7 | public string Issuer { get; set; } = null!;
8 |
9 | [JsonPropertyName("jwks_uri")]
10 | public string JwksUri { get; set; } = null!;
11 | }
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ResourceRegistry/Commands/SyncSubjectMap/SyncSubjectMapCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ResourceRegistry.Commands.SyncSubjectMap;
4 |
5 | internal sealed class SyncSubjectMapCommandValidator : AbstractValidator
6 | {
7 | public SyncSubjectMapCommandValidator()
8 | {
9 | RuleFor(x => x.BatchSize).InclusiveBetween(1, 1000);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/docker-compose-cdc.yml:
--------------------------------------------------------------------------------
1 | services:
2 | dialogporten-service:
3 | build:
4 | context: .
5 | dockerfile: src/Digdir.Domain.Dialogporten.Service/Dockerfile
6 | restart: always
7 | depends_on:
8 | dialogporten-postgres:
9 | condition: service_healthy
10 | dialogporten-migrations:
11 | condition: service_completed_successfully
12 | environment:
13 | - Infrastructure:DialogDbConnectionString=${DB_CONNECTION_STRING}
14 | - ASPNETCORE_ENVIRONMENT=Development
15 |
--------------------------------------------------------------------------------
/docs/schema/V1/gql-to-js.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | INPUT_FILE="schema.verified.graphql"
5 | OUTPUT_FILE="src/index.js"
6 | VAR_NAME="schema_verified_graphql"
7 |
8 | if [[ ! -f "$INPUT_FILE" ]]; then
9 | echo "Error: $INPUT_FILE not found!"
10 | exit 1
11 | fi
12 |
13 | CLEANED_SCHEMA=$(sed '1s/^\xEF\xBB\xBF//' "$INPUT_FILE" | sed "s/\`/'/g")
14 |
15 | {
16 | printf "export const %s = \`" "$VAR_NAME"
17 | printf "%s" "$CLEANED_SCHEMA"
18 | printf "\`"
19 | } > "$OUTPUT_FILE"
20 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Actors/ActorType.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions.Features.Lookup;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.Actors;
4 |
5 | public sealed class ActorType : AbstractLookupEntity
6 | {
7 | public ActorType(Values id) : base(id) { }
8 | public override ActorType MapValue(Values id) => new(id);
9 |
10 | public enum Values
11 | {
12 | PartyRepresentative = 1,
13 | ServiceOwner = 2
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.GraphQL/Common/Authorization/AuthorizationPolicy.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.GraphQL.Common.Authorization;
2 |
3 | internal static class AuthorizationPolicy
4 | {
5 | public const string EndUser = "enduser";
6 | public const string EndUserSubscription = "enduserSubscription";
7 | public const string ServiceProvider = "serviceprovider";
8 | public const string ServiceProviderSearch = "serviceproviderSearch";
9 | public const string Testing = "testing";
10 | }
11 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Parties/Abstractions/IPartyIdentifier.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.Parties.Abstractions;
4 |
5 | public interface IPartyIdentifier
6 | {
7 | string FullId { get; }
8 | string Id { get; }
9 | static abstract string Prefix { get; }
10 | static abstract string PrefixWithSeparator { get; }
11 | static abstract bool TryParse(ReadOnlySpan value, [NotNullWhen(true)] out IPartyIdentifier? identifier);
12 | }
13 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/Enumerables/General.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Common.Extensions.Enumerables;
5 |
6 | internal static class General
7 | {
8 | internal static bool IsNullOrEmpty([NotNullWhen(false)] this ICollection? values) => values is null || values.Count == 0;
9 |
10 | internal static IEnumerable EmptyIfNull(this IEnumerable? enumerable) => enumerable ?? [];
11 | }
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/EndUserContext/Queries/SearchLabelAssignmentLog/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.EndUserContext.Queries.SearchLabelAssignmentLog;
5 |
6 | public sealed class MappingProfile : Profile
7 | {
8 | public MappingProfile()
9 | {
10 | CreateMap();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/ServiceOwnerContext/Commands/Update/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Domain.DialogServiceOwnerContexts.Entities;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.ServiceOwnerContext.Commands.Update;
5 |
6 | internal sealed class MappingProfile : Profile
7 | {
8 | public MappingProfile()
9 | {
10 | CreateMap();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/ValueConverters/DateTimeOffsetConverter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
2 |
3 | namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.ValueConverters;
4 |
5 | internal sealed class DateTimeOffsetConverter : ValueConverter
6 | {
7 | public DateTimeOffsetConverter()
8 | : base(
9 | d => d.ToUniversalTime(),
10 | d => d.ToUniversalTime())
11 | {
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Tool.Dialogporten.LargeDataSetGenerator/EntityGenerators/SeenLog.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Tool.Dialogporten.LargeDataSetGenerator.EntityGenerators;
2 |
3 | internal static class SeenLog
4 | {
5 | public const string CopyCommand = """COPY "DialogSeenLog" ("Id", "CreatedAt", "IsViaServiceOwner", "DialogId", "EndUserTypeId") FROM STDIN (FORMAT csv, HEADER false, NULL '')""";
6 |
7 | public static string Generate(DialogTimestamp dto)
8 | => $"{dto.DialogId},{dto.FormattedTimestamp},FALSE,{dto.DialogId},1";
9 | }
10 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Actors/ActorValidationErrorMessages.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.Common.Actors;
2 |
3 | public static class ActorValidationErrorMessages
4 | {
5 | public const string ActorIdActorNameExclusiveOr = "If 'ActorType' is 'ServiceOwner', both 'ActorId' and 'ActorName' must be null. " +
6 | "For any other value of 'ActorType', 'ActorId' or 'ActorName' must be set, but not both simultaneously.";
7 | }
8 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.Abstractions/Digdir.Library.Entity.Abstractions.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Janitor/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "Serilog": {
8 | "MinimumLevel": {
9 | "Default": "Warning",
10 | "Override": {
11 | "Microsoft.EntityFrameworkCore": "Error",
12 | "Digdir.Domain.Dialogporten": "Information"
13 | }
14 | }
15 | },
16 | "Infrastructure": {
17 | "AltinnCdn": {
18 | "BaseUri": "https://altinncdn.no/"
19 | }
20 | },
21 | "AllowedHosts": "*"
22 | }
23 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Utils.AspNet/ServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using Microsoft.Extensions.DependencyInjection;
3 |
4 | namespace Digdir.Library.Utils.AspNet;
5 |
6 | public static class ServiceCollectionExtensions
7 | {
8 | public static IServiceCollection AddAspNetCommon(this IServiceCollection services, IConfiguration configuration)
9 | {
10 | services.AddOptions()
11 | .Bind(configuration);
12 |
13 | return services;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/ResourcePolicyInformation/ResourcePolicyInformation.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.ResourcePolicyInformation;
4 |
5 | public sealed class ResourcePolicyInformation : IEntity
6 | {
7 | public Guid Id { get; set; }
8 | public string Resource { get; set; } = null!;
9 | public int MinimumAuthenticationLevel { get; set; }
10 | public DateTimeOffset CreatedAt { get; set; }
11 | public DateTimeOffset UpdatedAt { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/AccessManagement/Queries/GetParties/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Application.Externals.AltinnAuthorization;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.AccessManagement.Queries.GetParties;
5 |
6 | internal sealed class MappingProfile : Profile
7 | {
8 | public MappingProfile()
9 | {
10 | CreateMap();
11 | CreateMap();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/GetSeenLog/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.GetSeenLog;
5 |
6 | public sealed class MappingProfile : Profile
7 | {
8 | public MappingProfile()
9 | {
10 | CreateMap()
11 | .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt));
12 |
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/ServiceOwnerContext/Queries/GetServiceOwnerLabels/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Domain.DialogServiceOwnerContexts.Entities;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.ServiceOwnerContext.Queries.GetServiceOwnerLabels;
5 |
6 | internal sealed class MappingProfile : Profile
7 | {
8 | public MappingProfile()
9 | {
10 | CreateMap();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.Abstractions/LibraryEntityAbstractionsAssemblyMarker.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace Digdir.Library.Entity.Abstractions;
4 |
5 | ///
6 | /// Provides a marker for the Entity Abstractions assembly.
7 | ///
8 | public sealed class LibraryEntityAbstractionsAssemblyMarker
9 | {
10 | ///
11 | /// Gets the assembly of the Entity Abstractions.
12 | ///
13 | public static readonly Assembly Assembly = typeof(LibraryEntityAbstractionsAssemblyMarker).Assembly;
14 | }
15 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/IntegrationTestCloudBus.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Externals;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Common;
4 |
5 | internal sealed class IntegrationTestCloudBus : ICloudEventBus
6 | {
7 | public List Events { get; } = [];
8 | public Task Publish(CloudEvent cloudEvent, CancellationToken cancellationToken)
9 | {
10 | Events.Add(cloudEvent);
11 | return Task.CompletedTask;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/tests/k6/suites/createremove-perf-10-vus-1-min.js:
--------------------------------------------------------------------------------
1 | import { default as run, setup as testSetup } from "../tests/serviceowner/performance/createremove-no-delay.js";
2 | import { default as summary } from "../common/summary.js";
3 | export let options = {
4 | vus: 10, // Number of virtual users
5 | duration: '1m', // Test duration
6 | };
7 |
8 | export function setup() {
9 | return testSetup();
10 | }
11 |
12 | export default function (data) { run(data); }
13 |
14 | export function handleSummary(data) {
15 | return summary(data);
16 | }
17 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/ReturnTypes/BadRequest.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation.Results;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Common.ReturnTypes;
4 |
5 | public sealed record BadRequest(List Reasons)
6 | {
7 | private const string BadRequestMessage = "BadRequest";
8 |
9 | public BadRequest(params string[] reasons) : this(reasons.ToList()) { }
10 |
11 | public List ToValidationResults() =>
12 | Reasons.Select(x => new ValidationFailure(BadRequestMessage, x)).ToList();
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/SearchSeenLogs/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.SearchSeenLogs;
5 |
6 | public sealed class MappingProfile : Profile
7 | {
8 | public MappingProfile()
9 | {
10 | CreateMap()
11 | .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/GetSeenLog/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.GetSeenLog;
5 |
6 | public sealed class MappingProfile : Profile
7 | {
8 | public MappingProfile()
9 | {
10 | CreateMap()
11 | .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Service/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "Serilog": {
8 | "MinimumLevel": {
9 | "Default": "Warning",
10 | "Override": {
11 | "Microsoft.AspNetCore": "Warning",
12 | "Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "Fatal"
13 | }
14 | }
15 | },
16 | "Infrastructure": {
17 | "AltinnCdn": {
18 | "BaseUri": "https://altinncdn.no/"
19 | }
20 | },
21 | "AllowedHosts": "*"
22 | }
23 |
--------------------------------------------------------------------------------
/tests/k6/tests/graphql/performance/graphqlGetParties.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The performance test for GraphQL search.
3 | * Run: k6 run tests/k6/tests/graphql/performance/graphqlGetParties.js --vus 1 --iterations 1 -e env=yt01
4 | */
5 |
6 | import { _default, getOptions, _setup } from './graphqlCommonFunctions.js';
7 |
8 | const label = "graphql-get-parties";
9 | const queryType = "getParties"
10 |
11 | export const options = getOptions([label]);
12 | export function setup() { return _setup(label, queryType); }
13 | export default function (data) { _default(data); }
14 |
--------------------------------------------------------------------------------
/.azure/applications/reindex-dialogsearch-job/test.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'test'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param replicaTimeOutInSeconds = 600
7 |
8 | //secrets
9 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
10 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
11 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/ReturnTypes/DomainError.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common;
2 | using FluentValidation.Results;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Common.ReturnTypes;
5 |
6 | public sealed record DomainError(IEnumerable Errors)
7 | {
8 | public DomainError(DomainFailure error) : this([error]) { }
9 | public List ToValidationResults() => Errors
10 | .Select(x => new ValidationFailure(x.PropertyName, x.ErrorMessage))
11 | .ToList();
12 | }
13 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/GetSeenLog/SeenLogDto.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.GetSeenLog;
4 |
5 | public sealed class SeenLogDto
6 | {
7 | public Guid Id { get; set; }
8 | public DateTimeOffset SeenAt { get; set; }
9 |
10 | public ActorDto SeenBy { get; set; } = null!;
11 |
12 | public bool? IsViaServiceOwner { get; set; }
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/MetadataGroup.cs:
--------------------------------------------------------------------------------
1 | using FastEndpoints;
2 |
3 | namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1;
4 |
5 | public sealed class MetadataGroup : Group
6 | {
7 | private const string GroupName = "Metadata";
8 | public MetadataGroup()
9 | {
10 | Configure(string.Empty, ep =>
11 | {
12 | ep.DontAutoTag();
13 | ep.Description(x => x.WithTags(GroupName));
14 | ep.AllowAnonymous();
15 | ep.EndpointVersion(1);
16 | });
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.azure/applications/reindex-dialogsearch-job/prod.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'prod'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param replicaTimeOutInSeconds = 172800
7 |
8 | //secrets
9 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
10 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
11 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
12 |
--------------------------------------------------------------------------------
/.azure/applications/reindex-dialogsearch-job/staging.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'staging'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param replicaTimeOutInSeconds = 600
7 |
8 | //secrets
9 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
10 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
11 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
12 |
--------------------------------------------------------------------------------
/.azure/applications/reindex-dialogsearch-job/yt01.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'yt01'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param replicaTimeOutInSeconds = 86400
7 |
8 | //secrets
9 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
10 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
11 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/GetActivity/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.GetActivity;
5 |
6 | internal sealed class MappingProfile : Profile
7 | {
8 | public MappingProfile()
9 | {
10 | CreateMap()
11 | .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId));
12 |
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/SearchActivities/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.SearchActivities;
5 |
6 | internal sealed class MappingProfile : Profile
7 | {
8 | public MappingProfile()
9 | {
10 | CreateMap()
11 | .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/SearchSeenLogs/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.SearchSeenLogs;
5 |
6 | public sealed class MappingProfile : Profile
7 | {
8 | public MappingProfile()
9 | {
10 | CreateMap()
11 | .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/SearchSeenLogs/SeenLogDto.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.SearchSeenLogs;
4 |
5 | public sealed class SeenLogDto
6 | {
7 | public Guid Id { get; set; }
8 | public DateTimeOffset SeenAt { get; set; }
9 |
10 | public ActorDto SeenBy { get; set; } = null!;
11 |
12 | public bool? IsViaServiceOwner { get; set; }
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Janitor/CostManagementAggregation/cost-coefficients.json:
--------------------------------------------------------------------------------
1 | {
2 | "CostCoefficients": {
3 | "CreateDialog": 1.5,
4 | "UpdateDialog": 1.8,
5 | "SoftDeleteDialog": 0.7,
6 | "HardDeleteDialog": 0.7,
7 | "GetDialogServiceOwner": 1.0,
8 | "GetDialogEndUser": 1.2,
9 | "SearchDialogsServiceOwner": 1.5,
10 | "SearchDialogsServiceOwnerWithEndUser": 2.5,
11 | "SearchDialogsEndUser": 2.5,
12 | "SetDialogLabel": 1.3,
13 | "BulkSetLabelsServiceOwnerWithEndUser": 2.0,
14 | "BulkSetLabelsEndUser": 2.0
15 | }
16 | }
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Service/ServiceUser.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Externals.Presentation;
2 | using System.Security.Claims;
3 |
4 | namespace Digdir.Domain.Dialogporten.Service;
5 |
6 | internal sealed class ServiceUser : IUser
7 | {
8 | public ClaimsPrincipal GetPrincipal()
9 | {
10 | throw new NotSupportedException(
11 | "At the time of this writing, Digdir.Domain.Dialogporten.Service should not " +
12 | "be using application commands or queries requiring the need for a user.");
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Digdir.Tool.Dialogporten.LargeDataSetGenerator/EntityGenerators/EndUserContext.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Tool.Dialogporten.LargeDataSetGenerator.EntityGenerators;
2 |
3 | internal static class EndUserContext
4 | {
5 | public const string CopyCommand = """COPY "DialogEndUserContext" ("Id", "CreatedAt", "UpdatedAt", "Revision", "DialogId", "SystemLabelId") FROM STDIN (FORMAT csv, HEADER false, NULL '')""";
6 |
7 | public static string Generate(DialogTimestamp dto) => $"{dto.DialogId},{dto.FormattedTimestamp},{dto.FormattedTimestamp},{Guid.NewGuid()},{dto.DialogId},1";
8 | }
9 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.EntityFrameworkCore/LibraryEntityFrameworkCoreAssemblyMarker.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace Digdir.Library.Entity.EntityFrameworkCore;
4 |
5 | ///
6 | /// Provides a marker for the Entity Framework Core assembly.
7 | ///
8 | public sealed class LibraryEntityFrameworkCoreAssemblyMarker
9 | {
10 | ///
11 | /// Gets the assembly of the Entity Framework Core.
12 | ///
13 | public static readonly Assembly Assembly = typeof(LibraryEntityFrameworkCoreAssemblyMarker).Assembly;
14 | }
15 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/ServiceOwnerContext/Commands/Update/UpdateServiceOwnerContextDto.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.ServiceOwnerContext.Commands.Update;
2 |
3 | public sealed class UpdateServiceOwnerContextDto
4 | {
5 | public List ServiceOwnerLabels { get; set; } = [];
6 | }
7 |
8 | public sealed class ServiceOwnerLabelDto
9 | {
10 | ///
11 | /// A label value.
12 | ///
13 | public string Value { get; set; } = null!;
14 | }
15 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/ReturnTypes/EntityNotVisible.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation.Results;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Common.ReturnTypes;
4 |
5 | public sealed record EntityNotVisible(DateTimeOffset VisibleFrom)
6 | : EntityNotVisible(typeof(T).Name, VisibleFrom);
7 |
8 | public record EntityNotVisible(string Name, DateTimeOffset VisibleFrom)
9 | {
10 | public string Message => $"Dialog is not visible until {VisibleFrom:s}";
11 |
12 | public List ToValidationResults() => [new(Name, Message)];
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Common/DomainEvents/DomainEvent.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 | using Digdir.Domain.Dialogporten.Domain.Common.EventPublisher;
3 |
4 | namespace Digdir.Domain.Dialogporten.Domain.Common.DomainEvents;
5 |
6 | public abstract record DomainEvent : IDomainEvent
7 | {
8 | [JsonInclude]
9 | public Guid EventId { get; private set; } = Guid.NewGuid();
10 |
11 | [JsonInclude]
12 | public DateTimeOffset OccurredAt { get; set; }
13 |
14 | [JsonInclude]
15 | public Dictionary Metadata { get; set; } = [];
16 | }
17 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.EntityFrameworkCore/Digdir.Library.Entity.EntityFrameworkCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/GetSeenLog/SeenLogDto.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.GetSeenLog;
4 |
5 | public sealed class SeenLogDto
6 | {
7 | public Guid Id { get; set; }
8 | public DateTimeOffset SeenAt { get; set; }
9 | public ActorDto SeenBy { get; set; } = null!;
10 |
11 | public bool IsViaServiceOwner { get; set; }
12 | public bool IsCurrentEndUser { get; set; }
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Purge/PurgeDialogCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Purge;
4 |
5 | internal sealed class PurgeDialogCommandValidator : AbstractValidator
6 | {
7 | public PurgeDialogCommandValidator()
8 | {
9 | RuleFor(x => x.DialogId)
10 | .NotEqual(Guid.Empty)
11 | .WithMessage($"{{PropertyName}} was either badly formatted or {Guid.Empty}");
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Attachments/AttachmentUrlConsumerType.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions.Features.Lookup;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.Attachments;
4 |
5 | public sealed class AttachmentUrlConsumerType : AbstractLookupEntity
6 | {
7 | public AttachmentUrlConsumerType(Values id) : base(id) { }
8 | public override AttachmentUrlConsumerType MapValue(Values id) => new(id);
9 |
10 | public enum Values
11 | {
12 | Gui = 1,
13 | Api = 2
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Common/EventPublisher/IEventPublisher.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Domain.Common.EventPublisher;
2 |
3 | ///
4 | /// Abstraction representing functionality to publish domain events.
5 | ///
6 | public interface IEventPublisher
7 | {
8 | ///
9 | /// Get a collection of queued up for dispatching at the end of the next unit of work.
10 | /// This also clears the collection on the EventPublisher.
11 | ///
12 | IEnumerable PopDomainEvents();
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Janitor/CostManagementAggregation/MetricsAggregationOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Janitor.CostManagementAggregation;
2 |
3 | public sealed class MetricsAggregationOptions
4 | {
5 | public const string SectionName = "MetricsAggregation";
6 |
7 | public string StorageConnectionString { get; set; } = string.Empty;
8 |
9 | public string StorageAccountName { get; set; } = string.Empty;
10 |
11 | public string StorageContainerName { get; set; } = string.Empty;
12 | public string SubscriptionId { get; set; } = string.Empty;
13 | }
14 |
--------------------------------------------------------------------------------
/.azure/modules/managedIdentity/main.bicep:
--------------------------------------------------------------------------------
1 | @description('The location where the resources will be deployed')
2 | param location string
3 |
4 | @description('The name of the managed identity')
5 | param name string
6 |
7 | @description('Tags to apply to resources')
8 | param tags object
9 |
10 | resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = {
11 | name: name
12 | location: location
13 | tags: tags
14 | }
15 |
16 | output managedIdentityId string = managedIdentity.id
17 | output managedIdentityPrincipalId string = managedIdentity.properties.principalId
18 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/IClock.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Common;
2 |
3 | internal interface IClock
4 | {
5 | DateTimeOffset UtcNowOffset { get; }
6 | DateTimeOffset NowOffset { get; }
7 | DateTime UtcNow { get; }
8 | DateTime Now { get; }
9 | }
10 |
11 | internal sealed class Clock : IClock
12 | {
13 | public DateTimeOffset UtcNowOffset => DateTimeOffset.UtcNow;
14 | public DateTimeOffset NowOffset => DateTimeOffset.Now;
15 | public DateTime UtcNow => DateTime.UtcNow;
16 | public DateTime Now => DateTime.Now;
17 | }
18 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/SearchSeenLogs/SeenLogDto.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.SearchSeenLogs;
4 |
5 | public sealed class SeenLogDto
6 | {
7 | public Guid Id { get; set; }
8 | public DateTimeOffset SeenAt { get; set; }
9 |
10 | public ActorDto SeenBy { get; set; } = null!;
11 |
12 | public bool IsViaServiceOwner { get; set; }
13 | public bool IsCurrentEndUser { get; set; }
14 | }
15 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.WebApi.Unit.Tests/Utils.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.WebApi.Unit.Tests;
2 |
3 | public static class Utils
4 | {
5 | public static string? GetSolutionRootFolder()
6 | {
7 | var currentDirectory = Directory.GetCurrentDirectory();
8 | var solutionFolder = currentDirectory;
9 | while (solutionFolder != null && Directory.GetFiles(solutionFolder, "*.sln").Length == 0)
10 | {
11 | solutionFolder = Directory.GetParent(solutionFolder)?.FullName;
12 | }
13 | return solutionFolder;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Extensions/TransmissionEntityExtensions.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.Common.Extensions;
4 |
5 | internal static class TransmissionEntityExtensions
6 | {
7 | internal static bool ContainsTransmissionByEndUser(this List transmissions) =>
8 | transmissions.Any(x => x.TypeId
9 | is DialogTransmissionType.Values.Submission
10 | or DialogTransmissionType.Values.Correction);
11 | }
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Common/Authorization/AuthorizationPolicy.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.WebApi.Common.Authorization;
2 |
3 | internal static class AuthorizationPolicy
4 | {
5 | public const string EndUser = "enduser";
6 | public const string ServiceProvider = "serviceprovider";
7 | public const string NotificationConditionCheck = "notificationConditionCheck";
8 | public const string ServiceProviderSearch = "serviceproviderSearch";
9 | public const string Testing = "testing";
10 | public const string ServiceProviderAdmin = "serviceproviderAdmin";
11 | }
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.GraphQL/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/launchsettings.json",
3 | "profiles": {
4 | "http": {
5 | "commandName": "Project",
6 | "dotnetRunMessages": true,
7 | "launchBrowser": false,
8 | "launchUrl": "graphql",
9 | "applicationUrl": "http://localhost:5181",
10 | "environmentVariables": {
11 | "DOTNET_ENVIRONMENT": "Development",
12 | "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
13 | "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317"
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Localizations/LocalizationSet.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions.Features.Aggregate;
2 | using Digdir.Library.Entity.Abstractions.Features.Identifiable;
3 | using Digdir.Library.Entity.Abstractions.Features.Immutable;
4 |
5 | namespace Digdir.Domain.Dialogporten.Domain.Localizations;
6 |
7 | public abstract class LocalizationSet : IIdentifiableEntity, IImmutableEntity
8 | {
9 | public Guid Id { get; set; }
10 |
11 | // === Plural principal relationships ===
12 | [AggregateChild]
13 | public List Localizations { get; set; } = [];
14 | }
15 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests/Utils.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.GraphQl.Integration.Tests;
2 |
3 | public static class Utils
4 | {
5 | public static string? GetSolutionRootFolder()
6 | {
7 | var currentDirectory = Directory.GetCurrentDirectory();
8 | var solutionFolder = currentDirectory;
9 | while (solutionFolder != null && Directory.GetFiles(solutionFolder, "*.sln").Length == 0)
10 | {
11 | solutionFolder = Directory.GetParent(solutionFolder)?.FullName;
12 | }
13 | return solutionFolder;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/tests/k6/tests/graphql/performance/graphqlGetAllDialogsForEnduser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The performance test for GraphQL search.
3 | * Run: k6 run tests/k6/tests/graphql/performance/graphqlGetAllDialogsForEnduser.js --vus 1 --iterations 1 -e env=yt01
4 | */
5 |
6 | import { _default, getOptions, _setup } from './graphqlCommonFunctions.js';
7 |
8 | const label = "graphql-getall-dialogs-for-enduser";
9 | const queryType = "getAllDialogsForEnduser"
10 |
11 | export const options = getOptions([label]);
12 | export function setup() { return _setup(label, queryType); }
13 | export default function (data) { _default(data); }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Events/Activities/DialogActivityCreatedDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common.DomainEvents;
2 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
3 |
4 | namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Events.Activities;
5 |
6 | public sealed record DialogActivityCreatedDomainEvent(
7 | Guid DialogId,
8 | Guid ActivityId,
9 | DialogActivityType.Values TypeId,
10 | string Party,
11 | string ServiceResource,
12 | string? Process,
13 | string? PrecedingProcess,
14 | Uri? ExtendedType) : DomainEvent;
15 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/DialogCqrsCollectionFixture.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1;
4 |
5 | [CollectionDefinition(nameof(DialogCqrsCollectionFixture))]
6 | public class DialogCqrsCollectionFixture : ICollectionFixture
7 | {
8 | // This class has no code, and is never created. Its purpose is simply
9 | // to be the place to apply [CollectionDefinition] and all the
10 | // ICollectionFixture<> interfaces.
11 | }
12 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Dialogporten.WebApiClient.WebApiSample/Digdir.Library.Dialogporten.WebApiClient.WebApiSample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 750256a4-f332-4783-8802-8a7d9566f9cb
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/WellKnown/Jwks/Queries/Get/GetJwksDto.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.WellKnown.Jwks.Queries.Get;
2 |
3 | public sealed class GetJwksDto
4 | {
5 | public List Keys { get; set; } = [];
6 | }
7 |
8 | public sealed class Jwk
9 | {
10 | public string Kty { get; set; } = "OKP";
11 | public string Use { get; set; } = "sig";
12 | public string Kid { get; set; } = null!;
13 | public string Crv { get; set; } = "Ed25519";
14 | public string X { get; set; } = null!;
15 | public string Alg { get; set; } = "EdDSA";
16 | }
17 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Common/FeatureMetric/FeatureMetricOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.WebApi.Common.FeatureMetric;
2 |
3 | ///
4 | /// Configuration options for feature metric tracking.
5 | ///
6 | public sealed class FeatureMetricOptions
7 | {
8 | ///
9 | /// Gets or sets the list of path prefixes to exclude from feature metric tracking.
10 | ///
11 | public List ExcludedPathPrefixes { get; set; } = new()
12 | {
13 | "/health",
14 | "/metrics",
15 | "/swagger",
16 | "/openapi"
17 | };
18 | }
19 |
--------------------------------------------------------------------------------
/nginx-webapi.conf:
--------------------------------------------------------------------------------
1 | # this nginx config is for the load balancer: dialogporten-webapi-ingress in docker-compose.yml
2 | events {}
3 |
4 | http {
5 | upstream webapi {
6 | least_conn;
7 | server dialogporten-webapi:8080;
8 | }
9 |
10 | server {
11 | listen 80;
12 |
13 | location / {
14 | proxy_pass http://webapi;
15 | proxy_set_header Host $host;
16 | proxy_set_header X-Real-IP $remote_addr;
17 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
18 | proxy_set_header X-Forwarded-Proto $scheme;
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Context/IApplicationContext.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Common.Context;
4 |
5 | public interface IApplicationContext
6 | {
7 | Dictionary Metadata { get; }
8 | void AddMetadata(string key, string value);
9 | }
10 |
11 | public static class ApplicationContextExtensions
12 | {
13 | public static bool IsSilentUpdate(this IApplicationContext context) =>
14 | context.Metadata.TryGetValue(Constants.IsSilentUpdate, out var value)
15 | && value == bool.TrueString;
16 | }
17 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.Abstractions/IEntity.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions.Features.Creatable;
2 | using Digdir.Library.Entity.Abstractions.Features.Identifiable;
3 | using Digdir.Library.Entity.Abstractions.Features.Updatable;
4 |
5 | namespace Digdir.Library.Entity.Abstractions;
6 |
7 | ///
8 | /// Convenience interface to mark an entity with
9 | /// ,
10 | /// , and
11 | /// .
12 | ///
13 | public interface IEntity :
14 | IIdentifiableEntity,
15 | ICreatableEntity,
16 | IUpdateableEntity;
17 |
--------------------------------------------------------------------------------
/tests/k6/tests/graphql/performance/graphqlGetAllDialogsForEnduserFTS.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The performance test for GraphQL search.
3 | * Run: k6 run tests/k6/tests/graphql/performance/graphqlGetAllDialogsForEnduserFTS.js --vus 1 --iterations 1 -e env=yt01
4 | */
5 |
6 | import { _default, getOptions, _setup } from './graphqlCommonFunctions.js';
7 |
8 | const label = "graphql-getall-dialogs-for-enduser-with-fts";
9 | const queryType = "getAllDialogsForEnduserFts"
10 |
11 | export const options = getOptions([label]);
12 | export function setup() { return _setup(label, queryType); }
13 | export default function (data) { _default(data); }
14 |
--------------------------------------------------------------------------------
/.azure/applications/sync-resource-policy-information-job/prod.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'prod'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param jobSchedule = '10 3 * * *' // 3:10AM every night
7 | param replicaTimeOutInSeconds = 600
8 |
9 | //secrets
10 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
11 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
12 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
13 |
--------------------------------------------------------------------------------
/.azure/applications/sync-resource-policy-information-job/test.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'test'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param jobSchedule = '20 3 * * *' // 3:20AM every night
7 | param replicaTimeOutInSeconds = 600
8 |
9 | //secrets
10 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
11 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
12 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
13 |
--------------------------------------------------------------------------------
/.azure/applications/sync-resource-policy-information-job/yt01.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'yt01'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param jobSchedule = '25 3 * * *' // 3:25AM every night
7 | param replicaTimeOutInSeconds = 600
8 |
9 | //secrets
10 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
11 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
12 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
13 |
--------------------------------------------------------------------------------
/.azure/applications/sync-subject-resource-mappings-job/prod.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'prod'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param jobSchedule = '*/5 * * * *' // Runs every 5 minutes
7 | param replicaTimeOutInSeconds = 600
8 |
9 | //secrets
10 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
11 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
12 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
13 |
--------------------------------------------------------------------------------
/.azure/applications/sync-subject-resource-mappings-job/test.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'test'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param jobSchedule = '*/5 * * * *' // Runs every 5 minutes
7 | param replicaTimeOutInSeconds = 600
8 |
9 | //secrets
10 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
11 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
12 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
13 |
--------------------------------------------------------------------------------
/.azure/applications/sync-subject-resource-mappings-job/yt01.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'yt01'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param jobSchedule = '*/5 * * * *' // Runs every 5 minutes
7 | param replicaTimeOutInSeconds = 600
8 |
9 | //secrets
10 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
11 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
12 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
13 |
--------------------------------------------------------------------------------
/nginx-graphql.conf:
--------------------------------------------------------------------------------
1 | # this nginx config is for the load balancer: dialogporten-graphql-ingress in docker-compose.yml
2 | events {}
3 |
4 | http {
5 | upstream graphql {
6 | least_conn;
7 | server dialogporten-graphql:8080;
8 | }
9 |
10 | server {
11 | listen 80;
12 |
13 | location / {
14 | proxy_pass http://graphql;
15 | proxy_set_header Host $host;
16 | proxy_set_header X-Real-IP $remote_addr;
17 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
18 | proxy_set_header X-Forwarded-Proto $scheme;
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/EndUserContext/Queries/SearchLabelAssignmentLog/LabelAssignmentLogDto.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.EndUserContext.Queries.SearchLabelAssignmentLog;
4 |
5 | public sealed class LabelAssignmentLogDto
6 | {
7 | public DateTimeOffset CreatedAt { get; set; }
8 |
9 | public string Name { get; set; } = null!;
10 |
11 | public string Action { get; set; } = null!;
12 |
13 | public ActorDto PerformedBy { get; set; } = null!;
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/Validators/CreateDialogSearchTagDtoValidator.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common;
2 | using FluentValidation;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create.Validators;
5 |
6 | internal sealed class CreateDialogSearchTagDtoValidator : AbstractValidator
7 | {
8 | public CreateDialogSearchTagDtoValidator()
9 | {
10 | RuleFor(x => x.Value)
11 | .MinimumLength(3)
12 | .MaximumLength(Constants.MaxSearchTagLength);
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/Validators/UpdateDialogSearchTagDtoValidator.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common;
2 | using FluentValidation;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update.Validators;
5 |
6 | internal sealed class UpdateDialogSearchTagDtoValidator : AbstractValidator
7 | {
8 | public UpdateDialogSearchTagDtoValidator()
9 | {
10 | RuleFor(x => x.Value)
11 | .MinimumLength(3)
12 | .MaximumLength(Constants.MaxSearchTagLength);
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.Abstractions/IJoinEntity.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions.Features.Creatable;
2 | using Digdir.Library.Entity.Abstractions.Features.Updatable;
3 |
4 | namespace Digdir.Library.Entity.Abstractions;
5 |
6 | ///
7 | /// Convenience interface to mark an entity with
8 | /// , and
9 | /// .
10 | ///
11 | ///
12 | /// Usually used by entities that join multiple entities together and thereby has a composite key.
13 | ///
14 | public interface IJoinEntity :
15 | ICreatableEntity,
16 | IUpdateableEntity;
17 |
--------------------------------------------------------------------------------
/.azure/applications/sync-resource-policy-information-job/staging.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'staging'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param jobSchedule = '15 3 * * *' // 3:15AM every night
7 | param replicaTimeOutInSeconds = 600
8 |
9 | //secrets
10 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
11 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
12 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
13 |
--------------------------------------------------------------------------------
/.azure/applications/sync-subject-resource-mappings-job/staging.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'staging'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param jobSchedule = '*/5 * * * *' // Runs every 5 minutes
7 | param replicaTimeOutInSeconds = 600
8 |
9 | //secrets
10 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
11 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
12 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
13 |
--------------------------------------------------------------------------------
/docker-compose-jobs.yml:
--------------------------------------------------------------------------------
1 | version: '3.9'
2 | include:
3 | - docker-compose-db-redis.yml
4 |
5 | services:
6 | dialogporten-sync-subject-resource-mappings-job:
7 | build:
8 | context: .
9 | dockerfile: src/Digdir.Domain.Dialogporten.Janitor/Dockerfile
10 | environment:
11 | - Infrastructure:DialogDbConnectionString=${DB_CONNECTION_STRING}
12 | - DOTNET_ENVIRONMENT=Development
13 | command: [ "sync-subject-resource-mappings" ]
14 | depends_on:
15 | dialogporten-postgres:
16 | condition: service_healthy
17 | dialogporten-migrations:
18 | condition: service_completed_successfully
19 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Actions/DialogGuiActionPriority.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions.Features.Lookup;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions;
4 |
5 | public sealed class DialogGuiActionPriority : AbstractLookupEntity
6 | {
7 | public DialogGuiActionPriority(Values id) : base(id) { }
8 | public override DialogGuiActionPriority MapValue(Values id) => new(id);
9 |
10 | public enum Values
11 | {
12 | Primary = 1,
13 | Secondary = 2,
14 | Tertiary = 3
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Dialogporten.WebApiClient.WebApiSample/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*",
9 | "DialogportenSettings": {
10 | "BaseUri": "https://localhost:7214",
11 | "ThrowOnPublicKeyFetchInit": false,
12 | "Maskinporten": {
13 | "Environment": "test",
14 | "Scope": "digdir:dialogporten.serviceprovider digdir:dialogporten.serviceprovider.search",
15 | "ClientId": "Configure in local secrets",
16 | "EncodedJwk": "Configure in local secrets"
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/SearchActivities/ActivityDto.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.SearchActivities;
4 |
5 | public sealed class ActivityDto
6 | {
7 | public Guid Id { get; set; }
8 | public DateTimeOffset CreatedAt { get; set; }
9 | public Uri? ExtendedType { get; set; }
10 | public string? SeenByEndUserIdHash { get; set; }
11 |
12 | public DialogActivityType.Values Type { get; set; }
13 |
14 | public Guid? TransmissionId { get; set; }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Actors/Actor.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions;
2 |
3 | // ReSharper disable ClassNeverInstantiated.Global
4 |
5 | namespace Digdir.Domain.Dialogporten.Domain.Actors;
6 |
7 | public abstract class Actor : IEntity
8 | {
9 | public Guid Id { get; set; }
10 | public DateTimeOffset CreatedAt { get; set; }
11 | public DateTimeOffset UpdatedAt { get; set; }
12 | public ActorType.Values ActorTypeId { get; set; }
13 | public ActorType ActorType { get; set; } = null!;
14 |
15 | public Guid? ActorNameEntityId { get; set; }
16 | public ActorName? ActorNameEntity { get; set; } = null!;
17 | }
18 |
--------------------------------------------------------------------------------
/tests/k6/tests/graphql/performance/graphqlGetAllDialogsForEnduserCount.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The performance test for GraphQL search.
3 | * Run: k6 run tests/k6/tests/graphql/performance/graphqlGetAllDialogsForEnduserCount.js --vus 1 --iterations 1 -e env=yt01
4 | */
5 | import { _default, getOptions, _setup } from './graphqlCommonFunctions.js';
6 |
7 | const label = "graphql-getall-dialogs-for-enduser-count";
8 | const queryType = "getAllDialogsForEnduserCount";
9 | const labels = [label];
10 |
11 | export const options = getOptions(labels);
12 | export function setup() { return _setup(label, queryType); }
13 | export default function (data) { _default(data); }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Actors/ActorName.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions.Features.Creatable;
2 | using Digdir.Library.Entity.Abstractions.Features.Identifiable;
3 | using Digdir.Library.Entity.Abstractions.Features.Immutable;
4 |
5 | namespace Digdir.Domain.Dialogporten.Domain.Actors;
6 |
7 | public sealed class ActorName : IImmutableEntity, IIdentifiableEntity, ICreatableEntity
8 | {
9 | public Guid Id { get; set; }
10 | public string? ActorId { get; set; }
11 | public string? Name { get; set; }
12 | public DateTimeOffset CreatedAt { get; set; }
13 |
14 | public List ActorEntities { get; set; } = [];
15 | }
16 |
--------------------------------------------------------------------------------
/tests/k6/tests/serviceowner/dialogCreateInvalidProcess.js:
--------------------------------------------------------------------------------
1 | import { describe, expect, expectStatusFor, postSO } from '../../common/testimports.js'
2 | import { default as dialogToInsert } from './testdata/01-create-dialog.js';
3 |
4 |
5 | export default function (){
6 |
7 | describe ('Attempt to create dialog with invalid URI', () => {
8 | let dialog = dialogToInsert();
9 | dialog.process = 'inval|d';
10 | let r = postSO('dialogs', dialog)
11 | expectStatusFor(r).to.equal(400);
12 | expect(r, 'response').to.have.validJsonBody();
13 | expect(r.json(), 'response body').to.have.property('errors');
14 | })
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/GetActivity/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.GetActivity;
5 |
6 | public sealed class MappingProfile : Profile
7 | {
8 | public MappingProfile()
9 | {
10 | CreateMap()
11 | .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId))
12 | .ForMember(dest => dest.DeletedAt, opt => opt.MapFrom(src => src.Dialog.DeletedAt));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/SearchActivities/ActivityDto.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.SearchActivities;
4 |
5 | public sealed class ActivityDto
6 | {
7 | public Guid Id { get; set; }
8 | public DateTimeOffset CreatedAt { get; set; }
9 | public Uri? ExtendedType { get; set; }
10 |
11 | public DialogActivityType.Values Type { get; set; }
12 |
13 | public DateTimeOffset? DeletedAt { get; set; }
14 |
15 | public Guid? TransmissionId { get; set; }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Digdir.Tool.Dialogporten.LargeDataSetGenerator/service_resources:
--------------------------------------------------------------------------------
1 | urn:altinn:resource:ttd-dialogporten-performance-test-01
2 | urn:altinn:resource:ttd-dialogporten-performance-test-02
3 | urn:altinn:resource:ttd-dialogporten-performance-test-03
4 | urn:altinn:resource:ttd-dialogporten-performance-test-04
5 | urn:altinn:resource:ttd-dialogporten-performance-test-05
6 | urn:altinn:resource:ttd-dialogporten-performance-test-06
7 | urn:altinn:resource:ttd-dialogporten-performance-test-07
8 | urn:altinn:resource:ttd-dialogporten-performance-test-08
9 | urn:altinn:resource:ttd-dialogporten-performance-test-09
10 | urn:altinn:resource:ttd-dialogporten-performance-test-10
11 |
--------------------------------------------------------------------------------
/.azure/modules/privateDnsZoneGroup/main.bicep:
--------------------------------------------------------------------------------
1 | param dnsZoneId string
2 | param privateEndpointName string
3 | param name string
4 | param dnsZoneGroupName string
5 |
6 | resource privateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' existing = {
7 | name: privateEndpointName
8 | }
9 |
10 | resource pe_dns_zone_group 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2024-05-01' = {
11 | name: name
12 | parent: privateEndpoint
13 | properties: {
14 | privateDnsZoneConfigs: [
15 | {
16 | name: dnsZoneGroupName
17 | properties: {
18 | privateDnsZoneId: dnsZoneId
19 | }
20 | }
21 | ]
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | enable
6 | enable
7 | 750256a4-f332-4783-8802-8a7d9566f9ca
8 |
9 |
10 |
11 | true
12 | true
13 | Recommended
14 | true
15 | 8
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/SearchActivities/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.SearchActivities;
5 |
6 | public sealed class MappingProfile : Profile
7 | {
8 | public MappingProfile()
9 | {
10 | CreateMap()
11 | .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId))
12 | .ForMember(dest => dest.DeletedAt, opt => opt.MapFrom(src => src.Dialog.DeletedAt));
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Description
4 |
5 |
6 |
7 | ## Related Issue(s)
8 |
9 | - #{issue number}
10 |
11 | ## Verification
12 |
13 | - [ ] **Your** code builds clean without any errors or warnings
14 | - [ ] Manual testing done (required)
15 | - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out)
16 |
17 | ## Documentation
18 |
19 | - [ ] Documentation is updated (either in `docs`-directory, Altinnpedia or a separate linked PR in [altinn-studio-docs.](https://github.com/Altinn/altinn-studio-docs), if applicable)
20 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Configurations/Actors/ActorNameConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Actors;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
4 |
5 | namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Configurations.Actors;
6 |
7 | internal sealed class ActorNameConfiguration : IEntityTypeConfiguration
8 | {
9 | public void Configure(EntityTypeBuilder builder)
10 | {
11 | builder
12 | .HasIndex(x => new { x.ActorId, x.Name })
13 | .IsUnique()
14 | .AreNullsDistinct(false);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Http/HttpVerb.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions.Features.Lookup;
2 | // ReSharper disable InconsistentNaming
3 |
4 | namespace Digdir.Domain.Dialogporten.Domain.Http;
5 |
6 | public sealed class HttpVerb : AbstractLookupEntity
7 | {
8 | public HttpVerb(Values id) : base(id) { }
9 | public override HttpVerb MapValue(Values id) => new(id);
10 |
11 | public enum Values
12 | {
13 | GET = 1,
14 | POST = 2,
15 | PUT = 3,
16 | PATCH = 4,
17 | DELETE = 5,
18 | HEAD = 6,
19 | OPTIONS = 7,
20 | TRACE = 8,
21 | CONNECT = 9
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Configurations/DialogServiceOwnerContexts/DialogServiceOwnerContextConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.DialogServiceOwnerContexts.Entities;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
4 |
5 | namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Configurations.DialogServiceOwnerContexts;
6 |
7 | internal sealed class DialogServiceOwnerContextConfiguration : IEntityTypeConfiguration
8 | {
9 | public void Configure(EntityTypeBuilder builder)
10 | => builder.HasKey(x => x.DialogId);
11 | }
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Sql/Dialog/Search/Function.UpsertDialogSearchOne.sql:
--------------------------------------------------------------------------------
1 | -- Refreshes a single dialog's search vector by reusing the canonical aggregated document view.
2 | CREATE OR REPLACE FUNCTION search."UpsertDialogSearchOne"(p_dialog_id uuid)
3 | RETURNS void
4 | LANGUAGE sql
5 | AS $$
6 | INSERT INTO search."DialogSearch" ("DialogId","UpdatedAt","SearchVector")
7 | SELECT "DialogId", now(), COALESCE("Document",''::tsvector)
8 | FROM search."VDialogDocument"
9 | WHERE "DialogId" = p_dialog_id
10 | ON CONFLICT ("DialogId") DO UPDATE
11 | SET "UpdatedAt" = EXCLUDED."UpdatedAt",
12 | "SearchVector" = EXCLUDED."SearchVector";
13 | $$;
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/DialogEndUserContexts/Entities/DialogEndUserContextSystemLabel.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions.Features.Creatable;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities;
4 |
5 | public sealed class DialogEndUserContextSystemLabel : ICreatableEntity
6 | {
7 | public SystemLabel.Values SystemLabelId { get; internal set; } = SystemLabel.Values.Default;
8 | public SystemLabel SystemLabel { get; private set; } = null!;
9 |
10 | public Guid DialogEndUserContextId { get; set; }
11 | public DialogEndUserContext DialogEndUserContext { get; set; } = null!;
12 |
13 | public DateTimeOffset CreatedAt { get; set; }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Configurations/SubjectResources/SubjectResourceConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.SubjectResources;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
4 |
5 | namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Configurations.SubjectResources;
6 |
7 | internal sealed class SubjectResourceConfiguration : IEntityTypeConfiguration
8 | {
9 | public void Configure(EntityTypeBuilder builder)
10 | {
11 | builder
12 | .HasIndex(sr => new { sr.Resource, sr.Subject })
13 | .IsUnique();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.GraphQL/Common/Authorization/AllowAnonymousHandler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 |
3 | namespace Digdir.Domain.Dialogporten.GraphQL.Common.Authorization;
4 |
5 | ///
6 | /// This authorization handler will bypass all requirements
7 | ///
8 | public sealed class AllowAnonymousHandler : IAuthorizationHandler
9 | {
10 | public Task HandleAsync(AuthorizationHandlerContext context)
11 | {
12 | foreach (var requirement in context.PendingRequirements)
13 | {
14 | //Simply pass all requirements
15 | context.Succeed(requirement);
16 | }
17 |
18 | return Task.CompletedTask;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Common/Authorization/AllowAnonymousHandler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 |
3 | namespace Digdir.Domain.Dialogporten.WebApi.Common.Authorization;
4 |
5 | ///
6 | /// This authorisation handler will bypass all requirements
7 | ///
8 | public sealed class AllowAnonymousHandler : IAuthorizationHandler
9 | {
10 | public Task HandleAsync(AuthorizationHandlerContext context)
11 | {
12 | foreach (var requirement in context.PendingRequirements)
13 | {
14 | //Simply pass all requirements
15 | context.Succeed(requirement);
16 | }
17 |
18 | return Task.CompletedTask;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/GraphQL/DummyRequestExecutorBuilder.cs:
--------------------------------------------------------------------------------
1 | using HotChocolate.Execution.Configuration;
2 | using Microsoft.Extensions.DependencyInjection;
3 |
4 | namespace Digdir.Domain.Dialogporten.Infrastructure.GraphQL;
5 |
6 | ///
7 | /// This implementation is a workaround to allow the use of the AddRedisSubscriptions extension method
8 | /// from HotChocolate.Subscriptions.Redis without having to take the entire HotChocolate library as a dependency.
9 | ///
10 | internal sealed class DummyRequestExecutorBuilder : IRequestExecutorBuilder
11 | {
12 | public string Name => string.Empty;
13 | public IServiceCollection Services { get; init; } = null!;
14 | }
15 |
--------------------------------------------------------------------------------
/tests/k6/suites/all-single-pass.js:
--------------------------------------------------------------------------------
1 | import { runAllTests } from "../tests/all-tests.js";
2 | import { default as summary } from "../common/summary.js";
3 | import { chai, describe } from '../common/testimports.js'
4 |
5 | export let options = {};
6 |
7 | export default function () {
8 | try {
9 | runAllTests();
10 | } catch (error) {
11 | describe('Exception during test suite', () => {
12 | // disable truncating so we can display the entire stack trace
13 | chai.config.truncateThreshold = 0;
14 | chai.assert(true == false, error.stack);
15 | });
16 | }
17 | }
18 |
19 | export function handleSummary(data) {
20 | return summary(data);
21 | }
22 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/ContentValueDto.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations;
2 | using Digdir.Domain.Dialogporten.Domain;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content;
5 |
6 | public sealed class ContentValueDto
7 | {
8 | ///
9 | /// A list of localizations for the content.
10 | ///
11 | public List Value { get; set; } = [];
12 |
13 | ///
14 | /// Media type of the content, this can also indicate that the content is embeddable.
15 | ///
16 | public string MediaType { get; set; } = MediaTypes.PlainText;
17 | }
18 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/Common/Utils/ApplicationEventHandlerUtilsTests.Developer_Should_Use_Caution_When_Modifying_Endpoints.verified.txt:
--------------------------------------------------------------------------------
1 | [
2 | DialogEventToAltinnForwarder_DialogActivityCreatedDomainEvent,
3 | DialogEventToAltinnForwarder_DialogCreatedDomainEvent,
4 | DialogEventToAltinnForwarder_DialogDeletedDomainEvent,
5 | DialogEventToAltinnForwarder_DialogRestoredDomainEvent,
6 | DialogEventToAltinnForwarder_DialogSeenDomainEvent,
7 | DialogEventToAltinnForwarder_DialogTransmissionCreatedDomainEvent,
8 | DialogEventToAltinnForwarder_DialogUpdatedDomainEvent,
9 | DialogSearchIndexer_DialogCreatedDomainEvent,
10 | DialogSearchIndexer_DialogUpdatedDomainEvent
11 | ]
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Common/Constants.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Domain.Common;
2 |
3 | public static class Constants
4 | {
5 | public const int MinSearchStringLength = 3;
6 | public const int MaxSearchTagLength = 63;
7 | public const int DefaultMaxStringLength = 255;
8 | public const int DefaultMaxUriLength = 1023;
9 | public const int CorrespondenceMaxStringLength = 512;
10 |
11 | public const string ServiceResourcePrefix = "urn:altinn:resource:";
12 | public const string AppResourceIdPrefix = "app_";
13 | public const string ServiceContextInstanceIdPrefix = "urn:altinn:integration:storage:";
14 |
15 | public const string IsSilentUpdate = "IsSilentUpdate";
16 | }
17 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Dialogporten.WebApiClient/.refitter:
--------------------------------------------------------------------------------
1 | {
2 | "openApiPath": "../Digdir.Domain.Dialogporten.WebApi/bin/Release/net9.0/swagger.json",
3 | "namespace": "Altinn.ApiClients.Dialogporten.Features.V1",
4 | "outputFolder": "Features/V1",
5 | "operationNameGenerator": "SingleClientFromOperationId",
6 | "trimUnusedSchema": true,
7 | "multipleInterfaces": "ByTag",
8 | "includeTags": [
9 | "Serviceowner"
10 | ],
11 | "useCancellationTokens": true,
12 | "returnIApiResponse": true,
13 | "useDynamicQuerystringParameters": true,
14 | "outputFilename": "RefitterInterface.cs",
15 | "codeGeneratorSettings": {
16 | "dateFormat": "yyyy-MM-ddTHH:mm:ssZ"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tests/k6/tests/graphql/performance/graphqlGetAllDialogsForParties.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The performance test for GraphQL search.
3 | * Run: k6 run tests/k6/tests/graphql/performance/graphqlGetAllDialogsForParties.js --vus 1 --iterations 1 -e env=yt01
4 | */
5 | import { getOptions, _setup, _defaultForParties } from './graphqlCommonFunctions.js';
6 | const dialogs_label = "graphql-getall-dialogs-for-parties";
7 | const parties_label = "graphql-get-parties";
8 | const queryType = "getAllDialogsForParties"
9 | const labels = [dialogs_label, parties_label];
10 |
11 | export const options = getOptions(labels);
12 | export function setup() { return _setup(dialogs_label, queryType); }
13 | export default function (data) { _defaultForParties(data); }
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/Validators/CreateDialogServiceOwnerLabelDtoValidator.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common;
2 | using FluentValidation;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create.Validators;
5 |
6 | internal sealed class CreateDialogServiceOwnerLabelDtoValidator : AbstractValidator
7 | {
8 | public CreateDialogServiceOwnerLabelDtoValidator()
9 | {
10 | RuleFor(x => x.Value)
11 | .NotEmpty()
12 | .MinimumLength(Constants.MinSearchStringLength)
13 | .MaximumLength(Constants.DefaultMaxStringLength);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/AccessManagement/Queries/GetParties/GetPartiesEndpointSummary.cs:
--------------------------------------------------------------------------------
1 | using FastEndpoints;
2 |
3 | namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.EndUser.AccessManagement.Queries.GetParties;
4 |
5 | public sealed class GetPartiesEndpointSummary : Summary
6 | {
7 | public GetPartiesEndpointSummary()
8 | {
9 | Summary = "Gets the list of authorized parties for the end user";
10 | Description = """
11 | Gets the list of authorized parties for the end user.
12 | """;
13 |
14 | Responses[StatusCodes.Status200OK] = "The list of authorized parties for the end user";
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Externals/AltinnAuthorization/ActionResource.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Common.Authorization;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Externals.AltinnAuthorization;
4 |
5 | public sealed record AltinnAction
6 | {
7 | public string Name { get; }
8 | public string AuthorizationAttribute { get; }
9 |
10 | public AltinnAction(string name, string? authorizationAttribute = null)
11 | {
12 | Name = name;
13 | AuthorizationAttribute = authorizationAttribute ?? Constants.MainResource;
14 | }
15 |
16 | public void Deconstruct(out string key, out string value)
17 | => (key, value) = (Name, AuthorizationAttribute);
18 | }
19 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Configurations/DialogEndUserContexts/DialogEndUserContextSystemLabelConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
4 |
5 | namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Configurations.DialogEndUserContexts;
6 |
7 | internal sealed class DialogEndUserContextSystemLabelConfiguration : IEntityTypeConfiguration
8 | {
9 | public void Configure(EntityTypeBuilder builder) =>
10 | builder.HasKey(x => new { x.DialogEndUserContextId, x.SystemLabelId });
11 | }
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Configurations/ResourcePolicyInformation/ResourcePolicyInformationConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
3 |
4 | namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Configurations.ResourcePolicyInformation;
5 |
6 | internal sealed class ResourcePolicyInformationConfiguration : IEntityTypeConfiguration
7 | {
8 | public void Configure(EntityTypeBuilder builder)
9 | {
10 | builder
11 | .HasIndex(r => r.Resource)
12 | .IsUnique();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/TestDomainEventConsumer.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Common.EventPublisher;
2 | using MassTransit;
3 | using MediatR;
4 |
5 | namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Common;
6 |
7 | internal sealed class TestDomainEventConsumer : IConsumer
8 | where T : class, IDomainEvent
9 | {
10 | private readonly IPublisher _publisher;
11 |
12 | public TestDomainEventConsumer(IPublisher publisher)
13 | {
14 | _publisher = publisher ?? throw new ArgumentNullException(nameof(publisher));
15 | }
16 |
17 | public async Task Consume(ConsumeContext context) => await _publisher.Publish(context.Message);
18 | }
19 |
--------------------------------------------------------------------------------
/tests/k6/common/console.js:
--------------------------------------------------------------------------------
1 | var originalLog = console.log;
2 |
3 | function customDir(obj) {
4 | var seen = [];
5 |
6 | var replacer = function(key, value) {
7 | if (typeof value === "object" && value !== null) {
8 | if (seen.indexOf(value) !== -1) {
9 | return "[Circular]";
10 | }
11 | seen.push(value);
12 | }
13 | return value;
14 | };
15 |
16 | var stringified = JSON.stringify(obj, replacer, 2);
17 | originalLog(stringified);
18 | }
19 |
20 | export var customConsole = {
21 | log: originalLog,
22 | dir: customDir,
23 | // You can manually add other console methods if needed, such as warn, error, etc.
24 | warn: console.warn,
25 | error: console.error
26 | };
27 |
--------------------------------------------------------------------------------
/.azure/applications/graphql/test.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'test'
4 | param location = 'norwayeast'
5 | param apimIp = '51.120.88.69'
6 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
7 | param revisionSuffix = readEnvironmentVariable('REVISION_SUFFIX')
8 |
9 | param otelTraceSamplerRatio = '1'
10 |
11 | // secrets
12 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
13 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
14 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
15 | param appConfigurationName = readEnvironmentVariable('AZURE_APP_CONFIGURATION_NAME')
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Configurations/Dialogs/Activities/DialogActivityConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
4 |
5 | namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Configurations.Dialogs.Activities;
6 |
7 | internal sealed class DialogActivityConfiguration : IEntityTypeConfiguration
8 | {
9 | public void Configure(EntityTypeBuilder builder)
10 | {
11 | builder.HasOne(x => x.Transmission)
12 | .WithMany(x => x.Activities)
13 | .OnDelete(DeleteBehavior.SetNull);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Sql/Dialog/Search/View.DialogSearchRebuildProgress.sql:
--------------------------------------------------------------------------------
1 | -- Summarises queue state for dashboards and periodic progress logs.
2 | CREATE OR REPLACE VIEW search."DialogSearchRebuildProgress" AS
3 | SELECT
4 | count(*) AS "Total",
5 | count(*) FILTER (WHERE "Status" = 0) AS "Pending",
6 | count(*) FILTER (WHERE "Status" = 1) AS "Processing",
7 | count(*) FILTER (WHERE "Status" = 2) AS "Done",
8 | (count(*) FILTER (WHERE "Status" = 2))::numeric
9 | / NULLIF(count(*), 0) AS "DoneRatio" -- Handy for log formatting and monitoring thresholds.
10 | FROM search."DialogSearchRebuildQueue";
11 |
--------------------------------------------------------------------------------
/src/Digdir.Tool.Dialogporten.LargeDataSetGenerator/Words.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Tool.Dialogporten.LargeDataSetGenerator;
2 |
3 | internal static class Words
4 | {
5 | internal static readonly string[]
6 | English = File.Exists("./wordlist_en") ? File.ReadAllLines("./wordlist_en") : [];
7 |
8 | internal static readonly string[]
9 | Norwegian = File.Exists("./wordlist_no") ? File.ReadAllLines("./wordlist_no") : [];
10 |
11 | static Words()
12 | {
13 | if (English.Length > Norwegian.Length)
14 | {
15 | English = English.Except(Norwegian).ToArray();
16 | }
17 | else
18 | {
19 | Norwegian = Norwegian.Except(English).ToArray();
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/k6/tests/graphql/performance/graphqlGetAllDialogsForPartiesFTS.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The performance test for GraphQL search.
3 | * Run: k6 run tests/k6/tests/graphql/performance/graphqlGetAllDialogsForPartiesFTS.js --vus 1 --iterations 1 -e env=yt01
4 | */
5 | import { getOptions, _setup, _defaultForParties } from './graphqlCommonFunctions.js';
6 | const dialogs_label = "graphql-getall-dialogs-for-parties-fts";
7 | const parties_label = "graphql-get-parties";
8 | const queryType = "getAllDialogsForPartiesFts"
9 | const labels = [dialogs_label, parties_label];
10 |
11 | export const options = getOptions(labels);
12 | export function setup() { return _setup(dialogs_label, queryType); }
13 | export default function (data) { _defaultForParties(data); }
--------------------------------------------------------------------------------
/tests/k6/tests/graphql/performance/graphqlGetAllDialogsForRandomParty.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The performance test for GraphQL search.
3 | * Run: k6 run tests/k6/tests/graphql/performance/graphqlGetAllDialogsForRandomParty.js --vus 1 --iterations 1 -e env=yt01
4 | */
5 | import { getOptions, _setup, _defaultForParties } from './graphqlCommonFunctions.js';
6 | const dialogs_label = "graphql-getall-dialogs-for-parties";
7 | const parties_label = "graphql-get-parties";
8 | const queryType = "getAllDialogsForParties"
9 | const labels = [dialogs_label, parties_label];
10 |
11 | export const options = getOptions(labels);
12 | export function setup() { return _setup(dialogs_label, queryType); }
13 | export default function (data) { _defaultForParties(data, false); }
--------------------------------------------------------------------------------
/.azure/applications/graphql/staging.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'staging'
4 | param location = 'norwayeast'
5 | param apimIp = '51.13.86.131'
6 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
7 | param revisionSuffix = readEnvironmentVariable('REVISION_SUFFIX')
8 |
9 | param otelTraceSamplerRatio = '1'
10 |
11 | // secrets
12 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
13 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
14 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
15 | param appConfigurationName = readEnvironmentVariable('AZURE_APP_CONFIGURATION_NAME')
16 |
--------------------------------------------------------------------------------
/.azure/applications/web-api-eu/test.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'test'
4 | param location = 'norwayeast'
5 | param apimIp = '51.120.88.69'
6 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
7 | param revisionSuffix = readEnvironmentVariable('REVISION_SUFFIX')
8 |
9 | param otelTraceSamplerRatio = '1'
10 |
11 | // secrets
12 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
13 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
14 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
15 | param appConfigurationName = readEnvironmentVariable('AZURE_APP_CONFIGURATION_NAME')
16 |
--------------------------------------------------------------------------------
/.azure/applications/web-api-so/test.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'test'
4 | param location = 'norwayeast'
5 | param apimIp = '51.120.88.69'
6 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
7 | param revisionSuffix = readEnvironmentVariable('REVISION_SUFFIX')
8 |
9 | param otelTraceSamplerRatio = '1'
10 |
11 | // secrets
12 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
13 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
14 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
15 | param appConfigurationName = readEnvironmentVariable('AZURE_APP_CONFIGURATION_NAME')
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.GraphQL/Common/ApplicationUser.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using Digdir.Domain.Dialogporten.Application.Externals.Presentation;
3 |
4 | namespace Digdir.Domain.Dialogporten.GraphQL.Common;
5 |
6 | internal sealed class ApplicationUser : IUser
7 | {
8 | private readonly IHttpContextAccessor _httpContextAccessor;
9 |
10 | public ApplicationUser(IHttpContextAccessor httpContextAccessor)
11 | {
12 | _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
13 | }
14 |
15 | public ClaimsPrincipal GetPrincipal()
16 | => _httpContextAccessor.HttpContext?.User ?? throw new InvalidOperationException("No user principal found");
17 | }
18 |
--------------------------------------------------------------------------------
/.azure/applications/web-api-eu/staging.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'staging'
4 | param location = 'norwayeast'
5 | param apimIp = '51.13.86.131'
6 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
7 | param revisionSuffix = readEnvironmentVariable('REVISION_SUFFIX')
8 |
9 | param otelTraceSamplerRatio = '1'
10 |
11 | // secrets
12 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
13 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
14 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
15 | param appConfigurationName = readEnvironmentVariable('AZURE_APP_CONFIGURATION_NAME')
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/Order/Order.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Common.Pagination.OrderOption;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Common.Pagination.Order;
4 |
5 | public sealed class Order
6 | {
7 | private readonly OrderSelector _selector;
8 | public string Key { get; }
9 | public OrderDirection Direction { get; }
10 |
11 | public Order(string key, OrderSelector selector, OrderDirection direction = PaginationConstants.DefaultOrderDirection)
12 | {
13 | _selector = selector;
14 | Key = key;
15 | Direction = direction;
16 | }
17 |
18 | public OrderSelector GetSelector() => _selector;
19 | }
20 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Sql/Dialog/Search/Function.UpsertDialogSearchOne.V2.sql:
--------------------------------------------------------------------------------
1 | -- Refreshes a single dialog's search vector by reusing the canonical aggregated document view.
2 | CREATE OR REPLACE FUNCTION search."UpsertDialogSearchOne"(p_dialog_id uuid)
3 | RETURNS void
4 | LANGUAGE sql
5 | AS $$
6 | INSERT INTO search."DialogSearch" ("DialogId","UpdatedAt","Party","SearchVector")
7 | SELECT "DialogId", now(), "Party", COALESCE("Document",''::tsvector)
8 | FROM search."VDialogDocument"
9 | WHERE "DialogId" = p_dialog_id
10 | ON CONFLICT ("DialogId") DO UPDATE
11 | SET "UpdatedAt" = EXCLUDED."UpdatedAt",
12 | "Party" = EXCLUDED."Party",
13 | "SearchVector" = EXCLUDED."SearchVector";
14 | $$;
15 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Common/WebApiSettings.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.WebApi.Common.Authentication;
2 | using FluentValidation;
3 |
4 | namespace Digdir.Domain.Dialogporten.WebApi.Common;
5 |
6 | public sealed class WebApiSettings
7 | {
8 | public const string SectionName = "WebApi";
9 |
10 | public required AuthenticationOptions Authentication { get; init; }
11 | }
12 |
13 | internal sealed class WebApiOptionsValidator : AbstractValidator
14 | {
15 | public WebApiOptionsValidator(
16 | IValidator authenticationOptionsValidator)
17 | {
18 | RuleFor(x => x.Authentication)
19 | .SetValidator(authenticationOptionsValidator);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug
3 | about: If you find something that is not working as it should.
4 | title: ''
5 | type: Bug
6 | assignees: ''
7 | ---
8 |
9 | ### Description
10 | A short and concise description of the error
11 |
12 | ### Reproduction
13 | Step-by-step instructions to reproduce the error, with information about the requests that were made (URLs with parameters, methods, headers (no tokens!), including any DTOs used if POST/PUT/PATCH)
14 |
15 | ### Expected behavior
16 | A short and concise description of what you expected to happen
17 |
18 | ### Actual behavior
19 | A short and concise description of what actually happened
20 |
21 | ### Additional information
22 | If there is additional context that is relevant to include.
23 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Service/Digdir.Domain.Dialogporten.Service.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/tests/k6/tests/graphql/performance/graphqlGetAllDialogsForPartiesCount.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The performance test for GraphQL search.
3 | * Run: k6 run tests/k6/tests/graphql/performance/graphqlGetAllDialogsForPartiesCount.js --vus 1 --iterations 1 -e env=yt01
4 | */
5 | import { getOptions, _setup, _defaultForParties } from './graphqlCommonFunctions.js';
6 | const dialogs_label = "graphql-getall-dialogs-for-parties-for-count";
7 | const parties_label = "graphql-get-parties";
8 | const queryType = "getAllDialogsForPartiesForCount"
9 | const labels = [dialogs_label, parties_label];
10 |
11 | export const options = getOptions(labels);
12 | export function setup() { return _setup(dialogs_label, queryType); }
13 | export default function (data) { _defaultForParties(data); }
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Common/ApplicationUser.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Externals.Presentation;
2 | using System.Security.Claims;
3 |
4 | namespace Digdir.Domain.Dialogporten.WebApi.Common;
5 |
6 | internal sealed class ApplicationUser : IUser
7 | {
8 | private readonly IHttpContextAccessor _httpContextAccessor;
9 |
10 | public ApplicationUser(IHttpContextAccessor httpContextAccessor)
11 | {
12 | _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
13 | }
14 |
15 | public ClaimsPrincipal GetPrincipal()
16 | => _httpContextAccessor.HttpContext?.User ??
17 | throw new InvalidOperationException("No user principal found");
18 | }
19 |
--------------------------------------------------------------------------------
/.azure/applications/aggregate-cost-metrics-job/prod.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'prod'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param jobSchedule = '0 2 * * *' // 2:00 AM UTC every day
7 | param replicaTimeOutInSeconds = 1800 // 30 minutes
8 | param storageContainerName = 'costmetrics'
9 |
10 | //secrets
11 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
12 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
13 | param azureSubscriptionId = readEnvironmentVariable('AZURE_SUBSCRIPTION_ID')
14 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
15 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/Common/Utils/ApplicationEventHandlerUtilsTests.Developer_Should_Use_Caution_When_Modifying_Events.verified.txt:
--------------------------------------------------------------------------------
1 | [
2 | Digdir.Domain.Dialogporten.Domain.Dialogs.Events.Activities.DialogActivityCreatedDomainEvent,
3 | Digdir.Domain.Dialogporten.Domain.Dialogs.Events.DialogCreatedDomainEvent,
4 | Digdir.Domain.Dialogporten.Domain.Dialogs.Events.DialogDeletedDomainEvent,
5 | Digdir.Domain.Dialogporten.Domain.Dialogs.Events.DialogRestoredDomainEvent,
6 | Digdir.Domain.Dialogporten.Domain.Dialogs.Events.DialogSeenDomainEvent,
7 | Digdir.Domain.Dialogporten.Domain.Dialogs.Events.DialogTransmissionCreatedDomainEvent,
8 | Digdir.Domain.Dialogporten.Domain.Dialogs.Events.DialogUpdatedDomainEvent
9 | ]
10 |
--------------------------------------------------------------------------------
/tests/k6/tests/graphql/performance/graphqlGetAllDialogsForRandomPartyCount.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The performance test for GraphQL search.
3 | * Run: k6 run tests/k6/tests/graphql/performance/graphqlGetAllDialogsForRandomPartyCount.js --vus 1 --iterations 1 -e env=yt01
4 | */
5 | import { getOptions, _setup, _defaultForParties } from './graphqlCommonFunctions.js';
6 | const dialogs_label = "graphql-getall-dialogs-for-party-for-count";
7 | const parties_label = "graphql-get-parties";
8 | const queryType = "getAllDialogsForPartyForCount"
9 | const labels = [dialogs_label, parties_label];
10 |
11 | export const options = getOptions(labels);
12 | export function setup() { return _setup(dialogs_label, queryType); }
13 | export default function (data) { _defaultForParties(data, false); }
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/OrderOption/OrderSelector.cs:
--------------------------------------------------------------------------------
1 | using System.Linq.Expressions;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Common.Pagination.OrderOption;
4 |
5 | public sealed record OrderSelector(Expression> Expression, Lazy> Compiled, Expression Body)
6 | {
7 | public OrderSelector(Expression> expression) : this(expression, new(expression.Compile), RemoveConvertWrapper(expression.Body)) { }
8 |
9 | private static Expression RemoveConvertWrapper(Expression body) =>
10 | body.NodeType == ExpressionType.Convert && body is UnaryExpression unaryExpression
11 | ? unaryExpression.Operand
12 | : body;
13 | }
14 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Configurations/DialogEndUserContexts/DialogEndUserContextConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
4 |
5 | namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Configurations.DialogEndUserContexts;
6 |
7 | internal sealed class DialogEndUserContextConfiguration : IEntityTypeConfiguration
8 | {
9 | public void Configure(EntityTypeBuilder builder)
10 | {
11 | builder.HasOne(d => d.Dialog)
12 | .WithOne(d => d.EndUserContext)
13 | .OnDelete(DeleteBehavior.SetNull);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.azure/applications/aggregate-cost-metrics-job/staging.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'staging'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param jobSchedule = '0 2 * * *' // 2:00 AM UTC every day
7 | param replicaTimeOutInSeconds = 1800 // 30 minutes
8 | param storageContainerName = 'costmetrics'
9 |
10 | //secrets
11 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
12 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
13 | param azureSubscriptionId = readEnvironmentVariable('AZURE_SUBSCRIPTION_ID')
14 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
15 |
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/PaginationConstants.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Common.Pagination.Order;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Common.Pagination;
4 |
5 | public static class PaginationConstants
6 | {
7 | public const int MinLimit = 1;
8 | public const int MaxLimit = 1000;
9 | public const int DefaultLimit = 100;
10 |
11 | public const char OrderSetDelimiter = ',';
12 | public const char OrderDelimiter = '_';
13 | public const string OrderIdKey = "id";
14 |
15 | public const char ContinuationTokenSetDelimiter = ',';
16 | public const char ContinuationTokenDelimiter = '_';
17 |
18 | public const OrderDirection DefaultOrderDirection = OrderDirection.Desc;
19 | }
20 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "Serilog": {
8 | "MinimumLevel": {
9 | "Default": "Warning",
10 | "Override": {
11 | "Microsoft.AspNetCore": "Warning",
12 | "Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "Fatal",
13 |
14 | // This enables logging of parties/service tuples counts being fed into authorization
15 | "Digdir.Domain.Dialogporten.Infrastructure.Persistence.Repositories.DialogSearchRepository": "Information"
16 | }
17 | }
18 | },
19 | "Infrastructure": {
20 | "AltinnCdn": {
21 | "BaseUri": "https://altinncdn.no/"
22 | }
23 | },
24 | "AllowedHosts": "*"
25 | }
26 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Content/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents;
3 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions.Contents;
4 |
5 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content;
6 |
7 | public sealed class MappingProfile : Profile
8 | {
9 | public MappingProfile()
10 | {
11 | // See IntermediateDialogContent
12 | CreateMap();
13 | CreateMap();
14 | CreateMap();
15 | CreateMap();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Configurations/Dialogs/Transmissions/DialogTransmissionConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
4 |
5 | namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Configurations.Dialogs.Transmissions;
6 |
7 | internal sealed class DialogTransmissionConfiguration : IEntityTypeConfiguration
8 | {
9 | public void Configure(EntityTypeBuilder builder)
10 | {
11 | builder.HasOne(x => x.RelatedTransmission)
12 | .WithMany(x => x.RelatedTransmissions)
13 | .OnDelete(DeleteBehavior.SetNull);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/ReturnTypes/Forbidden.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation.Results;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Common.ReturnTypes;
4 |
5 | public sealed record Forbidden(List Reasons)
6 | {
7 | private const string ForbiddenMessage = "Forbidden";
8 |
9 | public Forbidden(params string[] reasons) : this(reasons.ToList()) { }
10 |
11 | public List ToValidationResults() =>
12 | [.. Reasons.Select(x => new ValidationFailure(ForbiddenMessage, x))];
13 |
14 | public Forbidden WithInvalidDialogIds(List dialogIds)
15 | {
16 | Reasons.Add($"The following dialog ids are unauthorized and/or missing: ({string.Join(", ", dialogIds)}).");
17 | return this;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Dialogporten.WebApiClient.WebApiSample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/launchsettings.json",
3 | "profiles": {
4 | "http": {
5 | "commandName": "Project",
6 | "dotnetRunMessages": true,
7 | "launchBrowser": false,
8 | "applicationUrl": "http://localhost:5006",
9 | "environmentVariables": {
10 | "ASPNETCORE_ENVIRONMENT": "Development"
11 | }
12 | },
13 | "https": {
14 | "commandName": "Project",
15 | "dotnetRunMessages": true,
16 | "launchBrowser": false,
17 | "applicationUrl": "https://localhost:7171;http://localhost:5006",
18 | "environmentVariables": {
19 | "ASPNETCORE_ENVIRONMENT": "Development"
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.Abstractions/Features/Versionable/VersionableExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Library.Entity.Abstractions.Features.Versionable;
2 |
3 | ///
4 | /// Provides extension methods for .
5 | ///
6 | public static class VersionableExtensions
7 | {
8 | ///
9 | /// Sets properties related to .
10 | ///
11 | /// The to update.
12 | /// The entity tag unique to each version of the entity.
13 | public static void NewVersion(this IVersionableEntity concurrentEntity, Guid? eTag = null) =>
14 | concurrentEntity.Revision = eTag ?? Guid.NewGuid();
15 | }
16 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/Common/UpdateDialogServiceOwnerContextCommandExtensions.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.ServiceOwnerContext.Commands.Update;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.Common;
4 |
5 | internal static class UpdateDialogServiceOwnerContextCommandExtensions
6 | {
7 | public static UpdateDialogServiceOwnerContextCommand AddServiceOwnerLabels(this UpdateDialogServiceOwnerContextCommand command, params string[] labels)
8 | {
9 | foreach (var label in labels)
10 | {
11 | command.Dto.ServiceOwnerLabels.Add(new ServiceOwnerLabelDto { Value = label });
12 | }
13 | return command;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Tool.Dialogporten.GenerateFakeData/Digdir.Tool.Dialogporten.GenerateFakeData.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 |
6 |
7 |
8 |
9 | Release
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.azure/applications/service/test.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'test'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param revisionSuffix = readEnvironmentVariable('REVISION_SUFFIX')
7 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
8 | param appConfigurationName = readEnvironmentVariable('AZURE_APP_CONFIGURATION_NAME')
9 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
10 | param serviceBusNamespaceName = readEnvironmentVariable('AZURE_SERVICE_BUS_NAMESPACE_NAME')
11 |
12 | param otelTraceSamplerRatio = '1'
13 |
14 | // secrets
15 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/Enumerables/AsyncEnumerableExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Common.Extensions.Enumerables;
2 |
3 | public static class AsyncEnumerableExtensions
4 | {
5 | public static IAsyncEnumerable Empty() => EmptyAsyncEnumerator.Instance;
6 |
7 | private sealed class EmptyAsyncEnumerator : IAsyncEnumerable, IAsyncEnumerator
8 | {
9 | public static readonly EmptyAsyncEnumerator Instance = new();
10 | public T Current => default!;
11 | public ValueTask DisposeAsync() => default;
12 | public ValueTask MoveNextAsync() => new(false);
13 | public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = new()) => this;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Attachments/AttachmentUrl.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.Attachments;
4 |
5 | public sealed class AttachmentUrl : IEntity
6 | {
7 | public Guid Id { get; set; }
8 | public DateTimeOffset CreatedAt { get; set; }
9 | public DateTimeOffset UpdatedAt { get; set; }
10 |
11 | public string? MediaType { get; set; } = null!;
12 | public Uri Url { get; set; } = null!;
13 |
14 | // === Dependent relationships ===
15 | public AttachmentUrlConsumerType.Values ConsumerTypeId { get; set; }
16 | public AttachmentUrlConsumerType ConsumerType { get; set; } = null!;
17 |
18 | public Guid AttachmentId { get; set; }
19 | public Attachment Attachment { get; set; } = null!;
20 | }
21 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Common/EventPublisher/IDomainEvent.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace Digdir.Domain.Dialogporten.Domain.Common.EventPublisher;
4 |
5 | ///
6 | /// Represents a domain event.
7 | ///
8 | public interface IDomainEvent : INotification
9 | {
10 | ///
11 | /// The events' identification. Consumers can use this to ensure exactly once processing.
12 | ///
13 | Guid EventId { get; }
14 |
15 | ///
16 | /// The time at which the event, as well as all the actual changes, occurred.
17 | ///
18 | DateTimeOffset OccurredAt { get; set; }
19 |
20 | ///
21 | /// Dictionary of metadata.
22 | ///
23 | Dictionary Metadata { get; set; }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Dialogporten.WebApiClient/Services/DefaultValidationResult.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using System.Security.Claims;
3 |
4 | namespace Altinn.ApiClients.Dialogporten.Services;
5 |
6 | internal sealed class DefaultValidationResult : IValidationResult
7 | {
8 | public Dictionary> Errors { get; } = [];
9 | [MemberNotNullWhen(true, nameof(ClaimsPrincipal))]
10 | public bool IsValid => Errors.Count == 0;
11 | public ClaimsPrincipal? ClaimsPrincipal { get; internal set; }
12 |
13 | internal void AddError(string key, string message)
14 | {
15 | if (!Errors.TryGetValue(key, out var messages))
16 | {
17 | Errors[key] = messages = [];
18 | }
19 |
20 | messages.Add(message);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/AddingEndUserTransmissionSentLabelTestData.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Common;
4 |
5 | internal sealed class AddingEndUserTransmissionSentLabelTestData : TheoryData
6 | {
7 | public AddingEndUserTransmissionSentLabelTestData()
8 | {
9 | foreach (var type in Enum.GetValues())
10 | {
11 | // (transmissionType, shouldAddSentLabel)
12 | Add(type, type
13 | is DialogTransmissionType.Values.Submission
14 | or DialogTransmissionType.Values.Correction);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.azure/applications/service/staging.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'staging'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param revisionSuffix = readEnvironmentVariable('REVISION_SUFFIX')
7 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
8 | param appConfigurationName = readEnvironmentVariable('AZURE_APP_CONFIGURATION_NAME')
9 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
10 | param serviceBusNamespaceName = readEnvironmentVariable('AZURE_SERVICE_BUS_NAMESPACE_NAME')
11 |
12 | param otelTraceSamplerRatio = '1'
13 |
14 | // secrets
15 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
16 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Utils.AspNet/FusionCacheFilter.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 |
3 | namespace Digdir.Library.Utils.AspNet;
4 |
5 | public sealed class FusionCacheFilter : OpenTelemetry.BaseProcessor
6 | {
7 | public override void OnEnd(Activity activity)
8 | {
9 | if (!activity.Source.Name.StartsWith(Constants.FusionCache, StringComparison.OrdinalIgnoreCase))
10 | {
11 | base.OnEnd(activity);
12 | return;
13 | }
14 |
15 | var isError = activity.Tags.Any(t => t is { Key: Constants.OtelStatusCode, Value: Constants.Error });
16 | if (!isError)
17 | {
18 | activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded;
19 | return;
20 | }
21 |
22 | base.OnEnd(activity);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tests/k6/tests/serviceowner/performance/createDialogWithThresholds.js:
--------------------------------------------------------------------------------
1 | import { randomItem } from '../../../common/k6-utils.js';
2 | import { createDialog } from '../../performancetest_common/createDialog.js';
3 | import { serviceOwners, endUsers } from '../../performancetest_common/readTestdata.js';
4 | export let options = {
5 | summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'],
6 | vus: 1,
7 | duration: "30s",
8 | thresholds: {
9 | "http_req_duration{name:create dialog}": ["p(95)<300"],
10 | "http_reqs{name:create dialog}": []
11 | }
12 | }
13 |
14 | const traceCalls = (__ENV.traceCalls ?? 'false') === 'true';
15 |
16 | export default function () {
17 | createDialog(serviceOwners[0], randomItem(endUsers), traceCalls);
18 | }
19 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Janitor/ConsoleUser.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using Digdir.Domain.Dialogporten.Application.Common.Authorization;
3 | using Digdir.Domain.Dialogporten.Application.Common.Extensions;
4 | using Digdir.Domain.Dialogporten.Application.Externals.Presentation;
5 |
6 | namespace Digdir.Domain.Dialogporten.Janitor;
7 |
8 | public sealed class ConsoleUser : IUser
9 | {
10 | public ClaimsPrincipal GetPrincipal()
11 | {
12 | var claims = new[]
13 | {
14 | new Claim(ClaimsPrincipalExtensions.ScopeClaim, AuthorizationScope.ServiceOwnerAdminScope),
15 | new Claim(ClaimsPrincipalExtensions.AltinnOrgClaim, "digdir")
16 | };
17 | var identity = new ClaimsIdentity(claims);
18 | return new ClaimsPrincipal(identity);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Entity.Abstractions/Features/Creatable/CreatableExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Library.Entity.Abstractions.Features.Creatable;
2 |
3 | ///
4 | /// Provides extension methods for .
5 | ///
6 | public static class CreatableExtensions
7 | {
8 | ///
9 | /// Sets properties related to .
10 | ///
11 | /// The to update.
12 | /// The creation time in UTC.
13 | public static void Create(this ICreatableEntity creatable, DateTimeOffset utcNow)
14 | {
15 | creatable.CreatedAt = creatable.CreatedAt == default
16 | ? utcNow
17 | : creatable.CreatedAt;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.azure/applications/web-api-so/staging.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'staging'
4 | param location = 'norwayeast'
5 | param apimIp = '51.13.86.131'
6 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
7 | param revisionSuffix = readEnvironmentVariable('REVISION_SUFFIX')
8 |
9 | param resources = {
10 | cpu: 1
11 | memory: '2Gi'
12 | }
13 |
14 | param otelTraceSamplerRatio = '1'
15 |
16 | // secrets
17 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
18 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
19 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
20 | param appConfigurationName = readEnvironmentVariable('AZURE_APP_CONFIGURATION_NAME')
21 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogSearchTag.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions.Features.Creatable;
2 | using Digdir.Library.Entity.Abstractions.Features.Identifiable;
3 | using Digdir.Library.Entity.Abstractions.Features.Immutable;
4 |
5 | namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
6 |
7 | public sealed class DialogSearchTag : IImmutableEntity, IIdentifiableEntity, ICreatableEntity
8 | {
9 | private string _value = null!;
10 | public Guid Id { get; set; }
11 |
12 | public string Value
13 | {
14 | get => _value;
15 | set => _value = value.Trim().ToLowerInvariant();
16 | }
17 |
18 | public DateTimeOffset CreatedAt { get; set; }
19 |
20 | public Guid DialogId { get; set; }
21 | public DialogEntity Dialog { get; set; } = null!;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Sql/Dialog/Search/Function.SeedDialogSearchQueueFull.sql:
--------------------------------------------------------------------------------
1 | -- Enqueues every dialog for rebuilding the search index; handy when bootstrapping a new environment.
2 | CREATE OR REPLACE FUNCTION search."SeedDialogSearchQueueFull"(reset_existing boolean DEFAULT false)
3 | RETURNS int
4 | LANGUAGE plpgsql AS $$
5 | DECLARE n int;
6 | BEGIN
7 | IF reset_existing THEN
8 | -- Resetting keeps existing queue entries but clears transient error state.
9 | UPDATE search."DialogSearchRebuildQueue"
10 | SET "Status" = 0, "UpdatedAt" = now();
11 | END IF;
12 |
13 | INSERT INTO search."DialogSearchRebuildQueue" ("DialogId")
14 | SELECT d."Id" FROM "Dialog" d
15 | ON CONFLICT ("DialogId") DO NOTHING;
16 |
17 | GET DIAGNOSTICS n = ROW_COUNT;
18 | RETURN n;
19 | END; $$;
20 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Externals/ICloudEventBus.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Externals;
2 |
3 | public interface ICloudEventBus
4 | {
5 | Task Publish(CloudEvent cloudEvent, CancellationToken cancellationToken);
6 | }
7 |
8 | public sealed class CloudEvent
9 | {
10 | public string SpecVersion { get; set; } = "1.0";
11 | public Guid Id { get; set; }
12 | public string Type { get; set; } = null!;
13 | public DateTimeOffset Time { get; set; }
14 | public string Resource { get; set; } = null!;
15 | public string ResourceInstance { get; set; } = null!;
16 | public string? Subject { get; set; }
17 | public string? AlternativeSubject { get; set; }
18 | public string Source { get; set; } = null!;
19 | public Dictionary? Data { get; set; }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/UpdateFormSavedActivityTime/UpdateFormSavedActivityTimeCommandValidator.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Common;
2 | using Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation;
3 | using FluentValidation;
4 |
5 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.UpdateFormSavedActivityTime;
6 |
7 | internal sealed class UpdateFormSavedActivityTimeCommandValidator : AbstractValidator
8 | {
9 | public UpdateFormSavedActivityTimeCommandValidator(IClock clock)
10 | {
11 | RuleFor(x => x.DialogId).NotEmpty();
12 | RuleFor(x => x.ActivityId).NotEmpty();
13 | RuleFor(x => x.NewCreatedAt).IsInPast(clock);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/release-please-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": {
3 | ".": {
4 | "changelog-path": "CHANGELOG.md",
5 | "release-type": "simple",
6 | "draft": false,
7 | "prerelease": false
8 | }
9 | },
10 | "release-type": "simple",
11 | "changelog-sections": [
12 | {
13 | "type": "feat",
14 | "hidden": false,
15 | "section": "Features"
16 | },
17 | {
18 | "type": "feature",
19 | "hidden": false,
20 | "section": "Features"
21 | },
22 | {
23 | "type": "fix",
24 | "hidden": false,
25 | "section": "Bug Fixes"
26 | },
27 | {
28 | "type": "chore",
29 | "hidden": false,
30 | "section": "Miscellaneous Chores"
31 | }
32 | ],
33 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
34 | }
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/WellKnown/Jwks/Get/GetJwksEndpointSummary.cs:
--------------------------------------------------------------------------------
1 | using FastEndpoints;
2 |
3 | namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.WellKnown.Jwks.Get;
4 |
5 | public sealed class GetJwksEndpointSummary : Summary
6 | {
7 | public GetJwksEndpointSummary()
8 | {
9 | Summary = "Gets the JSON Web Key Set (JWKS) containing the public keys used to verify dialog token signatures";
10 | Description = """
11 | This endpoint can be used by client integrations supporting automatic discovery of "OAuth 2.0 Authorization Server" metadata, enabling verification of dialog tokens issued by Dialogporten.
12 | """;
13 | Responses[StatusCodes.Status200OK] = "The OAuth 2.0 Authorization Server Metadata";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/TestClock.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Common;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Common;
4 |
5 | public sealed class TestClock : IClock
6 | {
7 | private DateTimeOffset? _override;
8 |
9 | public DateTimeOffset UtcNowOffset => _override ?? DateTimeOffset.UtcNow;
10 | public DateTimeOffset NowOffset => UtcNowOffset.ToOffset(TimeSpan.FromHours(1));
11 |
12 | public DateTime UtcNow => UtcNowOffset.UtcDateTime;
13 | public DateTime Now => NowOffset.DateTime;
14 |
15 | public void OverrideUtc(DateTimeOffset dateTimeOffset) => _override = dateTimeOffset;
16 | public void OverrideUtc(TimeSpan skew) => _override = UtcNowOffset.Add(skew);
17 | public void Reset() => _override = null;
18 | }
19 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/ServiceOwnerContext/Queries/GetServiceOwnerLabels/ServiceOwnerLabelDto.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.ServiceOwnerContext.Queries.GetServiceOwnerLabels;
2 |
3 | public sealed class ServiceOwnerLabelResultDto
4 | {
5 | ///
6 | /// A list of labels.
7 | ///
8 | public List Labels { get; set; } = [];
9 |
10 | ///
11 | /// The unique identifier for the service owner context revision in UUIDv4 format.
12 | ///
13 | public Guid Revision { get; set; }
14 | }
15 |
16 | public sealed class ServiceOwnerLabelDto
17 | {
18 | ///
19 | /// A label value.
20 | ///
21 | public string Value { get; set; } = null!;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Pagination/Continuation/ContinuationToken.cs:
--------------------------------------------------------------------------------
1 | using System.Linq.Expressions;
2 | using System.Reflection;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Common.Pagination.Continuation;
5 |
6 | public sealed class ContinuationToken
7 | {
8 | private static readonly MemberInfo ValueMemberInfo = typeof(ContinuationToken).GetProperty(nameof(Value))!;
9 | private readonly Type _type;
10 | public string Key { get; }
11 | public object? Value { get; }
12 |
13 | public ContinuationToken(string key, object? value, Type type)
14 | {
15 | Key = key;
16 | Value = value;
17 | _type = type;
18 | }
19 |
20 | public Expression GetValueExpression() => Expression.Convert(Expression.MakeMemberAccess(Expression.Constant(this), ValueMemberInfo), _type);
21 | }
22 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/Localizations/LocalizationDto.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Localizations;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations;
4 |
5 | public sealed class LocalizationDto
6 | {
7 | private readonly string _languageCode = null!;
8 |
9 | ///
10 | /// The localized text (or URL if a front-channel embed).
11 | ///
12 | public required string Value { get; init; }
13 |
14 | ///
15 | /// The language code of the localization in ISO 639-1 format.
16 | ///
17 | /// nb
18 | public required string LanguageCode
19 | {
20 | get => _languageCode;
21 | init => _languageCode = Localization.NormalizeCultureCode(value)!;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/tests/Digdir.Domain.Dialogporten.Infrastructure.Unit.Tests/Digdir.Domain.Dialogporten.Infrastructure.Unit.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tests/k6/tests/graphql/performance/graphqlSearchWithThresholds.js:
--------------------------------------------------------------------------------
1 | import { _default, _setup } from "./graphqlCommonFunctions.js";
2 |
3 | const label = "graphql-getall-dialogs-for-party";
4 | const queryType = "getAllDialogsForParty"
5 |
6 | const numberOfEndUsers = 200; // Remove when altinn-testtools bulk get of endusers/tokens is fast
7 | export const options = {
8 | setupTimeout: '10m',
9 | vus: 1,
10 | duration: "30s",
11 | summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'],
12 | thresholds: {}
13 | };
14 |
15 |
16 | options.thresholds[`http_req_duration{name:${label}}`] = ["p(95)<500"];
17 | options.thresholds[`http_reqs{name:${label}}`] = [];
18 |
19 |
20 | export function setup() { return _setup(label, queryType, numberOfEndUsers); }
21 | export default function (data) { _default(data); }
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/Validators/CreateDialogDialogAttachmentUrlDtoValidator.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation;
2 | using Digdir.Domain.Dialogporten.Domain.Common;
3 | using FluentValidation;
4 |
5 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create.Validators;
6 |
7 | internal sealed class CreateDialogDialogAttachmentUrlDtoValidator : AbstractValidator
8 | {
9 | public CreateDialogDialogAttachmentUrlDtoValidator()
10 | {
11 | RuleFor(x => x.Url)
12 | .NotNull()
13 | .IsValidHttpsUrl()
14 | .MaximumLength(Constants.DefaultMaxUriLength);
15 |
16 | RuleFor(x => x.ConsumerType)
17 | .IsInEnum();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/Validators/UpdateDialogDialogAttachmentUrlDtoValidator.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation;
2 | using Digdir.Domain.Dialogporten.Domain.Common;
3 | using FluentValidation;
4 |
5 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update.Validators;
6 |
7 | internal sealed class UpdateDialogDialogAttachmentUrlDtoValidator : AbstractValidator
8 | {
9 | public UpdateDialogDialogAttachmentUrlDtoValidator()
10 | {
11 | RuleFor(x => x.Url)
12 | .NotNull()
13 | .IsValidHttpsUrl()
14 | .MaximumLength(Constants.DefaultMaxUriLength);
15 |
16 | RuleFor(x => x.ConsumerType)
17 | .IsInEnum();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Digdir.Library.Dialogporten.WebApiClient/Infrastructure/IInternalDialogportenApi.cs:
--------------------------------------------------------------------------------
1 | using Refit;
2 |
3 | namespace Altinn.ApiClients.Dialogporten.Infrastructure;
4 |
5 | internal interface IInternalDialogportenApi
6 | {
7 | [Get("/api/v1/.well-known/jwks.json")]
8 | Task GetJwks(CancellationToken cancellationToken);
9 | }
10 |
11 | internal sealed class DialogportenJwks
12 | {
13 | public required List Keys { get; init; }
14 |
15 | internal sealed class JsonWebKey
16 | {
17 | public required string Kty { get; init; }
18 | public required string Use { get; init; }
19 | public required string Kid { get; init; }
20 | public required string Crv { get; init; }
21 | public required string X { get; init; }
22 | public required string Alg { get; init; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDataLoader.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Common.Behaviours.DataLoader;
2 | using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.HorizontalDataLoaders;
3 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
4 |
5 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update;
6 |
7 | internal sealed class UpdateDialogDataLoader(FullDialogAggregateDataLoader fullDialogDataLoader) : TypedDataLoader
8 | {
9 | public override Task Load(UpdateDialogCommand request, CancellationToken cancellationToken) =>
10 | fullDialogDataLoader.LoadDialogEntity(request.Id, cancellationToken);
11 | }
12 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/DialogServiceOwnerContexts/Entities/DialogServiceOwnerLabel.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions.Features.Creatable;
2 | using Digdir.Library.Entity.Abstractions.Features.Immutable;
3 |
4 | namespace Digdir.Domain.Dialogporten.Domain.DialogServiceOwnerContexts.Entities;
5 |
6 | public sealed class DialogServiceOwnerLabel : IImmutableEntity, ICreatableEntity
7 | {
8 | public const int MaxNumberOfLabels = 20;
9 |
10 | private string _value = null!;
11 |
12 | public string Value
13 | {
14 | get => _value;
15 | set => _value = value.Trim().ToLowerInvariant();
16 | }
17 |
18 | public DateTimeOffset CreatedAt { get; set; }
19 |
20 | public Guid DialogServiceOwnerContextId { get; set; }
21 | public DialogServiceOwnerContext DialogServiceOwnerContext { get; set; } = null!;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogDataLoader.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Common.Behaviours.DataLoader;
2 | using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.HorizontalDataLoaders;
3 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
4 |
5 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get;
6 |
7 | internal sealed class GetDialogDataLoader(FullDialogAggregateDataLoader fullDialogAggregateDataLoader)
8 | : TypedDataLoader
9 | {
10 | public override Task Load(GetDialogQuery request, CancellationToken cancellationToken) =>
11 | fullDialogAggregateDataLoader.LoadDialogEntity(request.DialogId, cancellationToken);
12 | }
13 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Sql/Dialog/Search/View.VDialogDocument.sql:
--------------------------------------------------------------------------------
1 | -- Produces weighted tsvectors per dialog so upserts remain a simple INSERT ... SELECT.
2 | CREATE OR REPLACE VIEW search."VDialogDocument" AS
3 | SELECT d."Id" AS "DialogId",
4 | (
5 | SELECT
6 | string_agg(
7 | setweight(
8 | to_tsvector(COALESCE(isomap."TsConfigName", 'simple')::regconfig, c."Value"), -- Fall back to simple when the language map lacks a match.
9 | c."Weight"::"char"
10 | )::text,
11 | ' '
12 | )::tsvector
13 | FROM search."VDialogContent" c
14 | LEFT JOIN search."Iso639TsVectorMap" isomap
15 | ON c."LanguageCode" = isomap."IsoCode"
16 | WHERE c."DialogId" = d."Id"
17 | ) AS "Document"
18 | FROM "Dialog" d;
19 |
--------------------------------------------------------------------------------
/.azure/applications/service/yt01.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'yt01'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param revisionSuffix = readEnvironmentVariable('REVISION_SUFFIX')
7 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
8 | param appConfigurationName = readEnvironmentVariable('AZURE_APP_CONFIGURATION_NAME')
9 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
10 | param serviceBusNamespaceName = readEnvironmentVariable('AZURE_SERVICE_BUS_NAMESPACE_NAME')
11 | param resources = {
12 | cpu: 2
13 | memory: '4Gi'
14 | }
15 |
16 | param otelTraceSamplerRatio = '1'
17 |
18 | // secrets
19 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
20 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/Extensions/OptionExtensions/OptionsBuilderFluentValidationExtensions.cs:
--------------------------------------------------------------------------------
1 | using FluentValidation;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.Options;
4 |
5 | namespace Digdir.Domain.Dialogporten.Application.Common.Extensions.OptionExtensions;
6 |
7 | public static class OptionsBuilderFluentValidationExtensions
8 | {
9 | public static OptionsBuilder ValidateFluently(this OptionsBuilder optionsBuilder)
10 | where TOptions : class
11 | {
12 | optionsBuilder.Services.AddTransient>(x =>
13 | new FluentValidationOptions(
14 | optionsBuilder.Name,
15 | x.GetRequiredService>>()));
16 | return optionsBuilder;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Configurations/Dialogs/DialogSearchTagConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata.Builders;
4 | using static Digdir.Domain.Dialogporten.Domain.Common.Constants;
5 |
6 | namespace Digdir.Domain.Dialogporten.Infrastructure.Persistence.Configurations.Dialogs;
7 |
8 | internal sealed class DialogSearchTagConfiguration : IEntityTypeConfiguration
9 | {
10 | public void Configure(EntityTypeBuilder builder)
11 | {
12 | builder.Property(x => x.Value)
13 | .HasMaxLength(MaxSearchTagLength);
14 |
15 | builder.HasIndex(x => x.Value)
16 | .HasMethod(Constants.Gin)
17 | .HasOperators(Constants.GinTrgmOps);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/GetActivity/ActivityDto.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations;
2 | using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors;
3 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities;
4 |
5 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.GetActivity;
6 |
7 | public sealed class ActivityDto
8 | {
9 | public Guid Id { get; set; }
10 | public DateTimeOffset? CreatedAt { get; set; }
11 | public Uri? ExtendedType { get; set; }
12 |
13 | public DialogActivityType.Values Type { get; set; }
14 |
15 | public Guid? TransmissionId { get; set; }
16 |
17 | public ActorDto PerformedBy { get; set; } = null!;
18 | public List Description { get; set; } = [];
19 | }
20 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Common/DomainFailure.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Domain.Common;
2 |
3 | public sealed class DomainFailure
4 | {
5 | public string PropertyName { get; init; }
6 | public string ErrorMessage { get; init; }
7 |
8 | public DomainFailure(string propertyName, string error)
9 | {
10 | PropertyName = propertyName;
11 | ErrorMessage = error;
12 | }
13 |
14 | public static DomainFailure EntityExists(string propertyName, string entityName, IEnumerable keys)
15 | => new(propertyName, $"Entity '{entityName}' with the following key(s) already exists: ({string.Join(", ", keys)}).");
16 |
17 | public static DomainFailure EntityExists(IEnumerable keys)
18 | {
19 | var entityName = typeof(T).Name;
20 | return EntityExists(entityName, entityName, keys);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/MutationTypes/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.EndUserContext.Commands.BulkSetSystemLabels;
3 | using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.EndUserContext.Commands.SetSystemLabel;
4 |
5 | namespace Digdir.Domain.Dialogporten.GraphQL.EndUser.MutationTypes;
6 |
7 | public sealed class MappingProfile : Profile
8 | {
9 | public MappingProfile()
10 | {
11 | CreateMap();
12 |
13 | CreateMap();
14 |
15 | CreateMap();
16 |
17 | CreateMap()
18 | .ForMember(dest => dest.Dto, opt => opt.MapFrom(src => src));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/Validators/CreateDialogTransmissionAttachmentUrlDtoValidator.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation;
2 | using Digdir.Domain.Dialogporten.Domain.Common;
3 | using FluentValidation;
4 |
5 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create.Validators;
6 |
7 | internal sealed class CreateDialogTransmissionAttachmentUrlDtoValidator : AbstractValidator
8 | {
9 | public CreateDialogTransmissionAttachmentUrlDtoValidator()
10 | {
11 | RuleFor(x => x.Url)
12 | .NotNull()
13 | .IsValidHttpsUrl()
14 | .MaximumLength(Constants.DefaultMaxUriLength);
15 |
16 | RuleFor(x => x.ConsumerType)
17 | .IsInEnum();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/Validators/UpdateDialogTransmissionAttachmentUrlDtoValidator.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation;
2 | using Digdir.Domain.Dialogporten.Domain.Common;
3 | using FluentValidation;
4 |
5 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update.Validators;
6 |
7 | internal sealed class UpdateDialogTransmissionAttachmentUrlDtoValidator : AbstractValidator
8 | {
9 | public UpdateDialogTransmissionAttachmentUrlDtoValidator()
10 | {
11 | RuleFor(x => x.Url)
12 | .NotNull()
13 | .IsValidHttpsUrl()
14 | .MaximumLength(Constants.DefaultMaxUriLength);
15 |
16 | RuleFor(x => x.ConsumerType)
17 | .IsInEnum();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.github/workflows/workflow-get-current-version.yml:
--------------------------------------------------------------------------------
1 | name: "Get current version"
2 | # might use previous tag as a version instead of the current version in file
3 | # https://github.com/WyriHaximus/github-action-get-previous-tag
4 | on:
5 | workflow_call:
6 | outputs:
7 | version:
8 | description: "Version"
9 | value: ${{ jobs.get-current-version.outputs.version }}
10 |
11 | permissions:
12 | contents: read
13 |
14 | jobs:
15 | get-current-version:
16 | name: Filter
17 | runs-on: ubuntu-latest
18 | outputs:
19 | version: ${{ steps.set-current-version.outputs.version }}
20 | steps:
21 | - name: "Checkout GitHub Action"
22 | uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
23 | - name: Set current version
24 | id: set-current-version
25 | run: echo "version=$(cat version.txt)" >> $GITHUB_OUTPUT
26 |
--------------------------------------------------------------------------------
/.azure/applications/graphql/yt01.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'yt01'
4 | param location = 'norwayeast'
5 | param apimIp = '51.13.85.197'
6 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
7 | param revisionSuffix = readEnvironmentVariable('REVISION_SUFFIX')
8 | param resources = {
9 | cpu: 2
10 | memory: '4Gi'
11 | }
12 |
13 | param otelTraceSamplerRatio = '1'
14 |
15 | // Use dedicated workload profile
16 | param workloadProfileName = 'Dedicated-D8'
17 |
18 | // secrets
19 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
20 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
21 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
22 | param appConfigurationName = readEnvironmentVariable('AZURE_APP_CONFIGURATION_NAME')
23 |
--------------------------------------------------------------------------------
/.azure/applications/web-api-eu/yt01.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'yt01'
4 | param location = 'norwayeast'
5 | param apimIp = '51.13.85.197'
6 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
7 | param revisionSuffix = readEnvironmentVariable('REVISION_SUFFIX')
8 | param resources = {
9 | cpu: 2
10 | memory: '4Gi'
11 | }
12 |
13 | param otelTraceSamplerRatio = '1'
14 |
15 | // Use dedicated workload profile
16 | param workloadProfileName = 'Dedicated-D8'
17 |
18 | // secrets
19 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
20 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
21 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
22 | param appConfigurationName = readEnvironmentVariable('AZURE_APP_CONFIGURATION_NAME')
23 |
--------------------------------------------------------------------------------
/.azure/applications/web-api-so/yt01.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'yt01'
4 | param location = 'norwayeast'
5 | param apimIp = '51.13.85.197'
6 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
7 | param revisionSuffix = readEnvironmentVariable('REVISION_SUFFIX')
8 | param resources = {
9 | cpu: 2
10 | memory: '4Gi'
11 | }
12 |
13 | param otelTraceSamplerRatio = '1'
14 |
15 | // Use dedicated workload profile
16 | param workloadProfileName = 'Dedicated-D8'
17 |
18 | // secrets
19 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
20 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
21 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
22 | param appConfigurationName = readEnvironmentVariable('AZURE_APP_CONFIGURATION_NAME')
23 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Sql/Dialog/Search/View.VDialogDocument.V2.sql:
--------------------------------------------------------------------------------
1 | -- Produces weighted tsvectors per dialog so upserts remain a simple INSERT ... SELECT.
2 | CREATE OR REPLACE VIEW search."VDialogDocument" AS
3 | SELECT d."Id" AS "DialogId",
4 | (
5 | SELECT
6 | string_agg(
7 | setweight(
8 | to_tsvector(COALESCE(isomap."TsConfigName", 'simple')::regconfig, c."Value"), -- Fall back to simple when the language map lacks a match.
9 | c."Weight"::"char"
10 | )::text,
11 | ' '
12 | )::tsvector
13 | FROM search."VDialogContent" c
14 | LEFT JOIN search."Iso639TsVectorMap" isomap
15 | ON c."LanguageCode" = isomap."IsoCode"
16 | WHERE c."DialogId" = d."Id"
17 | ) AS "Document",
18 | d."Party" AS "Party"
19 | FROM "Dialog" d;
20 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/Common/Headers/HttpResponseHeaderExamples.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.WebApi.Common;
2 | using FastEndpoints;
3 |
4 | namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Headers;
5 |
6 | public static class HttpResponseHeaderExamples
7 | {
8 | public static ResponseHeader NewDialogETagHeader(int statusCode)
9 | => new(statusCode, Constants.ETag)
10 | {
11 | Description = "The new UUID ETag of the dialog",
12 | Example = "123e4567-e89b-12d3-a456-426614174000"
13 | };
14 |
15 | public static ResponseHeader NewServiceOwnerContextETagHeader(int statusCode)
16 | => new(statusCode, Constants.ETag)
17 | {
18 | Description = "The new UUID ETag of the ServiceOwner Context",
19 | Example = "123e4567-e89b-12d3-a456-426614174000"
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/.azure/applications/service/prod.bicepparam:
--------------------------------------------------------------------------------
1 | using './main.bicep'
2 |
3 | param environment = 'prod'
4 | param location = 'norwayeast'
5 | param imageTag = readEnvironmentVariable('IMAGE_TAG')
6 | param revisionSuffix = readEnvironmentVariable('REVISION_SUFFIX')
7 | param environmentKeyVaultName = readEnvironmentVariable('AZURE_ENVIRONMENT_KEY_VAULT_NAME')
8 | param appConfigurationName = readEnvironmentVariable('AZURE_APP_CONFIGURATION_NAME')
9 | param containerAppEnvironmentName = readEnvironmentVariable('AZURE_CONTAINER_APP_ENVIRONMENT_NAME')
10 | param serviceBusNamespaceName = readEnvironmentVariable('AZURE_SERVICE_BUS_NAMESPACE_NAME')
11 | param minReplicas = 2
12 |
13 | param resources = {
14 | cpu: 2
15 | memory: '4Gi'
16 | }
17 |
18 | param otelTraceSamplerRatio = '0.2'
19 |
20 | // secrets
21 | param appInsightConnectionString = readEnvironmentVariable('AZURE_APP_INSIGHTS_CONNECTION_STRING')
22 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Common/ITransactionTime.cs:
--------------------------------------------------------------------------------
1 | namespace Digdir.Domain.Dialogporten.Application.Common;
2 |
3 | ///
4 | /// Abstraction around the time that is to be used for all time
5 | /// related DB and event fields within the same transaction.
6 | ///
7 | public interface ITransactionTime
8 | {
9 | DateTimeOffset Value { get; }
10 | }
11 |
12 | ///
13 | /// This will wait to the last possible moment to get the
14 | /// current time and use that as the transaction time.
15 | ///
16 | internal sealed class TransactionTime : ITransactionTime
17 | {
18 | private readonly Lazy _lazyValue;
19 | public DateTimeOffset Value => _lazyValue.Value;
20 | public TransactionTime(IClock clock)
21 | {
22 | ArgumentNullException.ThrowIfNull(clock);
23 | _lazyValue = new(() => clock.UtcNow);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Common/Actors/ActorDto.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Actors;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors;
4 |
5 | public sealed class ActorDto
6 | {
7 | ///
8 | /// The type of actor; either the service owner, or someone representing the party.
9 | ///
10 | public ActorType.Values ActorType { get; set; }
11 |
12 | ///
13 | /// The name of the actor.
14 | ///
15 | /// Ola Nordmann
16 | public string? ActorName { get; set; }
17 |
18 | ///
19 | /// The identifier (national identity number or organization number) of the actor.
20 | ///
21 | /// urn:altinn:person:identifier-no:12018212345
22 | public string? ActorId { get; set; }
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Common/Actors/ActorDto.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.Actors;
2 |
3 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors;
4 |
5 | public sealed class ActorDto
6 | {
7 | ///
8 | /// The type of actor; either the service owner, or someone representing the party.
9 | ///
10 | public ActorType.Values ActorType { get; set; }
11 |
12 | ///
13 | /// The name of the actor.
14 | ///
15 | /// Ola Nordmann
16 | public string? ActorName { get; set; }
17 |
18 | ///
19 | /// The identifier (national identity number or organization number) of the actor.
20 | ///
21 | /// urn:altinn:person:identifier-no:12018212345
22 | public string? ActorId { get; set; }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Application/Features/V1/Common/ValidationErrorStrings.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities;
2 | using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions;
3 |
4 | namespace Digdir.Domain.Dialogporten.Application.Features.V1.Common;
5 |
6 | internal static class ValidationErrorStrings
7 | {
8 | internal const string PropertyNameMustBeLessThanOrEqualToComparisonProperty =
9 | "'{PropertyName}' must be less than or equal to '{ComparisonProperty}'.";
10 |
11 | internal static readonly string SentLabelNotAllowed =
12 | $"Cannot manually add or remove system label {SystemLabel.Values.Sent}. " +
13 | $"It is added automatically when a " +
14 | $"transmissions of type '{DialogTransmissionType.Values.Submission} or " +
15 | $"'{DialogTransmissionType.Values.Correction}' is added to the dialog.";
16 | }
17 |
--------------------------------------------------------------------------------
/tests/k6/tests/enduser/all-tests.js:
--------------------------------------------------------------------------------
1 | // This file is generated, see "scripts" directory
2 | import { default as DialogGetAcceptLanguage } from './DialogGetAcceptLanguage.js';
3 | import { default as dialogBulkSystemLabels } from './dialogBulkSystemLabels.js';
4 | import { default as dialogDetails } from './dialogDetails.js';
5 | import { default as dialogSearch } from './dialogSearch.js';
6 | import { default as dialogSystemLabelLog } from './dialogSystemLabelLog.js';
7 | import { default as dialogSystemUser } from './dialogSystemUser.js';
8 | import { default as dialogVisibleFrom } from './dialogVisibleFrom.js';
9 | import { default as parties } from './parties.js';
10 |
11 | export default function() {
12 | DialogGetAcceptLanguage();
13 | dialogBulkSystemLabels();
14 | dialogDetails();
15 | dialogSearch();
16 | dialogSystemLabelLog();
17 | dialogSystemUser();
18 | dialogVisibleFrom();
19 | parties();
20 | }
21 |
--------------------------------------------------------------------------------
/.github/workflows/ci-cd-pull-request-title.yml:
--------------------------------------------------------------------------------
1 | # Checks the title of a PR to ensure it follows the correct format
2 | name: "PR Title Checker"
3 | on:
4 | pull_request_target:
5 | types: [opened, edited, reopened, synchronize]
6 | branches:
7 | - main
8 |
9 | concurrency:
10 | group: ${{ github.workflow }}-${{ github.ref }}
11 |
12 | jobs:
13 | validate:
14 | permissions:
15 | contents: read
16 | pull-requests: read
17 | name: Validate PR Title
18 | runs-on: ubuntu-latest
19 | steps:
20 | - uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3
21 | with:
22 | egress-policy: audit
23 |
24 | - uses: thehanimo/pr-title-checker@7fbfe05602bdd86f926d3fb3bccb6f3aed43bc70 # v1.4.3
25 | with:
26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27 | configuration_path: ".github/pr-title-checker-config.json"
28 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Infrastructure/Persistence/Sql/Dialog/Search/Function.ClaimDialogsStandard.sql:
--------------------------------------------------------------------------------
1 | -- Claims pending dialogs in FIFO order so parallel workers share the queue deterministically.
2 | CREATE OR REPLACE FUNCTION search."ClaimDialogsStandard"(batch_size int)
3 | RETURNS uuid[] LANGUAGE sql AS $$
4 | WITH to_claim AS (
5 | SELECT q."DialogId"
6 | FROM search."DialogSearchRebuildQueue" q
7 | WHERE q."Status" = 0
8 | ORDER BY q."DialogId" -- Stable order avoids the same worker repeatedly claiming the tail.
9 | FOR UPDATE OF q SKIP LOCKED
10 | LIMIT batch_size
11 | ),
12 | mark_processing AS (
13 | UPDATE search."DialogSearchRebuildQueue" q
14 | SET "Status" = 1, "UpdatedAt" = now()
15 | WHERE q."DialogId" IN (SELECT "DialogId" FROM to_claim)
16 | RETURNING q."DialogId"
17 | )
18 | SELECT coalesce(array_agg("DialogId"), '{}') FROM mark_processing;
19 | $$;
20 |
--------------------------------------------------------------------------------
/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/Actions/DialogApiAction.cs:
--------------------------------------------------------------------------------
1 | using Digdir.Library.Entity.Abstractions;
2 | using Digdir.Library.Entity.Abstractions.Features.Aggregate;
3 |
4 | namespace Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions;
5 |
6 | public sealed class DialogApiAction : IEntity
7 | {
8 | public Guid Id { get; set; }
9 | public DateTimeOffset CreatedAt { get; set; }
10 | public DateTimeOffset UpdatedAt { get; set; }
11 |
12 | public string Action { get; set; } = null!;
13 | public string? AuthorizationAttribute { get; set; }
14 | public string? Name { get; set; }
15 |
16 | // === Dependent relationships ===
17 | public Guid DialogId { get; set; }
18 | public DialogEntity Dialog { get; set; } = null!;
19 |
20 | // === Principal relationships ===
21 | [AggregateChild]
22 | public List Endpoints { get; set; } = [];
23 | }
24 |
--------------------------------------------------------------------------------
/.azure/bicepconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://aka.ms/bicep/config for more information on Bicep configuration options
3 | // Press CTRL+SPACE/CMD+SPACE at any location to see Intellisense suggestions
4 | "analyzers": {
5 | "core": {
6 | "rules": {
7 | "no-unused-params": {
8 | "level": "error"
9 | },
10 | "no-unused-vars": {
11 | "level": "error"
12 | },
13 | "no-hardcoded-env-urls": {
14 | "level": "error"
15 | },
16 | "secure-secrets-in-params": {
17 | "level": "error"
18 | },
19 | "no-unnecessary-dependson": {
20 | "level": "error"
21 | },
22 | "outputs-should-not-contain-secrets": {
23 | "level": "error"
24 | }
25 | }
26 | }
27 | },
28 | "experimentalFeaturesEnabled": {
29 | "compileTimeImports": true,
30 | "userDefinedFunctions": true
31 | }
32 | }
--------------------------------------------------------------------------------