From d608adebadc12d099c04c4b174569392837603e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20J=C3=B8rgen=20Skogstad?= Date: Tue, 30 Jul 2024 15:18:00 +0200 Subject: [PATCH] feat(WebAPI): Add Transmission endpoints (#943) --- docs/schema/V1/swagger.verified.json | 901 ++++++++++++------ .../Queries/Get/GetDialogTransmissionDto.cs | 56 ++ .../Queries/Get/GetDialogTransmissionQuery.cs | 71 ++ .../Queries/Get/MappingProfile.cs | 28 + .../Queries/Search/MappingProfile.cs | 28 + .../Search/SearchDialogTransmissionDto.cs | 56 ++ .../Search/SearchDialogTransmissionQuery.cs | 68 ++ .../Queries/Get/GetDialogTransmissionDto.cs | 55 ++ .../Queries/Get/GetDialogTransmissionQuery.cs | 68 ++ .../Queries/Get/MappingProfile.cs | 28 + .../Queries/Search/MappingProfile.cs | 28 + .../Search/SearchDialogTransmissionDto.cs | 55 ++ .../Search/SearchDialogTransmissionQuery.cs | 58 ++ .../Dialogs/Queries/Get/GetDialogDto.cs | 1 - .../Common/Constants.cs | 1 + .../Get/GetDialogTransmissionEndpoint.cs | 35 + .../Get/GetDialogTransmissionSwaggerConfig.cs | 36 + .../SearchDialogTransmissionEndpoint.cs | 35 + .../SearchDialogTransmissionSwaggerConfig.cs | 33 + .../CreateDialogTransmissionEndpoint.cs | 77 ++ .../CreateDialogTransmissionSwaggerConfig.cs | 47 + .../Get/GetDialogTransmissionEndpoint.cs | 34 + .../Get/GetDialogTransmissionSwaggerConfig.cs | 36 + .../SearchDialogTransmissionEndpoint.cs | 34 + .../SearchDialogTransmissionSwaggerConfig.cs | 33 + 25 files changed, 1624 insertions(+), 278 deletions(-) create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/GetDialogTransmissionDto.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/GetDialogTransmissionQuery.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/MappingProfile.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/MappingProfile.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/SearchDialogTransmissionDto.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/SearchDialogTransmissionQuery.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/GetDialogTransmissionDto.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/GetDialogTransmissionQuery.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/MappingProfile.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/MappingProfile.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/SearchDialogTransmissionDto.cs create mode 100644 src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/SearchDialogTransmissionQuery.cs create mode 100644 src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Get/GetDialogTransmissionEndpoint.cs create mode 100644 src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Get/GetDialogTransmissionSwaggerConfig.cs create mode 100644 src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Search/SearchDialogTransmissionEndpoint.cs create mode 100644 src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Search/SearchDialogTransmissionSwaggerConfig.cs create mode 100644 src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionEndpoint.cs create mode 100644 src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionSwaggerConfig.cs create mode 100644 src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Get/GetDialogTransmissionEndpoint.cs create mode 100644 src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Get/GetDialogTransmissionSwaggerConfig.cs create mode 100644 src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Search/SearchDialogTransmissionEndpoint.cs create mode 100644 src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Search/SearchDialogTransmissionSwaggerConfig.cs diff --git a/docs/schema/V1/swagger.verified.json b/docs/schema/V1/swagger.verified.json index 896c8e4c1..b079623b7 100644 --- a/docs/schema/V1/swagger.verified.json +++ b/docs/schema/V1/swagger.verified.json @@ -1,4 +1,4 @@ -{ +{ "openapi": "3.0.0", "info": { "title": "Dialogporten", @@ -54,6 +54,212 @@ } } }, + "/api/v1/serviceowner/dialogs/{dialogId}/transmissions": { + "get": { + "tags": [ + "Serviceowner" + ], + "summary": "Gets a list of dialog transmissions", + "description": "Gets the list of transmissions belonging to a dialog", + "operationId": "GetDialogTransmissionListSO", + "parameters": [ + { + "name": "dialogId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "guid" + } + } + ], + "responses": { + "200": { + "description": "Successfully returned the dialog transmission list.", + "content": { + "text/plain": { + "schema": {} + }, + "application/json": { + "schema": {} + } + } + }, + "401": { + "description": "Missing or invalid authentication token. Requires a Maskinporten-token with the scope \"digdir:dialogporten.serviceprovider\"." + }, + "403": { + "description": "Unauthorized to get the supplied dialog (not owned by authenticated organization or has additional scope requirements defined in policy)." + } + }, + "security": [ + { + "JWTBearerAuth": [] + } + ] + }, + "post": { + "tags": [ + "Serviceowner" + ], + "summary": "Adds a transmission to a dialog", + "description": "The transmission is created with the given configuration. For more information see the documentation (link TBD).\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": "CreateDialogTransmission", + "parameters": [ + { + "name": "dialogId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "guid" + } + }, + { + "name": "if-Match", + "in": "header", + "schema": { + "type": "string", + "format": "guid", + "nullable": true + } + } + ], + "requestBody": { + "x-name": "CreateDialogTransmissionRequest", + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateDialogTransmissionRequest" + } + } + }, + "required": true, + "x-position": 1 + }, + "responses": { + "401": { + "description": "Missing or invalid authentication token. Requires a Maskinporten-token with the scope \"digdir:dialogporten.serviceprovider\"." + }, + "403": { + "description": "Unauthorized to create child entity for the given dialog (dialog not owned by authenticated organization or has additional scope requirements defined in service identifiers policy)." + }, + "201": { + "description": "The UUID of the created the dialog transmission. A relative URL to the newly created activity is set in the \"Location\" header.", + "content": { + "application/json": { + "schema": { + "type": "string" + }, + "example": "018bb8e5-d9d0-7434-8ec5-569a6c8e01fc" + } + } + }, + "400": { + "description": "Validation error occured. See problem details for a list of errors.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "The given dialog ID was not found or is already deleted.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "412": { + "description": "The supplied If-Match header did not match the current Revision value for the dialog. The request was not applied.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "422": { + "description": "Domain error occured. See problem details for a list of errors.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { + "JWTBearerAuth": [] + } + ] + } + }, + "/api/v1/serviceowner/dialogs/{dialogId}/transmissions/{transmissionId}": { + "get": { + "tags": [ + "Serviceowner" + ], + "summary": "Gets a single dialog transmission", + "description": "Gets a single transmission belonging to a dialog. For more information see the documentation (link TBD).", + "operationId": "GetDialogTransmissionSO", + "parameters": [ + { + "name": "dialogId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "guid" + } + }, + { + "name": "transmissionId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "guid" + } + } + ], + "responses": { + "401": { + "description": "Missing or invalid authentication token. Requires a Maskinporten-token with the scope \"digdir:dialogporten.serviceprovider\"." + }, + "403": { + "description": "Unauthorized to get child entity for the given dialog (dialog not owned by authenticated organization or has additional scope requirements defined in service identifiers policy)." + }, + "200": { + "description": "Successfully returned the dialog transmission." + }, + "404": { + "description": "The given dialog ID was not found or was deleted, or the given transmission ID was not found.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { + "JWTBearerAuth": [] + } + ] + } + }, "/api/v1/serviceowner/dialogs/{dialogId}/seenlog": { "get": { "tags": [ @@ -1239,14 +1445,14 @@ ] } }, - "/api/v1/enduser/dialogs/{dialogId}/seenlog": { + "/api/v1/enduser/dialogs/{dialogId}/transmissions": { "get": { "tags": [ "Enduser" ], - "summary": "Gets a single dialog seen log record", - "description": "Gets a single dialog seen log record. For more information see the documentation (link TBD).", - "operationId": "SearchDialogSeenLog", + "summary": "Gets a list of dialog transmissions", + "description": "Gets the list of transmissions belonging to a dialog", + "operationId": "GetDialogTransmissionListEU", "parameters": [ { "name": "dialogId", @@ -1259,34 +1465,22 @@ } ], "responses": { - "401": { - "description": "Missing or invalid authentication token. Requires a Maskinporten-token with the scope \"digdir:dialogporten\"." - }, - "403": { - "description": "Forbidden" - }, "200": { - "description": "Successfully returned the dialog seen log record.", + "description": "Successfully returned the dialog transmission list.", "content": { + "text/plain": { + "schema": {} + }, "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SearchDialogSeenLogDto" - } - } + "schema": {} } } }, - "404": { - "description": "Not Found", - "content": { - "application/problem+json": { - "schema": { - "$ref": "#/components/schemas/ProblemDetails" - } - } - } + "401": { + "description": "Missing or invalid authentication token. Requires a Maskinporten-token with the scope \"digdir:dialogporten\"." + }, + "403": { + "description": "Unauthorized to get the supplied dialog (not owned by authenticated organization or has additional scope requirements defined in policy)." } }, "security": [ @@ -1296,14 +1490,14 @@ ] } }, - "/api/v1/enduser/dialogs/{dialogId}/seenlog/{seenLogId}": { + "/api/v1/enduser/dialogs/{dialogId}/transmissions/{transmissionId}": { "get": { "tags": [ "Enduser" ], - "summary": "Gets a single dialog seen log record", - "description": "Gets a single dialog seen log record. For more information see the documentation (link TBD).", - "operationId": "GetDialogSeenLog", + "summary": "Gets a single dialog transmission", + "description": "Gets a single transmission belonging to a dialog. For more information see the documentation (link TBD).", + "operationId": "GetDialogTransmissionEU", "parameters": [ { "name": "dialogId", @@ -1315,7 +1509,7 @@ } }, { - "name": "seenLogId", + "name": "transmissionId", "in": "path", "required": true, "schema": { @@ -1329,20 +1523,13 @@ "description": "Missing or invalid authentication token. Requires a Maskinporten-token with the scope \"digdir:dialogporten\"." }, "403": { - "description": "Forbidden" + "description": "Unauthorized to get child entity for the given dialog (dialog not owned by authenticated organization or has additional scope requirements defined in service identifiers policy)." }, "200": { - "description": "Successfully returned the dialog seen log record.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GetDialogSeenLogDto" - } - } - } + "description": "Successfully returned the dialog transmission." }, "404": { - "description": "Not Found", + "description": "The given dialog ID was not found or was deleted, or the given transmission ID was not found.", "content": { "application/problem+json": { "schema": { @@ -1359,23 +1546,143 @@ ] } }, - "/api/v1/enduser/dialogs": { + "/api/v1/enduser/dialogs/{dialogId}/seenlog": { "get": { "tags": [ "Enduser" ], - "summary": "Gets a list of dialogs", - "description": "Performs a search for dialogs, returning a paginated list of dialogs. For more information see the documentation (link TBD).\n\n* All date parameters must contain explicit time zone. Example: 2023-10-27T10:00:00Z or 2023-10-27T10:00:00+01:00\n* See \"continuationToken\" in the response for how to get the next page of results.\n* hasNextPage will be set to true if there are more items to get.", - "operationId": "GetDialogList", + "summary": "Gets a single dialog seen log record", + "description": "Gets a single dialog seen log record. For more information see the documentation (link TBD).", + "operationId": "SearchDialogSeenLog", "parameters": [ { - "name": "org", - "in": "query", - "style": "form", - "explode": true, - "description": "Filter by one or more service owner codes", + "name": "dialogId", + "in": "path", + "required": true, "schema": { - "type": "array", + "type": "string", + "format": "guid" + } + } + ], + "responses": { + "401": { + "description": "Missing or invalid authentication token. Requires a Maskinporten-token with the scope \"digdir:dialogporten\"." + }, + "403": { + "description": "Forbidden" + }, + "200": { + "description": "Successfully returned the dialog seen log record.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SearchDialogSeenLogDto" + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { + "JWTBearerAuth": [] + } + ] + } + }, + "/api/v1/enduser/dialogs/{dialogId}/seenlog/{seenLogId}": { + "get": { + "tags": [ + "Enduser" + ], + "summary": "Gets a single dialog seen log record", + "description": "Gets a single dialog seen log record. For more information see the documentation (link TBD).", + "operationId": "GetDialogSeenLog", + "parameters": [ + { + "name": "dialogId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "guid" + } + }, + { + "name": "seenLogId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "guid" + } + } + ], + "responses": { + "401": { + "description": "Missing or invalid authentication token. Requires a Maskinporten-token with the scope \"digdir:dialogporten\"." + }, + "403": { + "description": "Forbidden" + }, + "200": { + "description": "Successfully returned the dialog seen log record.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetDialogSeenLogDto" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + }, + "security": [ + { + "JWTBearerAuth": [] + } + ] + } + }, + "/api/v1/enduser/dialogs": { + "get": { + "tags": [ + "Enduser" + ], + "summary": "Gets a list of dialogs", + "description": "Performs a search for dialogs, returning a paginated list of dialogs. For more information see the documentation (link TBD).\n\n* All date parameters must contain explicit time zone. Example: 2023-10-27T10:00:00Z or 2023-10-27T10:00:00+01:00\n* See \"continuationToken\" in the response for how to get the next page of results.\n* hasNextPage will be set to true if there are more items to get.", + "operationId": "GetDialogList", + "parameters": [ + { + "name": "org", + "in": "query", + "style": "form", + "explode": true, + "description": "Filter by one or more service owner codes", + "schema": { + "type": "array", "nullable": true, "items": { "type": "string" @@ -1760,35 +2067,284 @@ "keys": { "type": "array", "items": { - "$ref": "#/components/schemas/Jwk" + "$ref": "#/components/schemas/Jwk" + } + } + } + }, + "Jwk": { + "type": "object", + "additionalProperties": false, + "properties": { + "kty": { + "type": "string" + }, + "use": { + "type": "string" + }, + "kid": { + "type": "string" + }, + "crv": { + "type": "string" + }, + "x": { + "type": "string" + }, + "alg": { + "type": "string" + } + } + }, + "ProblemDetails": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "default": "https://www.rfc-editor.org/rfc/rfc7231#section-6.5.1" + }, + "title": { + "type": "string", + "default": "One or more validation errors occurred." + }, + "status": { + "type": "integer", + "format": "int32", + "default": 400 + }, + "instance": { + "type": "string", + "default": "/api/route" + }, + "traceId": { + "type": "string", + "default": "0HMPNHL0JHL76:00000001" + }, + "detail": { + "type": "string", + "nullable": true + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProblemDetails_Error" + } + } + } + }, + "ProblemDetails_Error": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "default": "Error or field name" + }, + "reason": { + "type": "string", + "default": "Error reason" + }, + "code": { + "type": "string", + "nullable": true + }, + "severity": { + "type": "string", + "nullable": true + } + } + }, + "CreateDialogTransmissionRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "format": "guid", + "nullable": true + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "authorizationAttribute": { + "type": "string", + "nullable": true + }, + "extendedType": { + "type": "string", + "nullable": true + }, + "relatedTransmissionId": { + "type": "string", + "format": "guid", + "nullable": true + }, + "type": { + "$ref": "#/components/schemas/DialogTransmissionType_Values" + }, + "sender": { + "$ref": "#/components/schemas/UpdateDialogDialogTransmissionSenderActorDto" + }, + "content": { + "$ref": "#/components/schemas/UpdateDialogDialogTransmissionContentDto" + }, + "attachments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UpdateDialogTransmissionAttachmentDto" + } + } + } + }, + "DialogTransmissionType_Values": { + "type": "string", + "description": "", + "x-enumNames": [ + "Information", + "Acceptance", + "Rejection", + "Request", + "Alert", + "Decision", + "Submission", + "Correction" + ], + "enum": [ + "Information", + "Acceptance", + "Rejection", + "Request", + "Alert", + "Decision", + "Submission", + "Correction" + ] + }, + "UpdateDialogDialogTransmissionSenderActorDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "actorType": { + "$ref": "#/components/schemas/DialogActorType_Values" + }, + "actorName": { + "type": "string" + }, + "actorId": { + "type": "string" + } + } + }, + "DialogActorType_Values": { + "type": "string", + "description": "", + "x-enumNames": [ + "PartyRepresentative", + "ServiceOwner" + ], + "enum": [ + "PartyRepresentative", + "ServiceOwner" + ] + }, + "UpdateDialogDialogTransmissionContentDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "title": { + "$ref": "#/components/schemas/ContentValueDto" + }, + "summary": { + "$ref": "#/components/schemas/ContentValueDto" + } + } + }, + "ContentValueDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "value": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LocalizationDto" + } + }, + "mediaType": { + "type": "string" + } + } + }, + "LocalizationDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "value": { + "type": "string" + }, + "languageCode": { + "type": "string" + } + } + }, + "UpdateDialogTransmissionAttachmentDto": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "format": "guid", + "nullable": true + }, + "displayName": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LocalizationDto" + } + }, + "urls": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UpdateDialogTransmissionAttachmentUrlDto" } } } }, - "Jwk": { + "UpdateDialogTransmissionAttachmentUrlDto": { "type": "object", "additionalProperties": false, "properties": { - "kty": { - "type": "string" - }, - "use": { - "type": "string" - }, - "kid": { - "type": "string" + "id": { + "type": "string", + "format": "guid", + "nullable": true }, - "crv": { - "type": "string" + "url": { + "type": "string", + "format": "uri" }, - "x": { - "type": "string" + "mediaType": { + "type": "string", + "nullable": true }, - "alg": { - "type": "string" + "consumerType": { + "$ref": "#/components/schemas/AttachmentUrlConsumerType_Values" } } }, + "AttachmentUrlConsumerType_Values": { + "type": "string", + "description": "", + "x-enumNames": [ + "Gui", + "Api" + ], + "enum": [ + "Gui", + "Api" + ] + }, "SearchDialogSeenLogDtoSO": { "type": "object", "additionalProperties": false, @@ -1826,65 +2382,6 @@ } } }, - "ProblemDetails": { - "type": "object", - "additionalProperties": false, - "properties": { - "type": { - "type": "string", - "default": "https://www.rfc-editor.org/rfc/rfc7231#section-6.5.1" - }, - "title": { - "type": "string", - "default": "One or more validation errors occurred." - }, - "status": { - "type": "integer", - "format": "int32", - "default": 400 - }, - "instance": { - "type": "string", - "default": "/api/route" - }, - "traceId": { - "type": "string", - "default": "0HMPNHL0JHL76:00000001" - }, - "detail": { - "type": "string", - "nullable": true - }, - "errors": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProblemDetails_Error" - } - } - } - }, - "ProblemDetails_Error": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string", - "default": "Error or field name" - }, - "reason": { - "type": "string", - "default": "Error reason" - }, - "code": { - "type": "string", - "nullable": true - }, - "severity": { - "type": "string", - "nullable": true - } - } - }, "GetDialogSeenLogDtoSO": { "type": "object", "additionalProperties": false, @@ -2071,33 +2568,6 @@ } } }, - "ContentValueDto": { - "type": "object", - "additionalProperties": false, - "properties": { - "value": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LocalizationDto" - } - }, - "mediaType": { - "type": "string" - } - } - }, - "LocalizationDto": { - "type": "object", - "additionalProperties": false, - "properties": { - "value": { - "type": "string" - }, - "languageCode": { - "type": "string" - } - } - }, "UpdateDialogSearchTagDto": { "type": "object", "additionalProperties": false, @@ -2150,126 +2620,6 @@ } } }, - "DialogTransmissionType_Values": { - "type": "string", - "description": "", - "x-enumNames": [ - "Information", - "Acceptance", - "Rejection", - "Request", - "Alert", - "Decision", - "Submission", - "Correction" - ], - "enum": [ - "Information", - "Acceptance", - "Rejection", - "Request", - "Alert", - "Decision", - "Submission", - "Correction" - ] - }, - "UpdateDialogDialogTransmissionSenderActorDto": { - "type": "object", - "additionalProperties": false, - "properties": { - "actorType": { - "$ref": "#/components/schemas/DialogActorType_Values" - }, - "actorName": { - "type": "string" - }, - "actorId": { - "type": "string" - } - } - }, - "DialogActorType_Values": { - "type": "string", - "description": "", - "x-enumNames": [ - "PartyRepresentative", - "ServiceOwner" - ], - "enum": [ - "PartyRepresentative", - "ServiceOwner" - ] - }, - "UpdateDialogDialogTransmissionContentDto": { - "type": "object", - "additionalProperties": false, - "properties": { - "title": { - "$ref": "#/components/schemas/ContentValueDto" - }, - "summary": { - "$ref": "#/components/schemas/ContentValueDto" - } - } - }, - "UpdateDialogTransmissionAttachmentDto": { - "type": "object", - "additionalProperties": false, - "properties": { - "id": { - "type": "string", - "format": "guid", - "nullable": true - }, - "displayName": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LocalizationDto" - } - }, - "urls": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UpdateDialogTransmissionAttachmentUrlDto" - } - } - } - }, - "UpdateDialogTransmissionAttachmentUrlDto": { - "type": "object", - "additionalProperties": false, - "properties": { - "id": { - "type": "string", - "format": "guid", - "nullable": true - }, - "url": { - "type": "string", - "format": "uri" - }, - "mediaType": { - "type": "string", - "nullable": true - }, - "consumerType": { - "$ref": "#/components/schemas/AttachmentUrlConsumerType_Values" - } - } - }, - "AttachmentUrlConsumerType_Values": { - "type": "string", - "description": "", - "x-enumNames": [ - "Gui", - "Api" - ], - "enum": [ - "Gui", - "Api" - ] - }, "UpdateDialogDialogAttachmentDto": { "type": "object", "additionalProperties": false, @@ -3004,9 +3354,6 @@ "type": "string", "nullable": true }, - "isAuthorized": { - "type": "boolean" - }, "extendedType": { "type": "string", "nullable": true diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/GetDialogTransmissionDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/GetDialogTransmissionDto.cs new file mode 100644 index 000000000..db61fd712 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/GetDialogTransmissionDto.cs @@ -0,0 +1,56 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogTransmissions.Queries.Get; + +public sealed class GetDialogTransmissionDto +{ + public Guid Id { get; set; } + public DateTimeOffset CreatedAt { get; set; } + public string? AuthorizationAttribute { get; set; } + public bool IsAuthorized { get; set; } + public string? ExtendedType { get; set; } + public Guid? RelatedTransmissionId { get; set; } + public DateTimeOffset? DeletedAt { get; set; } + + public DialogTransmissionType.Values Type { get; set; } + + public GetDialogTransmissionSenderActorDto Sender { get; set; } = null!; + + public GetDialogTransmissionContentDto Content { get; set; } = null!; + public List Attachments { get; set; } = []; +} + +public sealed class GetDialogTransmissionSenderActorDto +{ + public Guid Id { get; set; } + public DialogActorType.Values ActorType { get; set; } + public string ActorName { get; set; } = null!; + public string ActorId { get; set; } = null!; +} + +public sealed class GetDialogTransmissionContentDto +{ + public ContentValueDto Title { get; set; } = null!; + public ContentValueDto Summary { get; set; } = null!; +} + +public sealed class GetDialogTransmissionAttachmentDto +{ + public Guid Id { get; set; } + + public List DisplayName { get; set; } = []; + public List Urls { get; set; } = []; +} + +public sealed class GetDialogTransmissionAttachmentUrlDto +{ + public Guid Id { get; set; } + public Uri Url { get; set; } = null!; + public string? MediaType { get; set; } = null!; + + public AttachmentUrlConsumerType.Values ConsumerType { get; set; } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/GetDialogTransmissionQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/GetDialogTransmissionQuery.cs new file mode 100644 index 000000000..d167d2f4e --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/GetDialogTransmissionQuery.cs @@ -0,0 +1,71 @@ +using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Common; +using Digdir.Domain.Dialogporten.Application.Common.ReturnTypes; +using Digdir.Domain.Dialogporten.Application.Externals; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; +using MediatR; +using Microsoft.EntityFrameworkCore; +using OneOf; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogTransmissions.Queries.Get; + +public sealed class GetDialogTransmissionQuery : IRequest +{ + public Guid DialogId { get; set; } + public Guid TransmissionId { get; set; } +} + +[GenerateOneOf] +public partial class GetDialogTransmissionResult : OneOfBase; + +internal sealed class GetDialogTransmissionQueryHandler : IRequestHandler +{ + private readonly IMapper _mapper; + private readonly IDialogDbContext _dbContext; + private readonly IUserResourceRegistry _userResourceRegistry; + + public GetDialogTransmissionQueryHandler(IMapper mapper, IDialogDbContext dbContext, IUserResourceRegistry userResourceRegistry) + { + _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); + _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); + _userResourceRegistry = userResourceRegistry ?? throw new ArgumentNullException(nameof(userResourceRegistry)); + } + + public async Task Handle(GetDialogTransmissionQuery request, + CancellationToken cancellationToken) + { + var dialog = await _dbContext.Dialogs + .Include(x => x.Transmissions.Where(x => x.Id == request.TransmissionId)) + .ThenInclude(x => x.Content) + .ThenInclude(x => x.Value.Localizations) + .Include(x => x.Transmissions.Where(x => x.Id == request.TransmissionId)) + .ThenInclude(x => x.Attachments) + .ThenInclude(x => x.DisplayName!.Localizations) + .Include(x => x.Transmissions.Where(x => x.Id == request.TransmissionId)) + .ThenInclude(x => x.Attachments.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)) + .ThenInclude(x => x.Urls.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)) + .Include(x => x.Transmissions) + .ThenInclude(x => x.Sender) + .IgnoreQueryFilters() + .FirstOrDefaultAsync(x => x.Id == request.DialogId, + cancellationToken: cancellationToken); + + if (dialog is null) + { + return new EntityNotFound(request.DialogId); + } + + if (dialog.Deleted) + { + return new EntityDeleted(request.DialogId); + } + + var transmission = dialog.Transmissions.FirstOrDefault(); + + // TODO: Check auth + return transmission is null + ? (GetDialogTransmissionResult)new EntityNotFound(request.TransmissionId) + : _mapper.Map(transmission); + } +} 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 new file mode 100644 index 000000000..1f05abc71 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Get/MappingProfile.cs @@ -0,0 +1,28 @@ +using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogTransmissions.Queries.Get; + +public class MappingProfile : Profile +{ + 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)); + + CreateMap?, GetDialogTransmissionContentDto?>() + .ConvertUsing>(); + + CreateMap(); + CreateMap() + .ForMember(dest => dest.ConsumerType, opt => opt.MapFrom(src => src.ConsumerTypeId)); + } +} 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 new file mode 100644 index 000000000..0b8d38ebb --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/MappingProfile.cs @@ -0,0 +1,28 @@ +using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogTransmissions.Queries.Search; + +public class MappingProfile : Profile +{ + 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)); + + CreateMap?, SearchDialogTransmissionContentDto?>() + .ConvertUsing>(); + + CreateMap(); + CreateMap() + .ForMember(dest => dest.ConsumerType, opt => opt.MapFrom(src => src.ConsumerTypeId)); + } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/SearchDialogTransmissionDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/SearchDialogTransmissionDto.cs new file mode 100644 index 000000000..118778081 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/SearchDialogTransmissionDto.cs @@ -0,0 +1,56 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogTransmissions.Queries.Search; + +public sealed class SearchDialogTransmissionDto +{ + public Guid Id { get; set; } + public DateTimeOffset CreatedAt { get; set; } + public string? AuthorizationAttribute { get; set; } + public bool IsAuthorized { get; set; } + public string? ExtendedType { get; set; } + public Guid? RelatedTransmissionId { get; set; } + public DateTimeOffset? DeletedAt { get; set; } + + public DialogTransmissionType.Values Type { get; set; } + + public SearchDialogTransmissionSenderActorDto Sender { get; set; } = null!; + + public SearchDialogTransmissionContentDto Content { get; set; } = null!; + public List Attachments { get; set; } = []; +} + +public sealed class SearchDialogTransmissionSenderActorDto +{ + public Guid Id { get; set; } + public DialogActorType.Values ActorType { get; set; } + public string ActorName { get; set; } = null!; + public string ActorId { get; set; } = null!; +} + +public sealed class SearchDialogTransmissionContentDto +{ + public ContentValueDto Title { get; set; } = null!; + public ContentValueDto Summary { get; set; } = null!; +} + +public sealed class SearchDialogTransmissionAttachmentDto +{ + public Guid Id { get; set; } + + public List DisplayName { get; set; } = []; + public List Urls { get; set; } = []; +} + +public sealed class SearchDialogTransmissionAttachmentUrlDto +{ + public Guid Id { get; set; } + public Uri Url { get; set; } = null!; + public string? MediaType { get; set; } = null!; + + public AttachmentUrlConsumerType.Values ConsumerType { get; set; } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/SearchDialogTransmissionQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/SearchDialogTransmissionQuery.cs new file mode 100644 index 000000000..605d28ca7 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/EndUser/DialogTransmissions/Queries/Search/SearchDialogTransmissionQuery.cs @@ -0,0 +1,68 @@ +using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Common; +using Digdir.Domain.Dialogporten.Application.Common.ReturnTypes; +using Digdir.Domain.Dialogporten.Application.Externals; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; +using MediatR; +using Microsoft.EntityFrameworkCore; +using OneOf; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogTransmissions.Queries.Search; + +public sealed class SearchDialogTransmissionQuery : IRequest +{ + public Guid DialogId { get; set; } +} + +[GenerateOneOf] +public partial class SearchDialogTransmissionResult : OneOfBase, EntityNotFound, EntityDeleted>; + +internal sealed class SearchDialogTransmissionQueryHandler : IRequestHandler +{ + private readonly IDialogDbContext _db; + private readonly IMapper _mapper; + private readonly IUserResourceRegistry _userResourceRegistry; + + public SearchDialogTransmissionQueryHandler(IDialogDbContext db, IMapper mapper, IUserResourceRegistry userResourceRegistry) + { + _db = db ?? throw new ArgumentNullException(nameof(db)); + _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); + _userResourceRegistry = userResourceRegistry ?? throw new ArgumentNullException(nameof(userResourceRegistry)); + } + + public async Task Handle(SearchDialogTransmissionQuery request, CancellationToken cancellationToken) + { + var resourceIds = await _userResourceRegistry.GetCurrentUserResourceIds(cancellationToken); + + var dialog = await _db.Dialogs + .Include(x => x.Transmissions) + .ThenInclude(x => x.Content.OrderBy(x => x.Id).ThenBy(x => x.CreatedAt)) + .ThenInclude(x => x.Value.Localizations.OrderBy(x => x.CreatedAt).ThenBy(x => x.LanguageCode)) + .Include(x => x.Transmissions) + .ThenInclude(x => x.Attachments.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)) + .ThenInclude(x => x.DisplayName!.Localizations.OrderBy(x => x.CreatedAt).ThenBy(x => x.LanguageCode)) + .Include(x => x.Transmissions) + .ThenInclude(x => x.Attachments.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)) + .ThenInclude(x => x.Urls.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)) + .Include(x => x.Transmissions) + .ThenInclude(x => x.Sender) + .IgnoreQueryFilters() + .Where(x => resourceIds.Contains(x.ServiceResource)) + .FirstOrDefaultAsync(x => x.Id == request.DialogId, + cancellationToken: cancellationToken); + + if (dialog is null) + { + return new EntityNotFound(request.DialogId); + } + + if (dialog.Deleted) + { + return new EntityDeleted(request.DialogId); + } + + // TODO: Check auth + + return _mapper.Map>(dialog.Transmissions); + } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/GetDialogTransmissionDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/GetDialogTransmissionDto.cs new file mode 100644 index 000000000..92b97837b --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/GetDialogTransmissionDto.cs @@ -0,0 +1,55 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogTransmissions.Queries.Get; + +public sealed class GetDialogTransmissionDto +{ + public Guid Id { get; set; } + public DateTimeOffset CreatedAt { get; set; } + public string? AuthorizationAttribute { get; set; } + public string? ExtendedType { get; set; } + public Guid? RelatedTransmissionId { get; set; } + public DateTimeOffset? DeletedAt { get; set; } + + public DialogTransmissionType.Values Type { get; set; } + + public GetDialogTransmissionSenderActorDto Sender { get; set; } = null!; + + public GetDialogTransmissionContentDto Content { get; set; } = null!; + public List Attachments { get; set; } = []; +} + +public sealed class GetDialogTransmissionSenderActorDto +{ + public Guid Id { get; set; } + public DialogActorType.Values ActorType { get; set; } + public string ActorName { get; set; } = null!; + public string ActorId { get; set; } = null!; +} + +public sealed class GetDialogTransmissionContentDto +{ + public ContentValueDto Title { get; set; } = null!; + public ContentValueDto Summary { get; set; } = null!; +} + +public sealed class GetDialogTransmissionAttachmentDto +{ + public Guid Id { get; set; } + + public List DisplayName { get; set; } = []; + public List Urls { get; set; } = []; +} + +public sealed class GetDialogTransmissionAttachmentUrlDto +{ + public Guid Id { get; set; } + public Uri Url { get; set; } = null!; + public string? MediaType { get; set; } = null!; + + public AttachmentUrlConsumerType.Values ConsumerType { get; set; } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/GetDialogTransmissionQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/GetDialogTransmissionQuery.cs new file mode 100644 index 000000000..a3c0938db --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/GetDialogTransmissionQuery.cs @@ -0,0 +1,68 @@ +using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Common; +using Digdir.Domain.Dialogporten.Application.Common.ReturnTypes; +using Digdir.Domain.Dialogporten.Application.Externals; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; +using MediatR; +using Microsoft.EntityFrameworkCore; +using OneOf; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogTransmissions.Queries.Get; + +public sealed class GetDialogTransmissionQuery : IRequest +{ + public Guid DialogId { get; set; } + public Guid TransmissionId { get; set; } +} + +[GenerateOneOf] +public partial class GetDialogTransmissionResult : OneOfBase; + +internal sealed class GetDialogTransmissionQueryHandler : IRequestHandler +{ + private readonly IMapper _mapper; + private readonly IDialogDbContext _dbContext; + private readonly IUserResourceRegistry _userResourceRegistry; + + public GetDialogTransmissionQueryHandler(IMapper mapper, IDialogDbContext dbContext, IUserResourceRegistry userResourceRegistry) + { + _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); + _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); + _userResourceRegistry = userResourceRegistry ?? throw new ArgumentNullException(nameof(userResourceRegistry)); + } + + public async Task Handle(GetDialogTransmissionQuery request, + CancellationToken cancellationToken) + { + var resourceIds = await _userResourceRegistry.GetCurrentUserResourceIds(cancellationToken); + + var dialog = await _dbContext.Dialogs + .Include(x => x.Transmissions.Where(x => x.Id == request.TransmissionId)) + .ThenInclude(x => x.Content) + .ThenInclude(x => x.Value.Localizations) + .Include(x => x.Transmissions.Where(x => x.Id == request.TransmissionId)) + .ThenInclude(x => x.Attachments) + .ThenInclude(x => x.DisplayName!.Localizations) + .Include(x => x.Transmissions.Where(x => x.Id == request.TransmissionId)) + .ThenInclude(x => x.Attachments.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)) + .ThenInclude(x => x.Urls.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)) + .Include(x => x.Transmissions) + .ThenInclude(x => x.Sender) + .IgnoreQueryFilters() + .Where(x => resourceIds.Contains(x.ServiceResource)) + .FirstOrDefaultAsync(x => x.Id == request.DialogId, + cancellationToken: cancellationToken); + + if (dialog is null) + { + return new EntityNotFound(request.DialogId); + } + + var transmission = dialog.Transmissions.FirstOrDefault(); + + return transmission is null + ? (GetDialogTransmissionResult)new EntityNotFound(request.TransmissionId) + : _mapper.Map(transmission); + } +} 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 new file mode 100644 index 000000000..4eed3f36d --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Get/MappingProfile.cs @@ -0,0 +1,28 @@ +using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogTransmissions.Queries.Get; + +public class MappingProfile : Profile +{ + 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)); + + CreateMap?, GetDialogTransmissionContentDto?>() + .ConvertUsing>(); + + CreateMap(); + CreateMap() + .ForMember(dest => dest.ConsumerType, opt => opt.MapFrom(src => src.ConsumerTypeId)); + } +} 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 new file mode 100644 index 000000000..ce9a75cf3 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/MappingProfile.cs @@ -0,0 +1,28 @@ +using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Contents; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogTransmissions.Queries.Search; + +public class MappingProfile : Profile +{ + 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)); + + CreateMap?, SearchDialogTransmissionContentDto?>() + .ConvertUsing>(); + + CreateMap(); + CreateMap() + .ForMember(dest => dest.ConsumerType, opt => opt.MapFrom(src => src.ConsumerTypeId)); + } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/SearchDialogTransmissionDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/SearchDialogTransmissionDto.cs new file mode 100644 index 000000000..26b187b1e --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/SearchDialogTransmissionDto.cs @@ -0,0 +1,55 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Content; +using Digdir.Domain.Dialogporten.Application.Features.V1.Common.Localizations; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Actors; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Attachments; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities.Transmissions; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogTransmissions.Queries.Search; + +public sealed class SearchDialogTransmissionDto +{ + public Guid Id { get; set; } + public DateTimeOffset CreatedAt { get; set; } + public string? AuthorizationAttribute { get; set; } + public string? ExtendedType { get; set; } + public Guid? RelatedTransmissionId { get; set; } + public DateTimeOffset? DeletedAt { get; set; } + + public DialogTransmissionType.Values Type { get; set; } + + public SearchDialogTransmissionSenderActorDto Sender { get; set; } = null!; + + public SearchDialogTransmissionContentDto Content { get; set; } = null!; + public List Attachments { get; set; } = []; +} + +public sealed class SearchDialogTransmissionSenderActorDto +{ + public Guid Id { get; set; } + public DialogActorType.Values ActorType { get; set; } + public string ActorName { get; set; } = null!; + public string ActorId { get; set; } = null!; +} + +public sealed class SearchDialogTransmissionContentDto +{ + public ContentValueDto Title { get; set; } = null!; + public ContentValueDto Summary { get; set; } = null!; +} + +public sealed class SearchDialogTransmissionAttachmentDto +{ + public Guid Id { get; set; } + + public List DisplayName { get; set; } = []; + public List Urls { get; set; } = []; +} + +public sealed class SearchDialogTransmissionAttachmentUrlDto +{ + public Guid Id { get; set; } + public Uri Url { get; set; } = null!; + public string? MediaType { get; set; } = null!; + + public AttachmentUrlConsumerType.Values ConsumerType { get; set; } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/SearchDialogTransmissionQuery.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/SearchDialogTransmissionQuery.cs new file mode 100644 index 000000000..1dd4ed3cc --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/DialogTransmissions/Queries/Search/SearchDialogTransmissionQuery.cs @@ -0,0 +1,58 @@ +using AutoMapper; +using Digdir.Domain.Dialogporten.Application.Common; +using Digdir.Domain.Dialogporten.Application.Common.ReturnTypes; +using Digdir.Domain.Dialogporten.Application.Externals; +using Digdir.Domain.Dialogporten.Domain.Dialogs.Entities; +using MediatR; +using Microsoft.EntityFrameworkCore; +using OneOf; + +namespace Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogTransmissions.Queries.Search; + +public sealed class SearchDialogTransmissionQuery : IRequest +{ + public Guid DialogId { get; set; } +} + +[GenerateOneOf] +public partial class SearchDialogTransmissionResult : OneOfBase, EntityNotFound>; + +internal sealed class SearchDialogTransmissionQueryHandler : IRequestHandler +{ + private readonly IDialogDbContext _db; + private readonly IMapper _mapper; + private readonly IUserResourceRegistry _userResourceRegistry; + + public SearchDialogTransmissionQueryHandler(IDialogDbContext db, IMapper mapper, IUserResourceRegistry userResourceRegistry) + { + _db = db ?? throw new ArgumentNullException(nameof(db)); + _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); + _userResourceRegistry = userResourceRegistry ?? throw new ArgumentNullException(nameof(userResourceRegistry)); + } + + public async Task Handle(SearchDialogTransmissionQuery request, CancellationToken cancellationToken) + { + var resourceIds = await _userResourceRegistry.GetCurrentUserResourceIds(cancellationToken); + + var dialog = await _db.Dialogs + .Include(x => x.Transmissions) + .ThenInclude(x => x.Content.OrderBy(x => x.Id).ThenBy(x => x.CreatedAt)) + .ThenInclude(x => x.Value.Localizations.OrderBy(x => x.CreatedAt).ThenBy(x => x.LanguageCode)) + .Include(x => x.Transmissions) + .ThenInclude(x => x.Attachments.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)) + .ThenInclude(x => x.DisplayName!.Localizations.OrderBy(x => x.CreatedAt).ThenBy(x => x.LanguageCode)) + .Include(x => x.Transmissions) + .ThenInclude(x => x.Attachments.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)) + .ThenInclude(x => x.Urls.OrderBy(x => x.CreatedAt).ThenBy(x => x.Id)) + .Include(x => x.Transmissions) + .ThenInclude(x => x.Sender) + .IgnoreQueryFilters() + .Where(x => resourceIds.Contains(x.ServiceResource)) + .FirstOrDefaultAsync(x => x.Id == request.DialogId, + cancellationToken: cancellationToken); + + return dialog is null + ? (SearchDialogTransmissionResult)new EntityNotFound(request.DialogId) + : _mapper.Map>(dialog.Transmissions); + } +} diff --git a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogDto.cs b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogDto.cs index e434a15e5..74006f61e 100644 --- a/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogDto.cs +++ b/src/Digdir.Domain.Dialogporten.Application/Features/V1/ServiceOwner/Dialogs/Queries/Get/GetDialogDto.cs @@ -47,7 +47,6 @@ public sealed class GetDialogDialogTransmissionDto public Guid Id { get; set; } public DateTimeOffset CreatedAt { get; set; } public string? AuthorizationAttribute { get; set; } - public bool IsAuthorized { get; set; } public string? ExtendedType { get; set; } public Guid? RelatedTransmissionId { get; set; } diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Common/Constants.cs b/src/Digdir.Domain.Dialogporten.WebApi/Common/Constants.cs index 2e7bb7cf5..cb0b0741a 100644 --- a/src/Digdir.Domain.Dialogporten.WebApi/Common/Constants.cs +++ b/src/Digdir.Domain.Dialogporten.WebApi/Common/Constants.cs @@ -18,6 +18,7 @@ internal static class SwaggerSummary internal const string EndUserAuthenticationFailure = "Missing or invalid authentication token. Requires a Maskinporten-token with the scope \"digdir:dialogporten\"."; internal const string DialogNotFound = "The given dialog ID was not found or is already deleted."; internal const string DialogActivityNotFound = "The given dialog ID was not found or was deleted, or the given activity ID was not found."; + internal const string DialogTransmissionNotFound = "The given dialog ID was not found or was deleted, or the given transmission ID was not found."; internal const string RevisionMismatch = "The supplied If-Match header did not match the current Revision value for the dialog. The request was not applied."; internal const string AccessDeniedToDialog = "Unauthorized to {0} the supplied dialog (not owned by authenticated organization or has additional scope requirements defined in policy)."; internal const string AccessDeniedToDialogForChildEntity = "Unauthorized to {0} child entity for the given dialog (dialog not owned by authenticated organization or has additional scope requirements defined in service identifiers policy)."; diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Get/GetDialogTransmissionEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Get/GetDialogTransmissionEndpoint.cs new file mode 100644 index 000000000..83f00ccd9 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Get/GetDialogTransmissionEndpoint.cs @@ -0,0 +1,35 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogTransmissions.Queries.Get; +using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; +using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; +using FastEndpoints; +using MediatR; + +namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.EndUser.DialogTransmissions.Get; + +public class GetDialogTransmissionEndpoint : Endpoint +{ + private readonly ISender _sender; + + public GetDialogTransmissionEndpoint(ISender sender) + { + _sender = sender ?? throw new ArgumentNullException(nameof(sender)); + } + + public override void Configure() + { + Get("dialogs/{dialogId}/transmissions/{transmissionId}"); + Policies(AuthorizationPolicy.EndUser); + Group(); + + Description(b => GetDialogTransmissionSwaggerConfig.SetDescription(b)); + } + + public override async Task HandleAsync(GetDialogTransmissionQuery req, CancellationToken ct) + { + var result = await _sender.Send(req, ct); + await result.Match( + dto => SendOkAsync(dto, ct), + notFound => this.NotFoundAsync(notFound, ct), + deleted => this.GoneAsync(deleted, ct)); + } +} diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Get/GetDialogTransmissionSwaggerConfig.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Get/GetDialogTransmissionSwaggerConfig.cs new file mode 100644 index 000000000..302207ae9 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Get/GetDialogTransmissionSwaggerConfig.cs @@ -0,0 +1,36 @@ +using Digdir.Domain.Dialogporten.WebApi.Common; +using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; +using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; +using Digdir.Domain.Dialogporten.WebApi.Common.Swagger; +using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Extensions; +using FastEndpoints; + +namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.EndUser.DialogTransmissions.Get; + +public class GetDialogTransmissionSwaggerConfig : ISwaggerConfig +{ + public static string OperationId => "GetDialogTransmissionEU"; + + public static RouteHandlerBuilder SetDescription(RouteHandlerBuilder builder) + => builder.OperationId(OperationId) + .ProducesOneOf( + StatusCodes.Status200OK, + StatusCodes.Status404NotFound); + + public static object GetExample() => throw new NotImplementedException(); +} + +public sealed class GetDialogTransmissionEndpointSummary : Summary +{ + public GetDialogTransmissionEndpointSummary() + { + Summary = "Gets a single dialog transmission"; + Description = """ + Gets a single transmission belonging to a dialog. For more information see the documentation (link TBD). + """; + Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("transmission"); + Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.EndUserAuthenticationFailure.FormatInvariant(AuthorizationScope.EndUser); + Responses[StatusCodes.Status403Forbidden] = Constants.SwaggerSummary.AccessDeniedToDialogForChildEntity.FormatInvariant("get"); + Responses[StatusCodes.Status404NotFound] = Constants.SwaggerSummary.DialogTransmissionNotFound; + } +} diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Search/SearchDialogTransmissionEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Search/SearchDialogTransmissionEndpoint.cs new file mode 100644 index 000000000..93ba4e203 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Search/SearchDialogTransmissionEndpoint.cs @@ -0,0 +1,35 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogTransmissions.Queries.Search; +using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; +using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; +using FastEndpoints; +using MediatR; + +namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.EndUser.DialogTransmissions.Search; + +public class SearchDialogTransmissionEndpoint : Endpoint +{ + private readonly ISender _sender; + + public SearchDialogTransmissionEndpoint(ISender sender) + { + _sender = sender ?? throw new ArgumentNullException(nameof(sender)); + } + + public override void Configure() + { + Get("dialogs/{dialogId}/transmissions"); + Policies(AuthorizationPolicy.EndUser); + Group(); + + Description(b => SearchDialogTransmissionSwaggerConfig.SetDescription(b)); + } + + public override async Task HandleAsync(SearchDialogTransmissionQuery req, CancellationToken ct) + { + var result = await _sender.Send(req, ct); + await result.Match( + dto => SendOkAsync(dto, ct), + notFound => this.NotFoundAsync(notFound, ct), + deleted => this.GoneAsync(deleted, ct)); + } +} diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Search/SearchDialogTransmissionSwaggerConfig.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Search/SearchDialogTransmissionSwaggerConfig.cs new file mode 100644 index 000000000..60c8a566f --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/EndUser/DialogTransmissions/Search/SearchDialogTransmissionSwaggerConfig.cs @@ -0,0 +1,33 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.EndUser.DialogTransmissions.Queries.Search; +using Digdir.Domain.Dialogporten.WebApi.Common; +using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; +using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; +using Digdir.Domain.Dialogporten.WebApi.Common.Swagger; +using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Extensions; +using FastEndpoints; + +namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.EndUser.DialogTransmissions.Search; + +public class SearchDialogTransmissionSwaggerConfig : ISwaggerConfig +{ + public static string OperationId => "GetDialogTransmissionListEU"; + public static RouteHandlerBuilder SetDescription(RouteHandlerBuilder builder) + => builder.OperationId(OperationId); + + public static object GetExample() => throw new NotImplementedException(); +} + +public sealed class SearchDialogTransmissionEndpointSummary : Summary +{ + public SearchDialogTransmissionEndpointSummary() + { + Summary = "Gets a list of dialog transmissions"; + Description = """ + Gets the list of transmissions belonging to a dialog + """; + Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("transmission list"); + Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.EndUserAuthenticationFailure.FormatInvariant(AuthorizationScope.EndUser); + Responses[StatusCodes.Status403Forbidden] = Constants.SwaggerSummary.AccessDeniedToDialog.FormatInvariant("get"); + Responses[StatusCodes.Status404NotFound] = Constants.SwaggerSummary.DialogNotFound; + } +} diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionEndpoint.cs new file mode 100644 index 000000000..601caa738 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionEndpoint.cs @@ -0,0 +1,77 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Commands.Update; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.Dialogs.Queries.Get; +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogTransmissions.Queries.Get; +using Digdir.Domain.Dialogporten.WebApi.Common; +using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; +using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; +using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.DialogTransmissions.Get; +using FastEndpoints; +using MediatR; +using Medo; +using IMapper = AutoMapper.IMapper; + +namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.DialogTransmissions.Create; + +public sealed class CreateDialogTransmissionEndpoint : Endpoint +{ + private readonly IMapper _mapper; + private readonly ISender _sender; + + public CreateDialogTransmissionEndpoint(ISender sender, IMapper mapper) + { + _sender = sender ?? throw new ArgumentNullException(nameof(sender)); + _mapper = mapper; + } + + public override void Configure() + { + Post("dialogs/{dialogId}/transmissions"); + Policies(AuthorizationPolicy.ServiceProvider); + Group(); + + Description(b => CreateDialogTransmissionSwaggerConfig.SetDescription(b)); + } + + public override async Task HandleAsync(CreateDialogTransmissionRequest req, CancellationToken ct) + { + var dialogQueryResult = await _sender.Send(new GetDialogQuery { DialogId = req.DialogId }, ct); + if (dialogQueryResult.TryPickT1(out var entityNotFound, out var dialog)) + { + await this.NotFoundAsync(entityNotFound, cancellationToken: ct); + return; + } + + // Remove all existing transmissions, since this list is append only and + // existing transmissions should not be considered in the new update request. + dialog.Transmissions.Clear(); + + var updateDialogDto = _mapper.Map(dialog); + + req.Id = !req.Id.HasValue || req.Id.Value == default + ? Uuid7.NewUuid7().ToGuid() + : req.Id; + + updateDialogDto.Transmissions.Add(req); + + var updateDialogCommand = new UpdateDialogCommand { Id = req.DialogId, IfMatchDialogRevision = req.IfMatchDialogRevision, Dto = updateDialogDto }; + + var result = await _sender.Send(updateDialogCommand, ct); + + await result.Match( + success => SendCreatedAtAsync(new GetDialogTransmissionQuery { DialogId = dialog.Id, TransmissionId = req.Id.Value }, req.Id, cancellation: ct), + notFound => this.NotFoundAsync(notFound, ct), + badRequest => this.BadRequestAsync(badRequest, ct), + validationError => this.BadRequestAsync(validationError, ct), + forbidden => this.ForbiddenAsync(forbidden, ct), + domainError => this.UnprocessableEntityAsync(domainError, ct), + concurrencyError => this.PreconditionFailed(cancellationToken: ct)); + } +} + +public sealed class CreateDialogTransmissionRequest : UpdateDialogDialogTransmissionDto +{ + public Guid DialogId { get; set; } + + [FromHeader(headerName: Constants.IfMatch, isRequired: false, removeFromSchema: true)] + public Guid? IfMatchDialogRevision { get; set; } +} diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionSwaggerConfig.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionSwaggerConfig.cs new file mode 100644 index 000000000..6965bdef6 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Create/CreateDialogTransmissionSwaggerConfig.cs @@ -0,0 +1,47 @@ +using Digdir.Domain.Dialogporten.WebApi.Common; +using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; +using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; +using Digdir.Domain.Dialogporten.WebApi.Common.Swagger; +using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Extensions; +using FastEndpoints; + +namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.DialogTransmissions.Create; + +public class CreateDialogTransmissionSwaggerConfig : ISwaggerConfig +{ + public static string OperationId => "CreateDialogTransmission"; + + public static RouteHandlerBuilder SetDescription(RouteHandlerBuilder builder) + => builder.OperationId(OperationId) + .ProducesOneOf( + StatusCodes.Status201Created, + StatusCodes.Status400BadRequest, + StatusCodes.Status404NotFound, + StatusCodes.Status412PreconditionFailed, + StatusCodes.Status422UnprocessableEntity); + + public static object GetExample() => throw new NotImplementedException(); +} + +public sealed class CreateDialogTransmissionEndpointSummary : Summary +{ + public CreateDialogTransmissionEndpointSummary() + { + Summary = "Adds a transmission to a dialog"; + Description = $""" + The transmission is created with the given configuration. For more information see the documentation (link TBD). + + {Constants.SwaggerSummary.OptimisticConcurrencyNote} + """; + + ResponseExamples[StatusCodes.Status201Created] = "018bb8e5-d9d0-7434-8ec5-569a6c8e01fc"; + + Responses[StatusCodes.Status201Created] = Constants.SwaggerSummary.Created.FormatInvariant("transmission"); + Responses[StatusCodes.Status400BadRequest] = Constants.SwaggerSummary.ValidationError; + Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.ServiceOwnerAuthenticationFailure.FormatInvariant(AuthorizationScope.ServiceProvider); + Responses[StatusCodes.Status403Forbidden] = Constants.SwaggerSummary.AccessDeniedToDialogForChildEntity.FormatInvariant("create"); + Responses[StatusCodes.Status404NotFound] = Constants.SwaggerSummary.DialogNotFound; + Responses[StatusCodes.Status412PreconditionFailed] = Constants.SwaggerSummary.RevisionMismatch; + Responses[StatusCodes.Status422UnprocessableEntity] = Constants.SwaggerSummary.DomainError; + } +} diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Get/GetDialogTransmissionEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Get/GetDialogTransmissionEndpoint.cs new file mode 100644 index 000000000..0842b66e2 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Get/GetDialogTransmissionEndpoint.cs @@ -0,0 +1,34 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogTransmissions.Queries.Get; +using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; +using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; +using FastEndpoints; +using MediatR; + +namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.DialogTransmissions.Get; + +public class GetDialogTransmissionEndpoint : Endpoint +{ + private readonly ISender _sender; + + public GetDialogTransmissionEndpoint(ISender sender) + { + _sender = sender ?? throw new ArgumentNullException(nameof(sender)); + } + + public override void Configure() + { + Get("dialogs/{dialogId}/transmissions/{transmissionId}"); + Policies(AuthorizationPolicy.ServiceProvider); + Group(); + + Description(b => GetDialogTransmissionSwaggerConfig.SetDescription(b)); + } + + public override async Task HandleAsync(GetDialogTransmissionQuery req, CancellationToken ct) + { + var result = await _sender.Send(req, ct); + await result.Match( + dto => SendOkAsync(dto, ct), + notFound => this.NotFoundAsync(notFound, ct)); + } +} diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Get/GetDialogTransmissionSwaggerConfig.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Get/GetDialogTransmissionSwaggerConfig.cs new file mode 100644 index 000000000..ea835166f --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Get/GetDialogTransmissionSwaggerConfig.cs @@ -0,0 +1,36 @@ +using Digdir.Domain.Dialogporten.WebApi.Common; +using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; +using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; +using Digdir.Domain.Dialogporten.WebApi.Common.Swagger; +using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Extensions; +using FastEndpoints; + +namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.DialogTransmissions.Get; + +public class GetDialogTransmissionSwaggerConfig : ISwaggerConfig +{ + public static string OperationId => "GetDialogTransmissionSO"; + + public static RouteHandlerBuilder SetDescription(RouteHandlerBuilder builder) + => builder.OperationId(OperationId) + .ProducesOneOf( + StatusCodes.Status200OK, + StatusCodes.Status404NotFound); + + public static object GetExample() => throw new NotImplementedException(); +} + +public sealed class GetDialogTransmissionEndpointSummary : Summary +{ + public GetDialogTransmissionEndpointSummary() + { + Summary = "Gets a single dialog transmission"; + Description = """ + Gets a single transmission belonging to a dialog. For more information see the documentation (link TBD). + """; + Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("transmission"); + Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.ServiceOwnerAuthenticationFailure.FormatInvariant(AuthorizationScope.ServiceProvider); + Responses[StatusCodes.Status403Forbidden] = Constants.SwaggerSummary.AccessDeniedToDialogForChildEntity.FormatInvariant("get"); + Responses[StatusCodes.Status404NotFound] = Constants.SwaggerSummary.DialogTransmissionNotFound; + } +} diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Search/SearchDialogTransmissionEndpoint.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Search/SearchDialogTransmissionEndpoint.cs new file mode 100644 index 000000000..37a3ffb46 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Search/SearchDialogTransmissionEndpoint.cs @@ -0,0 +1,34 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogTransmissions.Queries.Search; +using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; +using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; +using FastEndpoints; +using MediatR; + +namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.DialogTransmissions.Search; + +public class SearchDialogTransmissionEndpoint : Endpoint +{ + private readonly ISender _sender; + + public SearchDialogTransmissionEndpoint(ISender sender) + { + _sender = sender ?? throw new ArgumentNullException(nameof(sender)); + } + + public override void Configure() + { + Get("dialogs/{dialogId}/transmissions"); + Policies(AuthorizationPolicy.ServiceProvider); + Group(); + + Description(b => SearchDialogTransmissionSwaggerConfig.SetDescription(b)); + } + + public override async Task HandleAsync(SearchDialogTransmissionQuery req, CancellationToken ct) + { + var result = await _sender.Send(req, ct); + await result.Match( + dto => SendOkAsync(dto, ct), + notFound => this.NotFoundAsync(notFound, ct)); + } +} diff --git a/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Search/SearchDialogTransmissionSwaggerConfig.cs b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Search/SearchDialogTransmissionSwaggerConfig.cs new file mode 100644 index 000000000..53ad14353 --- /dev/null +++ b/src/Digdir.Domain.Dialogporten.WebApi/Endpoints/V1/ServiceOwner/DialogTransmissions/Search/SearchDialogTransmissionSwaggerConfig.cs @@ -0,0 +1,33 @@ +using Digdir.Domain.Dialogporten.Application.Features.V1.ServiceOwner.DialogTransmissions.Queries.Search; +using Digdir.Domain.Dialogporten.WebApi.Common; +using Digdir.Domain.Dialogporten.WebApi.Common.Authorization; +using Digdir.Domain.Dialogporten.WebApi.Common.Extensions; +using Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.Common.Extensions; +using Digdir.Domain.Dialogporten.WebApi.Common.Swagger; +using FastEndpoints; + +namespace Digdir.Domain.Dialogporten.WebApi.Endpoints.V1.ServiceOwner.DialogTransmissions.Search; + +public class SearchDialogTransmissionSwaggerConfig : ISwaggerConfig +{ + public static string OperationId => "GetDialogTransmissionListSO"; + public static RouteHandlerBuilder SetDescription(RouteHandlerBuilder builder) + => builder.OperationId(OperationId); + + public static object GetExample() => throw new NotImplementedException(); +} + +public sealed class SearchDialogTransmissionEndpointSummary : Summary +{ + public SearchDialogTransmissionEndpointSummary() + { + Summary = "Gets a list of dialog transmissions"; + Description = """ + Gets the list of transmissions belonging to a dialog + """; + Responses[StatusCodes.Status200OK] = Constants.SwaggerSummary.ReturnedResult.FormatInvariant("transmission list"); + Responses[StatusCodes.Status401Unauthorized] = Constants.SwaggerSummary.ServiceOwnerAuthenticationFailure.FormatInvariant(AuthorizationScope.ServiceProvider); + Responses[StatusCodes.Status403Forbidden] = Constants.SwaggerSummary.AccessDeniedToDialog.FormatInvariant("get"); + Responses[StatusCodes.Status404NotFound] = Constants.SwaggerSummary.DialogNotFound; + } +}