diff --git a/backend/gradle/wrapper/gradle-wrapper.properties b/backend/gradle/wrapper/gradle-wrapper.properties index 62f495d..df97d72 100644 --- a/backend/gradle/wrapper/gradle-wrapper.properties +++ b/backend/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/backend/plugin/build.gradle b/backend/plugin/build.gradle index dfa041b..4d43113 100644 --- a/backend/plugin/build.gradle +++ b/backend/plugin/build.gradle @@ -33,6 +33,8 @@ plugins { id "org.jetbrains.kotlin.plugin.spring" version "${kotlinVersion}" id "org.jetbrains.kotlin.plugin.allopen" version "${kotlinVersion}" id "com.avast.gradle.docker-compose" version "0.16.9" + + id "org.openapi.generator" version "7.9.0" } repositories { @@ -49,9 +51,17 @@ dependencies { implementation "com.ritense.valtimo:value-resolver:${valtimoVersion}" implementation "org.springframework.boot:spring-boot-starter-webflux:${springVersion}" + implementation "org.springframework.boot:spring-boot-starter-data-jpa:${springVersion}" implementation "io.github.microutils:kotlin-logging:3.0.5" + implementation "jakarta.inject:jakarta.inject-api:2.0.1" + implementation "jakarta.persistence:jakarta.persistence-api:3.1.0" + + implementation("com.squareup.okhttp3:okhttp:4.12.0") + implementation("com.squareup.moshi:moshi:1.15.1") + implementation("com.squareup.moshi:moshi-kotlin:1.15.1") + // Testing testImplementation "com.ritense.valtimo:document:${valtimoVersion}" testImplementation "com.ritense.valtimo:local-resource:${valtimoVersion}" @@ -74,6 +84,23 @@ dependencies { testImplementation "org.jetbrains.kotlin:kotlin-test-junit5" } +openApiGenerate { + inputSpec.set("$rootDir/plugin/src/main/resources/dcsg_xential.yaml") + generatorName.set("kotlin") + outputDir.set("${getLayout().getBuildDirectory().get()}/generated") + apiPackage.set("com.rotterdam.xential.api") + invokerPackage.set("com.rotterdam.xential.invoker") + modelPackage.set("com.rotterdam.xential.model") +} + +sourceSets { + main { + java { + srcDir("${buildDir}/generated/src/main") + } + } +} + tasks.register("integrationTesting", Test) { group = "verification" useJUnitPlatform { diff --git a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/autoconfiguration/XentialAutoConfiguration.kt b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/autoconfiguration/XentialAutoConfiguration.kt index 1fcb38e..cb752a7 100644 --- a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/autoconfiguration/XentialAutoConfiguration.kt +++ b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/autoconfiguration/XentialAutoConfiguration.kt @@ -17,21 +17,64 @@ package com.ritense.valtimo.xential.autoconfiguration import com.ritense.plugin.service.PluginService -import com.ritense.resource.service.TemporaryResourceStorageService +import com.ritense.valtimo.contract.config.LiquibaseMasterChangeLogLocation import com.ritense.valtimo.xential.plugin.XentialPluginFactory +import com.ritense.valtimo.xential.repository.XentialTokenRepository +import com.ritense.valtimo.xential.service.DocumentGenerationService +import com.rotterdam.xential.api.DefaultApi +import org.openapitools.client.infrastructure.ApiClient +import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.AutoConfiguration import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.context.annotation.Bean +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.web.reactive.function.client.WebClient @AutoConfiguration +@EnableJpaRepositories(basePackages = ["com.ritense.valtimo.xential.repository"]) +@EntityScan("com.ritense.valtimo.xential.domain") class XentialAutoConfiguration { @Bean @ConditionalOnMissingBean(XentialPluginFactory::class) fun xentialPluginFactory( pluginService: PluginService, + documentGenerationService: DocumentGenerationService ): XentialPluginFactory { - return XentialPluginFactory(pluginService) + return XentialPluginFactory( + pluginService, + documentGenerationService, + ) } + @Bean + @ConditionalOnMissingBean(name = ["xentialLiquibaseMasterChangeLogLocation"]) + fun xentialLiquibaseMasterChangeLogLocation(): LiquibaseMasterChangeLogLocation { + return LiquibaseMasterChangeLogLocation("config/liquibase/xential-plugin-master.xml") + } + + @Bean + @ConditionalOnMissingBean + fun defaultApi(): DefaultApi { + return DefaultApi() + } + + @Bean + @ConditionalOnMissingBean + fun apiClient( + @Value("plugin.xential.baseurl") + baseUrl: String, + ) = ApiClient(baseUrl) + + @Bean + @ConditionalOnMissingBean + fun documentGenerationService( + defaultApi: DefaultApi, + xentialTokenRepository: XentialTokenRepository + ) = DocumentGenerationService( + defaultApi, + xentialTokenRepository, + ) + } diff --git a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/model/GenerateDocumentProperties.kt b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/domain/GenerateDocumentProperties.kt similarity index 61% rename from backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/model/GenerateDocumentProperties.kt rename to backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/domain/GenerateDocumentProperties.kt index 6cefce8..606a0e9 100644 --- a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/model/GenerateDocumentProperties.kt +++ b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/domain/GenerateDocumentProperties.kt @@ -1,12 +1,14 @@ -package com.ritense.valtimo.xential.model +package com.ritense.valtimo.xential.domain + +import java.util.UUID data class GenerateDocumentProperties( - val templateId: String, + val templateId: UUID, val fileFormat: FileFormat, val documentId: String, val templateData: Map ) enum class FileFormat { - DOCX, HTML, PDF, XML + WORD, PDF } diff --git a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/domain/XentialToken.kt b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/domain/XentialToken.kt new file mode 100644 index 0000000..2ec93ec --- /dev/null +++ b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/domain/XentialToken.kt @@ -0,0 +1,20 @@ +package com.ritense.valtimo.xential.domain + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.Id +import jakarta.persistence.Table +import java.net.URI +import java.util.UUID + +@Entity +@Table(name = "xential_tokens") +data class XentialToken ( + @Id + @Column(name = "token", nullable = false, updatable = false) + val token: UUID, + @Column(name = "process_id", nullable = false, updatable = false) + val processId: UUID, + @Column(name = "resume_url", nullable = true, updatable = false) + val resumeUrl: URI?, +) diff --git a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/plugin/XentialPlugin.kt b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/plugin/XentialPlugin.kt index 2a7922c..df2e19d 100644 --- a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/plugin/XentialPlugin.kt +++ b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/plugin/XentialPlugin.kt @@ -20,8 +20,14 @@ import com.ritense.plugin.annotation.Plugin import com.ritense.plugin.annotation.PluginAction import com.ritense.plugin.annotation.PluginProperty import com.ritense.processlink.domain.ActivityTypeWithEventName -import com.ritense.valtimo.xential.model.GenerateDocumentProperties +import com.ritense.valtimo.xential.domain.GenerateDocumentProperties +import com.ritense.valtimo.xential.domain.XentialToken +import com.ritense.valtimo.xential.repository.XentialTokenRepository +import com.ritense.valtimo.xential.service.DocumentGenerationService +import com.rotterdam.xential.api.DefaultApi +import com.rotterdam.xential.model.Sjabloondata import org.camunda.bpm.engine.delegate.DelegateExecution +import java.util.UUID @Plugin( key = "xential", @@ -29,11 +35,15 @@ import org.camunda.bpm.engine.delegate.DelegateExecution description = "" ) class XentialPlugin( + val documentGenerationService: DocumentGenerationService ) { @PluginProperty(key = "clientId", secret = false) private lateinit var clientId: String + @PluginProperty(key = "clientPassword", secret = true) + private lateinit var clientPassword: String + @PluginAction( key = "generate-document", title = "Generate document", @@ -41,7 +51,12 @@ class XentialPlugin( activityTypes = [ActivityTypeWithEventName.SERVICE_TASK_START] ) fun generateDocument(execution: DelegateExecution, generateDocumentProperties: GenerateDocumentProperties) { - println("$clientId, $generateDocumentProperties") + documentGenerationService.generateDocument( + UUID.fromString(execution.processInstanceId), + generateDocumentProperties, + clientId, + clientPassword + ) } } diff --git a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/plugin/XentialPluginFactory.kt b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/plugin/XentialPluginFactory.kt index 2b45656..5dfd1d2 100644 --- a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/plugin/XentialPluginFactory.kt +++ b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/plugin/XentialPluginFactory.kt @@ -18,12 +18,17 @@ package com.ritense.valtimo.xential.plugin import com.ritense.plugin.PluginFactory import com.ritense.plugin.service.PluginService +import com.ritense.valtimo.xential.repository.XentialTokenRepository +import com.ritense.valtimo.xential.service.DocumentGenerationService +import com.rotterdam.xential.api.DefaultApi +import org.springframework.web.client.RestClient class XentialPluginFactory( pluginService: PluginService, + val documentGenerationService: DocumentGenerationService ) : PluginFactory(pluginService) { override fun create(): XentialPlugin { - return XentialPlugin() + return XentialPlugin(documentGenerationService) } } diff --git a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/repository/XentialTokenRepository.kt b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/repository/XentialTokenRepository.kt new file mode 100644 index 0000000..4942071 --- /dev/null +++ b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/repository/XentialTokenRepository.kt @@ -0,0 +1,7 @@ +package com.ritense.valtimo.xential.repository + +import com.ritense.valtimo.xential.domain.XentialToken +import org.springframework.data.repository.CrudRepository +import java.util.UUID + +interface XentialTokenRepository : CrudRepository diff --git a/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/service/DocumentGenerationService.kt b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/service/DocumentGenerationService.kt new file mode 100644 index 0000000..bc987a3 --- /dev/null +++ b/backend/plugin/src/main/kotlin/com/ritense/valtimo/xential/service/DocumentGenerationService.kt @@ -0,0 +1,45 @@ +package com.ritense.valtimo.xential.service + +import com.ritense.valtimo.xential.domain.GenerateDocumentProperties +import com.ritense.valtimo.xential.domain.XentialToken +import com.ritense.valtimo.xential.repository.XentialTokenRepository +import com.rotterdam.xential.api.DefaultApi +import com.rotterdam.xential.model.Sjabloondata +import org.openapitools.client.infrastructure.ApiClient +import java.util.UUID + +class DocumentGenerationService( + val defaultApi: DefaultApi, + val xentialTokenRepository: XentialTokenRepository, + ) { + + fun generateDocument( + processId: UUID, + generateDocumentProperties: GenerateDocumentProperties, + clientId: String, + clientPassword: String, + ) { + val sjabloonVulData = generateDocumentProperties.templateData.entries.map { "<${it.key}>${it.value}" }.joinToString() + + ApiClient.username = clientId + ApiClient.password = clientPassword + val result = defaultApi.creeerDocument( + gebruikersId = clientId, + accepteerOnbekend = false, + sjabloondata = Sjabloondata( + sjabloonId = generateDocumentProperties.templateId.toString(), + bestandsFormaat = Sjabloondata.BestandsFormaat.valueOf(generateDocumentProperties.fileFormat.name), + documentkenmerk = generateDocumentProperties.documentId, + sjabloonVulData = "$sjabloonVulData" + ) + ) + + val xentialToken = XentialToken( + token = UUID.fromString(result.documentCreatieSessieId), + processId = processId, + resumeUrl = result.resumeUrl + ) + + xentialTokenRepository.save(xentialToken) + } +} diff --git a/backend/plugin/src/main/resources/config/liquibase/changelog/20241010-add-xential_tokens.xml b/backend/plugin/src/main/resources/config/liquibase/changelog/20241010-add-xential_tokens.xml new file mode 100644 index 0000000..0b9992f --- /dev/null +++ b/backend/plugin/src/main/resources/config/liquibase/changelog/20241010-add-xential_tokens.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + diff --git a/backend/plugin/src/main/resources/config/liquibase/xential-plugin-master.xml b/backend/plugin/src/main/resources/config/liquibase/xential-plugin-master.xml new file mode 100644 index 0000000..2b6a09b --- /dev/null +++ b/backend/plugin/src/main/resources/config/liquibase/xential-plugin-master.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/backend/plugin/src/main/resources/dcsg_xential.yaml b/backend/plugin/src/main/resources/dcsg_xential.yaml new file mode 100644 index 0000000..28a563d --- /dev/null +++ b/backend/plugin/src/main/resources/dcsg_xential.yaml @@ -0,0 +1,349 @@ +openapi: 3.0.2 +info: + title: Document Creatie Services Gateway (DCSG) API + description: 'Deze specificatie beschrijft generiek koppelvlak voor documentcreatie diensten van de gemeente Rotterdam.\ + \n\ + \nVoor alle functies geldt dat de volgende twee parameters als onderdeel van Basic authenticatie + meegestuurd moeten worden:\ + \n* Taakapplicatie naam\ + \n* Taakapplicatie wachtwoord' + contact: + url: https://www.rotterdam.nl + email: noreply@rotterdam.nl + version: 1.1.0 +paths: + /sjablonenlijst: + get: + operationId: geefSjablonenlijst + summary: Ophalen sjablonenlijst + description: "Documentsjablonen worden beheerd als een boom met sjabloongroepen en sjablonen + (geneste boomstructuur vergelijkbaar met mappenboom). Gebruikers worden geautoriseerd voor + de toegang tot een bepaald sjabloongroep. Daarmee krijgen gebruikers de toegang tot alle + onderliggende subgroepen en sjablonen op alle hiërarchische onderliggende niveaus. + Deze functie biedt de mogelijkheid om de lijst van sjablonen en sjabloongroepen binnen + een sjabloongroep op te halen. Indien sjabloongroep niet is opgegeven in het request + dan retourneert deze functie de inhoud van de hoofdgroep (root)." + parameters: + - name: gebruikersId + in: query + description: Gebruikersnaam van de gebruiker die de sjablonen ophaalt (medewerkerid) + required: true + example: '123456' + schema: + type: string + - name: sjabloongroepId + in: query + description: Optioneel ID van bovenliggende sjabloongroep. Als die niet is geleverd dan wordt de inhoud van de hoofdgroep geretourneerd. + required: false + example: bc9f22c4-9db8-4724-8424-c2b88404cf5e + schema: + type: string + responses: + '200': + description: OK, lijsten met sjablonen en sjabloongroepen + content: + application/json: + schema: + $ref: '#/components/schemas/Sjabloonitems' + '400': + description: Bad request, mogelijke invalide parameter; PARAMETER_FOUT; HEADER_FOUT + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Fout' + '401': + description: Unauthorized, authenticatie problemen; AUTHENTICATIE_FOUT of ONBEKENDE_GEBRUIKER + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Fout' + '403': + description: Forbidden, autorisatie problemen; ONVOLDOENDE_RECHTEN + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Fout' + '408': + description: Time-out, request time-out opgetreden; TIME_OUT + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Fout' + '500': + description: Internal Server Error; ONBEKENDE_FOUT + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Fout' + '503': + description: Service Unavailable, Xential of ESB niet bereikbaar (mogelijk in onderhoud) + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Fout' + /documentcreatie: + post: + operationId: creeerDocument + summary: Initiëren document creatie + description: "Middels deze functie initieert de taakapplicatie de creatie van het document op + basis van een sjabloon. Het wordt hier nadrukkelijk gesproken over initiatie omdat deze functie + niet direct (synchroon) het gecreëerde document teruglevert. Het document wordt op achtergrond + gecreëerd en asynchroon op een ander moment geleverd via de queue. Zie formaat van het bericht + op de queue in het schema DocumentData" + parameters: + - name: gebruikersId + in: query + description: Gebruikersnaam van de gebruiker die het document creëert (medewerkerid) + required: true + example: '123456' + schema: + type: string + - name: accepteerOnbekend + in: query + description: "Optionele indicatie of een onbekende gebruiker acceptabel is of niet. + Indien een onbekende gebruiker acceptabel is, wordt er een 'startDocumentUrl' + geretourneerd waarmee de gebruiker zich kan bekend maken bij Xential" + schema: + type: boolean + requestBody: + required: false + content: + application/json: + schema: + $ref: '#/components/schemas/Sjabloondata' + responses: + '200': + description: OK, start van documentcreatie is gelukt + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentCreatieResultaat' + '400': + description: Bad request, mogelijke invalide parameter; PARAMETER_FOUT; INVALIDE_INPUT; HEADER_FOUT + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Fout' + '401': + description: Unauthorized, authenticatie problemen; AUTHENTICATIE_FOUT of ONBEKENDE_GEBRUIKER + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Fout' + '403': + description: Forbidden, autorisatie problemen; ONVOLDOENDE_RECHTEN + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Fout' + '408': + description: Time-out, request time-out opgetreden; TIME_OUT + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Fout' + '500': + description: Internal Server Error; ONBEKENDE_FOUT + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Fout' + '503': + description: Service Unavailable, Xential of ESB niet bereikbaar (mogelijk vanwege onderhoud) + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Fout' + +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + schemas: + Fout: + description: 'Dit schema beschrijft formaat van het synchrone response van functies bij fouten.\ + \n\ + \nIn uitzonderingsgevallen (bij HTTP400 Bad request of HTTP500 Internal server error) kunnen echter + json-responses in het volgende formaat geretourneerd worden: + {\"timestamp\": \"2021-08-10\",\"status\": 500,\"error\": \"Internal Server Error\",\"message\": \"\",\"path\": \"/document\/generatie\/documentcreatie\"}' + required: + - code + - detail + type: object + properties: + type: + title: Type + description: URI referentie naar het type fout (wordt nog niet gevuld) + example: '' + type: string + code: + title: Code + description: Systeemcode die het type fout aangeeft + type: string + example: 'INVALIDE_INPUT' + minLength: 1 + title: + title: Title + description: Generieke titel voor het type fout (wordt nog niet gevuld) + type: string + example: '' + minLength: 1 + status: + title: Status + description: De HTTP status code + type: integer + example: 400 + detail: + title: Detail + description: Extra informatie bij de fout + type: string + example: 'XML data fout' + minLength: 1 + instance: + title: Instance + description: "End-to-end Request ID (uuid) die automatisch door ESB wordt gegeneerd bij elk request + en die ook in e-mail berichten FoutAfhandelaar is opgenomen" + type: string + example: 80a48aa5-3ada-4d5e-b378-765991c5cf66 + minLength: 0 + Sjabloonitems: + description: "Dit schema beschrijft formaat van het response van de functie Ophalen sjablonen" + required: + - sjablonen + - sjabloongroepen + type: object + properties: + sjablonen: + title: Sjablonen + description: Lijst van sjablonen + type: array + items: + $ref: '#/components/schemas/Sjabloonitem' + sjabloongroepen: + title: Sjabloongroepen + description: Lijst van sjabloongroepen + type: array + items: + $ref: '#/components/schemas/Sjabloonitem' + Sjabloonitem: + required: + - id + - naam + type: object + properties: + id: + title: Id + description: Unieke id van sjabloon/ sjabloongroep + type: string + example: 'e0a8ace5-06df-4ef7-bcbb-9d694d25e03b' + naam: + title: Naam + description: Naam van sjabloon/ sjabloongroep + type: string + example: 'Lorem ipsum' + Sjabloondata: + description: "Dit schema beschrijft formaat van het request voor de functie Initieren documentcreatie" + type: object + properties: + sjabloonId: + title: SjabloonId + description: "Optionele ID van het sjabloon. Indien niet meegegeven zal de eindgebruiker + dit sjabloon moet gaan selecteren via de client (zie Resume URL parameter in het response)" + type: string + example: 3e09b238-0617-47c1-8e6a-f6227b3d542e + bestandsFormaat: + title: BestandsFormaat + description: Optionele keuze van het bestandsformaat (WORD of PDF), WORD is default + type: string + enum: + - WORD + - PDF + documentkenmerk: + title: Documentkenmerk + description: "Kenmerk van het document dat samen met gecreëerd document zal worden geretourneerd + in het resultaat van documentcreatie. Dit kan zijn, bv, bestandsnaam, map Id of welke andere + identificatie dan ook die voor taakapplicaties waardevol kan zijn te ontvangen samen met + het gecreëerd document waardoor verdere afhandeling van dit document (bv, opslag) in de + taakapplicaties gefaciliteerd kan worden" + type: string + example: 'Lorem ipsum' + sjabloonVulData: + title: SjabloonVulData + description: "Optionele data in XML-formaat die zal worden gebruikt voor het vullen van + sjabloondata tijdens documentcreatie. Het formaat van deze data wordt gezamenlijk bepaald + door ontwikkelaars van taakapplicaties en ontwerpers van documentsjablonen, ESB ziet + deze data als een blackbox en controleert het alleen op well-formedness." + type: string + example: "test" + DocumentData: + description: "Dit schema beschrijft het berichtformaat van het eindresultaat van documentcreatie + welke in de queue wordt geplaatst." + required: + - taakapplicatie + - gebruiker + - documentCreatieSessieId + - formaat + - data + type: object + properties: + taakapplicatie: + title: Taakapplicatie + description: De naam van de taakapplicatie + type: string + example: 'applicatiexyz' + gebruiker: + title: Gebruiker + description: De naam van de gebruiker + type: string + example: '123456' + documentCreatieSessieId: + title: Document Creatie Sessie ID + description: "Unieke ID toegewezen door de ESB aan het document dat was geretourneerd + als resultaat van de functie Initiëren document creatie" + type: string + example: 6d4b2fa2-5d02-4949-ace3-53c9b0ff1e4e + formaat: + title: Bestandsformaat + description: Bestandsformaat (WORD of PDF) waarin het document is gecreëerd + type: string + enum: + - WORD + - PDF + documentkenmerk: + title: Documentkenmerk + description: Kenmerk van het document dat bij het initiëren documentcreatie is opgegeven. + type: string + example: 'Lorem ipsum' + data: + title: Data + description: Inhoud van document in base64-encoding zonder linebreaks + type: string + example: 'UEsDBBQACAgIA...' + DocumentCreatieResultaat: + description: "Dit schema beschrijft formaat van het response van de functie Initieren documentcreatie" + required: + - documentCreatieSessieId + - status + type: object + properties: + documentCreatieSessieId: + description: Unieke ID toegewezen door de ESB aan het document dat wordt gecreëerd. + type: string + example: 6d4b2fa2-5d02-4949-ace3-53c9b0ff1e4e + status: + description: "Dit veld geeft aan of het document volledig zonder interactie met + eindgebruiker opgebouwd zal worden (VOLTOOID), of dat er nog input van eindgebruiker + benodigd is (ONVOLTOOID). In het laatste geval wordt ook het veld Resume URL geretourneerd." + type: string + enum: + - VOLTOOID + - ONVOLTOOID + resumeUrl: + description: "Link naar de webpagina waarin eindgebruiker ontbrekende data benodigd voor + documentcreatie kan invullen. Wordt alleen geretourneerd indien status = ONVOLTOOID" + type: string + format: uri + example: "https://rotterdam.xentiallabs.com/xential?resumeApplication=b85c58db-e50a-4c11-a6a8-2b522b87483d&loginTicketUuid=0b9077f3-e662-4a85-b9f9-e7418fbecc73&afterBuildAction=close" +security: + - basicAuth: [] \ No newline at end of file diff --git a/backend/plugin/src/test/kotlin/com/ritense/valtimo/xential/service/DocumentGenerationServiceTest.kt b/backend/plugin/src/test/kotlin/com/ritense/valtimo/xential/service/DocumentGenerationServiceTest.kt new file mode 100644 index 0000000..92983d7 --- /dev/null +++ b/backend/plugin/src/test/kotlin/com/ritense/valtimo/xential/service/DocumentGenerationServiceTest.kt @@ -0,0 +1,68 @@ +package com.ritense.valtimo.xential.service + +import com.ritense.valtimo.xential.domain.FileFormat +import com.ritense.valtimo.xential.domain.GenerateDocumentProperties +import com.ritense.valtimo.xential.domain.XentialToken +import com.ritense.valtimo.xential.repository.XentialTokenRepository +import com.rotterdam.xential.api.DefaultApi +import com.rotterdam.xential.model.DocumentCreatieResultaat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.openapitools.client.infrastructure.ApiClient +import java.util.UUID + +class DocumentGenerationServiceTest { + + @Mock + lateinit var defaultApi: DefaultApi + + @Mock + lateinit var xentialTokenRepository: XentialTokenRepository + + @InjectMocks + lateinit var documentGenerationService: DocumentGenerationService + + @BeforeEach + fun setup() { + MockitoAnnotations.openMocks(this) + } + + @Test + fun shouldGenerateDocument() { + val executionId = UUID.randomUUID() + + val generateDocumentProperties = GenerateDocumentProperties( + templateId = UUID.randomUUID(), + fileFormat = FileFormat.PDF, + documentId = "mijn-kenmerk", + templateData = emptyMap() + ) + + val creatieResultaat = DocumentCreatieResultaat( + documentCreatieSessieId = UUID.randomUUID().toString(), + status = DocumentCreatieResultaat.Status.VOLTOOID, + resumeUrl = null + ) + whenever(defaultApi.creeerDocument(any(), any(), any())).thenReturn(creatieResultaat) + + documentGenerationService.generateDocument( + executionId, + generateDocumentProperties, + "client-id", + "client-password", + ) + + verify(xentialTokenRepository).save(XentialToken( + token = UUID.fromString(creatieResultaat.documentCreatieSessieId), + processId = executionId, + resumeUrl = null + )) + } + +} diff --git a/frontend/projects/valtimo/xential/src/lib/components/generate-document-configuration/generate-document-configuration.component.ts b/frontend/projects/valtimo/xential/src/lib/components/generate-document-configuration/generate-document-configuration.component.ts index 7849826..bd51715 100644 --- a/frontend/projects/valtimo/xential/src/lib/components/generate-document-configuration/generate-document-configuration.component.ts +++ b/frontend/projects/valtimo/xential/src/lib/components/generate-document-configuration/generate-document-configuration.component.ts @@ -18,7 +18,7 @@ export class GenerateDocumentConfigurationComponent implements FunctionConfigura new EventEmitter(); public fileFormats$ = new BehaviorSubject( - ['DOCX', 'HTML', 'PDF', 'XML'] + ['WORD', 'PDF'] .map(format => { return { id: format, diff --git a/frontend/projects/valtimo/xential/src/lib/components/xential-configuration/xential-configuration.component.html b/frontend/projects/valtimo/xential/src/lib/components/xential-configuration/xential-configuration.component.html index 3ddbc98..a0a4fa1 100644 --- a/frontend/projects/valtimo/xential/src/lib/components/xential-configuration/xential-configuration.component.html +++ b/frontend/projects/valtimo/xential/src/lib/components/xential-configuration/xential-configuration.component.html @@ -37,4 +37,11 @@ [defaultValue]="obs.prefill?.clientId" [title]="'clientId' | pluginTranslate: pluginId | async"> + + diff --git a/frontend/projects/valtimo/xential/src/lib/models/config.ts b/frontend/projects/valtimo/xential/src/lib/models/config.ts index 93b5579..34a3245 100644 --- a/frontend/projects/valtimo/xential/src/lib/models/config.ts +++ b/frontend/projects/valtimo/xential/src/lib/models/config.ts @@ -18,6 +18,7 @@ import {PluginConfigurationData} from '@valtimo/plugin'; interface XentialConfig extends PluginConfigurationData { clientId: string; + clientPassword: string; } export {XentialConfig}; diff --git a/frontend/projects/valtimo/xential/src/lib/models/generate-document-config.ts b/frontend/projects/valtimo/xential/src/lib/models/generate-document-config.ts index ed835b5..9e4dd7f 100644 --- a/frontend/projects/valtimo/xential/src/lib/models/generate-document-config.ts +++ b/frontend/projects/valtimo/xential/src/lib/models/generate-document-config.ts @@ -5,6 +5,6 @@ interface GenerateDocumentConfig { templateData: Array<{key: string; value: string}> } -type FileFormat = 'DOCX' | 'PDF' | 'XML' | 'HTML'; +type FileFormat = 'WORD' | 'PDF'; export {GenerateDocumentConfig, FileFormat} diff --git a/frontend/projects/valtimo/xential/src/lib/xential-plugin.specification.ts b/frontend/projects/valtimo/xential/src/lib/xential-plugin.specification.ts index 30705c5..539f741 100644 --- a/frontend/projects/valtimo/xential/src/lib/xential-plugin.specification.ts +++ b/frontend/projects/valtimo/xential/src/lib/xential-plugin.specification.ts @@ -33,7 +33,8 @@ const XentialPluginSpecification: PluginSpecification = { title: 'Xential', description: 'Xential plugin', configurationTitle: 'Configuratie naam', - clientId: 'Client ID', + clientId: 'Taak applicatie naam', + clientPassword: 'Taak applicatie wachtwoord', 'generate-document': 'Genereer document', templateId: 'Template ID', fileFormat: 'Bestandsformaat', @@ -45,6 +46,7 @@ const XentialPluginSpecification: PluginSpecification = { description: 'Xential plugin', configurationTitle: 'Configuration name', clientId: 'Client ID', + clientPassword: 'Client password', 'generate-document': 'Generate document', templateId: 'Sjabloon ID', fileFormat: 'File format', @@ -55,7 +57,8 @@ const XentialPluginSpecification: PluginSpecification = { title: 'Xential', description: 'Xential plugin', configurationTitle: 'Konfigurationsname', - clientId: 'Client ID', + clientId: 'Kunden-ID', + clientPassword: 'Kundenpasswort', 'generate-document': 'Dokument generieren', templateId: 'Vorlage ID', fileFormat: 'Dateiformat',