From 493d247cac4ccb25eb59209bd9e92cda80661a29 Mon Sep 17 00:00:00 2001 From: Dialogporten Automation Bot <164321870+dialogporten-bot@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:56:32 +0100 Subject: [PATCH 01/19] chore(main): release 1.28.2 (#1394) :robot: I have created a release *beep* *boop* --- ## [1.28.2](https://github.com/digdir/dialogporten/compare/v1.28.1...v1.28.2) (2024-11-05) ### Bug Fixes * Use yt01 token generator environment for k6 tests running on yt01 ([#1391](https://github.com/digdir/dialogporten/issues/1391)) ([393176c](https://github.com/digdir/dialogporten/commit/393176c1f21dc6f8b0ab7fbf294e16713bd4d6e0)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- CHANGELOG.md | 7 +++++++ version.txt | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d57160bdf..8f5dbeefc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.28.2](https://github.com/digdir/dialogporten/compare/v1.28.1...v1.28.2) (2024-11-05) + + +### Bug Fixes + +* Use yt01 token generator environment for k6 tests running on yt01 ([#1391](https://github.com/digdir/dialogporten/issues/1391)) ([393176c](https://github.com/digdir/dialogporten/commit/393176c1f21dc6f8b0ab7fbf294e16713bd4d6e0)) + ## [1.28.1](https://github.com/digdir/dialogporten/compare/v1.28.0...v1.28.1) (2024-11-05) diff --git a/version.txt b/version.txt index 450a687b2..bf4df28ef 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.28.1 +1.28.2 From 70a63cd73bb7c5827054de125e4f269b5b5cf687 Mon Sep 17 00:00:00 2001 From: Dagfinn Olsen Date: Wed, 6 Nov 2024 07:15:33 +0100 Subject: [PATCH 02/19] test: add more performance tests (#1373) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Added more performance tests and some improvements on github workflow for performance tests ## Related Issue(s) - #1326 ## Verification - [x] **Your** code builds clean without any errors or warnings - [x] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [x] 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) ## Summary by CodeRabbit - **New Features** - Introduced performance testing scripts for end-user search and GraphQL search functionalities. - Added a tagging system for performance tests with a new required input parameter. - Enhanced dialog creation and removal performance tests with improved data handling. - Implemented a new function for generating default thresholds for performance tests. - Added new performance testing scenarios for creating dialogs and executing searches. - **Bug Fixes** - Updated performance test configurations to ensure correct environment settings. - **Refactor** - Reorganized performance test scripts to utilize newly created utility functions for better maintainability. - Removed outdated performance testing scripts to streamline the testing framework. - **Documentation** - Updated workflow configurations for clarity and improved execution context. --------- Co-authored-by: Ole Jørgen Skogstad Co-authored-by: Are Almaas --- .github/workflows/dispatch-k6-performance.yml | 16 ++- .../workflows/workflow-run-k6-performance.yml | 1 + tests/k6/common/config.js | 49 +++++-- tests/k6/common/request.js | 8 +- tests/k6/common/testimports.js | 3 +- .../enduser/performance/enduser-search.js | 27 ++++ .../enduser/performance/simple-search.js | 57 -------- .../graphql/performance/graphql-search.js | 37 +++++ .../performancetest_common/createDialog.js | 61 +++++++++ .../getDefaultThresholds.js | 21 +++ .../performancetest_common/readTestdata.js | 52 +++++++ .../performancetest_common/simpleSearch.js | 129 ++++++++++++++++++ .../performancetest_data/01-create-dialog.js | 54 ++++++++ .../performancetest_data/endusers-yt01.csv | 73 ++++++++++ .../performancetest_data/graphql-search.js | 70 ++++++++++ .../serviceowners-yt01.csv | 2 + .../performance/create-dialog-and-search.js | 65 +++++++++ tests/k6/tests/scripts/generate_tokens.sh | 2 +- .../serviceowner/performance/create-dialog.js | 42 ++---- .../performance/create-remove-dialog.js | 25 ++++ 20 files changed, 682 insertions(+), 112 deletions(-) create mode 100644 tests/k6/tests/enduser/performance/enduser-search.js delete mode 100644 tests/k6/tests/enduser/performance/simple-search.js create mode 100644 tests/k6/tests/graphql/performance/graphql-search.js create mode 100644 tests/k6/tests/performancetest_common/createDialog.js create mode 100644 tests/k6/tests/performancetest_common/getDefaultThresholds.js create mode 100644 tests/k6/tests/performancetest_common/readTestdata.js create mode 100644 tests/k6/tests/performancetest_common/simpleSearch.js create mode 100644 tests/k6/tests/performancetest_data/01-create-dialog.js create mode 100644 tests/k6/tests/performancetest_data/endusers-yt01.csv create mode 100644 tests/k6/tests/performancetest_data/graphql-search.js create mode 100644 tests/k6/tests/performancetest_data/serviceowners-yt01.csv create mode 100644 tests/k6/tests/scenarios/performance/create-dialog-and-search.js create mode 100644 tests/k6/tests/serviceowner/performance/create-remove-dialog.js diff --git a/.github/workflows/dispatch-k6-performance.yml b/.github/workflows/dispatch-k6-performance.yml index b7e434d06..478b0c853 100644 --- a/.github/workflows/dispatch-k6-performance.yml +++ b/.github/workflows/dispatch-k6-performance.yml @@ -10,12 +10,12 @@ on: environment: description: 'Environment' required: true - default: 'staging' + default: 'yt01' type: choice options: - test - staging - - performance + - yt01 tokens: description: 'Tokens to generate; for create dialog, search, none, or both' required: true @@ -26,10 +26,15 @@ on: - enterprise - personal - none + tag: + description: 'tag the performance test' + required: true + default: 'Performance test' + type: string vus: description: 'Number of VUS' required: true - default: 10 + default: 1 type: number duration: description: 'Duration of test, ie 30s, 1m, 10m' @@ -43,8 +48,11 @@ on: type: choice options: - 'tests/k6/tests/serviceowner/performance/create-dialog.js' - - 'tests/k6/tests/enduser/performance/simple-search.js' + - 'tests/k6/tests/serviceowner/performance/create-remove-dialog.js' + - 'tests/k6/tests/enduser/performance/enduser-search.js' + - 'tests/k6/tests/graphql/performance/graphql-search.js' +run-name: ${{ inputs.tag }} vus ${{ inputs.vus }} duration ${{ inputs.duration }} jobs: k6-performance: name: "Run K6 performance test" diff --git a/.github/workflows/workflow-run-k6-performance.yml b/.github/workflows/workflow-run-k6-performance.yml index cd876c9e5..214c69173 100644 --- a/.github/workflows/workflow-run-k6-performance.yml +++ b/.github/workflows/workflow-run-k6-performance.yml @@ -34,6 +34,7 @@ on: jobs: k6-test: runs-on: ubuntu-latest + environment: ${{ inputs.environment }} permissions: checks: write pull-requests: write diff --git a/tests/k6/common/config.js b/tests/k6/common/config.js index faf9a4500..0478cff55 100644 --- a/tests/k6/common/config.js +++ b/tests/k6/common/config.js @@ -1,22 +1,41 @@ +const localBaseUrl = "https://localhost:7214/"; +const localDockerBaseUrl = "https://host.docker.internal:7214/"; +const testBaseUrl = "https://altinn-dev-api.azure-api.net/dialogporten/"; +const yt01BaseUrl = "https://platform.yt01.altinn.cloud/dialogporten/"; +const stagingBaseUrl = "https://platform.tt02.altinn.no/dialogporten/"; +const prodBaseUrl = "https://platform.altinn.no/dialogporten/"; + +const endUserPath = "api/v1/enduser/"; +const serviceOwnerPath = "api/v1/serviceowner/"; +const graphqlPath = "graphql"; + export const baseUrls = { v1: { enduser: { - localdev: "https://localhost:7214/api/v1/enduser/", - localdev_docker: "https://host.docker.internal:7214/api/v1/enduser/", - test: "https://altinn-dev-api.azure-api.net/dialogporten/api/v1/enduser/", - yt01: "https://platform.yt01.altinn.cloud/dialogporten/api/v1/enduser/", - staging: "https://platform.tt02.altinn.no/dialogporten/api/v1/enduser/", - prod: "https://platform.altinn.no/dialogporten/api/v1/enduser/" + localdev: localBaseUrl + endUserPath, + localdev_docker: localDockerBaseUrl + endUserPath, + test: testBaseUrl + endUserPath, + yt01: yt01BaseUrl + endUserPath, + staging: stagingBaseUrl + endUserPath, + prod: prodBaseUrl + endUserPath }, serviceowner: { - localdev: "https://localhost:7214/api/v1/serviceowner/", - localdev_docker: "https://host.docker.internal:7214/api/v1/serviceowner/", - test: "https://altinn-dev-api.azure-api.net/dialogporten/api/v1/serviceowner/", - yt01: "https://platform.yt01.altinn.cloud/dialogporten/api/v1/serviceowner/", - staging: "https://platform.tt02.altinn.no/dialogporten/api/v1/serviceowner/", - prod: "https://platform.altinn.no/dialogporten/api/v1/serviceowner/" - } - } + localdev: localBaseUrl + serviceOwnerPath, + localdev_docker: localDockerBaseUrl + serviceOwnerPath, + test: testBaseUrl + serviceOwnerPath, + yt01: yt01BaseUrl + serviceOwnerPath, + staging: stagingBaseUrl + serviceOwnerPath, + prod: prodBaseUrl + serviceOwnerPath + }, + graphql: { + localdev: localBaseUrl + graphqlPath, + localdev_docker: localDockerBaseUrl + graphqlPath, + test: testBaseUrl + graphqlPath, + yt01: yt01BaseUrl + graphqlPath, + staging: stagingBaseUrl + graphqlPath, + prod: prodBaseUrl + graphqlPath + }, + } }; export const defaultEndUserOrgNo = "310923044"; // ÆRLIG UROKKELIG TIGER AS @@ -43,4 +62,6 @@ export const baseUrlEndUser = baseUrls[__ENV.API_VERSION]["enduser"][__ENV.API_E export const baseUrlServiceOwner = baseUrls[__ENV.API_VERSION]["serviceowner"][__ENV.API_ENVIRONMENT]; export const tokenGeneratorEnv = __ENV.API_ENVIRONMENT == "yt01" ? "yt01" : "tt02"; // yt01 is the only environment that has a separate token generator environment +export const baseUrlGraphql = baseUrls[__ENV.API_VERSION]["graphql"][__ENV.API_ENVIRONMENT]; + export const sentinelValue = "dialogporten-e2e-sentinel"; diff --git a/tests/k6/common/request.js b/tests/k6/common/request.js index cc2be8457..206a45241 100644 --- a/tests/k6/common/request.js +++ b/tests/k6/common/request.js @@ -1,5 +1,5 @@ import { default as http } from 'k6/http'; -import { baseUrlEndUser, baseUrlServiceOwner } from './config.js' +import { baseUrlEndUser, baseUrlGraphql, baseUrlServiceOwner } from './config.js' import { getServiceOwnerTokenFromGenerator, getEnduserTokenFromGenerator } from './token.js' import { extend } from './extend.js' @@ -125,3 +125,9 @@ export function patchEU(url, body, params = null, tokenOptions = null) { export function deleteEU(url, params = null, tokenOptions = null) { return http.request('DELETE', baseUrlEndUser + url, getEnduserRequestParams(params, tokenOptions)); } + +export function postGQ(body, params = null) { + body = JSON.stringify({ query: body }) + params = extend(true, {}, params, { headers: { 'Content-Type': 'application/json' }}); + return http.post(baseUrlGraphql, body, params); +} diff --git a/tests/k6/common/testimports.js b/tests/k6/common/testimports.js index 14f1e919f..6463ecfd8 100644 --- a/tests/k6/common/testimports.js +++ b/tests/k6/common/testimports.js @@ -14,7 +14,8 @@ export { putSO, patchSO, deleteSO, - purgeSO + purgeSO, + postGQ } from './request.js'; export { setTitle, diff --git a/tests/k6/tests/enduser/performance/enduser-search.js b/tests/k6/tests/enduser/performance/enduser-search.js new file mode 100644 index 000000000..360d8e7d3 --- /dev/null +++ b/tests/k6/tests/enduser/performance/enduser-search.js @@ -0,0 +1,27 @@ +import { enduserSearch } from '../../performancetest_common/simpleSearch.js' +import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js'; +import { endUsersWithTokens } from '../../performancetest_common/readTestdata.js'; + +export let options = { + summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'], + thresholds: getDefaultThresholds(['http_req_duration', 'http_reqs'],['enduser search', + 'get dialog', + 'get dialog activities', + 'get dialog activity', + 'get seenlogs', + 'get seenlog', + 'get transmissions', + 'get transmission', + 'get labellog' + ]) +}; + +export default function() { + if ((options.vus === undefined || options.vus === 1) && (options.iterations === undefined || options.iterations === 1)) { + enduserSearch(endUsersWithTokens[0]); + } + else { + enduserSearch(randomItem(endUsersWithTokens)); + } +} + diff --git a/tests/k6/tests/enduser/performance/simple-search.js b/tests/k6/tests/enduser/performance/simple-search.js deleted file mode 100644 index be99517b2..000000000 --- a/tests/k6/tests/enduser/performance/simple-search.js +++ /dev/null @@ -1,57 +0,0 @@ -import { getEU, expect, expectStatusFor, describe } from "../../../common/testimports.js"; -import { SharedArray } from 'k6/data'; -import papaparse from 'https://jslib.k6.io/papaparse/5.1.1/index.js'; -import { randomItem } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; - -const filenameEndusers = '../../performancetest_data/.endusers-with-tokens.csv'; - -const endUsers = new SharedArray('endUsers', function () { - try { - const csvData = papaparse.parse(open(filenameEndusers), { header: true, skipEmptyLines: true }).data; - if (!csvData.length) { - throw new Error('No data found in CSV file'); - } - csvData.forEach((user, index) => { - if (!user.token || !user.ssn) { - throw new Error(`Missing required fields at row ${index + 1}`); - } - }); - return csvData; - } catch (error) { - throw new Error(`Failed to load end users: ${error.message}`); - } -}); - -export let options = { - summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'], - thresholds: { - 'http_req_duration{name:simple search}': [], - 'http_reqs{name:simple search}': [], - }, -}; - -export default function() { - if ((options.vus === undefined || options.vus === 1) && (options.iterations === undefined || options.iterations === 1)) { - simpleSearch(endUsers[0]); - } - else { - simpleSearch(randomItem(endUsers)); - } -} - -export function simpleSearch(enduser) { - let paramsWithToken = { - headers: { - Authorization: "Bearer " + enduser.token - }, - tags: { name: 'simple search' } - } - let defaultParty = "urn:altinn:person:identifier-no:" + enduser.ssn; - let defaultFilter = "?Party=" + defaultParty; - describe('Perform simple dialog list', () => { - let r = getEU('dialogs' + defaultFilter, paramsWithToken); - expectStatusFor(r).to.equal(200); - expect(r, 'response').to.have.validJsonBody(); - }); -} - diff --git a/tests/k6/tests/graphql/performance/graphql-search.js b/tests/k6/tests/graphql/performance/graphql-search.js new file mode 100644 index 000000000..de5a9c04d --- /dev/null +++ b/tests/k6/tests/graphql/performance/graphql-search.js @@ -0,0 +1,37 @@ +/** + * The performance test for GraphQL search. + * Run: k6 run tests/k6/tests/graphql/performance/graphql-search.js --vus 1 --iterations 1 -e env=yt01 + */ + +import { randomItem } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; +import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js'; +import { endUsersWithTokens as endUsers } from '../../performancetest_common/readTestdata.js'; +import { graphqlSearch } from "../../performancetest_common/simpleSearch.js"; + +/** + * The options object for configuring the performance test for GraphQL search. + * + * @property {string[]} summaryTrendStats - The summary trend statistics to include in the test results. + * @property {object} thresholds - The thresholds for the test metrics. + */ +export let options = { + summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'], + thresholds: getDefaultThresholds(['http_req_duration', 'http_reqs'],['graphql search']) +}; + +/** + * The default function for the performance test for GraphQL search. + */ +export default function() { + if (!endUsers || endUsers.length === 0) { + throw new Error('No end users loaded for testing'); + } + if ((options.vus === undefined || options.vus === 1) && (options.iterations === undefined || options.iterations === 1)) { + graphqlSearch(endUsers[0]); + } + else { + graphqlSearch(randomItem(endUsers)); + } +} + + diff --git a/tests/k6/tests/performancetest_common/createDialog.js b/tests/k6/tests/performancetest_common/createDialog.js new file mode 100644 index 000000000..ecd7591cb --- /dev/null +++ b/tests/k6/tests/performancetest_common/createDialog.js @@ -0,0 +1,61 @@ +/** + * Common functions for creating dialogs. + */ +import { uuidv4 } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js'; +import { describe } from "../../common/describe.js"; +import { postSO, purgeSO } from "../../common/request.js"; +import { expect } from "../../common/testimports.js"; +import dialogToInsert from "../performancetest_data/01-create-dialog.js"; + +/** + * Creates a dialog. + * + * @param {Object} serviceOwner - The service owner object. + * @param {Object} endUser - The end user object. + */ +export function createDialog(serviceOwner, endUser) { + var paramsWithToken = { + headers: { + Authorization: "Bearer " + serviceOwner.token, + traceparent: uuidv4() + }, + tags: { name: 'create dialog' } + }; + + describe('create dialog', () => { + let r = postSO('dialogs', dialogToInsert(endUser.ssn, endUser.resource), paramsWithToken); + expect(r.status, 'response status').to.equal(201); + }); + +} + +/** + * Creates a dialog and removes it. + * + * @param {Object} serviceOwner - The service owner object. + * @param {Object} endUser - The end user object. + */ +export function createAndRemoveDialog(serviceOwner, endUser) { + var paramsWithToken = { + headers: { + Authorization: "Bearer " + serviceOwner.token + }, + tags: { name: 'create dialog' } + } + + let dialogId = 0; + describe('create dialog', () => { + paramsWithToken.tags.name = 'create dialog'; + let r = postSO('dialogs', dialogToInsert(endUser.ssn, endUser.resource), paramsWithToken); + expect(r.status, 'response status').to.equal(201); + dialogId = r.json(); + }); + + describe('remove dialog', () => { + paramsWithToken.tags.name = 'remove dialog'; + if (dialogId) { + let r = purgeSO('dialogs/' + dialogId, paramsWithToken); + expect(r.status, 'response status').to.equal(204); + } + }); +} diff --git a/tests/k6/tests/performancetest_common/getDefaultThresholds.js b/tests/k6/tests/performancetest_common/getDefaultThresholds.js new file mode 100644 index 000000000..12b912711 --- /dev/null +++ b/tests/k6/tests/performancetest_common/getDefaultThresholds.js @@ -0,0 +1,21 @@ +/** + * Creates default thresholds configuration for K6 tests. + * @param {string[]} counters - Array of counter names + * @param {string[]} labels - Array of label names + * @returns {Object} Threshold configuration object + * @throws {Error} If inputs are invalid + */ +export function getDefaultThresholds(counters, labels) { + if (!Array.isArray(counters) || !Array.isArray(labels)) { + throw new Error('Both counters and labels must be arrays'); + } + let thresholds = { + http_req_failed: ['rate<0.01'] + }; + for (const counter of counters) { + for (const label of labels) { + thresholds[`${counter}{name:${label}}`] = []; + } + } + return thresholds; +} diff --git a/tests/k6/tests/performancetest_common/readTestdata.js b/tests/k6/tests/performancetest_common/readTestdata.js new file mode 100644 index 000000000..0fbea3624 --- /dev/null +++ b/tests/k6/tests/performancetest_common/readTestdata.js @@ -0,0 +1,52 @@ +/** + * This file contains the implementation of reading test data from CSV files. + * The test data includes service owners, end users, and end users with tokens. + * The data is read using the PapaParse library and stored in SharedArray variables. + * + * @module readTestdata + */ + +import papaparse from 'https://jslib.k6.io/papaparse/5.1.1/index.js'; +import { SharedArray } from "k6/data"; + +const filenameServiceowners = '../performancetest_data/.serviceowners-with-tokens.csv'; +if (!__ENV.API_ENVIRONMENT) { + throw new Error('API_ENVIRONMENT must be set'); +} +const filenameEndusers = `../performancetest_data/endusers-${__ENV.API_ENVIRONMENT}.csv`; +const filenameEndusersWithTokens = '../performancetest_data/.endusers-with-tokens.csv'; + +/** + * SharedArray variable that stores the service owners data. + * The data is parsed from the CSV file specified by the filenameServiceowners variable. + * + * @name serviceOwners + * @type {SharedArray} + */ +export const serviceOwners = new SharedArray('serviceOwners', function () { + return papaparse.parse(open(filenameServiceowners), { header: true, skipEmptyLines: true }).data; +}); + +/** + * SharedArray variable that stores the end users data. + * The data is parsed from the CSV file specified by the filenameEndusers variable. + * The filenameEndusers variable is dynamically generated based on the value of the API_ENVIRONMENT environment variable. + * + * @name endUsers + * @type {SharedArray} + */ +export const endUsers = new SharedArray('endUsers', function () { + return papaparse.parse(open(filenameEndusers), { header: true, skipEmptyLines: true }).data; +}); + +/** + * SharedArray variable that stores the end users with tokens data. + * The data is parsed from the CSV file specified by the filenameEndusersWithTokens variable. + * + * @name endUsersWithTokens + * @type {SharedArray} + */ +export const endUsersWithTokens = new SharedArray('endUsersWithTokens', function () { + return papaparse.parse(open(filenameEndusersWithTokens), { header: true, skipEmptyLines: true }).data; +}); + diff --git a/tests/k6/tests/performancetest_common/simpleSearch.js b/tests/k6/tests/performancetest_common/simpleSearch.js new file mode 100644 index 000000000..8feb8ef37 --- /dev/null +++ b/tests/k6/tests/performancetest_common/simpleSearch.js @@ -0,0 +1,129 @@ +/** + * This file contains common functions for performing simple searches + * and GraphQL searches. + */ +import { randomItem, uuidv4 } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js'; +import { expect, expectStatusFor } from "../../common/testimports.js"; +import { describe } from '../../common/describe.js'; +import { getEU, postGQ } from '../../common/request.js'; +import { getGraphqlParty } from '../performancetest_data/graphql-search.js'; + + +/** + * Retrieves the content for a dialog. + * Get dialog, dialog activities, seenlogs, labellog, and transmissions. + * @param {Object} response - The response object. + * @param {Object} paramsWithToken - The parameters with token. + * @returns {void} + */ +function retrieveDialogContent(response, paramsWithToken) { + const items = response.json().items; + if (!items?.length) return; + + const dialogId = items[0].id; + if (!dialogId) return; + + getContent(dialogId, paramsWithToken, 'get dialog'); + getContentChain(dialogId, paramsWithToken, 'get dialog activities', 'get dialog activity', '/activities/') + getContentChain(dialogId, paramsWithToken, 'get seenlogs', 'get seenlog', '/seenlog/') + getContent(dialogId, paramsWithToken, 'get labellog', '/labellog'); + getContentChain(dialogId, paramsWithToken, 'get transmissions', 'get transmission', '/transmissions/') +} + +/** + * Performs a simple search. + * @param {Object} enduser - The end user. + * @returns {void} + */ +export function enduserSearch(enduser) { + let paramsWithToken = { + headers: { + Authorization: "Bearer " + enduser.token, + traceparent: uuidv4() + }, + tags: { name: 'enduser search' } + } + let defaultParty = "urn:altinn:person:identifier-no:" + enduser.ssn; + let defaultFilter = "?Party=" + defaultParty; + describe('Perform enduser dialog list', () => { + let r = getEU('dialogs' + defaultFilter, paramsWithToken); + expectStatusFor(r).to.equal(200); + expect(r, 'response').to.have.validJsonBody(); + retrieveDialogContent(r, paramsWithToken); + }); +} + +/** + * Performs a enduser search. + * @param {string} dialogId - The dialog id. + * @param {Object} paramsWithToken - The parameters with token. + * @param {string} tag - Tagging the request. + * @param {string} path - The path to append to the URL. Can be empty or /labellog. + * @returns {void} + */ +export function getContent(dialogId, paramsWithToken, tag, path = '') { + const listParams = { + ...paramsWithToken, + tags: { ...paramsWithToken.tags, name: tag } + }; + getUrl('dialogs/' + dialogId + path, listParams); +} + +/** + * Retrieves the content chain. + * @param {string} dialogId - The dialog id. + * @param {Object} paramsWithToken - The parameters with token. + * @param {string} tag - Tagging the request. + * @param {string} subtag - Tagging the sub request. + * @param {string} endpoint - The endpoint to append to the URL. + * @returns {void} + */ +export function getContentChain(dialogId, paramsWithToken, tag, subtag, endpoint) { + const listParams = { + ...paramsWithToken, + tags: { ...paramsWithToken.tags, name: tag } + }; + let d = getUrl('dialogs/' + dialogId + endpoint, listParams); + let json = d.json(); + if (json.length > 0) { + const detailParams = { + ...paramsWithToken, + tags: { ...paramsWithToken.tags, name: subtag } + }; + getUrl('dialogs/' + dialogId + endpoint + randomItem(json).id, detailParams); + } +} + +/** + * Performs a GET request to the specified URL with the provided parameters. + * @param {string} url - The URL to send the GET request to. + * @param {Object} paramsWithToken - The parameters with token. + * @returns {Object} The response object. + */ +export function getUrl(url, paramsWithToken) { + let r = getEU(url, paramsWithToken); + expectStatusFor(r).to.equal(200); + expect(r, 'response').to.have.validJsonBody(); + return r; +} + +/** + * Performs a GraphQL search using the provided enduser token. + * + * @param {Object} enduser - The enduser object containing the token. + * @returns {void} + */ +export function graphqlSearch(enduser) { + let paramsWithToken = { + headers: { + Authorization: "Bearer " + enduser.token, + traceparent: uuidv4() + }, + tags: { name: 'graphql search' } + }; + describe('Perform graphql dialog list', () => { + let r = postGQ(getGraphqlParty(enduser.ssn), paramsWithToken); + expectStatusFor(r).to.equal(200); + expect(r, 'response').to.have.validJsonBody(); + }); +} diff --git a/tests/k6/tests/performancetest_data/01-create-dialog.js b/tests/k6/tests/performancetest_data/01-create-dialog.js new file mode 100644 index 000000000..a5e994cb8 --- /dev/null +++ b/tests/k6/tests/performancetest_data/01-create-dialog.js @@ -0,0 +1,54 @@ +import {default as createDialogPayload} from "../serviceowner/testdata/01-create-dialog.js" + +const ACTIVITY_TYPE_INFORMATION = 'Information'; + +function cleanUp(originalPayload) { + if (!originalPayload || typeof originalPayload !== 'object') { + throw new Error('Invalid payload'); + } + + const payload = { ...originalPayload }; + const { visibleFrom, ...payloadWithoutVisibleFrom } = payload; + + const activities = payload.activities?.map(activity => { + if (activity.type !== ACTIVITY_TYPE_INFORMATION) { + return activity; + } + + const { performedBy, ...rest } = activity; + const { actorId, ...performedByRest } = performedBy; + + return { + ...rest, + performedBy: { + ...performedByRest, + actorName: "some name" + } + }; + }) ?? []; + + return { + ...payloadWithoutVisibleFrom, + activities + }; +} + +/** + * Creates a dialog payload for performance testing + * @param {string} endUser - Norwegian national ID number (11 digits) + * @param {string} resource - Resource identifier + * @returns {Object} Dialog payload + * @throws {Error} If inputs are invalid + */ +export default function (endUser, resource) { + if (!endUser?.match(/^\d{11}$/)) { + throw new Error('endUser must be a 11-digit number'); + } + if (!resource?.trim()) { + throw new Error('resource is required'); + } + let payload = createDialogPayload(); + payload.serviceResource = "urn:altinn:resource:" +resource; + payload.party = "urn:altinn:person:identifier-no:" + endUser; + return cleanUp(payload); +} diff --git a/tests/k6/tests/performancetest_data/endusers-yt01.csv b/tests/k6/tests/performancetest_data/endusers-yt01.csv new file mode 100644 index 000000000..c5d6abd02 --- /dev/null +++ b/tests/k6/tests/performancetest_data/endusers-yt01.csv @@ -0,0 +1,73 @@ +ssn,resource,scopes +14886498226,ttd-dialogporten-performance-test-01,digdir:dialogporten +10865299538,ttd-dialogporten-performance-test-01,digdir:dialogporten +19886497337,ttd-dialogporten-performance-test-01,digdir:dialogporten +29878198024,ttd-dialogporten-performance-test-01,digdir:dialogporten +28826498708,ttd-dialogporten-performance-test-01,digdir:dialogporten +10867196183,ttd-dialogporten-performance-test-01,digdir:dialogporten +08837297959,ttd-dialogporten-performance-test-01,digdir:dialogporten +02818596203,ttd-dialogporten-performance-test-01,digdir:dialogporten +19866498574,ttd-dialogporten-performance-test-01,digdir:dialogporten +26916397126,ttd-dialogporten-performance-test-01,digdir:dialogporten +19815997363,ttd-dialogporten-performance-test-01,digdir:dialogporten +11897397503,ttd-dialogporten-performance-test-01,digdir:dialogporten +27845299582,ttd-dialogporten-performance-test-01,digdir:dialogporten +04825997135,ttd-dialogporten-performance-test-01,digdir:dialogporten +07907197896,ttd-dialogporten-performance-test-01,digdir:dialogporten +28827097898,ttd-dialogporten-performance-test-01,digdir:dialogporten +31905999977,ttd-dialogporten-performance-test-01,digdir:dialogporten +02834699772,ttd-dialogporten-performance-test-01,digdir:dialogporten +07876497993,ttd-dialogporten-performance-test-01,digdir:dialogporten +22929874319,ttd-dialogporten-performance-test-01,digdir:dialogporten +27917298128,ttd-dialogporten-performance-test-01,digdir:dialogporten +01846698058,ttd-dialogporten-performance-test-01,digdir:dialogporten +03866096766,ttd-dialogporten-performance-test-01,digdir:dialogporten +03885996786,ttd-dialogporten-performance-test-01,digdir:dialogporten +25926298626,ttd-dialogporten-performance-test-01,digdir:dialogporten +12826398025,ttd-dialogporten-performance-test-01,digdir:dialogporten +14858095353,ttd-dialogporten-performance-test-01,digdir:dialogporten +24913649709,ttd-dialogporten-performance-test-01,digdir:dialogporten +22852749311,ttd-dialogporten-performance-test-01,digdir:dialogporten +07926198712,ttd-dialogporten-performance-test-01,digdir:dialogporten +03876498730,ttd-dialogporten-performance-test-01,digdir:dialogporten +08856299083,ttd-dialogporten-performance-test-01,digdir:dialogporten +01884099192,ttd-dialogporten-performance-test-01,digdir:dialogporten +18826599975,ttd-dialogporten-performance-test-01,digdir:dialogporten +23894899573,ttd-dialogporten-performance-test-01,digdir:dialogporten +05906599602,ttd-dialogporten-performance-test-01,digdir:dialogporten +02826198799,ttd-dialogporten-performance-test-01,digdir:dialogporten +06906497962,ttd-dialogporten-performance-test-01,digdir:dialogporten +12856395543,ttd-dialogporten-performance-test-01,digdir:dialogporten +06857897119,ttd-dialogporten-performance-test-01,digdir:dialogporten +06876599986,ttd-dialogporten-performance-test-01,digdir:dialogporten +24878297780,ttd-dialogporten-performance-test-01,digdir:dialogporten +14926297903,ttd-dialogporten-performance-test-01,digdir:dialogporten +22909398049,ttd-dialogporten-performance-test-01,digdir:dialogporten +07905398150,ttd-dialogporten-performance-test-01,digdir:dialogporten +11824596141,ttd-dialogporten-performance-test-01,digdir:dialogporten +05887496988,ttd-dialogporten-performance-test-01,digdir:dialogporten +28896796951,ttd-dialogporten-performance-test-01,digdir:dialogporten +19825998147,ttd-dialogporten-performance-test-01,digdir:dialogporten +01917196806,ttd-dialogporten-performance-test-01,digdir:dialogporten +10918397944,ttd-dialogporten-performance-test-01,digdir:dialogporten +03844797469,ttd-dialogporten-performance-test-01,digdir:dialogporten +25862849763,ttd-dialogporten-performance-test-01,digdir:dialogporten +23835399729,ttd-dialogporten-performance-test-01,digdir:dialogporten +09886998144,ttd-dialogporten-performance-test-01,digdir:dialogporten +26897799382,ttd-dialogporten-performance-test-01,digdir:dialogporten +27866897323,ttd-dialogporten-performance-test-01,digdir:dialogporten +11854397992,ttd-dialogporten-performance-test-01,digdir:dialogporten +04848197152,ttd-dialogporten-performance-test-01,digdir:dialogporten +21858699425,ttd-dialogporten-performance-test-01,digdir:dialogporten +13899798567,ttd-dialogporten-performance-test-01,digdir:dialogporten +30905596574,ttd-dialogporten-performance-test-01,digdir:dialogporten +18826698779,ttd-dialogporten-performance-test-01,digdir:dialogporten +07876099769,ttd-dialogporten-performance-test-01,digdir:dialogporten +25887899889,ttd-dialogporten-performance-test-01,digdir:dialogporten +01905999954,ttd-dialogporten-performance-test-01,digdir:dialogporten +24826296980,ttd-dialogporten-performance-test-01,digdir:dialogporten +06886696920,ttd-dialogporten-performance-test-01,digdir:dialogporten +26877199125,ttd-dialogporten-performance-test-01,digdir:dialogporten +15917599510,ttd-dialogporten-performance-test-01,digdir:dialogporten +16896795523,ttd-dialogporten-performance-test-01,digdir:dialogporten +21849198170,ttd-dialogporten-performance-test-01,digdir:dialogporten \ No newline at end of file diff --git a/tests/k6/tests/performancetest_data/graphql-search.js b/tests/k6/tests/performancetest_data/graphql-search.js new file mode 100644 index 000000000..c7e1901df --- /dev/null +++ b/tests/k6/tests/performancetest_data/graphql-search.js @@ -0,0 +1,70 @@ +export function getGraphqlParty(identifier) { + return ` + query getAllDialogsForParties { + searchDialogs(input: { party: ["urn:altinn:person:identifier-no:${identifier}"]}) { + items { + id + party + org + progress + guiAttachmentCount + status + createdAt + updatedAt + extendedStatus + seenSinceLastUpdate { + id + seenAt + seenBy { + actorType + actorId + actorName + } + isCurrentEndUser + } + latestActivity { + description { + value + languageCode + } + performedBy { + actorType + actorId + actorName + } + } + content { + title { + mediaType + value { + value + languageCode + } + } + summary { + mediaType + value { + value + languageCode + } + } + senderName { + mediaType + value { + value + languageCode + } + } + extendedStatus { + mediaType + value { + value + languageCode + } + } + } + systemLabel + } + } + }` +} \ No newline at end of file diff --git a/tests/k6/tests/performancetest_data/serviceowners-yt01.csv b/tests/k6/tests/performancetest_data/serviceowners-yt01.csv new file mode 100644 index 000000000..449a5e7c6 --- /dev/null +++ b/tests/k6/tests/performancetest_data/serviceowners-yt01.csv @@ -0,0 +1,2 @@ +org,orgno,scopes,resource +digdir,991825827,digdir:dialogporten.serviceprovider,super-simple-service diff --git a/tests/k6/tests/scenarios/performance/create-dialog-and-search.js b/tests/k6/tests/scenarios/performance/create-dialog-and-search.js new file mode 100644 index 000000000..8329e7402 --- /dev/null +++ b/tests/k6/tests/scenarios/performance/create-dialog-and-search.js @@ -0,0 +1,65 @@ +/** + * Performance test for creating dialogs and searching dialogs. + * Run: k6 run tests/k6/tests/scenarios/performance/create-dialog-and-search.js -e env=yt01 -e svus=1 -e evus=1 -e duration=1m + */ +import { randomItem } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; +import { enduserSearch } from '../../performancetest_common/simpleSearch.js'; +import { createDialog } from '../../performancetest_common/createDialog.js'; +import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js'; +import { serviceOwners, endUsersWithTokens } from '../../performancetest_common/readTestdata.js'; + + +/** + * Options for performance scenarios. + * + * @typedef {Object} Options + * @property {Object} scenarios - The performance scenarios. + * @property {Object} summaryTrendStats - The summary trend statistics. + * @property {Object} thresholds - The thresholds for performance metrics. + */ + +/** + * Performance options. + * + * @type {Options} + */ +export const options = { + scenarios: { + create_dialogs: { + executor: 'constant-vus', + tags: { name: 'create dialog'}, + exec: 'createDialogs', + vus: __ENV.svus, + duration: __ENV.duration + }, + simple_search: { + executor: 'constant-vus', + tags: { name: 'search dialogs'}, + exec: 'enduserSearches', + vus: __ENV.evus, + duration: __ENV.duration, + } + }, + summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'], + thresholds: getDefaultThresholds(['http_req_duration', 'http_reqs'],[ + 'simple search', + 'create dialog', + 'get dialog', + 'get dialog activities', + 'get dialog activity', + 'get seenlogs', + 'get seenlog', + 'get transmissions', + 'get transmission', + 'get labellog' + ]) + +}; + +export function createDialogs() { + createDialog(randomItem(serviceOwners), randomItem(endUsersWithTokens)); +} + +export function enduserSearches() { + enduserSearch(randomItem(endUsersWithTokens)); +} \ No newline at end of file diff --git a/tests/k6/tests/scripts/generate_tokens.sh b/tests/k6/tests/scripts/generate_tokens.sh index 06df1ab24..353587043 100755 --- a/tests/k6/tests/scripts/generate_tokens.sh +++ b/tests/k6/tests/scripts/generate_tokens.sh @@ -28,7 +28,7 @@ case $API_ENVIRONMENT in env="at21" ;; "staging") env="tt02" ;; - "performance") + "yt01") env="yt01" ;; *) echo "Error: Unknown api environment $API_ENVIRONMENT" diff --git a/tests/k6/tests/serviceowner/performance/create-dialog.js b/tests/k6/tests/serviceowner/performance/create-dialog.js index 60aef04e9..df51bde99 100644 --- a/tests/k6/tests/serviceowner/performance/create-dialog.js +++ b/tests/k6/tests/serviceowner/performance/create-dialog.js @@ -1,27 +1,15 @@ -import { postSO, expect, describe } from "../../../common/testimports.js"; -import { SharedArray } from 'k6/data'; -import papaparse from 'https://jslib.k6.io/papaparse/5.1.1/index.js'; -import { default as dialogToInsert } from '../testdata/01-create-dialog.js'; +/** + * Performance test for creating a dialog + * Run: k6 run tests/k6/tests/serviceowner/performance/create-dialog.js --vus 1 --iterations 1 + */ import { randomItem } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; - -const filenameServiceowners = '../../performancetest_data/.serviceowners-with-tokens.csv'; -const filenameEndusers = `../../performancetest_data/endusers-${__ENV.API_ENVIRONMENT}.csv`; - -const serviceOwners = new SharedArray('serviceOwners', function () { - return papaparse.parse(open(filenameServiceowners), { header: true, skipEmptyLines: true }).data; -}); - -const endUsers = new SharedArray('endUsers', function () { - return papaparse.parse(open(filenameEndusers), { header: true, skipEmptyLines: true }).data; - }); +import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js'; +import { createDialog } from '../../performancetest_common/createDialog.js'; +import { serviceOwners, endUsers } from '../../performancetest_common/readTestdata.js'; export let options = { summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'], - thresholds: { - 'http_req_duration{scenario:default}': [`max>=0`], - 'http_req_duration{name:create dialog}': [], - 'http_reqs{name:create dialog}': [], - }, + thresholds: getDefaultThresholds(['http_req_duration', 'http_reqs'],['create dialog']) }; export default function() { @@ -33,17 +21,3 @@ export default function() { } } -export function createDialog(serviceOwner, endUser) { - var paramsWithToken = { - headers: { - Authorization: "Bearer " + serviceOwner.token - }, - tags: { name: 'create dialog' } - } - - describe('create dialog', () => { - let r = postSO('dialogs', dialogToInsert(endUser.ssn), paramsWithToken); - expect(r.status, 'response status').to.equal(201); - }); - -} \ No newline at end of file diff --git a/tests/k6/tests/serviceowner/performance/create-remove-dialog.js b/tests/k6/tests/serviceowner/performance/create-remove-dialog.js new file mode 100644 index 000000000..544f40d20 --- /dev/null +++ b/tests/k6/tests/serviceowner/performance/create-remove-dialog.js @@ -0,0 +1,25 @@ +/** + * Performance test for creating and removing a dialog + * Run: k6 run tests/k6/tests/serviceowner/performance/create-remove-dialog.js --vus 1 --iterations 1 + */ +import { randomItem } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; +import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js'; +import { serviceOwners, endUsers } from "../../performancetest_common/readTestdata.js"; +import { createAndRemoveDialog } from '../../performancetest_common/createDialog.js'; + +export let options = { + summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'], + thresholds: getDefaultThresholds(['http_req_duration', 'http_reqs'],[ + 'create dialog', + 'remove dialog' + ]) +}; + +export default function() { + if ((options.vus === undefined || options.vus === 1) && (options.iterations === undefined || options.iterations === 1)) { + createAndRemoveDialog(serviceOwners[0], endUsers[0]); + } + else { + createAndRemoveDialog(randomItem(serviceOwners), randomItem(endUsers)); + } + } From e0ea0afad3a62cf67b495c68405eb420586f80a3 Mon Sep 17 00:00:00 2001 From: Dagfinn Olsen Date: Wed, 6 Nov 2024 10:09:35 +0100 Subject: [PATCH 03/19] fix: avoid crash if testdata file is empty (#1403) return an empty array if testdatafile not found, check for empty testdata in k6 tests ## Description ## Related Issue(s) - #1402 ## Verification - [ ] **Your** code builds clean without any errors or warnings - [ ] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] 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) ## Summary by CodeRabbit - **New Features** - Introduced error handling in various performance testing scripts to ensure required data arrays are populated before execution. - Added a new function for reading and parsing CSV files, improving error handling during file operations. - **Bug Fixes** - Enhanced robustness of performance tests by preventing execution with empty datasets, which could lead to runtime errors. --- .../enduser/performance/enduser-search.js | 3 +++ .../performancetest_common/readTestdata.js | 20 ++++++++++++++++--- .../performance/create-dialog-and-search.js | 13 ++++++++++-- .../serviceowner/performance/create-dialog.js | 6 ++++++ .../performance/create-remove-dialog.js | 6 ++++++ 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/tests/k6/tests/enduser/performance/enduser-search.js b/tests/k6/tests/enduser/performance/enduser-search.js index 360d8e7d3..d32d2ba1d 100644 --- a/tests/k6/tests/enduser/performance/enduser-search.js +++ b/tests/k6/tests/enduser/performance/enduser-search.js @@ -17,6 +17,9 @@ export let options = { }; export default function() { + if (!endUsersWithTokens || endUsersWithTokens.length === 0) { + throw new Error('No end users loaded for testing'); + } if ((options.vus === undefined || options.vus === 1) && (options.iterations === undefined || options.iterations === 1)) { enduserSearch(endUsersWithTokens[0]); } diff --git a/tests/k6/tests/performancetest_common/readTestdata.js b/tests/k6/tests/performancetest_common/readTestdata.js index 0fbea3624..538c292a3 100644 --- a/tests/k6/tests/performancetest_common/readTestdata.js +++ b/tests/k6/tests/performancetest_common/readTestdata.js @@ -9,6 +9,20 @@ import papaparse from 'https://jslib.k6.io/papaparse/5.1.1/index.js'; import { SharedArray } from "k6/data"; +/** + * Function to read the CSV file specified by the filename parameter. + * @param {} filename + * @returns + */ +function readCsv(filename) { + try { + return papaparse.parse(open(filename), { header: true, skipEmptyLines: true }).data; + } catch (error) { + console.log(`Error reading CSV file: ${error}`); + return []; + } +} + const filenameServiceowners = '../performancetest_data/.serviceowners-with-tokens.csv'; if (!__ENV.API_ENVIRONMENT) { throw new Error('API_ENVIRONMENT must be set'); @@ -24,7 +38,7 @@ const filenameEndusersWithTokens = '../performancetest_data/.endusers-with-token * @type {SharedArray} */ export const serviceOwners = new SharedArray('serviceOwners', function () { - return papaparse.parse(open(filenameServiceowners), { header: true, skipEmptyLines: true }).data; + return readCsv(filenameServiceowners); }); /** @@ -36,7 +50,7 @@ export const serviceOwners = new SharedArray('serviceOwners', function () { * @type {SharedArray} */ export const endUsers = new SharedArray('endUsers', function () { - return papaparse.parse(open(filenameEndusers), { header: true, skipEmptyLines: true }).data; + return readCsv(filenameEndusers); }); /** @@ -47,6 +61,6 @@ export const endUsers = new SharedArray('endUsers', function () { * @type {SharedArray} */ export const endUsersWithTokens = new SharedArray('endUsersWithTokens', function () { - return papaparse.parse(open(filenameEndusersWithTokens), { header: true, skipEmptyLines: true }).data; + return readCsv(filenameEndusersWithTokens); }); diff --git a/tests/k6/tests/scenarios/performance/create-dialog-and-search.js b/tests/k6/tests/scenarios/performance/create-dialog-and-search.js index 8329e7402..e9d0dd679 100644 --- a/tests/k6/tests/scenarios/performance/create-dialog-and-search.js +++ b/tests/k6/tests/scenarios/performance/create-dialog-and-search.js @@ -57,9 +57,18 @@ export const options = { }; export function createDialogs() { - createDialog(randomItem(serviceOwners), randomItem(endUsersWithTokens)); + if (!endUsersWithTokens || endUsersWithTokens.length === 0) { + throw new Error('No end users loaded for testing'); + } + if (!serviceOwners || serviceOwners.length === 0) { + throw new Error('No service owners loaded for testing'); + } + createDialog(randomItem(serviceOwners), randomItem(endUsersWithTokens)); } export function enduserSearches() { - enduserSearch(randomItem(endUsersWithTokens)); + if (!endUsersWithTokens || endUsersWithTokens.length === 0) { + throw new Error('No end users loaded for testing'); + } + enduserSearch(randomItem(endUsersWithTokens)); } \ No newline at end of file diff --git a/tests/k6/tests/serviceowner/performance/create-dialog.js b/tests/k6/tests/serviceowner/performance/create-dialog.js index df51bde99..03cbadbc5 100644 --- a/tests/k6/tests/serviceowner/performance/create-dialog.js +++ b/tests/k6/tests/serviceowner/performance/create-dialog.js @@ -13,6 +13,12 @@ export let options = { }; export default function() { + if (!endUsers || endUsers.length === 0) { + throw new Error('No end users loaded for testing'); + } + if (!serviceOwners || serviceOwners.length === 0) { + throw new Error('No service owners loaded for testing'); + } if ((options.vus === undefined || options.vus === 1) && (options.iterations === undefined || options.iterations === 1)) { createDialog(serviceOwners[0], endUsers[0]); } diff --git a/tests/k6/tests/serviceowner/performance/create-remove-dialog.js b/tests/k6/tests/serviceowner/performance/create-remove-dialog.js index 544f40d20..e46134a8a 100644 --- a/tests/k6/tests/serviceowner/performance/create-remove-dialog.js +++ b/tests/k6/tests/serviceowner/performance/create-remove-dialog.js @@ -16,6 +16,12 @@ export let options = { }; export default function() { + if (!endUsers || endUsers.length === 0) { + throw new Error('No end users loaded for testing'); + } + if (!serviceOwners || serviceOwners.length === 0) { + throw new Error('No service owners loaded for testing'); + } if ((options.vus === undefined || options.vus === 1) && (options.iterations === undefined || options.iterations === 1)) { createAndRemoveDialog(serviceOwners[0], endUsers[0]); } From 117c707c5160c31352087839f1a2fb064bcb0994 Mon Sep 17 00:00:00 2001 From: Dialogporten Automation Bot <164321870+dialogporten-bot@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:17:39 +0100 Subject: [PATCH 04/19] chore(main): release 1.28.3 (#1404) :robot: I have created a release *beep* *boop* --- ## [1.28.3](https://github.com/digdir/dialogporten/compare/v1.28.2...v1.28.3) (2024-11-06) ### Bug Fixes * avoid crash if testdata file is empty ([#1403](https://github.com/digdir/dialogporten/issues/1403)) ([e0ea0af](https://github.com/digdir/dialogporten/commit/e0ea0afad3a62cf67b495c68405eb420586f80a3)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- CHANGELOG.md | 7 +++++++ version.txt | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f5dbeefc..4594113b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.28.3](https://github.com/digdir/dialogporten/compare/v1.28.2...v1.28.3) (2024-11-06) + + +### Bug Fixes + +* avoid crash if testdata file is empty ([#1403](https://github.com/digdir/dialogporten/issues/1403)) ([e0ea0af](https://github.com/digdir/dialogporten/commit/e0ea0afad3a62cf67b495c68405eb420586f80a3)) + ## [1.28.2](https://github.com/digdir/dialogporten/compare/v1.28.1...v1.28.2) (2024-11-05) diff --git a/version.txt b/version.txt index bf4df28ef..ac786b645 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.28.2 +1.28.3 From 920d7493d09e551a4207f61636a7188fea490223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Wed, 6 Nov 2024 12:44:42 +0100 Subject: [PATCH 05/19] feat(webAPI): Make all lists nullable in OpenAPI schema (#1359) ## Description ## Related Issue(s) - #1355 ## Verification - [ ] **Your** code builds clean without any errors or warnings - [ ] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] 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) ## Summary by CodeRabbit - **New Features** - Updated API specifications to allow properties to be nullable, enhancing flexibility in handling optional data. - Introduced a method to make array-type properties nullable in OpenAPI document schemas. - **Bug Fixes** - Updated serialization logic to include empty collections during data serialization, affecting the output of serialized data. - **Tests** - Enhanced assertion logic in integration tests to require closer matches for `DateTimeOffset` types. - Modified dialog retrieval tests to no longer exclude missing members in assertions, improving accuracy in test validations. - Refined response handling in dialog search tests for better clarity and accuracy in expectations. --- docs/schema/V1/swagger.verified.json | 84 ++++++++++++++++++- .../OpenApiDocumentExtensions.cs | 24 ++++++ .../Program.cs | 1 + .../Common/DialogApplication.cs | 1 - .../Dialogs/Queries/GetDialogTests.cs | 2 - tests/k6/tests/serviceowner/dialogSearch.js | 2 +- 6 files changed, 109 insertions(+), 5 deletions(-) diff --git a/docs/schema/V1/swagger.verified.json b/docs/schema/V1/swagger.verified.json index a27f1abdf..f21ef98b5 100644 --- a/docs/schema/V1/swagger.verified.json +++ b/docs/schema/V1/swagger.verified.json @@ -211,6 +211,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogsQueriesSearch_Dialog" }, + "nullable": true, "type": "array" }, "orderBy": { @@ -239,6 +240,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesSearch_Dialog" }, + "nullable": true, "type": "array" }, "orderBy": { @@ -260,6 +262,7 @@ "items": { "$ref": "#/components/schemas/ProblemDetails_Error" }, + "nullable": true, "type": "array" }, "instance": { @@ -320,6 +323,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" } }, @@ -352,6 +356,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "extendedType": { @@ -604,6 +609,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogsQueriesGet_DialogActivity" }, + "nullable": true, "type": "array" }, "apiActions": { @@ -611,6 +617,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogsQueriesGet_DialogApiAction" }, + "nullable": true, "type": "array" }, "attachments": { @@ -618,6 +625,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogsQueriesGet_DialogAttachment" }, + "nullable": true, "type": "array" }, "content": { @@ -668,6 +676,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogsQueriesGet_DialogGuiAction" }, + "nullable": true, "type": "array" }, "id": { @@ -713,6 +722,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogsQueriesGet_DialogSeenLog" }, + "nullable": true, "type": "array" }, "serviceResource": { @@ -745,6 +755,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogsQueriesGet_DialogTransmission" }, + "nullable": true, "type": "array" }, "updatedAt": { @@ -770,6 +781,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "extendedType": { @@ -851,6 +863,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogsQueriesGet_DialogApiActionEndpoint" }, + "nullable": true, "type": "array" }, "id": { @@ -931,6 +944,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "id": { @@ -943,6 +957,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogsQueriesGet_DialogAttachmentUrl" }, + "nullable": true, "type": "array" } }, @@ -1034,6 +1049,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "url": { @@ -1101,6 +1117,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogsQueriesGet_DialogTransmissionAttachment" }, + "nullable": true, "type": "array" }, "authorizationAttribute": { @@ -1170,6 +1187,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "id": { @@ -1182,6 +1200,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogsQueriesGet_DialogTransmissionAttachmentUrl" }, + "nullable": true, "type": "array" } }, @@ -1394,6 +1413,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogsQueriesSearch_DialogSeenLog" }, + "nullable": true, "type": "array" }, "serviceResource": { @@ -1444,6 +1464,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "extendedType": { @@ -1576,6 +1597,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "id": { @@ -1588,6 +1610,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogTransmissionsQueriesGet_AttachmentUrl" }, + "nullable": true, "type": "array" } }, @@ -1690,6 +1713,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogTransmissionsQueriesGet_Attachment" }, + "nullable": true, "type": "array" }, "authorizationAttribute": { @@ -1764,6 +1788,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "id": { @@ -1776,6 +1801,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogTransmissionsQueriesSearch_AttachmentUrl" }, + "nullable": true, "type": "array" } }, @@ -1878,6 +1904,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserDialogTransmissionsQueriesSearch_Attachment" }, + "nullable": true, "type": "array" }, "authorizationAttribute": { @@ -1991,6 +2018,7 @@ "items": { "$ref": "#/components/schemas/V1EndUserPartiesQueriesGet_AuthorizedParty" }, + "nullable": true, "type": "array" } }, @@ -2010,6 +2038,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "extendedType": { @@ -2067,6 +2096,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "extendedType": { @@ -2180,6 +2210,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "extendedType": { @@ -2265,6 +2296,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsCreate_ApiActionEndpoint" }, + "nullable": true, "type": "array" } }, @@ -2330,6 +2362,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "urls": { @@ -2337,6 +2370,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsCreate_AttachmentUrl" }, + "nullable": true, "type": "array" } }, @@ -2433,6 +2467,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsCreate_Activity" }, + "nullable": true, "type": "array" }, "apiActions": { @@ -2440,6 +2475,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsCreate_ApiAction" }, + "nullable": true, "type": "array" }, "attachments": { @@ -2447,6 +2483,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsCreate_Attachment" }, + "nullable": true, "type": "array" }, "content": { @@ -2492,6 +2529,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsCreate_GuiAction" }, + "nullable": true, "type": "array" }, "id": { @@ -2527,6 +2565,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsCreate_SearchTag" }, + "nullable": true, "type": "array" }, "serviceResource": { @@ -2556,6 +2595,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsCreate_Transmission" }, + "nullable": true, "type": "array" }, "updatedAt": { @@ -2621,6 +2661,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "url": { @@ -2650,6 +2691,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsCreate_TransmissionAttachment" }, + "nullable": true, "type": "array" }, "authorizationAttribute": { @@ -2717,6 +2759,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "id": { @@ -2731,6 +2774,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsCreate_TransmissionAttachmentUrl" }, + "nullable": true, "type": "array" } }, @@ -2832,6 +2876,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "extendedType": { @@ -2917,6 +2962,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_ApiActionEndpoint" }, + "nullable": true, "type": "array" }, "id": { @@ -2996,6 +3042,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "id": { @@ -3010,6 +3057,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_AttachmentUrl" }, + "nullable": true, "type": "array" } }, @@ -3113,6 +3161,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_Activity" }, + "nullable": true, "type": "array" }, "apiActions": { @@ -3120,6 +3169,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_ApiAction" }, + "nullable": true, "type": "array" }, "attachments": { @@ -3127,6 +3177,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_Attachment" }, + "nullable": true, "type": "array" }, "content": { @@ -3166,6 +3217,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_GuiAction" }, + "nullable": true, "type": "array" }, "progress": { @@ -3179,6 +3231,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_SearchTag" }, + "nullable": true, "type": "array" }, "status": { @@ -3194,6 +3247,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_Transmission" }, + "nullable": true, "type": "array" }, "visibleFrom": { @@ -3260,6 +3314,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "url": { @@ -3289,6 +3344,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_TransmissionAttachment" }, + "nullable": true, "type": "array" }, "authorizationAttribute": { @@ -3356,6 +3412,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "id": { @@ -3370,6 +3427,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_TransmissionAttachmentUrl" }, + "nullable": true, "type": "array" } }, @@ -3597,6 +3655,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesGet_DialogActivity" }, + "nullable": true, "type": "array" }, "apiActions": { @@ -3604,6 +3663,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesGet_DialogApiAction" }, + "nullable": true, "type": "array" }, "attachments": { @@ -3611,6 +3671,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesGet_DialogAttachment" }, + "nullable": true, "type": "array" }, "content": { @@ -3662,6 +3723,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesGet_DialogGuiAction" }, + "nullable": true, "type": "array" }, "id": { @@ -3715,6 +3777,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesGet_DialogSeenLog" }, + "nullable": true, "type": "array" }, "serviceResource": { @@ -3747,6 +3810,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesGet_DialogTransmission" }, + "nullable": true, "type": "array" }, "updatedAt": { @@ -3779,6 +3843,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "extendedType": { @@ -3860,6 +3925,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesGet_DialogApiActionEndpoint" }, + "nullable": true, "type": "array" }, "id": { @@ -3940,6 +4006,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "id": { @@ -3952,6 +4019,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesGet_DialogAttachmentUrl" }, + "nullable": true, "type": "array" } }, @@ -4044,6 +4112,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "url": { @@ -4110,6 +4179,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesGet_DialogTransmissionAttachment" }, + "nullable": true, "type": "array" }, "authorizationAttribute": { @@ -4180,6 +4250,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "id": { @@ -4192,6 +4263,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesGet_DialogTransmissionAttachmentUrl" }, + "nullable": true, "type": "array" } }, @@ -4420,6 +4492,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesSearch_DialogSeenLog" }, + "nullable": true, "type": "array" }, "serviceResource": { @@ -4476,6 +4549,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "extendedType": { @@ -4603,6 +4677,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_TransmissionAttachment" }, + "nullable": true, "type": "array" }, "authorizationAttribute": { @@ -4670,6 +4745,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "id": { @@ -4682,6 +4758,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogTransmissionsQueriesGet_AttachmentUrl" }, + "nullable": true, "type": "array" } }, @@ -4784,6 +4861,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogTransmissionsQueriesGet_Attachment" }, + "nullable": true, "type": "array" }, "authorizationAttribute": { @@ -4854,6 +4932,7 @@ "items": { "$ref": "#/components/schemas/V1CommonLocalizations_Localization" }, + "nullable": true, "type": "array" }, "id": { @@ -4866,6 +4945,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogTransmissionsQueriesSearch_AttachmentUrl" }, + "nullable": true, "type": "array" } }, @@ -4968,6 +5048,7 @@ "items": { "$ref": "#/components/schemas/V1ServiceOwnerDialogTransmissionsQueriesSearch_Attachment" }, + "nullable": true, "type": "array" }, "authorizationAttribute": { @@ -5037,6 +5118,7 @@ "items": { "$ref": "#/components/schemas/V1WellKnownJwksQueriesGet_Jwk" }, + "nullable": true, "type": "array" } }, @@ -7455,4 +7537,4 @@ "url": "https://altinn-dev-api.azure-api.net/dialogporten" } ] -} +} \ No newline at end of file diff --git a/src/Digdir.Domain.Dialogporten.WebApi/OpenApiDocumentExtensions.cs b/src/Digdir.Domain.Dialogporten.WebApi/OpenApiDocumentExtensions.cs index 785dc3bb2..81765706d 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/OpenApiDocumentExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/OpenApiDocumentExtensions.cs @@ -80,4 +80,28 @@ public static void ReplaceProblemDetailsDescriptions(this OpenApiDocument openAp } } } + + public static void MakeCollectionsNullable(this OpenApiDocument openApiDocument) + { + foreach (var schema in openApiDocument.Components.Schemas.Values) + { + MakeCollectionsNullable(schema); + } + } + + private static void MakeCollectionsNullable(JsonSchema schema) + { + if (schema.Properties == null) + { + return; + } + + foreach (var property in schema.Properties.Values) + { + if (property.Type.HasFlag(JsonObjectType.Array)) + { + property.IsNullableRaw = true; + } + } + } } diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Program.cs b/src/Digdir.Domain.Dialogporten.WebApi/Program.cs index dea11d4d3..77ea56e3a 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Program.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Program.cs @@ -194,6 +194,7 @@ static void BuildAndRun(string[] args, TelemetryConfiguration telemetryConfigura document.Generator = null; document.ReplaceProblemDetailsDescriptions(); document.ReplaceRequestExampleBodies(); + document.MakeCollectionsNullable(); }; }, uiConfig => { diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs index e8122c082..77ac51abc 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Common/DialogApplication.cs @@ -52,7 +52,6 @@ public async Task InitializeAsync() AssertionOptions.AssertEquivalencyUsing(options => { - //options.ExcludingMissingMembers(); options.Using(ctx => ctx.Subject.Should().BeCloseTo(ctx.Expectation, TimeSpan.FromMicroseconds(1))) .WhenTypeIs(); return options; diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/GetDialogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/GetDialogTests.cs index 5f5cb4e91..2493b064f 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/GetDialogTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Queries/GetDialogTests.cs @@ -27,7 +27,6 @@ public async Task Get_ReturnsSimpleDialog_WhenDialogExists() response.TryPickT0(out var result, out _).Should().BeTrue(); result.Should().NotBeNull(); result.Should().BeEquivalentTo(createDialogCommand, options => options - .ExcludingMissingMembers() .Excluding(x => x.UpdatedAt) .Excluding(x => x.CreatedAt) .Excluding(x => x.SystemLabel)); @@ -48,7 +47,6 @@ public async Task Get_ReturnsDialog_WhenDialogExists() response.TryPickT0(out var result, out _).Should().BeTrue(); result.Should().NotBeNull(); result.Should().BeEquivalentTo(createCommand, options => options - .ExcludingMissingMembers() .Excluding(x => x.UpdatedAt) .Excluding(x => x.CreatedAt) .Excluding(x => x.SystemLabel)); diff --git a/tests/k6/tests/serviceowner/dialogSearch.js b/tests/k6/tests/serviceowner/dialogSearch.js index 7d6e318b3..ef6e949ec 100644 --- a/tests/k6/tests/serviceowner/dialogSearch.js +++ b/tests/k6/tests/serviceowner/dialogSearch.js @@ -45,7 +45,7 @@ export default function () { let titleForLastItem = uuidv4(); let updatedAfter = (new Date()).toISOString(); // We use this on all tests to avoid clashing with unrelated dialogs let defaultFilter = "?UpdatedAfter=" + updatedAfter; - + describe('Arrange: Create some dialogs to test against', () => { for (let i = 0; i < 20; i++) { From 152417aa100bb779e68d302c0674e2f9ed2b649e Mon Sep 17 00:00:00 2001 From: Are Almaas Date: Wed, 6 Nov 2024 14:12:03 +0100 Subject: [PATCH 06/19] fix(graphql): ensure gql has maskinporten environment set (#1408) ## Description ## Related Issue(s) - #{issue number} ## Verification - [ ] **Your** code builds clean without any errors or warnings - [ ] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] 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) --- .../appsettings.Development.json | 8 +++----- .../appsettings.prod.json | 5 +++-- .../appsettings.staging.json | 5 +++-- .../appsettings.test.json | 5 +++-- .../appsettings.yt01.json | 3 ++- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.Development.json b/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.Development.json index 0c3edbeda..8704387eb 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.Development.json +++ b/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.Development.json @@ -9,17 +9,15 @@ "Maskinporten": { // 1. Valid values are test and prod "Environment": "test", - // 2. Client Id/integration as configured in Maskinporten "ClientId": "TODO: Add to local secrets", - // 3. Scope(s) requested, space seperated. Must be provisioned on supplied client id. "Scope": "altinn:events.publish altinn:events.publish.admin altinn:register/partylookup.admin altinn:authorization/authorize.admin altinn:accessmanagement/authorizedparties.admin", - // -------------------------- // Any additional settings are specific for the selected client definition type. // See below for examples using other types. - "EncodedJwk": "TODO: Add to local secrets" + "EncodedJwk": "TODO: Add to local secrets", + "TokenExchangeEnvironment": "tt02" }, "Altinn": { "BaseUri": "https://platform.tt02.altinn.no/", @@ -64,4 +62,4 @@ "DisableAuth": true, "UseInMemoryServiceBusTransport": true } -} +} \ No newline at end of file diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.prod.json b/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.prod.json index 68d322e2c..c1fce79c0 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.prod.json +++ b/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.prod.json @@ -16,7 +16,8 @@ // -------------------------- // Any additional settings are specific for the selected client definition type. // See below for examples using other types. - "EncodedJwk": "TODO: Add to local secrets" + "EncodedJwk": "TODO: Add to local secrets", + "TokenExchangeEnvironment": "prod" }, "Altinn": { "BaseUri": "https://platform.altinn.no/", @@ -59,4 +60,4 @@ ] } } -} +} \ No newline at end of file diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.staging.json b/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.staging.json index 7dad7d575..4d808be2c 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.staging.json +++ b/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.staging.json @@ -16,7 +16,8 @@ // -------------------------- // Any additional settings are specific for the selected client definition type. // See below for examples using other types. - "EncodedJwk": "TODO: Add to local secrets" + "EncodedJwk": "TODO: Add to local secrets", + "TokenExchangeEnvironment": "tt02" }, "Altinn": { "BaseUri": "https://platform.tt02.altinn.no/", @@ -59,4 +60,4 @@ ] } } -} +} \ No newline at end of file diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.test.json b/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.test.json index 18b959414..eda6fcc59 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.test.json +++ b/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.test.json @@ -16,7 +16,8 @@ // -------------------------- // Any additional settings are specific for the selected client definition type. // See below for examples using other types. - "EncodedJwk": "TODO: Add to local secrets" + "EncodedJwk": "TODO: Add to local secrets", + "TokenExchangeEnvironment": "tt02" }, "Altinn": { "BaseUri": "https://platform.tt02.altinn.no/", @@ -59,4 +60,4 @@ ] } } -} +} \ No newline at end of file diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.yt01.json b/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.yt01.json index 30cf793dc..d1d4c9e34 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.yt01.json +++ b/src/Digdir.Domain.Dialogporten.GraphQL/appsettings.yt01.json @@ -16,7 +16,8 @@ // -------------------------- // Any additional settings are specific for the selected client definition type. // See below for examples using other types. - "EncodedJwk": "TODO: Add to local secrets" + "EncodedJwk": "TODO: Add to local secrets", + "TokenExchangeEnvironment": "yt01" }, "Altinn": { "BaseUri": "https://platform.yt01.altinn.cloud/", From 510ee9352e332f7a1437fe4f9e2cc1d599a9d11c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 14:12:55 +0100 Subject: [PATCH 07/19] chore(deps): update dependency ziggycreatures.fusioncache to 1.4.1 (#1396) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [ZiggyCreatures.FusionCache](https://redirect.github.com/ZiggyCreatures/FusionCache) | `1.4.0` -> `1.4.1` | [![age](https://developer.mend.io/api/mc/badges/age/nuget/ZiggyCreatures.FusionCache/1.4.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/nuget/ZiggyCreatures.FusionCache/1.4.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/nuget/ZiggyCreatures.FusionCache/1.4.0/1.4.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/ZiggyCreatures.FusionCache/1.4.0/1.4.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
ZiggyCreatures/FusionCache (ZiggyCreatures.FusionCache) ### [`v1.4.1`](https://redirect.github.com/ZiggyCreatures/FusionCache/releases/tag/v1.4.1) This is a small version, just to update some transitive packages with security vulnerabilities. #### ⚠️ Update vulnerable dependencies The affected transitive packages are: - `System.Text.Json` - `MessagePack` - `Microsoft.Extensions.Caching.Memory` To that to update `Microsoft.Extensions.Caching.Memory`, an update for `System.Diagnostics.DiagnosticSource` was also needed. This is all. **PS:** if you have some time, please read the [Tagging](https://redirect.github.com/ZiggyCreatures/FusionCache/issues/319) proposal (eg: evict by tag + clear), which is planned for `v2.0`. This will be one of the biggest and most important features of FusionCache ever. Any help is welcome!
--- ### Configuration 📅 **Schedule**: Branch creation - "before 7am on Sunday,before 7am on Wednesday" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/digdir/dialogporten). --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Ole Jørgen Skogstad --- .../Digdir.Domain.Dialogporten.Infrastructure.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Digdir.Domain.Dialogporten.Infrastructure/Digdir.Domain.Dialogporten.Infrastructure.csproj b/src/Digdir.Domain.Dialogporten.Infrastructure/Digdir.Domain.Dialogporten.Infrastructure.csproj index 36d95a35c..bdf9f79cf 100644 --- a/src/Digdir.Domain.Dialogporten.Infrastructure/Digdir.Domain.Dialogporten.Infrastructure.csproj +++ b/src/Digdir.Domain.Dialogporten.Infrastructure/Digdir.Domain.Dialogporten.Infrastructure.csproj @@ -21,9 +21,9 @@ - - - + + + From fcb0dfc3f48fda0ab8db4dc88eb49d373e478c22 Mon Sep 17 00:00:00 2001 From: Dialogporten Automation Bot <164321870+dialogporten-bot@users.noreply.github.com> Date: Wed, 6 Nov 2024 14:44:56 +0100 Subject: [PATCH 08/19] chore(main): release 1.29.0 (#1407) :robot: I have created a release *beep* *boop* --- ## [1.29.0](https://github.com/digdir/dialogporten/compare/v1.28.3...v1.29.0) (2024-11-06) ### Features * **webAPI:** Make all lists nullable in OpenAPI schema ([#1359](https://github.com/digdir/dialogporten/issues/1359)) ([920d749](https://github.com/digdir/dialogporten/commit/920d7493d09e551a4207f61636a7188fea490223)) ### Bug Fixes * **graphql:** ensure gql has maskinporten environment set ([#1408](https://github.com/digdir/dialogporten/issues/1408)) ([152417a](https://github.com/digdir/dialogporten/commit/152417aa100bb779e68d302c0674e2f9ed2b649e)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- CHANGELOG.md | 12 ++++++++++++ version.txt | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4594113b0..b70eb9436 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [1.29.0](https://github.com/digdir/dialogporten/compare/v1.28.3...v1.29.0) (2024-11-06) + + +### Features + +* **webAPI:** Make all lists nullable in OpenAPI schema ([#1359](https://github.com/digdir/dialogporten/issues/1359)) ([920d749](https://github.com/digdir/dialogporten/commit/920d7493d09e551a4207f61636a7188fea490223)) + + +### Bug Fixes + +* **graphql:** ensure gql has maskinporten environment set ([#1408](https://github.com/digdir/dialogporten/issues/1408)) ([152417a](https://github.com/digdir/dialogporten/commit/152417aa100bb779e68d302c0674e2f9ed2b649e)) + ## [1.28.3](https://github.com/digdir/dialogporten/compare/v1.28.2...v1.28.3) (2024-11-06) diff --git a/version.txt b/version.txt index ac786b645..5e57fb895 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.28.3 +1.29.0 From d24eec2557b1c0a39cb2ca3bc2c7bf033820d159 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:36:11 +0100 Subject: [PATCH 09/19] chore(deps): update dependency nsubstitute to 5.3.0 (#1399) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [NSubstitute](https://nsubstitute.github.io/) ([source](https://redirect.github.com/nsubstitute/NSubstitute)) | `5.1.0` -> `5.3.0` | [![age](https://developer.mend.io/api/mc/badges/age/nuget/NSubstitute/5.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/nuget/NSubstitute/5.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/nuget/NSubstitute/5.1.0/5.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/NSubstitute/5.1.0/5.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
nsubstitute/NSubstitute (NSubstitute) ### [`v5.3.0`](https://redirect.github.com/nsubstitute/NSubstitute/blob/HEAD/CHANGELOG.md#530-October-2024) - \[NEW] Introduced `Substitute.ForTypeForwardingTo` to create substitutes that forward interceptable calls to a concrete class. This provides an easy way of implementing a test spy over an existing type. Designed and implemented by [@​marcoregueira](https://redirect.github.com/marcoregueira) in [https://github.com/nsubstitute/NSubstitute/pull/700](https://redirect.github.com/nsubstitute/NSubstitute/pull/700) from a proposal by [@​wsaeed](https://redirect.github.com/wsaeed). Thanks to all who contributed to discussions of this feature. - \[NEW] Support Raise.EventWith default constructor ([#​788](https://redirect.github.com/nsubstitute/NSubstitute/issues/788)) by [@​mihnea-radulescu](https://redirect.github.com/mihnea-radulescu) in [https://github.com/nsubstitute/NSubstitute/pull/813](https://redirect.github.com/nsubstitute/NSubstitute/pull/813) - \[NEW] Implement When(...).Throws to avoid confusion with Throw method ([#​803](https://redirect.github.com/nsubstitute/NSubstitute/issues/803)) by [@​mihnea-radulescu](https://redirect.github.com/mihnea-radulescu) in [https://github.com/nsubstitute/NSubstitute/pull/818](https://redirect.github.com/nsubstitute/NSubstitute/pull/818) - \[FIX] Arg.Any\() does not match arguments passed by reference ([#​787](https://redirect.github.com/nsubstitute/NSubstitute/issues/787)) by [@​mihnea-radulescu](https://redirect.github.com/mihnea-radulescu) in [https://github.com/nsubstitute/NSubstitute/pull/811](https://redirect.github.com/nsubstitute/NSubstitute/pull/811) - \[FIX] Support matching arguments whose type is generic, when their concrete type is not known ([#​786](https://redirect.github.com/nsubstitute/NSubstitute/issues/786)) by [@​mihnea-radulescu](https://redirect.github.com/mihnea-radulescu) in [https://github.com/nsubstitute/NSubstitute/pull/814](https://redirect.github.com/nsubstitute/NSubstitute/pull/814) - \[FIX] Release build workflow [https://github.com/nsubstitute/NSubstitute/pull/797](https://redirect.github.com/nsubstitute/NSubstitute/pull/797)7) - \[DOC] Add Throws for exceptions to the docs by [@​304NotModified](https://redirect.github.com/304NotModified) in [https://github.com/nsubstitute/NSubstitute/pull/795](https://redirect.github.com/nsubstitute/NSubstitute/pull/795) - \[DOC] Remove Visual Studio for Mac from readme by [@​Romfos](https://redirect.github.com/Romfos) in [https://github.com/nsubstitute/NSubstitute/pull/807](https://redirect.github.com/nsubstitute/NSubstitute/pull/807) - \[TECH] Migrate from NUnit 3 to NUnit 4 by [@​Romfos](https://redirect.github.com/Romfos) in [https://github.com/nsubstitute/NSubstitute/pull/783](https://redirect.github.com/nsubstitute/NSubstitute/pull/783) - \[TECH] Update build project to .net 8 by [@​Romfos](https://redirect.github.com/Romfos) in [https://github.com/nsubstitute/NSubstitute/pull/776](https://redirect.github.com/nsubstitute/NSubstitute/pull/776) - \[TECH] Code style: use C# 12 collection literals by [@​Romfos](https://redirect.github.com/Romfos) in [https://github.com/nsubstitute/NSubstitute/pull/810](https://redirect.github.com/nsubstitute/NSubstitute/pull/810) - \[TECH] Use c# 12 primary constructors by [@​Romfos](https://redirect.github.com/Romfos) in [https://github.com/nsubstitute/NSubstitute/pull/812](https://redirect.github.com/nsubstitute/NSubstitute/pull/812) - \[TECH] Added csharp_style_prefer_primary_constructors into editorconfig by [@​Romfos](https://redirect.github.com/Romfos) in [https://github.com/nsubstitute/NSubstitute/pull/819](https://redirect.github.com/nsubstitute/NSubstitute/pull/819) Thanks to first-time contributors [@​mihnea-radulescu](https://redirect.github.com/mihnea-radulescu) and [@​marcoregueira](https://redirect.github.com/marcoregueira)! Thanks also [@​304NotModified](https://redirect.github.com/304NotModified) and [@​Romfos](https://redirect.github.com/Romfos) for their continued support and contributions to this release.
--- ### Configuration 📅 **Schedule**: Branch creation - "before 7am on Sunday,before 7am on Wednesday" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/digdir/dialogporten). --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Ole Jørgen Skogstad --- .../Digdir.Domain.Dialogporten.Application.csproj | 2 +- ...r.Domain.Dialogporten.Application.Integration.Tests.csproj | 4 ++-- .../Digdir.Domain.Dialogporten.Application.Unit.Tests.csproj | 2 +- ...igdir.Domain.Dialogporten.GraphQl.Integration.Tests.csproj | 4 ++-- ...Digdir.Domain.Dialogporten.WebApi.Integration.Tests.csproj | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj b/src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj index a9923373e..b6ff8df13 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj +++ b/src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj @@ -18,7 +18,7 @@ - +
diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Digdir.Domain.Dialogporten.Application.Integration.Tests.csproj b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Digdir.Domain.Dialogporten.Application.Integration.Tests.csproj index 929aeec28..8d39efd6f 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Digdir.Domain.Dialogporten.Application.Integration.Tests.csproj +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Digdir.Domain.Dialogporten.Application.Integration.Tests.csproj @@ -8,9 +8,9 @@ - + - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Digdir.Domain.Dialogporten.Application.Unit.Tests.csproj b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Digdir.Domain.Dialogporten.Application.Unit.Tests.csproj index 3f62f42de..cd4e1b96c 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Digdir.Domain.Dialogporten.Application.Unit.Tests.csproj +++ b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Digdir.Domain.Dialogporten.Application.Unit.Tests.csproj @@ -12,7 +12,7 @@ - + all diff --git a/tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests.csproj b/tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests.csproj index cb8d080e5..1b046e015 100644 --- a/tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests.csproj +++ b/tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests/Digdir.Domain.Dialogporten.GraphQl.Integration.Tests.csproj @@ -12,8 +12,8 @@ - - + + all diff --git a/tests/Digdir.Domain.Dialogporten.WebApi.Integration.Tests/Digdir.Domain.Dialogporten.WebApi.Integration.Tests.csproj b/tests/Digdir.Domain.Dialogporten.WebApi.Integration.Tests/Digdir.Domain.Dialogporten.WebApi.Integration.Tests.csproj index e16989374..46cdbd781 100644 --- a/tests/Digdir.Domain.Dialogporten.WebApi.Integration.Tests/Digdir.Domain.Dialogporten.WebApi.Integration.Tests.csproj +++ b/tests/Digdir.Domain.Dialogporten.WebApi.Integration.Tests/Digdir.Domain.Dialogporten.WebApi.Integration.Tests.csproj @@ -8,7 +8,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive From ffd1e83c8499948d3ee2654213d36028e8f0561d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:52:49 +0100 Subject: [PATCH 10/19] chore(deps): update resource microsoft.network/networkinterfaces to 2024-03-01 (#1400) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Resource | Change | |---|---| | Microsoft.Network/networkInterfaces | `2023-11-01` -> `2024-03-01` | --- ### Configuration 📅 **Schedule**: Branch creation - "before 7am on Sunday,before 7am on Wednesday" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/digdir/dialogporten). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .azure/modules/ssh-jumper/main.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azure/modules/ssh-jumper/main.bicep b/.azure/modules/ssh-jumper/main.bicep index 3f9a044b7..e90d2633b 100644 --- a/.azure/modules/ssh-jumper/main.bicep +++ b/.azure/modules/ssh-jumper/main.bicep @@ -38,7 +38,7 @@ resource publicIp 'Microsoft.Network/publicIPAddresses@2023-11-01' = { tags: tags } -resource networkInterface 'Microsoft.Network/networkInterfaces@2023-11-01' = { +resource networkInterface 'Microsoft.Network/networkInterfaces@2024-03-01' = { name: name location: location properties: { From 8d0a7b3c3c8b245428d88dd3f250395e109beab6 Mon Sep 17 00:00:00 2001 From: Are Almaas Date: Wed, 6 Nov 2024 16:48:47 +0100 Subject: [PATCH 11/19] ci: ensure we store the versions as gh variables (#1405) ## Description ## Related Issue(s) Fixes issues in the current workflow regarding missing .git-directory https://github.com/digdir/dialogporten/actions/runs/11686732981/job/32543782272 ## Verification - [ ] **Your** code builds clean without any errors or warnings - [ ] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] 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) ## Summary by CodeRabbit - **New Features** - Introduced a new workflow for storing GitHub environment variables, enhancing version management. - Updated deployment jobs to conditionally execute based on detected changes, improving deployment efficiency. - **Bug Fixes** - Improved error handling and clarity in the CI/CD process. - **Documentation** - Added documentation for the new workflow to assist users in understanding its purpose and usage. --- .github/workflows/ci-cd-prod.yml | 24 ++++++------ .github/workflows/ci-cd-staging.yml | 24 ++++++------ .github/workflows/ci-cd-yt01.yml | 24 ++++++------ .../workflow-store-github-env-variable.yml | 39 +++++++++++++++++++ 4 files changed, 75 insertions(+), 36 deletions(-) create mode 100644 .github/workflows/workflow-store-github-env-variable.yml diff --git a/.github/workflows/ci-cd-prod.yml b/.github/workflows/ci-cd-prod.yml index f25bcedf1..c9e68aa0d 100644 --- a/.github/workflows/ci-cd-prod.yml +++ b/.github/workflows/ci-cd-prod.yml @@ -69,15 +69,15 @@ jobs: store-infra-version: name: Store Latest Deployed Infra Version as GitHub Variable - runs-on: ubuntu-latest needs: [deploy-infra, get-current-version] if: ${{ needs.deploy-infra.result == 'success' }} - env: + uses: ./.github/workflows/workflow-store-github-env-variable.yml + with: + variable_name: LATEST_DEPLOYED_INFRA_VERSION + variable_value: ${{ needs.get-current-version.outputs.version }} + environment: prod + secrets: GH_TOKEN: ${{ secrets.RELEASE_VERSION_STORAGE_PAT }} - steps: - - name: Set GitHub variable for latest deployed infra version - run: | - gh variable set LATEST_DEPLOYED_INFRA_VERSION --body "${{ needs.get-current-version.outputs.version }}" --env prod --repo ${{ github.repository }} dry-run-deploy-apps: name: Dry run deploy apps to prod @@ -129,15 +129,15 @@ jobs: store-apps-version: name: Store Latest Deployed Apps Version as GitHub Variable - runs-on: ubuntu-latest needs: [deploy-apps, get-current-version] if: ${{ needs.deploy-apps.result == 'success' }} - env: + uses: ./.github/workflows/workflow-store-github-env-variable.yml + with: + variable_name: LATEST_DEPLOYED_APPS_VERSION + variable_value: ${{ needs.get-current-version.outputs.version }} + environment: prod + secrets: GH_TOKEN: ${{ secrets.RELEASE_VERSION_STORAGE_PAT }} - steps: - - name: Set GitHub variable for latest deployed apps version - run: | - gh variable set LATEST_DEPLOYED_APPS_VERSION --body "${{ needs.get-current-version.outputs.version }}" --env prod --repo ${{ github.repository }} deploy-slack-notifier: name: Deploy slack notifier (prod) diff --git a/.github/workflows/ci-cd-staging.yml b/.github/workflows/ci-cd-staging.yml index adc7d558c..2ff5247e3 100644 --- a/.github/workflows/ci-cd-staging.yml +++ b/.github/workflows/ci-cd-staging.yml @@ -50,15 +50,15 @@ jobs: store-infra-version: name: Store Latest Deployed Infra Version as GitHub Variable - runs-on: ubuntu-latest needs: [deploy-infra, get-current-version] if: ${{ needs.deploy-infra.result == 'success' }} - env: + uses: ./.github/workflows/workflow-store-github-env-variable.yml + with: + variable_name: LATEST_DEPLOYED_INFRA_VERSION + variable_value: ${{ needs.get-current-version.outputs.version }} + environment: staging + secrets: GH_TOKEN: ${{ secrets.RELEASE_VERSION_STORAGE_PAT }} - steps: - - name: Set GitHub variable for latest deployed infra version - run: | - gh variable set LATEST_DEPLOYED_INFRA_VERSION --body "${{ needs.get-current-version.outputs.version }}" --env staging --repo ${{ github.repository }} publish: name: Build and publish docker images @@ -95,15 +95,15 @@ jobs: store-apps-version: name: Store Latest Deployed Apps Version as GitHub Variable - runs-on: ubuntu-latest needs: [deploy-apps, get-current-version] if: ${{ needs.deploy-apps.result == 'success' }} - env: + uses: ./.github/workflows/workflow-store-github-env-variable.yml + with: + variable_name: LATEST_DEPLOYED_APPS_VERSION + variable_value: ${{ needs.get-current-version.outputs.version }} + environment: staging + secrets: GH_TOKEN: ${{ secrets.RELEASE_VERSION_STORAGE_PAT }} - steps: - - name: Set GitHub variable for latest deployed apps version - run: | - gh variable set LATEST_DEPLOYED_APPS_VERSION --body "${{ needs.get-current-version.outputs.version }}" --env staging deploy-slack-notifier: name: Deploy slack notifier (staging) diff --git a/.github/workflows/ci-cd-yt01.yml b/.github/workflows/ci-cd-yt01.yml index d3a994af9..1dee21b98 100644 --- a/.github/workflows/ci-cd-yt01.yml +++ b/.github/workflows/ci-cd-yt01.yml @@ -61,15 +61,15 @@ jobs: store-infra-version: name: Store Latest Deployed Infra Version as GitHub Variable - runs-on: ubuntu-latest needs: [deploy-infra, get-current-version] if: ${{ needs.deploy-infra.result == 'success' }} - env: + uses: ./.github/workflows/workflow-store-github-env-variable.yml + with: + variable_name: LATEST_DEPLOYED_INFRA_VERSION + variable_value: ${{ needs.get-current-version.outputs.version }} + environment: yt01 + secrets: GH_TOKEN: ${{ secrets.RELEASE_VERSION_STORAGE_PAT }} - steps: - - name: Set GitHub variable for latest deployed infra version - run: | - gh variable set LATEST_DEPLOYED_INFRA_VERSION --body "${{ needs.get-current-version.outputs.version }}" --env yt01 --repo ${{ github.repository }} deploy-apps: name: Deploy apps to yt01 @@ -97,15 +97,15 @@ jobs: store-apps-version: name: Store Latest Deployed Apps Version as GitHub Variable - runs-on: ubuntu-latest needs: [deploy-apps, get-current-version] if: ${{ needs.deploy-apps.result == 'success' }} - env: + uses: ./.github/workflows/workflow-store-github-env-variable.yml + with: + variable_name: LATEST_DEPLOYED_APPS_VERSION + variable_value: ${{ needs.get-current-version.outputs.version }} + environment: yt01 + secrets: GH_TOKEN: ${{ secrets.RELEASE_VERSION_STORAGE_PAT }} - steps: - - name: Set GitHub variable for latest deployed apps version - run: | - gh variable set LATEST_DEPLOYED_APPS_VERSION --body "${{ needs.get-current-version.outputs.version }}" --env yt01 --repo ${{ github.repository }} deploy-slack-notifier: name: Deploy slack notifier (yt01) diff --git a/.github/workflows/workflow-store-github-env-variable.yml b/.github/workflows/workflow-store-github-env-variable.yml new file mode 100644 index 000000000..387888e2b --- /dev/null +++ b/.github/workflows/workflow-store-github-env-variable.yml @@ -0,0 +1,39 @@ +name: Store GitHub Environment Variable + +on: + workflow_call: + inputs: + variable_name: + required: true + type: string + description: "Name of the variable to store" + variable_value: + required: true + type: string + description: "Value to store in the variable" + environment: + required: true + type: string + description: "GitHub environment to store the variable in" + secrets: + GH_TOKEN: + required: true + description: "GitHub token with permission to set variables" + +jobs: + store-variable: + name: Store GitHub Environment Variable + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set GitHub variable + env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} + run: | + gh variable set ${{ inputs.variable_name }} \ + --body "${{ inputs.variable_value }}" \ + --env ${{ inputs.environment }} \ + --repo ${{ github.repository }} From e79dbfa590725c9da413557ac21ef49ba22079a6 Mon Sep 17 00:00:00 2001 From: Amund Date: Thu, 7 Nov 2024 08:21:52 +0100 Subject: [PATCH 12/19] chore: Manually set operationId of Patch (#1410) ## Related Issue(s) - #{issue number} ## Verification - [ ] **Your** code builds clean without any errors or warnings - [ ] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] 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) ## Summary by CodeRabbit - **New Features** - Enhanced OpenAPI specification for the Dialogporten API with new schemas for managing dialogs and their activities. - Improved clarity in endpoint operations with updated operation IDs for various dialog-related actions. - **Bug Fixes** - Enhanced error handling for dialog updates, ensuring appropriate HTTP responses. - **Documentation** - Expanded descriptions for endpoints and schemas to improve usability and understanding of the API. --------- Co-authored-by: Amund Myrbostad --- docs/schema/V1/swagger.verified.json | 2 +- .../V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/schema/V1/swagger.verified.json b/docs/schema/V1/swagger.verified.json index f21ef98b5..5f481c350 100644 --- a/docs/schema/V1/swagger.verified.json +++ b/docs/schema/V1/swagger.verified.json @@ -6501,7 +6501,7 @@ }, "patch": { "description": "Patches a dialog aggregate with a RFC6902 JSON Patch document. The patch document must be a JSON array of RFC6902 operations.\nSee [https://tools.ietf.org/html/rfc6902](https://tools.ietf.org/html/rfc6902) for more information.\n \nOptimistic concurrency control is implemented using the If-Match header. Supply the Revision value from the GetDialog endpoint to ensure that the dialog is not modified/deleted by another request in the meantime.", - "operationId": "PatchDialogs_Patch", + "operationId": "V1ServiceOwnerDialogsPatchDialog", "parameters": [ { "in": "path", diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs index bb71709d8..96906a286 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.JsonPatch; using Microsoft.AspNetCore.Mvc; +using NSwag.Annotations; using DialogportenAuthorizationPolicy = Digdir.Domain.Dialogporten.WebApi.Common.Authorization.AuthorizationPolicy; using IMapper = AutoMapper.IMapper; using ProblemDetails = FastEndpoints.ProblemDetails; @@ -51,6 +52,7 @@ public PatchDialogsController(ISender sender, IMapper mapper) /// Domain error occured. See problem details for a list of errors. [HttpPatch("{dialogId}")] + [OpenApiOperation("V1ServiceOwnerDialogsPatchDialog")] [ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] From ca18a993f21e488bfe4be7c167c822a7954b2683 Mon Sep 17 00:00:00 2001 From: Amund Date: Thu, 7 Nov 2024 08:37:32 +0100 Subject: [PATCH 13/19] feat(webapi): Combine actorDtos (#1374) ## Description Replaced all actorDtos with one common enduser actordto, and one common serivceowner actordto ## Related Issue(s) ## Verification - [x] **Your** code builds clean without any errors or warnings - [x] Manual testing done (required) - [x] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] 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) ## Summary by CodeRabbit - **New Features** - Introduced a unified `ActorDto` for actor representation across various components, replacing multiple specific actor DTOs. - Added validation for `ActorDto` to ensure proper data integrity based on actor type. - **Bug Fixes** - Updated mappings and properties to reflect the new actor structure, enhancing data consistency across the application. - **Documentation** - Updated Swagger configuration to reflect changes in actor representation. - **Tests** - Added comprehensive unit tests for the new `ActorValidator` to ensure validation rules are correctly enforced. --------- Co-authored-by: Amund Myrbostad Co-authored-by: Magnus Sandgren <5285192+MagnusSandgren@users.noreply.github.com> --- docs/schema/V1/swagger.verified.json | 640 +++--------------- .../V1/EndUser/Common/Actors/ActorDto.cs | 26 + .../EndUser/Common/Actors/MappingProfile.cs | 32 + .../Queries/Get/ActivityDto.cs | 12 +- .../Queries/Get/MappingProfile.cs | 4 - .../Queries/Search/LabelAssignmentLogDto.cs | 12 +- .../Queries/Search/MappingProfile.cs | 1 - .../Queries/Get/MappingProfile.cs | 4 - .../DialogSeenLogs/Queries/Get/SeenLogDto.cs | 10 +- .../Queries/Search/MappingProfile.cs | 3 - .../Queries/Search/SeenLogDto.cs | 11 +- .../Queries/Get/MappingProfile.cs | 2 - .../Queries/Get/TransmissionDto.cs | 27 +- .../Queries/Search/MappingProfile.cs | 2 - .../Queries/Search/TransmissionDto.cs | 27 +- .../EndUser/Dialogs/Queries/Get/DialogDto.cs | 63 +- .../Dialogs/Queries/Get/MappingProfile.cs | 12 - .../Dialogs/Queries/Search/DialogDtoBase.cs | 43 +- .../Dialogs/Queries/Search/MappingProfile.cs | 8 - .../V1/ServiceOwner/Common/Actors/ActorDto.cs | 25 + .../Common/Actors/ActorValidator.cs | 26 + .../Common/Actors/MappingProfile.cs | 40 ++ .../Queries/Get/ActivityDto.cs | 12 +- .../Queries/Get/MappingProfile.cs | 4 - .../Queries/Get/MappingProfile.cs | 1 - .../DialogSeenLogs/Queries/Get/SeenLogDto.cs | 10 +- .../Queries/Search/MappingProfile.cs | 1 - .../Queries/Search/SeenLogDto.cs | 10 +- .../Queries/Get/MappingProfile.cs | 2 - .../Queries/Get/TransmissionDto.cs | 26 +- .../Queries/Search/MappingProfile.cs | 2 - .../Queries/Search/TransmissionDto.cs | 27 +- .../Create/CreateDialogCommandValidator.cs | 45 +- .../Commands/Create/CreateDialogDto.cs | 50 +- .../Dialogs/Commands/Create/MappingProfile.cs | 8 - .../Dialogs/Commands/Update/MappingProfile.cs | 9 - .../Update/UpdateDialogCommandValidator.cs | 45 +- .../Commands/Update/UpdateDialogDto.cs | 50 +- .../Dialogs/Queries/Get/DialogDto.cs | 63 +- .../Dialogs/Queries/Get/MappingProfile.cs | 8 +- .../Dialogs/Queries/Search/DialogDtoBase.cs | 41 +- .../Dialogs/Queries/Search/MappingProfile.cs | 7 - .../Dialogs/Entities/DialogEntity.cs | 4 +- .../EndUser/Common/MappingProfile.cs | 11 +- .../EndUser/DialogById/MappingProfile.cs | 2 +- .../Update/UpdateDialogSwaggerConfig.cs | 3 +- .../DialogGenerator.cs | 4 +- .../Dialogs/Commands/UpdateDialogTests.cs | 6 +- .../ServiceOwner/Common/ActorValidatorTest.cs | 80 +++ 49 files changed, 377 insertions(+), 1184 deletions(-) create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Common/Actors/ActorDto.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Common/Actors/MappingProfile.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Common/Actors/ActorDto.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Common/Actors/ActorValidator.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Common/Actors/MappingProfile.cs create mode 100644 tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Common/ActorValidatorTest.cs diff --git a/docs/schema/V1/swagger.verified.json b/docs/schema/V1/swagger.verified.json index 5f481c350..9b5573caa 100644 --- a/docs/schema/V1/swagger.verified.json +++ b/docs/schema/V1/swagger.verified.json @@ -344,6 +344,32 @@ }, "type": "object" }, + "V1EndUserCommonActors_Actor": { + "additionalProperties": false, + "properties": { + "actorId": { + "description": "The identifier of the person or organization that sent the transmission. Mutually exclusive with ActorName.\nMight be omitted if ActorType is \u0022ServiceOwner\u0022.", + "example": "urn:altinn:person:identifier-no:12018212345", + "nullable": true, + "type": "string" + }, + "actorName": { + "description": "Specifies the name of the entity that sent the transmission. Mutually exclusive with ActorId. If ActorId\nis supplied, the name will be automatically populated from the name registries.", + "example": "Ola Nordmann", + "nullable": true, + "type": "string" + }, + "actorType": { + "description": "The type of actor that sent the transmission.", + "oneOf": [ + { + "$ref": "#/components/schemas/Actors_ActorType" + } + ] + } + }, + "type": "object" + }, "V1EndUserDialogActivitiesQueriesGet_Activity": { "additionalProperties": false, "properties": { @@ -369,7 +395,7 @@ "type": "string" }, "performedBy": { - "$ref": "#/components/schemas/V1EndUserDialogActivitiesQueriesGet_PerformedByActor" + "$ref": "#/components/schemas/V1EndUserCommonActors_Actor" }, "transmissionId": { "format": "guid", @@ -382,27 +408,6 @@ }, "type": "object" }, - "V1EndUserDialogActivitiesQueriesGet_PerformedByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "nullable": true, - "type": "string" - }, - "actorName": { - "nullable": true, - "type": "string" - }, - "actorType": { - "$ref": "#/components/schemas/Actors_ActorType" - }, - "id": { - "format": "guid", - "type": "string" - } - }, - "type": "object" - }, "V1EndUserDialogActivitiesQueriesSearch_Activity": { "additionalProperties": false, "properties": { @@ -448,19 +453,7 @@ "type": "string" }, "performedBy": { - "$ref": "#/components/schemas/V1EndUserDialogLabelAssignmentLogQueriesSearch_LabelAssignmentLogActor" - } - }, - "type": "object" - }, - "V1EndUserDialogLabelAssignmentLogQueriesSearch_LabelAssignmentLogActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "type": "string" - }, - "actorName": { - "type": "string" + "$ref": "#/components/schemas/V1EndUserCommonActors_Actor" } }, "type": "object" @@ -483,23 +476,7 @@ "type": "string" }, "seenBy": { - "$ref": "#/components/schemas/V1EndUserDialogSeenLogsQueriesGet_SeenLogSeenByActor" - } - }, - "type": "object" - }, - "V1EndUserDialogSeenLogsQueriesGet_SeenLogSeenByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "type": "string" - }, - "actorName": { - "type": "string" - }, - "id": { - "format": "guid", - "type": "string" + "$ref": "#/components/schemas/V1EndUserCommonActors_Actor" } }, "type": "object" @@ -522,23 +499,7 @@ "type": "string" }, "seenBy": { - "$ref": "#/components/schemas/V1EndUserDialogSeenLogsQueriesSearch_SeenLogSeenByActor" - } - }, - "type": "object" - }, - "V1EndUserDialogSeenLogsQueriesSearch_SeenLogSeenByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "type": "string" - }, - "actorName": { - "type": "string" - }, - "id": { - "format": "guid", - "type": "string" + "$ref": "#/components/schemas/V1EndUserCommonActors_Actor" } }, "type": "object" @@ -799,7 +760,7 @@ "description": "The actor that performed the activity.", "oneOf": [ { - "$ref": "#/components/schemas/V1EndUserDialogsQueriesGet_DialogActivityPerformedByActor" + "$ref": "#/components/schemas/V1EndUserCommonActors_Actor" } ] }, @@ -820,30 +781,6 @@ }, "type": "object" }, - "V1EndUserDialogsQueriesGet_DialogActivityPerformedByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person or organization that performed the activity.\nMay be omitted if ActorType is \u0022ServiceOwner\u0022.", - "nullable": true, - "type": "string" - }, - "actorName": { - "description": "The name of the person or organization that performed the activity.\nOnly set if the actor type is \u0022PartyRepresentative\u0022.", - "nullable": true, - "type": "string" - }, - "actorType": { - "description": "The type of actor that performed the activity.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - } - }, - "type": "object" - }, "V1EndUserDialogsQueriesGet_DialogApiAction": { "additionalProperties": false, "properties": { @@ -1087,28 +1024,13 @@ "description": "The actor that saw the dialog revision.", "oneOf": [ { - "$ref": "#/components/schemas/V1EndUserDialogsQueriesGet_DialogSeenLogSeenByActor" + "$ref": "#/components/schemas/V1EndUserCommonActors_Actor" } ] } }, "type": "object" }, - "V1EndUserDialogsQueriesGet_DialogSeenLogSeenByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person/business that saw the dialog revision.", - "example": "urn:altinn:person:identifier-no:01125512345\nurn:altinn:organization:identifier-no:912345678", - "type": "string" - }, - "actorName": { - "description": "The natural name of the person/business that saw the dialog revision.", - "type": "string" - } - }, - "type": "object" - }, "V1EndUserDialogsQueriesGet_DialogTransmission": { "additionalProperties": false, "properties": { @@ -1164,7 +1086,7 @@ "description": "The actor that sent the transmission.", "oneOf": [ { - "$ref": "#/components/schemas/V1EndUserDialogsQueriesGet_DialogTransmissionSenderActor" + "$ref": "#/components/schemas/V1EndUserCommonActors_Actor" } ] }, @@ -1263,30 +1185,6 @@ }, "type": "object" }, - "V1EndUserDialogsQueriesGet_DialogTransmissionSenderActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person or organization that sent the transmission.", - "example": "urn:altinn:person:identifier-no:12018212345", - "type": "string" - }, - "actorName": { - "description": "The name of the person or organization that sent the transmission.", - "example": "Ola Nordmann", - "type": "string" - }, - "actorType": { - "description": "The type of actor that sent the transmission.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - } - }, - "type": "object" - }, "V1EndUserDialogsQueriesSearch_Content": { "additionalProperties": false, "properties": { @@ -1482,7 +1380,7 @@ "description": "The actor that performed the activity.", "oneOf": [ { - "$ref": "#/components/schemas/V1EndUserDialogsQueriesSearch_DialogActivityPerformedByActor" + "$ref": "#/components/schemas/V1EndUserCommonActors_Actor" } ] }, @@ -1503,30 +1401,6 @@ }, "type": "object" }, - "V1EndUserDialogsQueriesSearch_DialogActivityPerformedByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person or organization that performed the activity.\nMay be omitted if ActorType is \u0022ServiceOwner\u0022.", - "nullable": true, - "type": "string" - }, - "actorName": { - "description": "The name of the person or organization that performed the activity.\nOnly set if the actor type is \u0022PartyRepresentative\u0022.", - "nullable": true, - "type": "string" - }, - "actorType": { - "description": "What type of actor performed the activity.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - } - }, - "type": "object" - }, "V1EndUserDialogsQueriesSearch_DialogSeenLog": { "additionalProperties": false, "properties": { @@ -1553,28 +1427,13 @@ "description": "The actor that saw the dialog revision.", "oneOf": [ { - "$ref": "#/components/schemas/V1EndUserDialogsQueriesSearch_DialogSeenLogSeenByActor" + "$ref": "#/components/schemas/V1EndUserCommonActors_Actor" } ] } }, "type": "object" }, - "V1EndUserDialogsQueriesSearch_DialogSeenLogSeenByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person/business that saw the dialog revision.", - "example": "urn:altinn:person:identifier-no:01125512345\nurn:altinn:organization:identifier-no:912345678", - "type": "string" - }, - "actorName": { - "description": "The natural name of the person/business that saw the dialog revision.", - "type": "string" - } - }, - "type": "object" - }, "V1EndUserDialogSystemLabelsCommandsSet_SystemLabelCommand": { "additionalProperties": false, "properties": { @@ -1678,33 +1537,6 @@ }, "type": "object" }, - "V1EndUserDialogTransmissionsQueriesGet_SenderActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the actor.", - "type": "string" - }, - "actorName": { - "description": "The name of the actor.", - "type": "string" - }, - "actorType": { - "description": "The type of the actor.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - }, - "id": { - "description": "The unique identifier for the sender actor in UUIDv7 format.", - "format": "guid", - "type": "string" - } - }, - "type": "object" - }, "V1EndUserDialogTransmissionsQueriesGet_Transmission": { "additionalProperties": false, "properties": { @@ -1765,7 +1597,7 @@ "description": "The sender actor information for the transmission.", "oneOf": [ { - "$ref": "#/components/schemas/V1EndUserDialogTransmissionsQueriesGet_SenderActor" + "$ref": "#/components/schemas/V1EndUserCommonActors_Actor" } ] }, @@ -1869,33 +1701,6 @@ }, "type": "object" }, - "V1EndUserDialogTransmissionsQueriesSearch_SenderActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the actor.", - "type": "string" - }, - "actorName": { - "description": "The name of the actor.", - "type": "string" - }, - "actorType": { - "description": "The type of the actor.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - }, - "id": { - "description": "The unique identifier for the sender actor in UUIDv7 format.", - "format": "guid", - "type": "string" - } - }, - "type": "object" - }, "V1EndUserDialogTransmissionsQueriesSearch_Transmission": { "additionalProperties": false, "properties": { @@ -1956,7 +1761,7 @@ "description": "The sender actor information for the transmission.", "oneOf": [ { - "$ref": "#/components/schemas/V1EndUserDialogTransmissionsQueriesSearch_SenderActor" + "$ref": "#/components/schemas/V1EndUserCommonActors_Actor" } ] }, @@ -2024,6 +1829,32 @@ }, "type": "object" }, + "V1ServiceOwnerCommonActors_Actor": { + "additionalProperties": false, + "properties": { + "actorId": { + "description": "The identifier of the person or organization that sent the transmission. Mutually exclusive with ActorName.\nMight be omitted if ActorType is \u0022ServiceOwner\u0022.", + "example": "urn:altinn:person:identifier-no:12018212345", + "nullable": true, + "type": "string" + }, + "actorName": { + "description": "Specifies the name of the entity that sent the transmission. Mutually exclusive with ActorId. If ActorId\nis supplied, the name will be automatically populated from the name registries.", + "example": "Ola Nordmann", + "nullable": true, + "type": "string" + }, + "actorType": { + "description": "The type of actor that sent the transmission.", + "oneOf": [ + { + "$ref": "#/components/schemas/Actors_ActorType" + } + ] + } + }, + "type": "object" + }, "V1ServiceOwnerDialogActivitiesCreate_ActivityRequest": { "additionalProperties": false, "properties": { @@ -2058,7 +1889,7 @@ "description": "The actor that performed the activity.", "oneOf": [ { - "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_ActivityPerformedByActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } ] }, @@ -2109,7 +1940,7 @@ "type": "string" }, "performedBy": { - "$ref": "#/components/schemas/V1ServiceOwnerDialogActivitiesQueriesGet_ActivityPerformedByActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" }, "transmissionId": { "format": "guid", @@ -2122,27 +1953,6 @@ }, "type": "object" }, - "V1ServiceOwnerDialogActivitiesQueriesGet_ActivityPerformedByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "nullable": true, - "type": "string" - }, - "actorName": { - "nullable": true, - "type": "string" - }, - "actorType": { - "$ref": "#/components/schemas/Actors_ActorType" - }, - "id": { - "format": "guid", - "type": "string" - } - }, - "type": "object" - }, "V1ServiceOwnerDialogActivitiesQueriesNotificationCondition_NotificationCondition": { "additionalProperties": false, "properties": { @@ -2230,7 +2040,7 @@ "description": "The actor that performed the activity.", "oneOf": [ { - "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsCreate_ActivityPerformedByActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } ] }, @@ -2251,32 +2061,6 @@ }, "type": "object" }, - "V1ServiceOwnerDialogsCommandsCreate_ActivityPerformedByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person or organization that performed the activity. Mutually exclusive with ActorName.\nMight be omitted if ActorType is \u0022ServiceOwner\u0022.", - "example": "urn:altinn:person:identifier-no:12018212345", - "nullable": true, - "type": "string" - }, - "actorName": { - "description": "Specifies the name of the entity that performed the activity. Mutually exclusive with ActorId. If ActorId\nis supplied, the name will be automatically populated from the name registries.", - "example": "Ola Nordmann", - "nullable": true, - "type": "string" - }, - "actorType": { - "description": "What type of actor performed the activity.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - } - }, - "type": "object" - }, "V1ServiceOwnerDialogsCommandsCreate_ApiAction": { "additionalProperties": false, "properties": { @@ -2736,7 +2520,7 @@ "description": "The actor that sent the transmission.", "oneOf": [ { - "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsCreate_TransmissionSenderActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } ] }, @@ -2836,32 +2620,6 @@ }, "type": "object" }, - "V1ServiceOwnerDialogsCommandsCreate_TransmissionSenderActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person or organization that sent the transmission. Mutually exclusive with ActorName.\nMight be omitted if ActorType is \u0022ServiceOwner\u0022.", - "example": "urn:altinn:person:identifier-no:12018212345", - "nullable": true, - "type": "string" - }, - "actorName": { - "description": "Specifies the name of the entity that sent the transmission. Mutually exclusive with ActorId. If ActorId\nis supplied, the name will be automatically populated from the name registries.", - "example": "Ola Nordmann", - "nullable": true, - "type": "string" - }, - "actorType": { - "description": "The type of actor that sent the transmission.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - } - }, - "type": "object" - }, "V1ServiceOwnerDialogsCommandsUpdate_Activity": { "additionalProperties": false, "properties": { @@ -2896,7 +2654,7 @@ "description": "The actor that performed the activity.", "oneOf": [ { - "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_ActivityPerformedByActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } ] }, @@ -2917,32 +2675,6 @@ }, "type": "object" }, - "V1ServiceOwnerDialogsCommandsUpdate_ActivityPerformedByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person or organization that performed the activity. Mutually exclusive with ActorName.\nMight be omitted if ActorType is \u0022ServiceOwner\u0022.", - "example": "urn:altinn:person:identifier-no:12018212345", - "nullable": true, - "type": "string" - }, - "actorName": { - "description": "Specifies the name of the entity that performed the activity. Mutually exclusive with ActorId. If ActorId\nis supplied, the name will be automatically populated from the name registries.", - "example": "Ola Nordmann", - "nullable": true, - "type": "string" - }, - "actorType": { - "description": "What type of actor performed the activity.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - } - }, - "type": "object" - }, "V1ServiceOwnerDialogsCommandsUpdate_ApiAction": { "additionalProperties": false, "properties": { @@ -3389,7 +3121,7 @@ "description": "The actor that sent the transmission.", "oneOf": [ { - "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_TransmissionSenderActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } ] }, @@ -3489,32 +3221,6 @@ }, "type": "object" }, - "V1ServiceOwnerDialogsCommandsUpdate_TransmissionSenderActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person or organization that sent the transmission. Mutually exclusive with ActorName.\nMight be omitted if ActorType is \u0022ServiceOwner\u0022.", - "example": "urn:altinn:person:identifier-no:12018212345", - "nullable": true, - "type": "string" - }, - "actorName": { - "description": "Specifies the name of the entity that sent the transmission. Mutually exclusive with ActorId. If ActorId\nis supplied, the name will be automatically populated from the name registries.", - "example": "Ola Nordmann", - "nullable": true, - "type": "string" - }, - "actorType": { - "description": "The type of actor that sent the transmission.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - } - }, - "type": "object" - }, "V1ServiceOwnerDialogSeenLogsQueriesGet_SeenLog": { "additionalProperties": false, "properties": { @@ -3531,39 +3237,7 @@ "type": "string" }, "seenBy": { - "$ref": "#/components/schemas/V1ServiceOwnerDialogSeenLogsQueriesGet_SeenLogSeenByActor" - } - }, - "type": "object" - }, - "V1ServiceOwnerDialogSeenLogsQueriesGet_SeenLogSeenByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "type": "string" - }, - "actorName": { - "type": "string" - }, - "id": { - "format": "guid", - "type": "string" - } - }, - "type": "object" - }, - "V1ServiceOwnerDialogSeenLogsQueriesSearch_SeenByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "type": "string" - }, - "actorName": { - "type": "string" - }, - "id": { - "format": "guid", - "type": "string" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } }, "type": "object" @@ -3584,7 +3258,7 @@ "type": "string" }, "seenBy": { - "$ref": "#/components/schemas/V1ServiceOwnerDialogSeenLogsQueriesSearch_SeenByActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } }, "type": "object" @@ -3861,7 +3535,7 @@ "description": "The actor that performed the activity.", "oneOf": [ { - "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesGet_DialogActivityPerformedByActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } ] }, @@ -3882,30 +3556,6 @@ }, "type": "object" }, - "V1ServiceOwnerDialogsQueriesGet_DialogActivityPerformedByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person or organization that performed the activity.\nMay be omitted if ActorType is \u0022ServiceOwner\u0022.", - "nullable": true, - "type": "string" - }, - "actorName": { - "description": "The name of the person or organization that performed the activity.\nOnly set if the actor type is \u0022PartyRepresentative\u0022.", - "nullable": true, - "type": "string" - }, - "actorType": { - "description": "What type of actor performed the activity.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - } - }, - "type": "object" - }, "V1ServiceOwnerDialogsQueriesGet_DialogApiAction": { "additionalProperties": false, "properties": { @@ -4149,28 +3799,13 @@ "description": "The actor that saw the dialog revision.", "oneOf": [ { - "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesGet_DialogSeenLogSeenByActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } ] } }, "type": "object" }, - "V1ServiceOwnerDialogsQueriesGet_DialogSeenLogSeenByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person/business that saw the dialog revision.", - "example": "urn:altinn:person:identifier-no:01125512345\nurn:altinn:organization:identifier-no:912345678", - "type": "string" - }, - "actorName": { - "description": "The natural name of the person/business that saw the dialog revision.", - "type": "string" - } - }, - "type": "object" - }, "V1ServiceOwnerDialogsQueriesGet_DialogTransmission": { "additionalProperties": false, "properties": { @@ -4227,7 +3862,7 @@ "description": "The actor that sent the transmission.", "oneOf": [ { - "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesGet_DialogTransmissionSenderActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } ] }, @@ -4326,30 +3961,6 @@ }, "type": "object" }, - "V1ServiceOwnerDialogsQueriesGet_DialogTransmissionSenderActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person or organization that sent the transmission.", - "example": "urn:altinn:person:identifier-no:12018212345", - "type": "string" - }, - "actorName": { - "description": "The name of the person or organization that sent the transmission.", - "example": "Ola Nordmann", - "type": "string" - }, - "actorType": { - "description": "The type of actor that sent the transmission.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - } - }, - "type": "object" - }, "V1ServiceOwnerDialogsQueriesGet_SearchTag": { "additionalProperties": false, "properties": { @@ -4567,7 +4178,7 @@ "description": "The actor that performed the activity.", "oneOf": [ { - "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesSearch_DialogActivityPerformedByActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } ] }, @@ -4588,30 +4199,6 @@ }, "type": "object" }, - "V1ServiceOwnerDialogsQueriesSearch_DialogActivityPerformedByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person or organization that performed the activity.\nMay be omitted if ActorType is \u0022ServiceOwner\u0022.", - "nullable": true, - "type": "string" - }, - "actorName": { - "description": "The name of the person or organization that performed the activity.\nOnly set if the actor type is \u0022PartyRepresentative\u0022.", - "nullable": true, - "type": "string" - }, - "actorType": { - "description": "What type of actor performed the activity.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - } - }, - "type": "object" - }, "V1ServiceOwnerDialogsQueriesSearch_DialogSeenLog": { "additionalProperties": false, "properties": { @@ -4638,28 +4225,13 @@ "description": "The actor that saw the dialog revision.", "oneOf": [ { - "$ref": "#/components/schemas/V1ServiceOwnerDialogsQueriesSearch_DialogSeenLogSeenByActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } ] } }, "type": "object" }, - "V1ServiceOwnerDialogsQueriesSearch_DialogSeenLogSeenByActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the person/business that saw the dialog revision.", - "example": "urn:altinn:person:identifier-no:01125512345\nurn:altinn:organization:identifier-no:912345678", - "type": "string" - }, - "actorName": { - "description": "The natural name of the person/business that saw the dialog revision.", - "type": "string" - } - }, - "type": "object" - }, "V1ServiceOwnerDialogsUpdate_DialogRequest": { "additionalProperties": false, "properties": { @@ -4722,7 +4294,7 @@ "description": "The actor that sent the transmission.", "oneOf": [ { - "$ref": "#/components/schemas/V1ServiceOwnerDialogsCommandsUpdate_TransmissionSenderActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } ] }, @@ -4826,33 +4398,6 @@ }, "type": "object" }, - "V1ServiceOwnerDialogTransmissionsQueriesGet_SenderActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the actor.", - "type": "string" - }, - "actorName": { - "description": "The name of the actor.", - "type": "string" - }, - "actorType": { - "description": "The type of the actor.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - }, - "id": { - "description": "The unique identifier for the sender actor in UUIDv7 format.", - "format": "guid", - "type": "string" - } - }, - "type": "object" - }, "V1ServiceOwnerDialogTransmissionsQueriesGet_Transmission": { "additionalProperties": false, "properties": { @@ -4909,7 +4454,7 @@ "description": "The sender actor information for the transmission.", "oneOf": [ { - "$ref": "#/components/schemas/V1ServiceOwnerDialogTransmissionsQueriesGet_SenderActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } ] }, @@ -5013,33 +4558,6 @@ }, "type": "object" }, - "V1ServiceOwnerDialogTransmissionsQueriesSearch_SenderActor": { - "additionalProperties": false, - "properties": { - "actorId": { - "description": "The identifier of the actor.", - "type": "string" - }, - "actorName": { - "description": "The name of the actor.", - "type": "string" - }, - "actorType": { - "description": "The type of the actor.", - "oneOf": [ - { - "$ref": "#/components/schemas/Actors_ActorType" - } - ] - }, - "id": { - "description": "The unique identifier for the sender actor in UUIDv7 format.", - "format": "guid", - "type": "string" - } - }, - "type": "object" - }, "V1ServiceOwnerDialogTransmissionsQueriesSearch_Transmission": { "additionalProperties": false, "properties": { @@ -5096,7 +4614,7 @@ "description": "The sender actor information for the transmission.", "oneOf": [ { - "$ref": "#/components/schemas/V1ServiceOwnerDialogTransmissionsQueriesSearch_SenderActor" + "$ref": "#/components/schemas/V1ServiceOwnerCommonActors_Actor" } ] }, diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Common/Actors/ActorDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Common/Actors/ActorDto.cs new file mode 100644 index 000000000..e32459496 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Common/Actors/ActorDto.cs @@ -0,0 +1,26 @@ +using Digdir.Domain.Dialogporten.Domain.Actors; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors; + +public sealed class ActorDto +{ + /// + /// The type of actor that sent the transmission. + /// + public ActorType.Values ActorType { get; set; } + + /// + /// Specifies the name of the entity that sent the transmission. Mutually exclusive with ActorId. If ActorId + /// is supplied, the name will be automatically populated from the name registries. + /// + /// Ola Nordmann + public string? ActorName { get; set; } + + /// + /// The identifier of the person or organization that sent the transmission. Mutually exclusive with ActorName. + /// Might be omitted if ActorType is "ServiceOwner". + /// + /// urn:altinn:person:identifier-no:12018212345 + public string? ActorId { get; set; } + +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Common/Actors/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Common/Actors/MappingProfile.cs new file mode 100644 index 000000000..789d0026f --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Common/Actors/MappingProfile.cs @@ -0,0 +1,32 @@ +using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Common; +using Digdir.Domain.Dialogporten.Domain; +using Digdir.Domain.Dialogporten.Domain.Actors; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors; + +internal sealed class MappingProfile : Profile +{ + public MappingProfile() + { + + var actorDtoType = typeof(ActorDto); + var actorType = typeof(Actor); + + var derivedActorTypes = DomainAssemblyMarker + .Assembly + .GetTypes() + .Where(x => x.IsClass && !x.IsAbstract && x.IsSubclassOf(actorType)) + .ToList(); + + CreateMap() + .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)) + .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); + + foreach (var outputActor in derivedActorTypes) + { + CreateMap(outputActor, actorDtoType) + .IncludeBase(actorType, actorDtoType); + } + } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/ActivityDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/ActivityDto.cs index 75aefae05..9450a2311 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/ActivityDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/ActivityDto.cs @@ -1,5 +1,5 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogActivities.Queries.Get; @@ -14,14 +14,6 @@ public sealed class ActivityDto public Guid? TransmissionId { get; set; } - public PerformedByActorDto PerformedBy { get; set; } = null!; + public ActorDto PerformedBy { get; set; } = null!; public List Description { get; set; } = []; } - -public sealed class PerformedByActorDto -{ - public Guid Id { get; set; } - public ActorType.Values ActorType { get; set; } - public string? ActorName { get; set; } - public string? ActorId { get; set; } -} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/MappingProfile.cs index dad292bc6..acc0c1250 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogActivities/Queries/Get/MappingProfile.cs @@ -1,5 +1,4 @@ using AutoMapper; -using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogActivities.Queries.Get; @@ -11,8 +10,5 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)) - .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogLabelAssignmentLog/Queries/Search/LabelAssignmentLogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogLabelAssignmentLog/Queries/Search/LabelAssignmentLogDto.cs index 8e20618ae..aa6bce8b8 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogLabelAssignmentLog/Queries/Search/LabelAssignmentLogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogLabelAssignmentLog/Queries/Search/LabelAssignmentLogDto.cs @@ -1,3 +1,5 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors; + namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogLabelAssignmentLog.Queries.Search; public sealed class LabelAssignmentLogDto @@ -8,14 +10,6 @@ public sealed class LabelAssignmentLogDto public string Action { get; set; } = null!; - public LabelAssignmentLogActorDto PerformedBy { get; set; } = null!; - -} - -public sealed class LabelAssignmentLogActorDto -{ - - public string ActorName { get; set; } = null!; + public ActorDto PerformedBy { get; set; } = null!; - public string ActorId { get; set; } = null!; } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogLabelAssignmentLog/Queries/Search/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogLabelAssignmentLog/Queries/Search/MappingProfile.cs index 9d801ae4e..75104715d 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogLabelAssignmentLog/Queries/Search/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogLabelAssignmentLog/Queries/Search/MappingProfile.cs @@ -8,6 +8,5 @@ public sealed class MappingProfile : Profile public MappingProfile() { CreateMap(); - CreateMap(); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/MappingProfile.cs index c3cb377a3..df8360782 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/MappingProfile.cs @@ -1,5 +1,4 @@ using AutoMapper; -using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Get; @@ -11,8 +10,5 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); - CreateMap() - .ForMember(dest => dest.ActorId, - opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/SeenLogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/SeenLogDto.cs index eab7c1eda..6d210ae99 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/SeenLogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Get/SeenLogDto.cs @@ -1,18 +1,14 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors; + namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Get; public sealed class SeenLogDto { public Guid Id { get; set; } public DateTimeOffset SeenAt { get; set; } - public SeenLogSeenByActorDto SeenBy { get; set; } = null!; + public ActorDto SeenBy { get; set; } = null!; public bool IsViaServiceOwner { get; set; } public bool IsCurrentEndUser { get; set; } } -public sealed class SeenLogSeenByActorDto -{ - public Guid Id { get; set; } - public string ActorName { get; set; } = null!; - public string ActorId { get; set; } = null!; -} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/MappingProfile.cs index be1f80c87..e1b6b59f6 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/MappingProfile.cs @@ -1,5 +1,4 @@ using AutoMapper; -using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Search; @@ -11,7 +10,5 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); - CreateMap() - .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SeenLogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SeenLogDto.cs index 0bf9b14c0..b766d2e6c 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SeenLogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogSeenLogs/Queries/Search/SeenLogDto.cs @@ -1,3 +1,5 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors; + namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogSeenLogs.Queries.Search; public sealed class SeenLogDto @@ -5,15 +7,8 @@ public sealed class SeenLogDto public Guid Id { get; set; } public DateTimeOffset SeenAt { get; set; } - public SeenLogSeenByActorDto SeenBy { get; set; } = null!; + public ActorDto SeenBy { get; set; } = null!; public bool IsViaServiceOwner { get; set; } public bool IsCurrentEndUser { get; set; } } - -public sealed class SeenLogSeenByActorDto -{ - public Guid Id { get; set; } - public string ActorName { get; set; } = null!; - public string ActorId { get; set; } = null!; -} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/MappingProfile.cs index 315cc06f2..94059a6ff 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/MappingProfile.cs @@ -14,8 +14,6 @@ public MappingProfile() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)) .ForMember(dest => dest.DeletedAt, opt => opt.MapFrom(src => src.Dialog.DeletedAt)); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)); CreateMap?, ContentDto?>() .ConvertUsing>(); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/TransmissionDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/TransmissionDto.cs index b065b0c66..8a482b8b2 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/TransmissionDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/TransmissionDto.cs @@ -1,6 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors; using Digdir.Domain.Dialogporten.Domain.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; @@ -52,7 +52,7 @@ public sealed class TransmissionDto /// /// The sender actor information for the transmission. /// - public SenderActorDto Sender { get; set; } = null!; + public ActorDto Sender { get; set; } = null!; /// /// The content of the transmission. @@ -65,29 +65,6 @@ public sealed class TransmissionDto public List Attachments { get; set; } = []; } -public sealed class SenderActorDto -{ - /// - /// The unique identifier for the sender actor in UUIDv7 format. - /// - public Guid Id { get; set; } - - /// - /// The type of the actor. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// The name of the actor. - /// - public string ActorName { get; set; } = null!; - - /// - /// The identifier of the actor. - /// - public string ActorId { get; set; } = null!; -} - public sealed class ContentDto { /// diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/MappingProfile.cs index 32b686063..8feaae07c 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/MappingProfile.cs @@ -14,8 +14,6 @@ public MappingProfile() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)) .ForMember(dest => dest.DeletedAt, opt => opt.MapFrom(src => src.Dialog.DeletedAt)); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)); CreateMap?, ContentDto?>() .ConvertUsing>(); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/TransmissionDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/TransmissionDto.cs index e35a0b21e..49b6132bb 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/TransmissionDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/TransmissionDto.cs @@ -1,6 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors; using Digdir.Domain.Dialogporten.Domain.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; @@ -52,7 +52,7 @@ public sealed class TransmissionDto /// /// The sender actor information for the transmission. /// - public SenderActorDto Sender { get; set; } = null!; + public ActorDto Sender { get; set; } = null!; /// /// The content of the transmission. @@ -65,29 +65,6 @@ public sealed class TransmissionDto public List Attachments { get; set; } = []; } -public sealed class SenderActorDto -{ - /// - /// The unique identifier for the sender actor in UUIDv7 format. - /// - public Guid Id { get; set; } - - /// - /// The type of the actor. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// The name of the actor. - /// - public string ActorName { get; set; } = null!; - - /// - /// The identifier of the actor. - /// - public string ActorId { get; set; } = null!; -} - public sealed class ContentDto { /// diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/DialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/DialogDto.cs index d20183a80..2389ae6ff 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/DialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/DialogDto.cs @@ -1,6 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors; using Digdir.Domain.Dialogporten.Domain.Attachments; using Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; @@ -217,7 +217,7 @@ public sealed class DialogTransmissionDto /// /// The actor that sent the transmission. /// - public DialogTransmissionSenderActorDto Sender { get; set; } = null!; + public ActorDto Sender { get; set; } = null!; /// /// The transmission unstructured text content. @@ -245,7 +245,7 @@ public sealed class DialogSeenLogDto /// /// The actor that saw the dialog revision. /// - public DialogSeenLogSeenByActorDto SeenBy { get; set; } = null!; + public ActorDto SeenBy { get; set; } = null!; /// /// Flag indicating whether the seen log entry was created via the service owner. @@ -260,42 +260,6 @@ public sealed class DialogSeenLogDto public bool IsCurrentEndUser { get; set; } } -public sealed class DialogSeenLogSeenByActorDto -{ - /// - /// The natural name of the person/business that saw the dialog revision. - /// - public string ActorName { get; set; } = null!; - - /// - /// The identifier of the person/business that saw the dialog revision. - /// - /// - /// urn:altinn:person:identifier-no:01125512345 - /// urn:altinn:organization:identifier-no:912345678 - /// - public string ActorId { get; set; } = null!; -} - -public sealed class DialogTransmissionSenderActorDto -{ - /// - /// The type of actor that sent the transmission. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// The name of the person or organization that sent the transmission. - /// - /// Ola Nordmann - public string ActorName { get; set; } = null!; - - /// - /// The identifier of the person or organization that sent the transmission. - /// - /// urn:altinn:person:identifier-no:12018212345 - public string ActorId { get; set; } = null!; -} public sealed class ContentDto { @@ -381,7 +345,7 @@ public sealed class DialogActivityDto /// /// The actor that performed the activity. /// - public DialogActivityPerformedByActorDto PerformedBy { get; set; } = null!; + public ActorDto PerformedBy { get; set; } = null!; /// /// Unstructured text describing the activity. Only set if the activity type is "Information". @@ -389,25 +353,6 @@ public sealed class DialogActivityDto public List Description { get; set; } = []; } -public sealed class DialogActivityPerformedByActorDto -{ - /// - /// The type of actor that performed the activity. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// The name of the person or organization that performed the activity. - /// Only set if the actor type is "PartyRepresentative". - /// - public string? ActorName { get; set; } - - /// - /// The identifier of the person or organization that performed the activity. - /// May be omitted if ActorType is "ServiceOwner". - /// - public string? ActorId { get; set; } -} public sealed class DialogApiActionDto { diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/MappingProfile.cs index f98167e5f..04153d89c 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Get/MappingProfile.cs @@ -1,5 +1,4 @@ using AutoMapper; -using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Domain.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; @@ -24,16 +23,9 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); - CreateMap() - .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); - CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)) - .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); - CreateMap(); CreateMap() @@ -51,10 +43,6 @@ public MappingProfile() CreateMap?, ContentDto?>() .ConvertUsing>(); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)) - .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); - CreateMap?, DialogTransmissionContentDto?>() .ConvertUsing>(); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/DialogDtoBase.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/DialogDtoBase.cs index 7d904e170..e523f2cbe 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/DialogDtoBase.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/DialogDtoBase.cs @@ -1,5 +1,5 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors; using Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; @@ -131,7 +131,7 @@ public sealed class DialogSeenLogDto /// /// The actor that saw the dialog revision. /// - public DialogSeenLogSeenByActorDto SeenBy { get; set; } = null!; + public ActorDto SeenBy { get; set; } = null!; /// /// Flag indicating whether the seen log entry was created via the service owner. @@ -146,23 +146,6 @@ public sealed class DialogSeenLogDto public bool IsCurrentEndUser { get; set; } } -public sealed class DialogSeenLogSeenByActorDto -{ - /// - /// The natural name of the person/business that saw the dialog revision. - /// - public string ActorName { get; set; } = null!; - - /// - /// The identifier of the person/business that saw the dialog revision. - /// - /// - /// urn:altinn:person:identifier-no:01125512345 - /// urn:altinn:organization:identifier-no:912345678 - /// - public string ActorId { get; set; } = null!; -} - public sealed class DialogActivityDto { /// @@ -195,30 +178,10 @@ public sealed class DialogActivityDto /// /// The actor that performed the activity. /// - public DialogActivityPerformedByActorDto PerformedBy { get; set; } = null!; + public ActorDto PerformedBy { get; set; } = null!; /// /// Unstructured text describing the activity. Only set if the activity type is "Information". /// public List Description { get; set; } = []; } - -public sealed class DialogActivityPerformedByActorDto -{ - /// - /// What type of actor performed the activity. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// The name of the person or organization that performed the activity. - /// Only set if the actor type is "PartyRepresentative". - /// - public string? ActorName { get; set; } - - /// - /// The identifier of the person or organization that performed the activity. - /// May be omitted if ActorType is "ServiceOwner". - /// - public string? ActorId { get; set; } -} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/MappingProfile.cs index badefbcb3..d2bee9209 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/Dialogs/Queries/Search/MappingProfile.cs @@ -1,5 +1,4 @@ using AutoMapper; -using Digdir.Domain.Dialogporten.Application.Common; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Domain.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; @@ -33,16 +32,9 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); - CreateMap() - .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); - CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)) - .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => IdentifierMasker.GetMaybeMaskedIdentifier(src.ActorId))); - CreateMap?, ContentDto?>() .ConvertUsing>(); } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Common/Actors/ActorDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Common/Actors/ActorDto.cs new file mode 100644 index 000000000..3cea258a3 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Common/Actors/ActorDto.cs @@ -0,0 +1,25 @@ +using Digdir.Domain.Dialogporten.Domain.Actors; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; + +public sealed class ActorDto +{ + /// + /// The type of actor that sent the transmission. + /// + public ActorType.Values ActorType { get; set; } + + /// + /// Specifies the name of the entity that sent the transmission. Mutually exclusive with ActorId. If ActorId + /// is supplied, the name will be automatically populated from the name registries. + /// + /// Ola Nordmann + public string? ActorName { get; set; } + + /// + /// The identifier of the person or organization that sent the transmission. Mutually exclusive with ActorName. + /// Might be omitted if ActorType is "ServiceOwner". + /// + /// urn:altinn:person:identifier-no:12018212345 + public string? ActorId { get; set; } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Common/Actors/ActorValidator.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Common/Actors/ActorValidator.cs new file mode 100644 index 000000000..0227e2be1 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Common/Actors/ActorValidator.cs @@ -0,0 +1,26 @@ +using Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation; +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Actors; +using Digdir.Domain.Dialogporten.Domain.Actors; +using FluentValidation; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; + +public sealed class ActorValidator : AbstractValidator +{ + + public ActorValidator() + { + RuleFor(x => x.ActorType) + .IsInEnum(); + + RuleFor(x => x) + .Must(dto => (dto.ActorId is null || dto.ActorName is null) && + ((dto.ActorType == ActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) || + (dto.ActorType != ActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null)))) + .WithMessage(ActorValidationErrorMessages.ActorIdActorNameExclusiveOr); + + RuleFor(x => x.ActorId!) + .IsValidPartyIdentifier() + .When(x => x.ActorId is not null); + } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Common/Actors/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Common/Actors/MappingProfile.cs new file mode 100644 index 000000000..82c5a164e --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Common/Actors/MappingProfile.cs @@ -0,0 +1,40 @@ +using AutoMapper; +using Digdir.Domain.Dialogporten.Domain; +using Digdir.Domain.Dialogporten.Domain.Actors; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; + +internal sealed class MappingProfile : Profile +{ + public MappingProfile() + { + var actorDtoType = typeof(ActorDto); + var actorType = typeof(Actor); + + var derivedActorTypes = DomainAssemblyMarker + .Assembly + .GetTypes() + .Where(x => x.IsClass && !x.IsAbstract && x.IsSubclassOf(actorType)) + .ToList(); + + CreateMap() + .ForMember(dest => dest.ActorType, opt => opt.Ignore()) + .ForMember(dest => dest.ActorTypeId, opt => opt.MapFrom(src => src.ActorType)); + + foreach (var inputActor in derivedActorTypes) + { + CreateMap(actorDtoType, inputActor) + .IncludeBase(actorDtoType, actorType); + } + + CreateMap() + .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => src.ActorId)) + .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)); + + foreach (var outputActor in derivedActorTypes) + { + CreateMap(outputActor, actorDtoType) + .IncludeBase(actorType, actorDtoType); + } + } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/ActivityDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/ActivityDto.cs index 4175bfe73..30b290f2e 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/ActivityDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/ActivityDto.cs @@ -1,5 +1,5 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogActivities.Queries.Get; @@ -16,14 +16,6 @@ public sealed class ActivityDto public Guid? TransmissionId { get; set; } - public ActivityPerformedByActorDto PerformedBy { get; set; } = null!; + public ActorDto PerformedBy { get; set; } = null!; public List Description { get; set; } = []; } - -public sealed class ActivityPerformedByActorDto -{ - public Guid Id { get; set; } - public ActorType.Values ActorType { get; set; } - public string? ActorName { get; set; } - public string? ActorId { get; set; } -} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/MappingProfile.cs index e5c55c1df..95a39be73 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogActivities/Queries/Get/MappingProfile.cs @@ -10,9 +10,5 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)) .ForMember(dest => dest.DeletedAt, opt => opt.MapFrom(src => src.Dialog.DeletedAt)); - - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)); - } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/MappingProfile.cs index 3b03e5be7..40762b058 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/MappingProfile.cs @@ -10,6 +10,5 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); - CreateMap(); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/SeenLogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/SeenLogDto.cs index bc679ce0a..44c5384d5 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/SeenLogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Get/SeenLogDto.cs @@ -1,3 +1,5 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; + namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogSeenLogs.Queries.Get; public sealed class SeenLogDto @@ -5,14 +7,8 @@ public sealed class SeenLogDto public Guid Id { get; set; } public DateTimeOffset SeenAt { get; set; } - public SeenLogSeenByActorDto SeenBy { get; set; } = null!; + public ActorDto SeenBy { get; set; } = null!; public bool? IsViaServiceOwner { get; set; } } -public sealed class SeenLogSeenByActorDto -{ - public Guid Id { get; set; } - public string ActorName { get; set; } = null!; - public string ActorId { get; set; } = null!; -} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/MappingProfile.cs index 00aa44509..4f3492709 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/MappingProfile.cs @@ -10,6 +10,5 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); - CreateMap(); } } diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SeenLogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SeenLogDto.cs index 818d7e208..b62276943 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SeenLogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogSeenLogs/Queries/Search/SeenLogDto.cs @@ -1,3 +1,5 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; + namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogSeenLogs.Queries.Search; public sealed class SeenLogDto @@ -5,14 +7,8 @@ public sealed class SeenLogDto public Guid Id { get; set; } public DateTimeOffset SeenAt { get; set; } - public SeenByActorDto SeenBy { get; set; } = null!; + public ActorDto SeenBy { get; set; } = null!; public bool? IsViaServiceOwner { get; set; } } -public sealed class SeenByActorDto -{ - public Guid Id { get; set; } - public string ActorName { get; set; } = null!; - public string ActorId { get; set; } = null!; -} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/MappingProfile.cs index 5d219c6e8..a2b3961fd 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/MappingProfile.cs @@ -14,8 +14,6 @@ public MappingProfile() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)) .ForMember(dest => dest.DeletedAt, opt => opt.MapFrom(src => src.Dialog.DeletedAt)); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)); CreateMap?, ContentDto?>() .ConvertUsing>(); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/TransmissionDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/TransmissionDto.cs index 9bbbce81d..e94fb55fd 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/TransmissionDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/TransmissionDto.cs @@ -1,6 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; using Digdir.Domain.Dialogporten.Domain.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; @@ -46,7 +46,7 @@ public sealed class TransmissionDto /// /// The sender actor information for the transmission. /// - public SenderActorDto Sender { get; set; } = null!; + public ActorDto Sender { get; set; } = null!; /// /// The content of the transmission. @@ -59,28 +59,6 @@ public sealed class TransmissionDto public List Attachments { get; set; } = []; } -public sealed class SenderActorDto -{ - /// - /// The unique identifier for the sender actor in UUIDv7 format. - /// - public Guid Id { get; set; } - - /// - /// The type of the actor. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// The name of the actor. - /// - public string ActorName { get; set; } = null!; - - /// - /// The identifier of the actor. - /// - public string ActorId { get; set; } = null!; -} public sealed class ContentDto { diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/MappingProfile.cs index 559a2a1b0..292410634 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/MappingProfile.cs @@ -14,8 +14,6 @@ public MappingProfile() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)) .ForMember(dest => dest.DeletedAt, opt => opt.MapFrom(src => src.Dialog.DeletedAt)); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)); CreateMap?, ContentDto?>() .ConvertUsing>(); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/TransmissionDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/TransmissionDto.cs index 0a2cc431e..5fe23c019 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/TransmissionDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/TransmissionDto.cs @@ -1,6 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; using Digdir.Domain.Dialogporten.Domain.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; @@ -46,7 +46,7 @@ public sealed class TransmissionDto /// /// The sender actor information for the transmission. /// - public SenderActorDto Sender { get; set; } = null!; + public ActorDto Sender { get; set; } = null!; /// /// The content of the transmission. @@ -59,29 +59,6 @@ public sealed class TransmissionDto public List Attachments { get; set; } = []; } -public sealed class SenderActorDto -{ - /// - /// The unique identifier for the sender actor in UUIDv7 format. - /// - public Guid Id { get; set; } - - /// - /// The type of the actor. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// The name of the actor. - /// - public string ActorName { get; set; } = null!; - - /// - /// The identifier of the actor. - /// - public string ActorId { get; set; } = null!; -} - public sealed class ContentDto { /// diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommandValidator.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommandValidator.cs index 4f04c7a75..42a867469 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommandValidator.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogCommandValidator.cs @@ -2,10 +2,9 @@ using Digdir.Domain.Dialogporten.Application.Common.Extensions.Enumerables; using Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation; using Digdir.Domain.Dialogporten.Application.Externals.Presentation; -using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Actors; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; using Digdir.Domain.Dialogporten.Domain.Common; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; @@ -150,7 +149,7 @@ public CreateDialogCommandValidator( internal sealed class CreateDialogDialogTransmissionDtoValidator : AbstractValidator { public CreateDialogDialogTransmissionDtoValidator( - IValidator actorValidator, + IValidator actorValidator, IValidator contentValidator, IValidator attachmentValidator) { @@ -409,7 +408,7 @@ internal sealed class CreateDialogDialogActivityDtoValidator : AbstractValidator { public CreateDialogDialogActivityDtoValidator( IValidator> localizationsValidator, - IValidator actorValidator) + IValidator actorValidator) { RuleFor(x => x.Id) .IsValidUuidV7() @@ -443,41 +442,3 @@ public CreateDialogDialogActivityDtoValidator( .When(x => x.Type == DialogActivityType.Values.TransmissionOpened); } } - -internal sealed class CreateDialogDialogTransmissionActorDtoValidator : AbstractValidator -{ - public CreateDialogDialogTransmissionActorDtoValidator() - { - RuleFor(x => x.ActorType) - .IsInEnum(); - - RuleFor(x => x) - .Must(dto => (dto.ActorId is null || dto.ActorName is null) && - ((dto.ActorType == ActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) || - (dto.ActorType != ActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null)))) - .WithMessage(ActorValidationErrorMessages.ActorIdActorNameExclusiveOr); - - RuleFor(x => x.ActorId!) - .IsValidPartyIdentifier() - .When(x => x.ActorId is not null); - } -} - -internal sealed class CreateDialogDialogActivityActorDtoValidator : AbstractValidator -{ - public CreateDialogDialogActivityActorDtoValidator() - { - RuleFor(x => x.ActorType) - .IsInEnum(); - - RuleFor(x => x) - .Must(dto => (dto.ActorId is null || dto.ActorName is null) && - ((dto.ActorType == ActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) || - (dto.ActorType != ActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null)))) - .WithMessage(ActorValidationErrorMessages.ActorIdActorNameExclusiveOr); - - RuleFor(x => x.ActorId!) - .IsValidPartyIdentifier() - .When(x => x.ActorId is not null); - } -} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs index 1458d12a5..5b7791a71 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/CreateDialogDto.cs @@ -1,6 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; using Digdir.Domain.Dialogporten.Domain.Attachments; using Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; @@ -193,7 +193,7 @@ public sealed class TransmissionDto /// /// The actor that sent the transmission. /// - public TransmissionSenderActorDto Sender { get; set; } = null!; + public ActorDto Sender { get; set; } = null!; /// /// The transmission unstructured text content. @@ -305,7 +305,7 @@ public sealed class ActivityDto /// /// The actor that performed the activity. /// - public ActivityPerformedByActorDto PerformedBy { get; set; } = null!; + public ActorDto PerformedBy { get; set; } = null!; /// /// Unstructured text describing the activity. Only set if the activity type is "Information". @@ -313,50 +313,6 @@ public sealed class ActivityDto public List Description { get; set; } = []; } -public sealed class ActivityPerformedByActorDto -{ - /// - /// What type of actor performed the activity. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// Specifies the name of the entity that performed the activity. Mutually exclusive with ActorId. If ActorId - /// is supplied, the name will be automatically populated from the name registries. - /// - /// Ola Nordmann - public string? ActorName { get; set; } - - /// - /// The identifier of the person or organization that performed the activity. Mutually exclusive with ActorName. - /// Might be omitted if ActorType is "ServiceOwner". - /// - /// urn:altinn:person:identifier-no:12018212345 - public string? ActorId { get; set; } -} - -public sealed class TransmissionSenderActorDto -{ - /// - /// The type of actor that sent the transmission. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// Specifies the name of the entity that sent the transmission. Mutually exclusive with ActorId. If ActorId - /// is supplied, the name will be automatically populated from the name registries. - /// - /// Ola Nordmann - public string? ActorName { get; set; } - - /// - /// The identifier of the person or organization that sent the transmission. Mutually exclusive with ActorName. - /// Might be omitted if ActorType is "ServiceOwner". - /// - /// urn:altinn:person:identifier-no:12018212345 - public string? ActorId { get; set; } -} - public sealed class ApiActionDto { /// diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/MappingProfile.cs index abc884a5f..702eef3d2 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Create/MappingProfile.cs @@ -40,20 +40,12 @@ public MappingProfile() .ForMember(dest => dest.Type, opt => opt.Ignore()) .ForMember(dest => dest.TypeId, opt => opt.MapFrom(src => src.Type)); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.Ignore()) - .ForMember(dest => dest.ActorTypeId, opt => opt.MapFrom(src => src.ActorType)); - CreateMap?>() .ConvertUsing>(); CreateMap?>() .ConvertUsing>(); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.Ignore()) - .ForMember(dest => dest.ActorTypeId, opt => opt.MapFrom(src => src.ActorType)); - CreateMap() .ForMember(dest => dest.Type, opt => opt.Ignore()) .ForMember(dest => dest.TypeId, opt => opt.MapFrom(src => src.Type)); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/MappingProfile.cs index d13fd80f0..49a6fa612 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/MappingProfile.cs @@ -66,10 +66,6 @@ public MappingProfile() .ForMember(dest => dest.Type, opt => opt.Ignore()) .ForMember(dest => dest.TypeId, opt => opt.MapFrom(src => src.Type)); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.Ignore()) - .ForMember(dest => dest.ActorTypeId, opt => opt.MapFrom(src => src.ActorType)); - CreateMap() .ForMember(dest => dest.Type, opt => opt.Ignore()) .ForMember(dest => dest.TypeId, opt => opt.MapFrom(src => src.Type)); @@ -77,10 +73,6 @@ public MappingProfile() CreateMap?>() .ConvertUsing>(); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.Ignore()) - .ForMember(dest => dest.ActorTypeId, opt => opt.MapFrom(src => src.ActorType)); - CreateMap(); CreateMap() @@ -98,7 +90,6 @@ public MappingProfile() .ForMember(dest => dest.Transmissions, opt => opt.Ignore()); CreateMap(); CreateMap(); - CreateMap(); CreateMap(); CreateMap(); CreateMap(); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommandValidator.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommandValidator.cs index 8778a3550..909d47e7a 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommandValidator.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogCommandValidator.cs @@ -2,10 +2,9 @@ using Digdir.Domain.Dialogporten.Application.Common.Extensions.Enumerables; using Digdir.Domain.Dialogporten.Application.Common.Extensions.FluentValidation; using Digdir.Domain.Dialogporten.Application.Externals.Presentation; -using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Actors; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; using Digdir.Domain.Dialogporten.Domain.Common; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; @@ -139,25 +138,6 @@ public UpdateDialogTransmissionAttachmentUrlDtoValidator() } } -internal sealed class UpdateDialogDialogTransmissionActorDtoValidator : AbstractValidator -{ - public UpdateDialogDialogTransmissionActorDtoValidator() - { - RuleFor(x => x.ActorType) - .IsInEnum(); - - RuleFor(x => x) - .Must(dto => (dto.ActorId is null || dto.ActorName is null) && - ((dto.ActorType == ActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) || - (dto.ActorType != ActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null)))) - .WithMessage(ActorValidationErrorMessages.ActorIdActorNameExclusiveOr); - - RuleFor(x => x.ActorId!) - .IsValidPartyIdentifier() - .When(x => x.ActorId is not null); - } -} - internal sealed class UpdateDialogDialogTransmissionContentDtoValidator : AbstractValidator { private static readonly NullabilityInfoContext Context = new(); @@ -201,7 +181,7 @@ public UpdateDialogDialogTransmissionContentDtoValidator() internal sealed class UpdateDialogDialogTransmissionDtoValidator : AbstractValidator { public UpdateDialogDialogTransmissionDtoValidator( - IValidator actorValidator, + IValidator actorValidator, IValidator contentValidator, IValidator attachmentValidator) { @@ -394,7 +374,7 @@ internal sealed class UpdateDialogDialogActivityDtoValidator : AbstractValidator { public UpdateDialogDialogActivityDtoValidator( IValidator> localizationsValidator, - IValidator actorValidator) + IValidator actorValidator) { RuleFor(x => x.Id) .IsValidUuidV7() @@ -428,22 +408,3 @@ public UpdateDialogDialogActivityDtoValidator( .When(x => x.Type == DialogActivityType.Values.TransmissionOpened); } } - -internal sealed class UpdateDialogDialogActivityActorDtoValidator : AbstractValidator -{ - public UpdateDialogDialogActivityActorDtoValidator() - { - RuleFor(x => x.ActorType) - .IsInEnum(); - - RuleFor(x => x) - .Must(dto => (dto.ActorId is null || dto.ActorName is null) && - ((dto.ActorType == ActorType.Values.ServiceOwner && dto.ActorId is null && dto.ActorName is null) || - (dto.ActorType != ActorType.Values.ServiceOwner && (dto.ActorId is not null || dto.ActorName is not null)))) - .WithMessage(ActorValidationErrorMessages.ActorIdActorNameExclusiveOr); - - RuleFor(x => x.ActorId!) - .IsValidPartyIdentifier() - .When(x => x.ActorId is not null); - } -} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDto.cs index f65c6347e..44b15b702 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Commands/Update/UpdateDialogDto.cs @@ -1,6 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; using Digdir.Domain.Dialogporten.Domain.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; @@ -145,7 +145,7 @@ public class TransmissionDto /// /// The actor that sent the transmission. /// - public TransmissionSenderActorDto Sender { get; set; } = null!; + public ActorDto Sender { get; set; } = null!; /// /// The transmission unstructured text content. @@ -177,28 +177,6 @@ public sealed class TransmissionContentDto public ContentValueDto? ContentReference { get; set; } } -public sealed class TransmissionSenderActorDto -{ - /// - /// The type of actor that sent the transmission. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// Specifies the name of the entity that sent the transmission. Mutually exclusive with ActorId. If ActorId - /// is supplied, the name will be automatically populated from the name registries. - /// - /// Ola Nordmann - public string? ActorName { get; set; } - - /// - /// The identifier of the person or organization that sent the transmission. Mutually exclusive with ActorName. - /// Might be omitted if ActorType is "ServiceOwner". - /// - /// urn:altinn:person:identifier-no:12018212345 - public string? ActorId { get; set; } -} - public sealed class ContentDto { /// @@ -274,7 +252,7 @@ public class ActivityDto /// /// The actor that performed the activity. /// - public ActivityPerformedByActorDto PerformedBy { get; set; } = null!; + public ActorDto PerformedBy { get; set; } = null!; /// /// Unstructured text describing the activity. Only set if the activity type is "Information". @@ -282,28 +260,6 @@ public class ActivityDto public List Description { get; set; } = []; } -public sealed class ActivityPerformedByActorDto -{ - /// - /// What type of actor performed the activity. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// Specifies the name of the entity that performed the activity. Mutually exclusive with ActorId. If ActorId - /// is supplied, the name will be automatically populated from the name registries. - /// - /// Ola Nordmann - public string? ActorName { get; set; } - - /// - /// The identifier of the person or organization that performed the activity. Mutually exclusive with ActorName. - /// Might be omitted if ActorType is "ServiceOwner". - /// - /// urn:altinn:person:identifier-no:12018212345 - public string? ActorId { get; set; } -} - public sealed class ApiActionDto { /// diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/DialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/DialogDto.cs index 84bc3ae9c..8d5ce8951 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/DialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/DialogDto.cs @@ -1,6 +1,6 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; using Digdir.Domain.Dialogporten.Domain.Attachments; using Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; @@ -227,7 +227,7 @@ public sealed class DialogTransmissionDto /// /// The actor that sent the transmission. /// - public DialogTransmissionSenderActorDto Sender { get; set; } = null!; + public ActorDto Sender { get; set; } = null!; /// /// The transmission unstructured text content. @@ -255,7 +255,7 @@ public sealed class DialogSeenLogDto /// /// The actor that saw the dialog revision. /// - public DialogSeenLogSeenByActorDto SeenBy { get; set; } = null!; + public ActorDto SeenBy { get; set; } = null!; /// /// Flag indicating whether the seen log entry was created via the service owner. @@ -270,42 +270,6 @@ public sealed class DialogSeenLogDto public bool IsCurrentEndUser { get; set; } } -public sealed class DialogSeenLogSeenByActorDto -{ - /// - /// The natural name of the person/business that saw the dialog revision. - /// - public string ActorName { get; set; } = null!; - - /// - /// The identifier of the person/business that saw the dialog revision. - /// - /// - /// urn:altinn:person:identifier-no:01125512345 - /// urn:altinn:organization:identifier-no:912345678 - /// - public string ActorId { get; set; } = null!; -} - -public sealed class DialogTransmissionSenderActorDto -{ - /// - /// The type of actor that sent the transmission. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// The name of the person or organization that sent the transmission. - /// - /// Ola Nordmann - public string ActorName { get; set; } = null!; - - /// - /// The identifier of the person or organization that sent the transmission. - /// - /// urn:altinn:person:identifier-no:12018212345 - public string ActorId { get; set; } = null!; -} public sealed class ContentDto { @@ -400,7 +364,7 @@ public sealed class DialogActivityDto /// /// The actor that performed the activity. /// - public DialogActivityPerformedByActorDto PerformedBy { get; set; } = null!; + public ActorDto PerformedBy { get; set; } = null!; /// /// Unstructured text describing the activity. Only set if the activity type is "Information". @@ -408,25 +372,6 @@ public sealed class DialogActivityDto public List Description { get; set; } = []; } -public sealed class DialogActivityPerformedByActorDto -{ - /// - /// What type of actor performed the activity. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// The name of the person or organization that performed the activity. - /// Only set if the actor type is "PartyRepresentative". - /// - public string? ActorName { get; set; } - - /// - /// The identifier of the person or organization that performed the activity. - /// May be omitted if ActorType is "ServiceOwner". - /// - public string? ActorId { get; set; } -} public sealed class DialogApiActionDto { diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/MappingProfile.cs index 4e01c904d..2b46a8a3c 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/MappingProfile.cs @@ -1,5 +1,6 @@ using AutoMapper; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; using Digdir.Domain.Dialogporten.Domain.Attachments; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actions; @@ -19,14 +20,12 @@ public MappingProfile() .ForMember(dest => dest.SeenSinceLastUpdate, opt => opt.Ignore()) .ForMember(dest => dest.SystemLabel, opt => opt.MapFrom(src => src.DialogEndUserContext.SystemLabelId)); - CreateMap(); + CreateMap(); CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)); CreateMap(); CreateMap() @@ -45,9 +44,6 @@ public MappingProfile() CreateMap?, ContentDto?>() .ConvertUsing>(); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)); - CreateMap?, DialogTransmissionContentDto?>() .ConvertUsing>(); diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/DialogDtoBase.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/DialogDtoBase.cs index 1f28661fc..f086ed884 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/DialogDtoBase.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/DialogDtoBase.cs @@ -1,5 +1,5 @@ using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; -using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; using Digdir.Domain.Dialogporten.Domain.DialogEndUserContexts.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Activities; @@ -143,7 +143,7 @@ public sealed class DialogSeenLogDto /// /// The actor that saw the dialog revision. /// - public DialogSeenLogSeenByActorDto SeenBy { get; set; } = null!; + public ActorDto SeenBy { get; set; } = null!; /// /// Flag indicating whether the seen log entry was created via the service owner. @@ -158,22 +158,6 @@ public sealed class DialogSeenLogDto public bool IsCurrentEndUser { get; set; } } -public sealed class DialogSeenLogSeenByActorDto -{ - /// - /// The natural name of the person/business that saw the dialog revision. - /// - public string ActorName { get; set; } = null!; - - /// - /// The identifier of the person/business that saw the dialog revision. - /// - /// - /// urn:altinn:person:identifier-no:01125512345 - /// urn:altinn:organization:identifier-no:912345678 - /// - public string ActorId { get; set; } = null!; -} public sealed class DialogActivityDto { @@ -207,7 +191,7 @@ public sealed class DialogActivityDto /// /// The actor that performed the activity. /// - public DialogActivityPerformedByActorDto PerformedBy { get; set; } = null!; + public ActorDto PerformedBy { get; set; } = null!; /// /// Unstructured text describing the activity. Only set if the activity type is "Information". @@ -215,22 +199,3 @@ public sealed class DialogActivityDto public List Description { get; set; } = []; } -public sealed class DialogActivityPerformedByActorDto -{ - /// - /// What type of actor performed the activity. - /// - public ActorType.Values ActorType { get; set; } - - /// - /// The name of the person or organization that performed the activity. - /// Only set if the actor type is "PartyRepresentative". - /// - public string? ActorName { get; set; } - - /// - /// The identifier of the person or organization that performed the activity. - /// May be omitted if ActorType is "ServiceOwner". - /// - public string? ActorId { get; set; } -} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/MappingProfile.cs index 0f61aa4e8..3039a68d2 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Search/MappingProfile.cs @@ -32,16 +32,9 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.SeenAt, opt => opt.MapFrom(src => src.CreatedAt)); - CreateMap() - .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => src.ActorId)); - CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.TypeId)); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorTypeId)) - .ForMember(dest => dest.ActorId, opt => opt.MapFrom(src => src.ActorId)); - CreateMap?, ContentDto?>() .ConvertUsing>(); } diff --git a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs index cee98bf2b..61e524950 100644 --- a/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs +++ b/src/Digdir.Domain.Dialogporten.Domain/Dialogs/Entities/DialogEntity.cs @@ -9,6 +9,7 @@ using Digdir.Domain.Dialogporten.Domain.Dialogs.Events; using Digdir.Library.Entity.Abstractions; using Digdir.Library.Entity.Abstractions.Features.Aggregate; +using Digdir.Library.Entity.Abstractions.Features.Identifiable; using Digdir.Library.Entity.Abstractions.Features.SoftDeletable; using Digdir.Library.Entity.Abstractions.Features.Versionable; @@ -97,7 +98,7 @@ public void UpdateSeenAt(string endUserId, DialogUserType.Values userTypeId, str return; } - SeenLog.Add(new() + SeenLog.Add(new DialogSeenLog { EndUserTypeId = userTypeId, IsViaServiceOwner = userTypeId == DialogUserType.Values.ServiceOwnerOnBehalfOfPerson, @@ -108,7 +109,6 @@ public void UpdateSeenAt(string endUserId, DialogUserType.Values userTypeId, str ActorName = endUserName } }); - _domainEvents.Add(new DialogSeenDomainEvent(Id, ServiceResource, Party, Process, PrecedingProcess)); } diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Common/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Common/MappingProfile.cs index 28124881a..90370570c 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Common/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/Common/MappingProfile.cs @@ -1,10 +1,9 @@ using AutoMapper; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Common.Actors; using DialogActivityDto = Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Search.DialogActivityDto; -using DialogActivityPerformedByActorDto = Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Search.DialogActivityPerformedByActorDto; using DialogSeenLogDto = Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Search.DialogSeenLogDto; -using DialogSeenLogSeenByActorDto = Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Search.DialogSeenLogSeenByActorDto; namespace Digdir.Domain.Dialogporten.GraphQL.EndUser.Common; @@ -16,20 +15,16 @@ public MappingProfile() CreateMap(); - CreateMap(); - CreateMap(); + CreateMap(); CreateMap(); - CreateMap(); CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.Type)); - CreateMap() - .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorType)); CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.Type)); - CreateMap() + CreateMap() .ForMember(dest => dest.ActorType, opt => opt.MapFrom(src => src.ActorType)); } } diff --git a/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/MappingProfile.cs b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/MappingProfile.cs index b42d1a997..dffcaf849 100644 --- a/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/MappingProfile.cs +++ b/src/Digdir.Domain.Dialogporten.GraphQL/EndUser/DialogById/MappingProfile.cs @@ -1,4 +1,5 @@ using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Actors; using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Get; using Digdir.Domain.Dialogporten.GraphQL.EndUser.Common; @@ -28,7 +29,6 @@ public MappingProfile() CreateMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.Type)); - CreateMap(); CreateMap(); CreateMap() .ForMember(dest => dest.ConsumerType, opt => opt.MapFrom(src => src.ConsumerType)); diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogSwaggerConfig.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogSwaggerConfig.cs index 99dbb6e82..c8b7a2afc 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogSwaggerConfig.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Update/UpdateDialogSwaggerConfig.cs @@ -1,5 +1,6 @@ using System.Globalization; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update; using Digdir.Domain.Dialogporten.Domain; using Digdir.Domain.Dialogporten.Domain.Actors; @@ -148,7 +149,7 @@ internal abstract class UpdateDialogSwaggerConfig { Id = Guid.Parse("8b95d42d-d2b6-4c01-8ca0-a817a4b3c50d"), Type = DialogActivityType.Values.Information, - PerformedBy = new ActivityPerformedByActorDto + PerformedBy = new ActorDto { ActorType = ActorType.Values.ServiceOwner }, diff --git a/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs b/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs index eadeca86f..b08569874 100644 --- a/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs +++ b/src/Digdir.Tool.Dialogporten.GenerateFakeData/DialogGenerator.cs @@ -1,6 +1,8 @@ using System.Globalization; using Bogus; +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Actors; using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create; using Digdir.Domain.Dialogporten.Domain.Actors; using Digdir.Domain.Dialogporten.Domain.Attachments; @@ -265,7 +267,7 @@ public static List GenerateFakeDialogActivities(int? count = null, .RuleFor(o => o.CreatedAt, f => f.Date.Past()) .RuleFor(o => o.ExtendedType, f => new Uri(f.Internet.UrlWithPath(Uri.UriSchemeHttps))) .RuleFor(o => o.Type, f => type ?? f.PickRandom(activityTypes)) - .RuleFor(o => o.PerformedBy, f => new ActivityPerformedByActorDto { ActorType = ActorType.Values.PartyRepresentative, ActorName = f.Name.FullName() }) + .RuleFor(o => o.PerformedBy, f => new ActorDto { ActorType = ActorType.Values.PartyRepresentative, ActorName = f.Name.FullName() }) .RuleFor(o => o.Description, (f, o) => o.Type == DialogActivityType.Values.Information ? GenerateFakeLocalizations(f.Random.Number(4, 8)) : null) .Generate(count ?? new Randomizer().Number(1, 4)); } diff --git a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/UpdateDialogTests.cs b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/UpdateDialogTests.cs index 871ad6faf..d8e752f60 100644 --- a/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/UpdateDialogTests.cs +++ b/tests/Digdir.Domain.Dialogporten.Application.Integration.Tests/Features/V1/ServiceOwner/Dialogs/Commands/UpdateDialogTests.cs @@ -1,4 +1,5 @@ -using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get; using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Create; using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update; using Digdir.Domain.Dialogporten.Application.Integration.Tests.Common; @@ -8,7 +9,6 @@ using Digdir.Tool.Dialogporten.GenerateFakeData; using FluentAssertions; using ActivityDto = Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update.ActivityDto; -using ActivityPerformedByActorDto = Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update.ActivityPerformedByActorDto; using TransmissionDto = Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update.TransmissionDto; namespace Digdir.Domain.Dialogporten.Application.Integration.Tests.Features.V1.ServiceOwner.Dialogs.Commands; @@ -32,7 +32,7 @@ public async Task Cannot_Include_Old_Activities_To_UpdateCommand() { Id = getDialogDto.AsT0.Activities.First().Id, Type = DialogActivityType.Values.DialogCreated, - PerformedBy = new ActivityPerformedByActorDto + PerformedBy = new ActorDto { ActorType = ActorType.Values.ServiceOwner } diff --git a/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Common/ActorValidatorTest.cs b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Common/ActorValidatorTest.cs new file mode 100644 index 000000000..5e1baa51c --- /dev/null +++ b/tests/Digdir.Domain.Dialogporten.Application.Unit.Tests/Features/V1/ServiceOwner/Common/ActorValidatorTest.cs @@ -0,0 +1,80 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Common.Actors; +using Digdir.Domain.Dialogporten.Domain.Actors; +using Digdir.Tool.Dialogporten.GenerateFakeData; + +namespace Digdir.Domain.Dialogporten.Application.Unit.Tests.Features.V1.ServiceOwner.Common; + +public class ActorValidatorTest +{ + private readonly ActorValidator _actorValidator = new(); + + [Fact] + public void Given_Invalid_ActorId_Should_Return_Error() + { + var actorDto = new ActorDto + { + ActorType = ActorType.Values.PartyRepresentative, + ActorName = null, + ActorId = "InvalidId!" + }; + var result = _actorValidator.Validate(actorDto); + Assert.NotEmpty(result.Errors); + } + + [Theory] + [InlineData(ActorType.Values.PartyRepresentative)] + [InlineData(ActorType.Values.ServiceOwner)] + public void Given_Valid_ActorId_Should_Return_Success(ActorType.Values actorType) + { + + var actorDto = new ActorDto + { + ActorType = actorType, + ActorId = actorType == ActorType.Values.PartyRepresentative ? DialogGenerator.GenerateRandomParty(forcePerson: true) : null + }; + var result = _actorValidator.Validate(actorDto); + Assert.Empty(result.Errors); + } + + [Fact] + public void Given_Null_ActorType_Should_Return_Error() + { + var actorDto = new ActorDto + { + ActorId = DialogGenerator.GenerateRandomParty(forcePerson: true) + }; + var result = _actorValidator.Validate(actorDto); + Assert.NotEmpty(result.Errors); + } + + + [Theory] + [InlineData(ActorType.Values.PartyRepresentative)] + [InlineData(ActorType.Values.ServiceOwner)] + public void Given_ActorId_And_ActorName_Should_Return_Error(ActorType.Values actorType) + { + var actorDto = new ActorDto + { + ActorType = actorType, + ActorName = "Fredrik Testland", + ActorId = DialogGenerator.GenerateRandomParty(forcePerson: true) + }; + var result = _actorValidator.Validate(actorDto); + Assert.NotEmpty(result.Errors); + } + + [Theory] + [InlineData("Fredik TestLand", false)] + [InlineData(null, true)] + public void ActorType_ServiceOwner_Rules(string? actorName, bool generateActorId) + { + var actorDto = new ActorDto + { + ActorType = ActorType.Values.ServiceOwner, + ActorName = actorName, + ActorId = generateActorId ? DialogGenerator.GenerateRandomParty(forcePerson: true) : null + }; + var result = _actorValidator.Validate(actorDto); + Assert.NotEmpty(result.Errors); + } +} From f1096a4eec7e7ea0b08d34bd4c9776f3c86fcd66 Mon Sep 17 00:00:00 2001 From: Dagfinn Olsen Date: Thu, 7 Nov 2024 10:25:22 +0100 Subject: [PATCH 14/19] feat(performance): Performance/create serviceowner search (#1413) Add a simple serviceowner search on enduser(ssn) and service resource ## Description ## Related Issue(s) - #1326 ## Verification - [ ] **Your** code builds clean without any errors or warnings - [ ] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] 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) ## Summary by CodeRabbit - **New Features** - Introduced a new performance testing script for service owner searches. - Added functionality to perform searches specifically for service owners. - **Improvements** - Enhanced existing search functionality for end users. - Updated performance metrics tracking to reflect the new focus on end user searches. - **Bug Fixes** - Improved URL construction in the token generation script for better handling of special characters. --- .github/workflows/dispatch-k6-performance.yml | 1 + .../performancetest_common/simpleSearch.js | 28 +++++++++++++-- .../serviceowners-yt01.csv | 2 +- .../performance/create-dialog-and-search.js | 2 +- tests/k6/tests/scripts/generate_tokens.sh | 4 +-- .../performance/serviceowner-search.js | 34 +++++++++++++++++++ 6 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 tests/k6/tests/serviceowner/performance/serviceowner-search.js diff --git a/.github/workflows/dispatch-k6-performance.yml b/.github/workflows/dispatch-k6-performance.yml index 478b0c853..87dbd26a8 100644 --- a/.github/workflows/dispatch-k6-performance.yml +++ b/.github/workflows/dispatch-k6-performance.yml @@ -49,6 +49,7 @@ on: options: - 'tests/k6/tests/serviceowner/performance/create-dialog.js' - 'tests/k6/tests/serviceowner/performance/create-remove-dialog.js' + - 'tests/k6/tests/serviceowner/performance/serviceowner-search.js' - 'tests/k6/tests/enduser/performance/enduser-search.js' - 'tests/k6/tests/graphql/performance/graphql-search.js' diff --git a/tests/k6/tests/performancetest_common/simpleSearch.js b/tests/k6/tests/performancetest_common/simpleSearch.js index 8feb8ef37..7a41e5b0e 100644 --- a/tests/k6/tests/performancetest_common/simpleSearch.js +++ b/tests/k6/tests/performancetest_common/simpleSearch.js @@ -5,7 +5,7 @@ import { randomItem, uuidv4 } from 'https://jslib.k6.io/k6-utils/1.4.0/index.js'; import { expect, expectStatusFor } from "../../common/testimports.js"; import { describe } from '../../common/describe.js'; -import { getEU, postGQ } from '../../common/request.js'; +import { getEU, postGQ, getSO } from '../../common/request.js'; import { getGraphqlParty } from '../performancetest_data/graphql-search.js'; @@ -31,7 +31,7 @@ function retrieveDialogContent(response, paramsWithToken) { } /** - * Performs a simple search. + * Performs a enduser search. * @param {Object} enduser - The end user. * @returns {void} */ @@ -127,3 +127,27 @@ export function graphqlSearch(enduser) { expect(r, 'response').to.have.validJsonBody(); }); } + +/** + * Performs a serviceowner search. + * @param {P} serviceowner + * @param {*} enduser + */ +export function serviceownerSearch(serviceowner, enduser, tag_name) { + let paramsWithToken = { + headers: { + Authorization: "Bearer " + serviceowner.token, + traceparent: uuidv4() + }, + tags: { name: tag_name } + } + + let enduserid = encodeURIComponent(`urn:altinn:person:identifier-no:${enduser.ssn}`); + let serviceResource = encodeURIComponent(`urn:altinn:resource:${serviceowner.resource}`); + let defaultFilter = `?enduserid=${enduserid}&serviceResource=${serviceResource}`; + describe('Perform serviceowner dialog list', () => { + let r = getSO('dialogs' + defaultFilter, paramsWithToken); + expectStatusFor(r).to.equal(200); + expect(r, 'response').to.have.validJsonBody(); + }); +} diff --git a/tests/k6/tests/performancetest_data/serviceowners-yt01.csv b/tests/k6/tests/performancetest_data/serviceowners-yt01.csv index 449a5e7c6..6d85f0b5c 100644 --- a/tests/k6/tests/performancetest_data/serviceowners-yt01.csv +++ b/tests/k6/tests/performancetest_data/serviceowners-yt01.csv @@ -1,2 +1,2 @@ org,orgno,scopes,resource -digdir,991825827,digdir:dialogporten.serviceprovider,super-simple-service +digdir,991825827,digdir:dialogporten.serviceprovider digdir:dialogporten.serviceprovider.search,ttd-dialogporten-performance-test-01 diff --git a/tests/k6/tests/scenarios/performance/create-dialog-and-search.js b/tests/k6/tests/scenarios/performance/create-dialog-and-search.js index e9d0dd679..5caf31f9c 100644 --- a/tests/k6/tests/scenarios/performance/create-dialog-and-search.js +++ b/tests/k6/tests/scenarios/performance/create-dialog-and-search.js @@ -42,7 +42,7 @@ export const options = { }, summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'], thresholds: getDefaultThresholds(['http_req_duration', 'http_reqs'],[ - 'simple search', + 'enduser search', 'create dialog', 'get dialog', 'get dialog activities', diff --git a/tests/k6/tests/scripts/generate_tokens.sh b/tests/k6/tests/scripts/generate_tokens.sh index 353587043..4373de459 100755 --- a/tests/k6/tests/scripts/generate_tokens.sh +++ b/tests/k6/tests/scripts/generate_tokens.sh @@ -57,8 +57,8 @@ if [ "$tokens" = "both" ] || [ "$tokens" = "enterprise" ]; then echo "org,orgno,scopes,resource,token" > $serviceowner_tokenfile while IFS=, read -r org orgno scopes resource do - url="https://altinn-testtools-token-generator.azurewebsites.net/api/GetEnterpriseToken?org=$org&env=$env&scopes=$scopes&orgno=$orgno&ttl=3600" - token=$(curl -s -f $url -u "$tokengenuser:$tokengenpasswd" ) + url="https://altinn-testtools-token-generator.azurewebsites.net/api/GetEnterpriseToken?org=$org&env=$env&orgno=$orgno&ttl=3600" + token=$(curl -s -f --get --data-urlencode "scopes=$scopes" $url -u "$tokengenuser:$tokengenpasswd" ) if [ $? -ne 0 ]; then echo "Error: Failed to generate enterprise token for: $env, $org, $orgno, $scopes " continue diff --git a/tests/k6/tests/serviceowner/performance/serviceowner-search.js b/tests/k6/tests/serviceowner/performance/serviceowner-search.js new file mode 100644 index 000000000..89ef62697 --- /dev/null +++ b/tests/k6/tests/serviceowner/performance/serviceowner-search.js @@ -0,0 +1,34 @@ +import { randomItem } from 'https://jslib.k6.io/k6-utils/1.1.0/index.js'; +import { serviceownerSearch } from '../../performancetest_common/simpleSearch.js' +import { getDefaultThresholds } from '../../performancetest_common/getDefaultThresholds.js'; +import { serviceOwners ,endUsersWithTokens } from '../../performancetest_common/readTestdata.js'; + +const tag_name = 'serviceowner search'; + +export let options = { + summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'p(99.5)', 'p(99.9)', 'count'], + thresholds: getDefaultThresholds(['http_req_duration', 'http_reqs'],[tag_name]) +}; + +/** + * Perform a service owner search. + * In single user mode, the first service owner and end user with token is used. Only one iteration is performed. + * In multi user mode, a random service owner and end user with token is used. + */ +export default function() { + if (!endUsersWithTokens || endUsersWithTokens.length === 0) { + throw new Error('No end users loaded for testing'); + } + if (!serviceOwners || serviceOwners.length === 0) { + throw new Error('No service owners loaded for testing'); + } + + const isSingleUserMode = (options.vus ?? 1) === 1 && (options.iterations ?? 1) === 1 && (options.duration ?? 0) === 0; + if (isSingleUserMode) { + serviceownerSearch(serviceOwners[0], endUsersWithTokens[0], tag_name); + } + else { + serviceownerSearch(randomItem(serviceOwners), randomItem(endUsersWithTokens), tag_name); + } +} + From 44be20affccdb8f879b7118ebd69a72bef9d5f50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Thu, 7 Nov 2024 15:37:18 +0100 Subject: [PATCH 15/19] feat(webapi): Limit Content-Length / request body size (#1416) ## Description Large request bodies can be used as a form of DDOS, especially when it comes to transmissions because they have a more complex hierarchy validation Limiting the body size on requests to 100 kB ## Related Issue(s) - #1415 ## Verification - [ ] **Your** code builds clean without any errors or warnings - [ ] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] 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) ## Summary by CodeRabbit - **New Features** - Introduced a maximum request body size limit to enhance server request handling. - **Bug Fixes** - Improved error handling for invalid `endUserId` and unknown user types, providing more informative responses. - Standardized response generation for various error scenarios in the `PatchDialogsController`. - **Documentation** - Enhanced clarity in error handling processes across multiple middleware components. - **Refactor** - Streamlined error response construction methods for consistency and efficiency. Co-authored-by: Magnus Sandgren <5285192+MagnusSandgren@users.noreply.github.com> --- .../ServiceOwnerOnBehalfOfPersonMiddleware.cs | 2 +- .../UserTypeValidationMiddleware.cs | 2 +- .../Common/Constants.cs | 1 + .../ErrorResponseBuilderExtensions.cs | 43 +++++++++++++------ .../Extensions/GlobalExceptionHandler.cs | 28 ++++++++---- .../Dialogs/Patch/PatchDialogsController.cs | 16 +++---- .../Program.cs | 5 +++ 7 files changed, 65 insertions(+), 32 deletions(-) diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/ServiceOwnerOnBehalfOfPersonMiddleware.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/ServiceOwnerOnBehalfOfPersonMiddleware.cs index 7e991e06e..b09be9aab 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/ServiceOwnerOnBehalfOfPersonMiddleware.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/ServiceOwnerOnBehalfOfPersonMiddleware.cs @@ -37,7 +37,7 @@ public Task InvokeAsync(HttpContext context) if (!NorwegianPersonIdentifier.TryParse(endUserIdQuery.First(), out var endUserId)) { context.Response.StatusCode = StatusCodes.Status400BadRequest; - context.Response.WriteAsJsonAsync(context.ResponseBuilder( + context.Response.WriteAsJsonAsync(context.GetResponseOrDefault( context.Response.StatusCode, [ new("EndUserId", diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/UserTypeValidationMiddleware.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/UserTypeValidationMiddleware.cs index 96642cc5b..eb61f7f17 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/UserTypeValidationMiddleware.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Authentication/UserTypeValidationMiddleware.cs @@ -21,7 +21,7 @@ public async Task InvokeAsync(HttpContext context) if (userType == UserIdType.Unknown) { context.Response.StatusCode = StatusCodes.Status403Forbidden; - await context.Response.WriteAsJsonAsync(context.ResponseBuilder( + await context.Response.WriteAsJsonAsync(context.GetResponseOrDefault( context.Response.StatusCode, [ new("Type", diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Constants.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Constants.cs index cb0b0741a..f4fb45977 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Common/Constants.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Constants.cs @@ -5,6 +5,7 @@ internal static class Constants internal const string IfMatch = "If-Match"; internal const string Authorization = "Authorization"; internal const string CurrentTokenIssuer = "CurrentIssuer"; + internal const int MaxRequestBodySize = 100_000; internal static class SwaggerSummary { diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/ErrorResponseBuilderExtensions.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/ErrorResponseBuilderExtensions.cs index 08ca550e4..e7f1d9be2 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/ErrorResponseBuilderExtensions.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/ErrorResponseBuilderExtensions.cs @@ -6,16 +6,41 @@ namespace Digdir.Domain.Dialogporten.WebApi.Common.Extensions; internal static class ErrorResponseBuilderExtensions { - public static object ResponseBuilder(this HttpContext ctx, int statusCode, List? failures = null) => - ResponseBuilder(failures ?? [], ctx, statusCode); + public static ProblemDetails DefaultResponse(this HttpContext ctx, int? statusCode = null) => new() + { + Title = "An error occurred while processing the request.", + Detail = "Something went wrong during the request.", + Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1", + Status = statusCode ?? ctx.Response.StatusCode, + Instance = ctx.Request.Path, + Extensions = { { "traceId", Activity.Current?.Id ?? ctx.TraceIdentifier } } + }; + + public static ProblemDetails GetResponseOrDefault(this HttpContext ctx, int statusCode, + List? failures = null) => + ctx.ResponseBuilder(failures, statusCode) ?? ctx.DefaultResponse(statusCode); public static object ResponseBuilder(List failures, HttpContext ctx, int statusCode) + => ctx.ResponseBuilder(failures, statusCode) ?? ctx.DefaultResponse(statusCode); + + public static ProblemDetails? ResponseBuilder(this HttpContext ctx, List? failures = null, int? statusCode = null) { - var errors = failures + var errors = failures? .GroupBy(f => f.PropertyName) - .ToDictionary(x => x.Key, x => x.Select(m => m.ErrorMessage).ToArray()); + .ToDictionary(x => x.Key, x => x.Select(m => m.ErrorMessage).ToArray()) + ?? []; + + statusCode ??= ctx.Response.StatusCode; return statusCode switch { + StatusCodes.Status413PayloadTooLarge => new ProblemDetails + { + Title = $"Payload too large. The maximum allowed size is {Constants.MaxRequestBodySize} bytes.", + Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.11", + Status = statusCode, + Instance = ctx.Request.Path, + Extensions = { { "traceId", Activity.Current?.Id ?? ctx.TraceIdentifier } } + }, StatusCodes.Status400BadRequest => new ValidationProblemDetails(errors) { Title = "One or more validation errors occurred.", @@ -73,15 +98,7 @@ public static object ResponseBuilder(List failures, HttpConte Instance = ctx.Request.Path, Extensions = { { "traceId", Activity.Current?.Id ?? ctx.TraceIdentifier } } }, - _ => new ProblemDetails - { - Title = "An error occurred while processing the request.", - Detail = "Something went wrong during the request.", - Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.1", - Status = ctx.Response.StatusCode, - Instance = ctx.Request.Path, - Extensions = { { "traceId", Activity.Current?.Id ?? ctx.TraceIdentifier } } - } + _ => null }; } } diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/GlobalExceptionHandler.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/GlobalExceptionHandler.cs index a082715d7..8876ef6fe 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/GlobalExceptionHandler.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Extensions/GlobalExceptionHandler.cs @@ -9,16 +9,26 @@ internal sealed class GlobalExceptionHandler : IExceptionHandler public async ValueTask TryHandleAsync(HttpContext ctx, Exception exception, CancellationToken cancellationToken) { - var http = $"{ctx.Request.Scheme}: {ctx.Request.Method} {ctx.Request.Path}"; - var type = exception.GetType().Name; - var error = exception.Message; - var logger = ctx.Resolve>(); - logger.LogError(exception, "{@Http}{@Type}{@Reason}", http, type, error); - ctx.Response.StatusCode = exception is IUpstreamServiceError - ? StatusCodes.Status502BadGateway - : StatusCodes.Status500InternalServerError; + ctx.Response.StatusCode = exception switch + { + BadHttpRequestException badHttpRequestException => badHttpRequestException.StatusCode, + IUpstreamServiceError => StatusCodes.Status502BadGateway, + _ => StatusCodes.Status500InternalServerError + }; + ctx.Response.ContentType = "application/problem+json"; - await ctx.Response.WriteAsJsonAsync(ctx.ResponseBuilder(ctx.Response.StatusCode), cancellationToken); + var response = ctx.ResponseBuilder(); + + if (ctx.Response.StatusCode >= 500 || response is null) + { + var http = $"{ctx.Request.Scheme}: {ctx.Request.Method} {ctx.Request.Path}"; + var type = exception.GetType().Name; + var error = exception.Message; + var logger = ctx.Resolve>(); + logger.LogError(exception, "{@Http}{@Type}{@Reason}", http, type, error); + } + + await ctx.Response.WriteAsJsonAsync(response ?? ctx.DefaultResponse(), cancellationToken); return true; } } diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs index 96906a286..b439574cb 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/Dialogs/Patch/PatchDialogsController.cs @@ -70,10 +70,10 @@ public async Task Patch( if (!dialogQueryResult.TryPickT0(out var dialog, out var errors)) { return errors.Match( - notFound => NotFound(HttpContext.ResponseBuilder(StatusCodes.Status404NotFound, + notFound => NotFound(HttpContext.GetResponseOrDefault(StatusCodes.Status404NotFound, notFound.ToValidationResults())), validationFailed => - BadRequest(HttpContext.ResponseBuilder(StatusCodes.Status400BadRequest, + BadRequest(HttpContext.GetResponseOrDefault(StatusCodes.Status400BadRequest, validationFailed.Errors.ToList()))); } @@ -88,12 +88,12 @@ public async Task Patch( var result = await _sender.Send(command, ct); return result.Match( success => (IActionResult)NoContent(), - notFound => NotFound(HttpContext.ResponseBuilder(StatusCodes.Status404NotFound, notFound.ToValidationResults())), - badRequest => BadRequest(HttpContext.ResponseBuilder(StatusCodes.Status400BadRequest, badRequest.ToValidationResults())), - validationFailed => BadRequest(HttpContext.ResponseBuilder(StatusCodes.Status400BadRequest, validationFailed.Errors.ToList())), - forbidden => new ObjectResult(HttpContext.ResponseBuilder(StatusCodes.Status403Forbidden, forbidden.ToValidationResults())), - domainError => UnprocessableEntity(HttpContext.ResponseBuilder(StatusCodes.Status422UnprocessableEntity, domainError.ToValidationResults())), - concurrencyError => new ObjectResult(HttpContext.ResponseBuilder(StatusCodes.Status412PreconditionFailed)) { StatusCode = StatusCodes.Status412PreconditionFailed } + notFound => NotFound(HttpContext.GetResponseOrDefault(StatusCodes.Status404NotFound, notFound.ToValidationResults())), + badRequest => BadRequest(HttpContext.GetResponseOrDefault(StatusCodes.Status400BadRequest, badRequest.ToValidationResults())), + validationFailed => BadRequest(HttpContext.GetResponseOrDefault(StatusCodes.Status400BadRequest, validationFailed.Errors.ToList())), + forbidden => new ObjectResult(HttpContext.GetResponseOrDefault(StatusCodes.Status403Forbidden, forbidden.ToValidationResults())), + domainError => UnprocessableEntity(HttpContext.GetResponseOrDefault(StatusCodes.Status422UnprocessableEntity, domainError.ToValidationResults())), + concurrencyError => new ObjectResult(HttpContext.GetResponseOrDefault(StatusCodes.Status412PreconditionFailed)) { StatusCode = StatusCodes.Status412PreconditionFailed } ); } } diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Program.cs b/src/Digdir.Domain.Dialogporten.WebApi/Program.cs index 77ea56e3a..30e60ba03 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Program.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Program.cs @@ -52,6 +52,11 @@ static void BuildAndRun(string[] args, TelemetryConfiguration telemetryConfigura { var builder = WebApplication.CreateBuilder(args); + builder.WebHost.ConfigureKestrel(kestrelOptions => + { + kestrelOptions.Limits.MaxRequestBodySize = Constants.MaxRequestBodySize; + }); + builder.Host.UseSerilog((context, services, configuration) => configuration .MinimumLevel.Warning() .ReadFrom.Configuration(context.Configuration) From a1be7c663618d3472115208d60af6c2473b62d3e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:18:53 +0100 Subject: [PATCH 16/19] chore(deps): update resource microsoft.network/networksecuritygroups to 2024-03-01 (#1401) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Resource | Change | |---|---| | Microsoft.Network/networkSecurityGroups | `2023-11-01` -> `2024-03-01` | --- ### Configuration 📅 **Schedule**: Branch creation - "before 7am on Sunday,before 7am on Wednesday" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/digdir/dialogporten). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .azure/modules/vnet/main.bicep | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.azure/modules/vnet/main.bicep b/.azure/modules/vnet/main.bicep index 55d5de720..8e4639579 100644 --- a/.azure/modules/vnet/main.bicep +++ b/.azure/modules/vnet/main.bicep @@ -7,7 +7,7 @@ param location string @description('Tags to apply to resources') param tags object -resource defaultNSG 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { +resource defaultNSG 'Microsoft.Network/networkSecurityGroups@2024-03-01' = { name: '${namePrefix}-default-nsg' location: location properties: { @@ -47,7 +47,7 @@ resource defaultNSG 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { } // https://learn.microsoft.com/en-us/azure/container-apps/firewall-integration?tabs=consumption-only -resource containerAppEnvironmentNSG 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { +resource containerAppEnvironmentNSG 'Microsoft.Network/networkSecurityGroups@2024-03-01' = { name: '${namePrefix}-container-app-environment-nsg' location: location properties: { @@ -146,7 +146,7 @@ resource containerAppEnvironmentNSG 'Microsoft.Network/networkSecurityGroups@202 tags: tags } -resource postgresqlNSG 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { +resource postgresqlNSG 'Microsoft.Network/networkSecurityGroups@2024-03-01' = { name: '${namePrefix}-postgresql-nsg' location: location properties: { @@ -185,7 +185,7 @@ resource postgresqlNSG 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { tags: tags } -resource redisNSG 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { +resource redisNSG 'Microsoft.Network/networkSecurityGroups@2024-03-01' = { name: '${namePrefix}-redis-nsg' location: location properties: { @@ -224,7 +224,7 @@ resource redisNSG 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { tags: tags } -resource serviceBusNSG 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { +resource serviceBusNSG 'Microsoft.Network/networkSecurityGroups@2024-03-01' = { name: '${namePrefix}-service-bus-nsg' location: location properties: { From 3678e18dfab7c19434f5542cd6123078e0a36073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Fri, 8 Nov 2024 11:54:48 +0100 Subject: [PATCH 17/19] chore(graphql): Test against correct search content object type (#1419) ## Summary by CodeRabbit - **Tests** - Updated test references to align with new data structures for improved accuracy in dialog content type comparisons. --- .../ObjectTypes/ContentTypeTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Digdir.Domain.Dialogporten.GraphQl.Unit.Tests/ObjectTypes/ContentTypeTests.cs b/tests/Digdir.Domain.Dialogporten.GraphQl.Unit.Tests/ObjectTypes/ContentTypeTests.cs index 1367a1408..dbfdeccb3 100644 --- a/tests/Digdir.Domain.Dialogporten.GraphQl.Unit.Tests/ObjectTypes/ContentTypeTests.cs +++ b/tests/Digdir.Domain.Dialogporten.GraphQl.Unit.Tests/ObjectTypes/ContentTypeTests.cs @@ -1,5 +1,5 @@ -using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.Dialogs.Queries.Search; using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents; +using Digdir.Domain.Dialogporten.GraphQL.EndUser.SearchDialogs; using Content = Digdir.Domain.Dialogporten.GraphQL.EndUser.DialogById.Content; namespace Digdir.Domain.Dialogporten.GraphQl.Unit.Tests.ObjectTypes; @@ -35,7 +35,7 @@ public void OutPutInList_DialogContentType_Names_Should_Match_Props_On_SearchCon .Select(x => x.Name) .ToList(); - var dtoPropertyNames = typeof(ContentDto) + var dtoPropertyNames = typeof(SearchContent) .GetProperties() .Select(p => p.Name) .ToList(); From 8e2248729adc3e4bd9949470ae3daa7621b96a76 Mon Sep 17 00:00:00 2001 From: Dialogporten Automation Bot <164321870+dialogporten-bot@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:00:32 +0100 Subject: [PATCH 18/19] chore(main): release 1.30.0 (#1412) :robot: I have created a release *beep* *boop* --- ## [1.30.0](https://github.com/digdir/dialogporten/compare/v1.29.0...v1.30.0) (2024-11-08) ### Features * **performance:** Performance/create serviceowner search ([#1413](https://github.com/digdir/dialogporten/issues/1413)) ([f1096a4](https://github.com/digdir/dialogporten/commit/f1096a4eec7e7ea0b08d34bd4c9776f3c86fcd66)) * **webapi:** Combine actorDtos ([#1374](https://github.com/digdir/dialogporten/issues/1374)) ([ca18a99](https://github.com/digdir/dialogporten/commit/ca18a993f21e488bfe4be7c167c822a7954b2683)) * **webapi:** Limit Content-Length / request body size ([#1416](https://github.com/digdir/dialogporten/issues/1416)) ([44be20a](https://github.com/digdir/dialogporten/commit/44be20affccdb8f879b7118ebd69a72bef9d5f50)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- CHANGELOG.md | 9 +++++++++ version.txt | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b70eb9436..c37eb7e12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [1.30.0](https://github.com/digdir/dialogporten/compare/v1.29.0...v1.30.0) (2024-11-08) + + +### Features + +* **performance:** Performance/create serviceowner search ([#1413](https://github.com/digdir/dialogporten/issues/1413)) ([f1096a4](https://github.com/digdir/dialogporten/commit/f1096a4eec7e7ea0b08d34bd4c9776f3c86fcd66)) +* **webapi:** Combine actorDtos ([#1374](https://github.com/digdir/dialogporten/issues/1374)) ([ca18a99](https://github.com/digdir/dialogporten/commit/ca18a993f21e488bfe4be7c167c822a7954b2683)) +* **webapi:** Limit Content-Length / request body size ([#1416](https://github.com/digdir/dialogporten/issues/1416)) ([44be20a](https://github.com/digdir/dialogporten/commit/44be20affccdb8f879b7118ebd69a72bef9d5f50)) + ## [1.29.0](https://github.com/digdir/dialogporten/compare/v1.28.3...v1.29.0) (2024-11-06) diff --git a/version.txt b/version.txt index 5e57fb895..034552a83 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.29.0 +1.30.0 From bb832d8d923114e204b448d3fbb6a23c249aad3a Mon Sep 17 00:00:00 2001 From: Are Almaas Date: Fri, 8 Nov 2024 12:01:38 +0100 Subject: [PATCH 19/19] feat(azure): enable query performance insights for postgres (#1417) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description - Sets the necessary server properties to enable Query Performance Insights - Enabled on Test, Staging and YT01 - Minor refactor to make things a tad cleaner ## Related Issue(s) - #{issue number} ## Verification - [ ] **Your** code builds clean without any errors or warnings - [ ] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] 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) ## Summary by CodeRabbit - **New Features** - Introduced a new configuration structure for PostgreSQL with enhanced settings, including the ability to enable query performance insights. - Added parameters to manage PostgreSQL SKU details more effectively. - **Bug Fixes** - Resolved inconsistencies in PostgreSQL parameter definitions across multiple configuration files. - **Documentation** - Updated documentation to reflect changes in PostgreSQL configuration management. --------- Co-authored-by: Ole Jørgen Skogstad --- .azure/infrastructure/main.bicep | 9 +++++-- .azure/infrastructure/prod.bicepparam | 9 ++++--- .azure/infrastructure/staging.bicepparam | 9 ++++--- .azure/infrastructure/test.bicepparam | 9 ++++--- .azure/infrastructure/yt01.bicepparam | 9 ++++--- .azure/modules/postgreSql/create.bicep | 30 ++++++++++++++++++++++++ 6 files changed, 61 insertions(+), 14 deletions(-) diff --git a/.azure/infrastructure/main.bicep b/.azure/infrastructure/main.bicep index 576a1861b..a3f6ec9fd 100644 --- a/.azure/infrastructure/main.bicep +++ b/.azure/infrastructure/main.bicep @@ -55,7 +55,11 @@ import { Sku as SlackNotifierSku } from '../modules/functionApp/slackNotifier.bi param slackNotifierSku SlackNotifierSku import { Sku as PostgresSku } from '../modules/postgreSql/create.bicep' -param postgresSku PostgresSku + +param postgresConfiguration { + sku: PostgresSku + enableQueryPerformanceInsight: bool +} import { Sku as ServiceBusSku } from '../modules/serviceBus/main.bicep' param serviceBusSku ServiceBusSku @@ -199,7 +203,8 @@ module postgresql '../modules/postgreSql/create.bicep' = { administratorLoginPassword: contains(keyVaultSourceKeys, 'dialogportenPgAdminPassword${environment}') ? srcKeyVaultResource.getSecret('dialogportenPgAdminPassword${environment}') : secrets.dialogportenPgAdminPassword - sku: postgresSku + sku: postgresConfiguration.sku + enableQueryPerformanceInsight: postgresConfiguration.enableQueryPerformanceInsight subnetId: vnet.outputs.postgresqlSubnetId vnetId: vnet.outputs.virtualNetworkId tags: tags diff --git a/.azure/infrastructure/prod.bicepparam b/.azure/infrastructure/prod.bicepparam index 2eb2d4c17..2d1284449 100644 --- a/.azure/infrastructure/prod.bicepparam +++ b/.azure/infrastructure/prod.bicepparam @@ -29,9 +29,12 @@ param slackNotifierSku = { applicationServicePlanName: 'Y1' applicationServicePlanTier: 'Dynamic' } -param postgresSku = { - name: 'Standard_B1ms' - tier: 'Burstable' +param postgresConfiguration = { + sku: { + name: 'Standard_B1ms' + tier: 'Burstable' + } + enableQueryPerformanceInsight: false } param redisSku = { diff --git a/.azure/infrastructure/staging.bicepparam b/.azure/infrastructure/staging.bicepparam index 40ebabf97..2aef7d15e 100644 --- a/.azure/infrastructure/staging.bicepparam +++ b/.azure/infrastructure/staging.bicepparam @@ -29,9 +29,12 @@ param slackNotifierSku = { applicationServicePlanName: 'Y1' applicationServicePlanTier: 'Dynamic' } -param postgresSku = { - name: 'Standard_B1ms' - tier: 'Burstable' +param postgresConfiguration = { + sku: { + name: 'Standard_B1ms' + tier: 'Burstable' + } + enableQueryPerformanceInsight: true } param redisSku = { diff --git a/.azure/infrastructure/test.bicepparam b/.azure/infrastructure/test.bicepparam index ce73b3286..80e51c54f 100644 --- a/.azure/infrastructure/test.bicepparam +++ b/.azure/infrastructure/test.bicepparam @@ -29,9 +29,12 @@ param slackNotifierSku = { applicationServicePlanName: 'Y1' applicationServicePlanTier: 'Dynamic' } -param postgresSku = { - name: 'Standard_B2s' - tier: 'Burstable' +param postgresConfiguration = { + sku: { + name: 'Standard_B2s' + tier: 'Burstable' + } + enableQueryPerformanceInsight: true } param redisSku = { diff --git a/.azure/infrastructure/yt01.bicepparam b/.azure/infrastructure/yt01.bicepparam index 0e40d97a0..c471b737d 100644 --- a/.azure/infrastructure/yt01.bicepparam +++ b/.azure/infrastructure/yt01.bicepparam @@ -29,9 +29,12 @@ param slackNotifierSku = { applicationServicePlanName: 'Y1' applicationServicePlanTier: 'Dynamic' } -param postgresSku = { - name: 'Standard_B1ms' - tier: 'Burstable' +param postgresConfiguration = { + sku: { + name: 'Standard_B1ms' + tier: 'Burstable' + } + enableQueryPerformanceInsight: true } param redisSku = { diff --git a/.azure/modules/postgreSql/create.bicep b/.azure/modules/postgreSql/create.bicep index e209cd610..b0c992b1d 100644 --- a/.azure/modules/postgreSql/create.bicep +++ b/.azure/modules/postgreSql/create.bicep @@ -31,6 +31,9 @@ type Sku = { @description('The SKU of the PostgreSQL server') param sku Sku +@description('Enable query performance insight') +param enableQueryPerformanceInsight bool + @description('The Key Vault to store the PostgreSQL administrator login password') @secure() param srcKeyVault object @@ -106,6 +109,33 @@ resource postgres 'Microsoft.DBforPostgreSQL/flexibleServers@2024-08-01' = { tags: tags } +resource track_io_timing 'Microsoft.DBforPostgreSQL/flexibleServers/configurations@2023-12-01-preview' = if (enableQueryPerformanceInsight) { + parent: postgres + name: 'track_io_timing' + properties: { + value: 'on' + source: 'user-override' + } +} + +resource pg_qs_query_capture_mode 'Microsoft.DBforPostgreSQL/flexibleServers/configurations@2023-12-01-preview' = if (enableQueryPerformanceInsight) { + parent: postgres + name: 'pg_qs.query_capture_mode' + properties: { + value: 'all' + source: 'user-override' + } +} + +resource pgms_wait_sampling_query_capture_mode 'Microsoft.DBforPostgreSQL/flexibleServers/configurations@2023-12-01-preview' = if (enableQueryPerformanceInsight) { + parent: postgres + name: 'pgms_wait_sampling.query_capture_mode' + properties: { + value: 'all' + source: 'user-override' + } +} + module adoConnectionString '../keyvault/upsertSecret.bicep' = { name: 'adoConnectionString' params: {