diff --git a/Jenkinsfile b/Jenkinsfile index ef44d538b0f0f4cf970a4f323f586c31d43b6f5c..c28bf0d2fa1f10b024edee98db52ddecd73f2b6b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -44,6 +44,7 @@ pipeline { } } } + stage('Client') { environment { FORCE_COLOR = 'false' @@ -83,7 +84,29 @@ pipeline { // } // } } - stage('Server') { + + stage('Set Version') { + when { + not { + anyOf { + branch 'master' + branch 'release' + } + } + } + steps { + script { + FAILED_STAGE=env.STAGE_NAME + JAR_TAG = getRootPomVersion().replace("SNAPSHOT", "${env.BRANCH_NAME}-SNAPSHOT") + } + configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { + sh "mvn -s $MAVEN_SETTINGS versions:set -DnewVersion=${JAR_TAG} -DprocessAllModules=true" + + } + } + } + + stage('Build Server artefacts, build and push docker image') { steps { script { FAILED_STAGE=env.STAGE_NAME @@ -120,12 +143,6 @@ pipeline { } } stage('Deploy Maven Artifacts to Nexus') { - when { - anyOf { - branch 'master' - branch 'release' - } - } steps { script { FAILED_STAGE = env.STAGE_NAME @@ -133,6 +150,7 @@ pipeline { configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS -pl -alfa-client -DskipTests deploy' + sh "mvn -s $MAVEN_SETTINGS versions:revert" } } } @@ -143,6 +161,7 @@ pipeline { branch 'release' } } + steps { script { FAILED_STAGE = env.STAGE_NAME diff --git a/alfa-client/Jenkinsfile.storybook b/alfa-client/Jenkinsfile.storybook new file mode 100644 index 0000000000000000000000000000000000000000..b589706023ff460ecc9ec5c164b431b464c8c610 --- /dev/null +++ b/alfa-client/Jenkinsfile.storybook @@ -0,0 +1,248 @@ +pipeline { + agent { + node { + label 'ozgcloud-jenkins-build-agent' + } + } + + environment { + BLUE_OCEAN_URL = "https://jenkins.infra.ozg-cloud.systems/job/storybook/job/${env.BRANCH_NAME}/${env.BUILD_NUMBER}/" + RELEASE_REGEX = /\d+.\d+.\d+/ + SNAPSHOT_REGEX = /\d+.\d+.\d+-SNAPSHOT/ + FAILED_STAGE = "" + SH_SUCCESS_STATUS_CODE = 0 + + } + + options { + timeout(time: 1, unit: 'HOURS') + disableConcurrentBuilds() + buildDiscarder(logRotator(numToKeepStr: '5')) + } + + stages { + stage('Check Version') { + steps { + script { + FAILED_STAGE = env.STAGE_NAME + dir('alfa-client') { + VERSION = getPackagejsonVersion() + } + } + } + } + + stage('build storybook and its docker image') { + steps { + script { + FAILED_STAGE=env.STAGE_NAME + + dir('alfa-client') { + + withNPM(npmrcConfig: 'a4272a20-bb67-4cf7-8ce5-a1c3ac66613c') { + sh 'npm cache verify' + sh 'npm install' + sh 'npm run ci-storybook' + } + + + + + } + + } + } + } + + stage('Tag and Push Docker image') { + steps { + script { + FAILED_STAGE=env.STAGE_NAME + IMAGE_TAG = generateImageTag() + + tagAndPushDockerImage(IMAGE_TAG) + + if (isMasterBranch()) { + tagAndPushDockerImage('snapshot-latest') + } + } + } + } + + stage('Test, build and deploy Helm Chart') { + steps { + script { + HELM_CHART_VERSION = generateHelmChartVersion() + + dir('alfa-client/libs/design-system') { + sh "./run_helm_test.sh" + } + + dir('alfa-client/libs/design-system/main/helm') { + + sh "helm package --version=${HELM_CHART_VERSION} ." + + deployHelmChart(HELM_CHART_VERSION) + } + } + } + } + + stage('Trigger Dev rollout') { + when { + branch 'master' + } + steps { + script { + FAILED_STAGE = env.STAGE_NAME + + cloneGitopsRepo() + + setNewGitopsVersion() + pushGitopsRepo() + } + } + } + } + + post { + failure { + script { + if (isMasterBranch()) { + sendFailureMessage() + } + } + } + } +} + + + +String generateImageTag() { + def imageTag = "${env.BRANCH_NAME}-${VERSION}" + + if (isMasterBranch()) { + imageTag += "-${env.GIT_COMMIT.take(7)}" + } + + return imageTag +} + +Void cloneGitopsRepo() { + withCredentials([usernamePassword(credentialsId: 'jenkins-gitea-access-token', passwordVariable: 'TOKEN', usernameVariable: 'USER')]) { + sh 'git clone https://${USER}:${TOKEN}@git.ozg-sh.de/ozgcloud-devops/gitops.git' + } + configureGit() +} + +Void configureGit() { + final email = "jenkins@ozg-sh.de" + final name = "jenkins" + + dir("gitops") { + sh "git config user.email '${email}'" + sh "git config user.name '${name}'" + } +} + + + +Void setNewGitopsVersion() { + dir("gitops") { + def envFile = "dev/application/values/storybook-values.yaml" + + def envVersions = readYaml file: envFile + + envVersions.storybook.image.tag = IMAGE_TAG + envVersions.storybook.helm.version = HELM_CHART_VERSION + + writeYaml file: envFile, data: envVersions, overwrite: true + + if (hasValuesFileChanged()) { + sh "git add ${envFile}" + sh "git commit -m 'jenkins rollout dev storybook version ${IMAGE_TAG}'" + } + } +} + +Boolean hasValuesFileChanged() { + return sh (script: "git status | grep 'dev/application/values/storybook-values.yaml'", returnStatus: true) == env.SH_SUCCESS_STATUS_CODE as Integer +} + + +Void pushGitopsRepo() { + withCredentials([usernamePassword(credentialsId: 'jenkins-gitea-access-token', passwordVariable: 'TOKEN', usernameVariable: 'USER')]) { + dir("gitops") { + if (hasUnpushedCommits()) { + sh 'git push https://${USER}:${TOKEN}@git.ozg-sh.de/ozgcloud-devops/gitops.git' + } + } + } +} + +Boolean hasUnpushedCommits() { + return sh (script: "git cherry -v | grep .", returnStatus: true) == env.SH_SUCCESS_STATUS_CODE as Integer +} + +Void tagAndPushDockerImage(String newTag){ + withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) { + sh 'docker login docker.ozg-sh.de -u ${USER} -p ${PASSWORD}' + + sh "docker tag docker.ozg-sh.de/storybook:build-latest docker.ozg-sh.de/storybook:${newTag}" + sh "docker push docker.ozg-sh.de/storybook:${newTag}" + } +} +String getPackagejsonVersion() { + def packageJSON = readJSON file: 'package.json' + def packageJSONVersion = packageJSON.version + echo packageJSONVersion + return packageJSONVersion +} + +Void deployHelmChart(String helmChartVersion) { + withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]){ + + result = sh script: '''curl -u $USERNAME:$PASSWORD https://nexus.ozg-sh.de/service/rest/v1/components?repository=ozg-base-apps-snapshot -F file=@storybook-'''+helmChartVersion+'''.tgz''', returnStdout: true + + if (result != '') { + error(result) + } + } +} +String generateHelmChartVersion() { + def chartVersion = "${VERSION}" + + if (isMasterBranch()) { + chartVersion += "-${env.GIT_COMMIT.take(7)}" + } + else { + chartVersion += "-${env.BRANCH_NAME}" + } + + return chartVersion.replaceAll("_", "-") +} + +Boolean isMasterBranch() { + return env.BRANCH_NAME == 'master' +} + +Void sendFailureMessage() { + def room = '' + def data = """{"msgtype":"m.text", \ + "body":"storybook: Build Failed. Stage: ${FAILED_STAGE} Build-ID: ${env.BUILD_NUMBER} Link: ${BLUE_OCEAN_URL}", \ + "format": "org.matrix.custom.html", \ + "formatted_body":"storybook: Build Failed. Stage: ${FAILED_STAGE} Build-ID: <a href='${BLUE_OCEAN_URL}'>${env.BUILD_NUMBER}</a>"}""" + + if (isMasterBranch()) { + room = "!iQPAvQIiRwRpNOszjw:matrix.ozg-sh.de" + } + + sh "curl -XPOST -H 'authorization: Bearer ${getElementAccessToken()}' -d '${data}' https://matrix.ozg-sh.de/_matrix/client/v3/rooms/$room/send/m.room.message" +} + + +String getElementAccessToken() { + withCredentials([string(credentialsId: 'element-login-json', variable: 'LOGIN_JSON')]) { + return readJSON ( text: sh (script: '''curl -XPOST -d \"$LOGIN_JSON\" https://matrix.ozg-sh.de/_matrix/client/v3/login''', returnStdout: true)).access_token + } +} diff --git a/alfa-client/apps/admin/project.json b/alfa-client/apps/admin/project.json index e369bed13cdc4d079e36c28c7ed0d5b52b6552a7..9c6af90763bf5aec332dd62f254377b6de58f77a 100644 --- a/alfa-client/apps/admin/project.json +++ b/alfa-client/apps/admin/project.json @@ -29,6 +29,7 @@ "scripts": [], "stylePreprocessorOptions": { "includePaths": [ + "apps/alfa/src/styles/abstracts", "node_modules/@angular", "node_modules/include-media", "node_modules/typeface-roboto" diff --git a/alfa-client/apps/admin/src/test/helm/network_policy_test.yaml b/alfa-client/apps/admin/src/test/helm/network_policy_test.yaml index 7f7cedbca17f4c24972c206bf45d4d079cf771c7..2032db88a659c5b7fcf67aa199d6dba893bf5397 100644 --- a/alfa-client/apps/admin/src/test/helm/network_policy_test.yaml +++ b/alfa-client/apps/admin/src/test/helm/network_policy_test.yaml @@ -28,20 +28,26 @@ release: namespace: by-helm-test templates: - templates/network_policy.yaml -set: - networkPolicy: - dnsServerNamespace: kube-system - ssoPublicIp: 1.1.1.1/32 + tests: - it: should match apiVersion + set: + networkPolicy: + dnsServerNamespace: kube-system asserts: - isAPIVersion: of: networking.k8s.io/v1 - it: should match kind + set: + networkPolicy: + dnsServerNamespace: kube-system asserts: - isKind: of: NetworkPolicy - it: validate metadata + set: + networkPolicy: + dnsServerNamespace: kube-system asserts: - equal: path: metadata @@ -50,6 +56,9 @@ tests: namespace: by-helm-test - it: should add egress rule to administration service + set: + networkPolicy: + dnsServerNamespace: kube-system asserts: - contains: path: spec.egress @@ -84,7 +93,6 @@ tests: - it: should add additionalIngressConfig local set: networkPolicy: - ssoPublicIp: 51.89.117.53/32 dnsServerNamespace: test-namespace-dns additionalIngressConfigLocal: - from: @@ -102,7 +110,6 @@ tests: - it: should add additionalIngressConfig global set: networkPolicy: - ssoPublicIp: 51.89.117.53/32 dnsServerNamespace: test-namespace-dns additionalIngressConfigGlobal: - from: @@ -121,7 +128,6 @@ tests: - it: should add additionalEgressConfig local set: networkPolicy: - ssoPublicIp: 51.89.117.53/32 dnsServerNamespace: test-dns-namespace additionalEgressConfigLocal: - to: @@ -138,7 +144,6 @@ tests: - it: should add additionalEgressConfig global set: networkPolicy: - ssoPublicIp: 51.89.117.53/32 dnsServerNamespace: test-dns-namespace additionalEgressConfigGlobal: - to: @@ -164,9 +169,23 @@ tests: - it: test network policy unset should be disabled set: networkPolicy: - ssoPublicIp: 1.1.1.1 disabled: false dnsServerNamespace: test-dns-server-namespace asserts: - hasDocuments: count: 1 + - it: test network policy dnsServerNamespace must be set message + set: + networkPolicy: + disabled: false + asserts: + - failedTemplate: + errorMessage: networkPolicy.dnsServerNamespace must be set + + - it: test network policy should be enabled by default + set: + networkPolicy: + dnsServerNamespace: test-dns-server-namespace + asserts: + - hasDocuments: + count: 1 \ No newline at end of file diff --git a/alfa-client/apps/alfa-e2e/Jenkinsfile b/alfa-client/apps/alfa-e2e/Jenkinsfile index 55255bd3130fbca4cff8452df6fa7ae5e6957928..4d9192728334431b0219afb7c8ac6372cf9bdaab 100644 --- a/alfa-client/apps/alfa-e2e/Jenkinsfile +++ b/alfa-client/apps/alfa-e2e/Jenkinsfile @@ -15,7 +15,7 @@ pipeline { } environment { - BLUE_OCEAN_URL = "https://jenkins.ozg-sh.de/job/E2E%20Tests/job/${env.BRANCH_NAME}/${env.BUILD_NUMBER}/" + BLUE_OCEAN_URL = "https://jenkins.infra.ozg-cloud.systems/job/E2E%20Tests/job/${env.BRANCH_NAME}/${env.BUILD_NUMBER}/" BUNDESLAND = "by" SSO_URL = "sso.dev.by.ozg-cloud.de" CLUSTER_BASE_URL = "dev.by.ozg-cloud.de" @@ -325,7 +325,7 @@ pipeline { failure { script { if (isMasterBranch() || isReleaseBranch()) { - //sendFailureMessage() + sendFailureMessage() } } } diff --git a/alfa-client/apps/alfa-e2e/src/components/attachment/attachment.e2e.component.ts b/alfa-client/apps/alfa-e2e/src/components/attachment/attachment.e2e.component.ts index 005abef8cb58c8fb185e7b7855173fae2e27f5c7..3277623c9aec9bf551906c97e921c752cf292862 100644 --- a/alfa-client/apps/alfa-e2e/src/components/attachment/attachment.e2e.component.ts +++ b/alfa-client/apps/alfa-e2e/src/components/attachment/attachment.e2e.component.ts @@ -38,6 +38,7 @@ export class AttachmentContainerE2EComponent { export class AttachmentListE2EComponent { private readonly locatorRoot: string = 'file-list'; + private readonly downloadAttachmentsButton: string = 'download-archive-file-button'; public getRoot() { return cy.getTestElement(this.locatorRoot); @@ -46,6 +47,14 @@ export class AttachmentListE2EComponent { public getItem(fileName: string): AttachmentE2EItem { return new AttachmentE2EItem(fileName); } + + public getDownloadAttachmentsButton(parent: any): Cypress.Chainable<JQuery<HTMLElement>> { + return cy.get(parent).getTestElementWithClass(this.downloadAttachmentsButton); + } + + public downloadAttachments(parent: any): Cypress.Chainable<any> { + return this.getDownloadAttachmentsButton(parent).click(); + } } class AttachmentE2EItem { diff --git a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-bescheid-wizard.e2e.component.ts b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-bescheid-wizard.e2e.component.ts index 17d751a99d64ce4c1fac63beb76ee18f4f365346..5884ec6fe2f829351178b820cfc5a96e8752c343 100644 --- a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-bescheid-wizard.e2e.component.ts +++ b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-bescheid-wizard.e2e.component.ts @@ -22,6 +22,10 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { contains, enterWith } from '../../support/cypress.util'; +import { uploadFile } from '../../support/file-upload'; +import { getAdjustedDateGerman } from '../../support/tech.util'; + export class VorgangBescheidWizardE2EComponent { private readonly bewilligtButton: string = 'button-bewilligt'; private readonly abgelehntButton: string = 'button-abgelehnt'; @@ -44,15 +48,21 @@ export class VorgangBescheidWizardE2EComponent { 'bescheiderstellung-abbrechen-entwurf-verwerfen'; private readonly bescheidSpeichernButton: string = 'bescheiderstellung-abbrechen-entwurf-speichern'; - private readonly uploadBescheidFile: string = '-single-file-upload-button'; - private readonly uploadAttachment: string = 'Anhang_hochladen-file-upload-button'; + private readonly uploadBescheidFileButton: string = '-single-file-upload-button'; + private readonly uploadAttachmentButton: string = 'Anhang_hochladen-file-upload-button'; + private readonly uploadAutomaticBescheid: string = 'create-bescheid-document-button'; private readonly mailTextArea: string = 'Text-textarea-editor'; private readonly saveBescheid: string = 'save-button'; private readonly sendBescheid: string = 'send-button'; private readonly confirmAndSaveButton: string = 'confirm-and-save-button'; + private readonly antragstellerText: string = 'bescheid-nachricht-an-antragsteller'; + private readonly betreffText: string = 'Betreff-text-input'; + private readonly nachrichtText: string = 'Text-textarea'; private readonly fileBescheidValid: string = 'Bescheid_validpdf-file-item'; private readonly fileAnhangValid: string = 'Anhang_validpdf-file-item'; + private readonly fileAutomaticBescheid: string = + 'KFAS_LIVE_KI_10_Haltverbot_befristetpdf-file-item'; private readonly bescheidDocument: string = 'bescheid-document'; private readonly attachmentDocument: string = 'bescheid-attachments'; @@ -66,145 +76,221 @@ export class VorgangBescheidWizardE2EComponent { private locatorRoot: string = 'bescheid-wizard'; - public getRoot() { + private readonly bescheidVersenden: string = 'Bescheid versenden'; + private readonly dokumenteHinzufuegen: string = 'Dokumente hinzufügen'; + + public getRoot(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.locatorRoot); } - public getBewilligtButton() { + public getBewilligtButton(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.bewilligtButton); } - public getAbgelehntButton() { + public getAbgelehntButton(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.abgelehntButton); } - public getUeberspringenButton() { + public getUeberspringenButton(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.ueberspringenButton); } - public getStatusText() { + public getStatusText(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.statusText); } - public getUeberspringenDialog() { + public getUeberspringenDialog(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.ueberspringenDialog); } - public getUeberspringenAbbrechen() { + public getUeberspringenAbbrechen(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.ueberspringenAbbrechen); } - public getUeberspringenAbschliessen() { + public getUeberspringenAbschliessen(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.ueberspringenAbschliessen); } - public getDateInput() { + public getDateInput(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.dateInput); } - public getDateError() { + public getDateError(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.dateError); } - public getWeiterButton() { + public getWeiterButton(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.weiterButton); } - public getStepCaption() { + public getStepCaption(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.stepCaption); } - public getStepButton1() { + public getStepButton1(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.stepButton1); } - public getStepButton2() { + public getStepButton2(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.stepButton2); } - public getStepButton3() { + public getStepButton3(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.stepButton3); } - public getCloseButton() { + public getCloseButton(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.closeButton); } - public getCloseDialog() { + public getCloseDialog(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.closeDialog); } - public getCloseVerwerfenButton() { + public getCloseVerwerfenButton(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.bescheidVerwerfenButton); } - public getCloseSpeichernButton() { + public getCloseSpeichernButton(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.bescheidSpeichernButton); } - public getUploadBescheidButton() { - return cy.getTestElement(this.uploadBescheidFile); + public getUploadBescheidButton(): Cypress.Chainable<JQuery<HTMLElement>> { + return cy.getTestElement(this.uploadBescheidFileButton); } - public getUploadAttachmentButton() { - return cy.getTestElement(this.uploadAttachment); + public getUploadAttachmentButton(): Cypress.Chainable<JQuery<HTMLElement>> { + return cy.getTestElement(this.uploadAttachmentButton); } - public getFileBescheidValidInWizard() { + public getFileBescheidValidInWizard(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.locatorRoot).find(`[data-test-id=${this.fileBescheidValid}]`); } - public getFileAnhangValidInWizard() { + public getFileAnhangValidInWizard(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.locatorRoot).find(`[data-test-id=${this.fileAnhangValid}]`); } - public getDeleteButtonOfElement(element: string) { + public getAutomaticBescheidFileInWizard() { + return cy.getTestElement(this.locatorRoot).find(`[data-test-id=${this.fileAutomaticBescheid}]`); + } + + public getDeleteButtonOfElement(element: string): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(element).find('[title="Anhang löschen"]'); } - public getElementFromFileName(filename: string) { + public getElementFromFileName(filename: string): string { return filename.replace(/\./g, '') + '-file-item'; } - public getBescheidDocument() { + public getBescheidDocument(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.bescheidDocument); } - public getAttachmentDocument() { + public getAttachmentDocument(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.attachmentDocument); } - public getBescheidUploadSpinner() { + public getBescheidUploadSpinner(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.get(this.bescheidUploadSpinner); } - public getAttachmentUploadSpinner() { + public getAttachmentUploadSpinner(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.get(this.attachmentUploadSpinner); } - public getBescheidSaveSpinner() { + public getBescheidSaveSpinner(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.get(this.bescheidSaveSpinner); } - public getSendenSpinner() { + public getSendenSpinner(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.get(this.sendenSpinner); } - public getMailTextArea() { - return cy.get(this.mailTextArea); + public getMailText(): Cypress.Chainable<JQuery<HTMLElement>> { + return cy.getTestElement(this.nachrichtText); } - public getSaveButton() { + public getSaveButton(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.saveBescheid); } - public getSendButton() { + public getSendButton(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.sendBescheid); } - public getResultBox() { + public getResultBox(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.bescheidResultBox); } - public getConfirmAndSaveButton() { + public getConfirmAndSaveButton(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.confirmAndSaveButton); } + + public getAntragstellerText(): Cypress.Chainable<JQuery<HTMLElement>> { + return cy.getTestElement(this.antragstellerText); + } + + public getBetreffText(): Cypress.Chainable<JQuery<HTMLElement>> { + return cy.getTestElement(this.betreffText); + } + + public getNachrichtText(): Cypress.Chainable<JQuery<HTMLElement>> { + return cy.getTestElement(this.nachrichtText); + } + + public isBescheidDocumentsStep(): void { + contains(this.getRoot(), this.dokumenteHinzufuegen); + } + + public isBescheidVersendenStep(): void { + contains(this.getRoot(), this.bescheidVersenden); + } + + public ablehnen(): void { + this.getAbgelehntButton().should('exist').click(); + } + + public weiter(): void { + this.getWeiterButton().should('exist').click(); + } + + public close(): void { + this.getCloseButton().should('exist').click(); + } + + public isOpened(): void { + this.getRoot().should('exist'); + } + + public isClosed(): void { + this.getRoot().should('not.exist'); + } + + public getUploadAutomaticBescheidButton() { + return cy.getTestElement(this.uploadAutomaticBescheid); + } + + public uploadBescheid(fileName: string): void { + uploadFile(this.getUploadBescheidButton(), fileName); + } + + public uploadAttachment(fileName: string): void { + uploadFile(this.getUploadAttachmentButton(), fileName); + } + + public bescheidUploadSpinnerIsClosed(): void { + this.getBescheidUploadSpinner().should('not.exist'); + } + + public attachmentSpinnerIsClosed(): void { + this.getAttachmentUploadSpinner().should('not.exist'); + } + + public enterDate(difference: number): void { + this.getDateInput().should('exist'); + enterWith(this.getDateInput(), getAdjustedDateGerman(difference)); + } + + public closeDialogIsShown(): void { + this.getCloseDialog().should('exist'); + } } diff --git a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-formular.e2e.component.ts b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-formular.e2e.component.ts index 7cbf56b323b60afa837fc79bae7b9b296e28448a..26821f5985776515387d535b6c6060f0773b0c35 100644 --- a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-formular.e2e.component.ts +++ b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-formular.e2e.component.ts @@ -30,6 +30,7 @@ import { export class VorgangFormularDatenE2EComponent { private readonly locatorRoot: string = 'formulardaten-panel'; + private readonly locatorDateien: string = 'dateien'; private readonly locatorMetadaten: string = 'metadaten'; private readonly locatorAntragdaten: string = 'antragdaten'; private readonly locatorTabset: string = 'div[role=tab]'; @@ -39,11 +40,14 @@ export class VorgangFormularDatenE2EComponent { private readonly historieContainer: VorgangFormularDatenHistorieE2EComponent = new VorgangFormularDatenHistorieE2EComponent(); + private readonly fileListHeader: string = 'file-list'; + private readonly attachmentListHeader: string = 'attachment-list'; + private readonly antragdatenTab: number = 0; - private readonly metadatenTab: number = 1; - private readonly representationTab: number = 2; - private readonly attachmentTab: number = 3; - private readonly historieTab: number = 4; + private readonly metadatenTab: number = 9; + private readonly representationTab: number = 9; + private readonly dateienTab: number = 1; + private readonly historieTab: number = 2; public getRoot() { return cy.getTestElement(this.locatorRoot); @@ -65,8 +69,8 @@ export class VorgangFormularDatenE2EComponent { return this.getTabsetElement().eq(this.representationTab); } - public getAttachmentTab() { - return this.getTabsetElement().eq(this.attachmentTab); + public getDateienTab() { + return this.getTabsetElement().eq(this.dateienTab); } public getHistorieTab() { @@ -85,6 +89,14 @@ export class VorgangFormularDatenE2EComponent { return getTestElement(this.locatorAntragdaten); } + public getDateien() { + return getTestElement(this.locatorDateien); + } + + public getTabset() { + return getTestElement(this.locatorTabset); + } + public getHistorieContainer(): VorgangFormularDatenHistorieE2EComponent { return this.historieContainer; } @@ -92,4 +104,17 @@ export class VorgangFormularDatenE2EComponent { public getHistorieItemByIndex(index: number): VorgangFormularDatenHistorieItemE2EComponent { return this.getHistorieContainer().getListItemByIndex(index); } + + public getFileListHeader() { + return getTestElement(this.fileListHeader); + } + + public getAttachmentListHeader() { + return getTestElement(this.attachmentListHeader); + } + + public getFileElementByName(fileName: string) { + fileName = fileName.replace(/\./g, '') + '-file-item'; + return getTestElement(fileName); + } } diff --git a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-search.e2e.component.ts b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-search.e2e.component.ts index d879ee9fc1c21ef8e1318dad1ca87283118e2439..fba7e4dfd79867f2b53aeda47cd108742548cce1 100644 --- a/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-search.e2e.component.ts +++ b/alfa-client/apps/alfa-e2e/src/components/vorgang/vorgang-search.e2e.component.ts @@ -21,6 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { enterWith } from '../../support/cypress.util'; import { convertToDataTestId } from '../../support/tech.util'; export class VorgangSearchE2EComponent { @@ -36,7 +37,7 @@ export class VorgangSearchE2EComponent { return cy.getTestElement(this.locatorRoot); } - public getInput() { + public getInput(): Cypress.Chainable<JQuery<HTMLElement>> { return cy.getTestElement(this.locatorInput); } @@ -61,6 +62,10 @@ export class VorgangSearchE2EComponent { public getClearButton() { return cy.getTestElement(this.locatorClearButton); } + + public doSearch(searchString: string): void { + enterWith(this.getInput(), searchString, 300); + } } export class VorgangSearchPreviewListItemE2EComponent { diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-anhang/anhang-herunterladen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-anhang/vorgang-anhang-herunterladen.cy.ts similarity index 86% rename from alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-anhang/anhang-herunterladen.cy.ts rename to alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-anhang/vorgang-anhang-herunterladen.cy.ts index 69dfac76104a5ea135bb876ffc97766877219999..0d31452e0acf9a4540d9c3e0b409f3faca96f1dc 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-anhang/anhang-herunterladen.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-anhang/vorgang-anhang-herunterladen.cy.ts @@ -24,6 +24,7 @@ import { AttachmentContainerE2EComponent } from 'apps/alfa-e2e/src/components/attachment/attachment.e2e.component'; import { VorgangFormularDatenE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-formular.e2e.component'; import { convertToDataTestId } from 'apps/alfa-e2e/src/support/tech.util'; +import { AttachmentListE2EComponent } from '../../../components/attachment/attachment.e2e.component'; import { VorgangDetailHeaderE2EComponent } from '../../../components/vorgang/vorgang-detail-header.e2e.component'; import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-list.e2e.component'; import { VorgangSubnavigationE2EComponent } from '../../../components/vorgang/vorgang-subnavigation'; @@ -40,7 +41,14 @@ import { createXmlRepresentation, initGridFs, } from '../../../support/binary-file-util'; -import { dropCollections, readFileFromDownloads } from '../../../support/cypress-helper'; +import { + countDownloadFiles, + deleteDownloadFolder, + dropCollections, + getDownloadFiles, + readFileFromDownloads, + unzipDownloadFile, +} from '../../../support/cypress-helper'; import { exist, notExist } from '../../../support/cypress.util'; import { loginAsSabine } from '../../../support/user-util'; import { @@ -58,8 +66,11 @@ describe('Vorgang Anhänge', () => { const vorgangHeader: VorgangDetailHeaderE2EComponent = vorgangPage.getVorgangDetailHeader(); const subnavigation: VorgangSubnavigationE2EComponent = vorgangPage.getSubnavigation(); const attachmentContainer: AttachmentContainerE2EComponent = vorgangPage.getAttachmentContainer(); + const attachmentList: AttachmentListE2EComponent = new AttachmentListE2EComponent(); const formularDatenContainer: VorgangFormularDatenE2EComponent = vorgangPage.getFormularDatenContainer(); + const vorgangDatenFormular: VorgangFormularDatenE2EComponent = + vorgangPage.getFormularDatenContainer(); const xmlRepresentation: FileDataE2E = createXmlRepresentation(); @@ -104,7 +115,7 @@ describe('Vorgang Anhänge', () => { }); it('should show attachment on click on tab', () => { - formularDatenContainer.getAttachmentTab().click(); + formularDatenContainer.getDateienTab().click(); notExist(vorgangPage.getSpinner()); exist(attachmentContainer.getList().getRoot()); @@ -130,6 +141,22 @@ describe('Vorgang Anhänge', () => { exist(readFileFromDownloads(buildDownloadFileName(pdfAttachmentName))); }); + it('should download attachment zip file', () => { + deleteDownloadFolder().then(() => { + attachmentList.downloadAttachments(vorgangDatenFormular.getRoot()); + }); + }); + + it('should unzip attachment file', () => { + getDownloadFiles().then((files) => { + unzipDownloadFile(files[0]).then(() => { + countDownloadFiles().then((count) => { + expect(count).to.eq(3); + }); + }); + }); + }); + it('should navigate to vorganglist on back button', () => { subnavigation.getBackButton().click(); waitForSpinnerToDisappear(); @@ -155,7 +182,7 @@ describe('Vorgang Anhänge', () => { }); it('should show attachment on click on tab', () => { - formularDatenContainer.getRepresentationTab().click(); + formularDatenContainer.getDateienTab().click(); notExist(vorgangPage.getSpinner()); exist(attachmentContainer.getList().getRoot()); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-abbrechen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-abbrechen.cy.ts index c2943c6ef548e9872c2e2e6ad16eefff928ed4c6..c711f48169700c17117d351e530c258d6b7a2784 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-abbrechen.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-abbrechen.cy.ts @@ -14,8 +14,8 @@ import 'cypress-real-events/support'; import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-list.e2e.component'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; -import { dropCollections } from '../../../support/cypress-helper'; -import { contains, enterWith, exist, haveText, notExist } from '../../../support/cypress.util'; +import { dropCollections, wait } from '../../../support/cypress-helper'; +import { contains, exist, haveText, notExist } from '../../../support/cypress.util'; import { TEST_FILE_BESCHEID_ANHANG_VALID, TEST_FILE_BESCHEID_VALID, @@ -25,12 +25,13 @@ import { buildVorgang, initVorgaenge, objectIds } from '../../../support/vorgang registerLocaleData(localeDe, 'de', localeDeExtra); -describe('Bescheid abbrechen', () => { +describe('Vorgang - Bescheid abbrechen', () => { const mainPage: MainPage = new MainPage(); const vorgangList: VorgangListE2EComponent = mainPage.getVorgangList(); const bewilligtText: string = 'Bewilligt am'; const abgelehntText: string = 'Abgelehnt am'; + const stepCaption2: string = 'Dokumente hinzufügen'; const vorgangPage: VorgangPage = new VorgangPage(); const bescheidWizard: VorgangBescheidWizardE2EComponent = vorgangPage.getBescheidWizard(); @@ -57,59 +58,94 @@ describe('Bescheid abbrechen', () => { dropCollections(); }); - describe('Discard changes', () => { - it('should discard all data after clicking X and confirm dicard', () => { + describe('Discard changes after click on Verwerfen', () => { + it('should click abgelehnt, enter date, and continue to step 2', () => { vorgangList.getListItem(bescheidVorgang.name).getRoot().click(); waitForSpinnerToDisappear(); vorgangFormularButtons.getBescheidenButton().click(); - bescheidWizard.getAbgelehntButton().click(); - enterWith(bescheidWizard.getDateInput(), getAdjustedDateGerman(-1)); - bescheidWizard.getWeiterButton().click(); - uploadFile(bescheidWizard.getUploadBescheidButton(), TEST_FILE_BESCHEID_VALID); - notExist(bescheidWizard.getBescheidUploadSpinner()); - uploadFile(bescheidWizard.getUploadAttachmentButton(), TEST_FILE_BESCHEID_ANHANG_VALID); - notExist(bescheidWizard.getAttachmentUploadSpinner()); - bescheidWizard.getWeiterButton().click(); - bescheidWizard.getCloseButton().click(); - exist(bescheidWizard.getCloseDialog()); + bescheidWizard.ablehnen(); + bescheidWizard.enterDate(-1); + bescheidWizard.weiter(); + + bescheidWizard.isBescheidDocumentsStep(); + }); + + it('should upload files and continue to step 3', () => { + bescheidWizard.uploadBescheid(TEST_FILE_BESCHEID_VALID); + bescheidWizard.bescheidUploadSpinnerIsClosed(); + bescheidWizard.uploadAttachment(TEST_FILE_BESCHEID_ANHANG_VALID); + bescheidWizard.attachmentSpinnerIsClosed(); + bescheidWizard.weiter(); + bescheidWizard.isBescheidVersendenStep(); + }); + + it('should open dialog after click on X', () => { + bescheidWizard.close(); + + bescheidWizard.closeDialogIsShown(); + }); + + it('should close Wizard after click on Verwerfen', () => { bescheidWizard.getCloseVerwerfenButton().click(); - notExist(bescheidWizard.getRoot()); + waitForSpinnerToDisappear(); + + bescheidWizard.isClosed(); + }); + + it('should show status In Bearbeitung', () => { haveText( vorgangPage.getVorgangDetailHeader().getStatus(), vorgangStatusLabelE2E[VorgangStatusE2E.IN_BEARBEITUNG], ); + }); + }); + describe('Keep changes after click on Entwurf speichern', () => { + it('should have default Bescheid status and continue to step 2', () => { + wait(1000); vorgangFormularButtons.getBescheidenButton().click(); contains(bescheidWizard.getStatusText(), bewilligtText + ' ' + getAdjustedDateGerman(0)); - bescheidWizard.getWeiterButton().click(); + bescheidWizard.weiter(); + }); + + it('should not show file from previous Bescheid', () => { notExist(bescheidWizard.getFileAnhangValidInWizard()); notExist(bescheidWizard.getFileBescheidValidInWizard()); }); - it('should save all data after clicking X and confirm save', () => { - bescheidWizard.getAbgelehntButton().click(); - enterWith(bescheidWizard.getDateInput(), getAdjustedDateGerman(-1)); - bescheidWizard.getWeiterButton().click(); + it('should upload Bescheid and attachment', () => { uploadFile(bescheidWizard.getUploadBescheidButton(), TEST_FILE_BESCHEID_VALID); notExist(bescheidWizard.getBescheidUploadSpinner()); uploadFile(bescheidWizard.getUploadAttachmentButton(), TEST_FILE_BESCHEID_ANHANG_VALID); notExist(bescheidWizard.getAttachmentUploadSpinner()); - bescheidWizard.getCloseButton().click(); + }); + it('should open dialog after click on X', () => { + bescheidWizard.close(); + exist(bescheidWizard.getCloseDialog()); + }); + + it('should close Wizard after click on Speichern', () => { bescheidWizard.getCloseSpeichernButton().click(); waitForSpinnerToDisappear(); notExist(bescheidWizard.getRoot()); + }); + it('should show status In Bearbeitung', () => { haveText( vorgangPage.getVorgangDetailHeader().getStatus(), vorgangStatusLabelE2E[VorgangStatusE2E.IN_BEARBEITUNG], ); + }); + it('should keep date and status from current Bescheid in progress', () => { vorgangFormularButtons.getBescheidenButton().click(); - contains(bescheidWizard.getStatusText(), abgelehntText + ' ' + getAdjustedDateGerman(-1)); + contains(bescheidWizard.getStatusText(), bewilligtText + ' ' + getAdjustedDateGerman(0)); + }); - bescheidWizard.getWeiterButton().click(); + it('should keep files from current Bescheid in progress', () => { + bescheidWizard.weiter(); exist(bescheidWizard.getFileAnhangValidInWizard()); exist(bescheidWizard.getFileBescheidValidInWizard()); }); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-automatisch-erstellen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-automatisch-erstellen.cy.ts new file mode 100644 index 0000000000000000000000000000000000000000..c2a6b888ce2b2bad7055322952440965512d27b5 --- /dev/null +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-automatisch-erstellen.cy.ts @@ -0,0 +1,121 @@ +import { registerLocaleData } from '@angular/common'; +import localeDe from '@angular/common/locales/de'; +import localeDeExtra from '@angular/common/locales/extra/de'; +import { VorgangBescheidWizardE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-bescheid-wizard.e2e.component'; +import { VorgangFormularButtonsE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-formular-buttons.e2e.components'; +import { VorgangSubnavigationE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-subnavigation'; +import { SmockerMocks } from 'apps/alfa-e2e/src/model/smocker'; +import { + EingangE2E, + EingangHeaderE2E, + VorgangE2E, + VorgangStatusE2E, +} from 'apps/alfa-e2e/src/model/vorgang'; +import 'cypress-real-events/support'; +import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-list.e2e.component'; +import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; +import { VorgangPage } from '../../../page-objects/vorgang.po'; +import { addSmockerMock, dropCollections } from '../../../support/cypress-helper'; +import { exist, haveText, notExist } from '../../../support/cypress.util'; +import { initUsermanagerUsers, loginAsSabine } from '../../../support/user-util'; +import { + buildVorgang, + createVorgang, + initVorgaenge, + objectIds, +} from '../../../support/vorgang-util'; + +registerLocaleData(localeDe, 'de', localeDeExtra); + +//TODO: Jenkins konfigurieren +describe.skip('Upload automatic Bescheid', () => { + const mainPage: MainPage = new MainPage(); + const vorgangList: VorgangListE2EComponent = mainPage.getVorgangList(); + + const vorgangPage: VorgangPage = new VorgangPage(); + const bescheidWizard: VorgangBescheidWizardE2EComponent = vorgangPage.getBescheidWizard(); + + const formIdP: string = 'KFAS_STAGE_KI_10_Haltverbot_LANDESHACKATHON'; + const formEngineName: string = 'FormSolutions'; + const eingangHeader: EingangHeaderE2E = { ...createVorgang().eingangs[0].header }; + + const eingangP: EingangE2E = { + ...createVorgang().eingangs[0], + header: { ...eingangHeader, formId: formIdP, formEngineName }, + }; + + const bescheidAutomatik: VorgangE2E = { + ...createVorgang(), + status: VorgangStatusE2E.IN_BEARBEITUNG, + name: 'automatischerBescheid', + eingangs: [eingangP], + }; + + const standardVorgang: VorgangE2E = { + ...buildVorgang(objectIds[1], 'Standard Vorgang'), + status: VorgangStatusE2E.IN_BEARBEITUNG, + }; + + const vorgangFormularButtons: VorgangFormularButtonsE2EComponent = + vorgangPage.getFormularButtons(); + + const vorgangSubnavigation: VorgangSubnavigationE2EComponent = vorgangPage.getSubnavigation(); + + before(() => { + initVorgaenge([bescheidAutomatik, standardVorgang]); + initUsermanagerUsers(); + + addSmockerMock(SmockerMocks.SMARTDOCUMENT_MOCK); + + loginAsSabine(); + + waitForSpinnerToDisappear(); + exist(vorgangList.getRoot()); + }); + + after(() => { + dropCollections(); + }); + + describe.skip('Upload automatic Bescheid document', () => { + it('should show automatic Bescheid button', () => { + vorgangList.getListItem(bescheidAutomatik.name).getRoot().click(); + waitForSpinnerToDisappear(); + + vorgangFormularButtons.getBescheidenButton().click(); + bescheidWizard.getWeiterButton().click(); + exist(bescheidWizard.getUploadAutomaticBescheidButton()); + }); + + it('should upload automatic Bescheid file', () => { + waitForSpinnerToDisappear(); + bescheidWizard.getUploadAutomaticBescheidButton().click(); + + exist(bescheidWizard.getBescheidUploadSpinner()); + notExist(bescheidWizard.getBescheidUploadSpinner()); + exist(bescheidWizard.getAutomaticBescheidFileInWizard()); + }); + + it('should show automatic header and text in step 3', () => { + bescheidWizard.getWeiterButton().click(); + waitForSpinnerToDisappear(); + + haveText(bescheidWizard.getMailText(), ''); + }); + }); + + describe('Do not show automatic upload button on other Vorgang', () => { + it('should not show button on Vorgang with different formID', () => { + bescheidWizard.getCloseButton().click(); + bescheidWizard.getCloseVerwerfenButton().click(); + vorgangSubnavigation.getBackButton().click(); + vorgangList.getListItem(standardVorgang.name).getRoot().click(); + waitForSpinnerToDisappear(); + + vorgangFormularButtons.getBescheidenButton().click(); + bescheidWizard.getWeiterButton().click(); + + notExist(bescheidWizard.getUploadAutomaticBescheidButton()); + }); + }); +}); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-dokumente-hochladen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-dokumente-hochladen.cy.ts index fecc0d94c2591421c36835b202e9e9b5e4ab2653..6e114b91917aa3a237f1eff680e867053633b652 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-dokumente-hochladen.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-dokumente-hochladen.cy.ts @@ -10,7 +10,7 @@ import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-lis import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; import { dropCollections, getTestElement } from '../../../support/cypress-helper'; -import { contains, exist, notExist } from '../../../support/cypress.util'; +import { contains, exist, haveValue, notContains, notExist } from '../../../support/cypress.util'; import { TEST_FILE_BESCHEID_ANHANG_BIG, TEST_FILE_BESCHEID_ANHANG_VALID, @@ -43,6 +43,11 @@ describe('Bescheid Dokumente hochladen', () => { const documentError: string = 'Erlaubte Dateiendungen'; const sizeError: string = 'Anhänge größer'; const missingBescheidError: string = 'Bitte fügen Sie'; + const nachrichtHeader: string = 'Neue Nachricht'; + const antragstellerName: string = 'An: Max Testermann'; + const betreffText: string = 'Ihr Bescheid zum Antrag'; + const nachrichtText: string = + 'Sehr geehrte/r Antragsteller/in,\n\nim Folgenden erhalten Sie Ihren Bescheid.\n\nMit freundlichen Grüßen\n\nIhre Verwaltung'; before(() => { initVorgaenge([bescheidVorgang]); @@ -144,4 +149,22 @@ describe('Bescheid Dokumente hochladen', () => { contains(bescheidWizard.getBescheidDocument(), missingBescheidError); }); }); + + describe('check contents of step 3', () => { + it('should show Max Testermann as Antragsteller, the default message text', () => { + uploadFile(bescheidWizard.getUploadBescheidButton(), TEST_FILE_BESCHEID_VALID); + notExist(bescheidWizard.getBescheidUploadSpinner()); + + bescheidWizard.getWeiterButton().click(); + + contains(bescheidWizard.getAntragstellerText(), nachrichtHeader); + contains(bescheidWizard.getAntragstellerText(), antragstellerName); + haveValue(bescheidWizard.getBetreffText(), betreffText); + haveValue(bescheidWizard.getNachrichtText(), nachrichtText); + }); + + it('should not contain error message from upload', () => { + notContains(bescheidWizard.getAttachmentDocument(), sizeError); + }); + }); }); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-info-anzeigen.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-info-anzeigen.cy.ts index dd926439b5d110258be4ea3f702018343668ea53..8a72acae98a5c36a1fe67582333a156466723c4b 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-info-anzeigen.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-info-anzeigen.cy.ts @@ -14,13 +14,17 @@ import { TEST_FILE_BESCHEID_ANHANG_VALID, TEST_FILE_BESCHEID_VALID, } from 'apps/alfa-e2e/src/support/data.util'; -import { uploadFile } from 'apps/alfa-e2e/src/support/file-upload'; import { getAdjustedDateGerman } from 'apps/alfa-e2e/src/support/tech.util'; import 'cypress-real-events/support'; import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-list.e2e.component'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; import { VorgangPage } from '../../../page-objects/vorgang.po'; -import { countDownloadFiles, dropCollections } from '../../../support/cypress-helper'; +import { + countDownloadFiles, + deleteDownloadFolder, + dropCollections, + readFileFromDownloads, +} from '../../../support/cypress-helper'; import { contains, enterWith, @@ -75,51 +79,79 @@ describe('Bescheid Info anzeigen', () => { }); describe('Show Bescheid Entwurf info on details page', () => { - it('should show Entwurf on details page', () => { + it('should not show Bescheid info in list', () => { notExist(bescheide.getBescheidListItem()); + }); + + it('should open Wizard', () => { vorgangList.getListItem(bescheidVorgang.name).getRoot().click(); waitForSpinnerToDisappear(); vorgangFormularButtons.getBescheidenButton().click(); - bescheidWizard.getAbgelehntButton().click(); - enterWith(bescheidWizard.getDateInput(), getAdjustedDateGerman(-1)); - bescheidWizard.getWeiterButton().click(); - bescheidWizard.getCloseButton().click(); + + bescheidWizard.isOpened(); + }); + + it('should click abgelehnt, set date and continue to step 2', () => { + bescheidWizard.ablehnen(); + bescheidWizard.enterDate(-1); + bescheidWizard.weiter(); + bescheidWizard.isBescheidDocumentsStep(); + }); + + it('should close Wizard', () => { + bescheidWizard.close(); bescheidWizard.getCloseSpeichernButton().click(); + bescheidWizard.isClosed(); + }); + + it('should show status In Bearbeitung', () => { haveText( vorgangPage.getVorgangDetailHeader().getStatus(), vorgangStatusLabelE2E[VorgangStatusE2E.IN_BEARBEITUNG], ); + }); + it('should show Bescheid as Entwurf with abgelehnt status and date yesterday', () => { exist(bescheide.getBescheidContainer()); - contains(bescheide.getBescheidContainer(), abgelehntText + ' ' + getAdjustedDateGerman(-1)); - contains(bescheide.getBescheidContainer(), entwurfText); }); it('should show caption Bescheid fortsetzen on button', () => { contains(vorgangFormularButtons.getBescheidenButton(), fortsetzenText); }); + }); - it('should show complete info after saving Bescheid', () => { + describe('should show complete info after saving Bescheid', () => { + it('should set date to two days before and continue to step 2', () => { vorgangFormularButtons.getBescheidenButton().click(); enterWith(bescheidWizard.getDateInput(), getAdjustedDateGerman(-2)); bescheidWizard.getWeiterButton().click(); - uploadFile(bescheidWizard.getUploadBescheidButton(), TEST_FILE_BESCHEID_VALID); - notExist(bescheidWizard.getBescheidUploadSpinner()); - uploadFile(bescheidWizard.getUploadAttachmentButton(), TEST_FILE_BESCHEID_ANHANG_VALID); - notExist(bescheidWizard.getAttachmentUploadSpinner()); + bescheidWizard.isBescheidDocumentsStep(); + }); - bescheidWizard.getWeiterButton().click(); + it('should upload files and continue to step 3', () => { + bescheidWizard.uploadBescheid(TEST_FILE_BESCHEID_VALID); + bescheidWizard.bescheidUploadSpinnerIsClosed(); + bescheidWizard.uploadAttachment(TEST_FILE_BESCHEID_ANHANG_VALID); + bescheidWizard.attachmentSpinnerIsClosed(); + bescheidWizard.weiter(); + + bescheidWizard.isBescheidVersendenStep(); + }); + + it('should save Bescheid and close wizard', () => { bescheidWizard.getSaveButton().click(); bescheidWizard.getConfirmAndSaveButton().click(); waitForSpinnerToDisappear(); + bescheidWizard.isClosed(); + }); + it('should show Bescheid as abgelehnt, and contain both files', () => { exist(bescheide.getBescheidContainer()); contains(bescheide.getBescheidContainer(), abgelehntText); - exist(bescheide.getFileBescheidValidInVorgang()); exist(bescheide.getFileAnhangValidInVorgang()); }); @@ -139,21 +171,35 @@ describe('Bescheid Info anzeigen', () => { }); it('should download both attachments', () => { - bescheide.getFileBescheidValidInVorgang().click(); - bescheide.getFileAnhangValidInVorgang().click(); - countDownloadFiles().then((count) => { - expect(count).to.eq(2); + deleteDownloadFolder().then(() => { + bescheide.getFileBescheidValidInVorgang().click(); + bescheide.getFileAnhangValidInVorgang().click(); + readFileFromDownloads(TEST_FILE_BESCHEID_ANHANG_VALID).then(() => { + countDownloadFiles().then((count) => { + expect(count).to.eq(2); + }); + }); }); }); + }); - it('should show second Bescheid after reopening Vorgang and creating new Bescheid', () => { + describe('should show second Bescheid after reopening Vorgang and creating new Bescheid', () => { + it('create new Bescheid and set it to abgelehnt', () => { vorgangFormularButtons.getWiedereroeffnenButton().click(); vorgangFormularButtons.getBescheidenButton().click(); - bescheidWizard.getAbgelehntButton().click(); - bescheidWizard.getWeiterButton().click(); - uploadFile(bescheidWizard.getUploadBescheidButton(), TEST_FILE_BESCHEID_VALID); - notExist(bescheidWizard.getBescheidUploadSpinner()); - bescheidWizard.getWeiterButton().click(); + bescheidWizard.ablehnen(); + bescheidWizard.weiter(); + bescheidWizard.isBescheidDocumentsStep(); + }); + + it('should upload Bescheid and continue to step 3', () => { + bescheidWizard.uploadBescheid(TEST_FILE_BESCHEID_VALID); + bescheidWizard.bescheidUploadSpinnerIsClosed(); + bescheidWizard.weiter(); + bescheidWizard.isBescheidVersendenStep(); + }); + + it('should show 2 Bescheid containers after saving', () => { bescheidWizard.getSaveButton().click(); bescheidWizard.getConfirmAndSaveButton().click(); exist(bescheidWizard.getBescheidSaveSpinner()); @@ -167,8 +213,10 @@ describe('Bescheid Info anzeigen', () => { exist(bescheide.getAbgelehntIconInHeader()); contains(bescheide.getBeschiedenDateInHeader(), getAdjustedDateGerman(0)); }); + }); - it('should show info on list page', () => { + describe('show Bescheid info on Vorgang list', () => { + it('should show Bescheid info on list page', () => { vorgangSubnavigationButtons.getBackButton().click(); exist(bescheide.getBescheidListItem()); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-senden.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-senden.cy.ts index ee5b49ac5ea8a9551b58a159f2007fdfeaef868b..20f733647019d1d1f7bb2f70ecb7aff0206291cd 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-senden.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-bescheid/vorgang-bescheid-senden.cy.ts @@ -26,11 +26,11 @@ import { buildVorgang, initVorgaenge, objectIds } from '../../../support/vorgang registerLocaleData(localeDe, 'de', localeDeExtra); -describe.skip('Bescheid senden', () => { +describe('Bescheid senden', () => { const mainPage: MainPage = new MainPage(); const vorgangList: VorgangListE2EComponent = mainPage.getVorgangList(); - const abgelehntText: string = 'Ihr Antrag wurde abgelehnt'; + const anredeText: string = 'Sehr geehrte/r Antragsteller/in,'; const mailText: string = 'Ihr Bescheid zum Antrag'; const vorgangPage: VorgangPage = new VorgangPage(); @@ -67,7 +67,6 @@ describe.skip('Bescheid senden', () => { waitForSpinnerToDisappear(); vorgangFormularButtons.getBescheidenButton().click(); - bescheidWizard.getAbgelehntButton().click(); enterWith(bescheidWizard.getDateInput(), getAdjustedDateGerman(1)); bescheidWizard.getWeiterButton().click(); uploadFile(bescheidWizard.getUploadBescheidButton(), TEST_FILE_BESCHEID_VALID); @@ -86,13 +85,11 @@ describe.skip('Bescheid senden', () => { it('should contain correct text and both attachments', () => { const postfachMailItem: PostfachMailListItem = postfachMailContainer.getListItem(mailText); - postfachMailItem.getRoot().click(); waitForSpinnerToDisappear(); const postfachListItem: PostfachMailListItem = postfachMailPage.getListItem(mailText); - - contains(postfachMailPage.getMailText(), abgelehntText); + contains(postfachMailPage.getMailText(), anredeText); exist( postfachListItem diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-dateien-tab.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-dateien-tab.cy.ts new file mode 100644 index 0000000000000000000000000000000000000000..85fe66298c9e1653dd2d47ddb8b6e4108c71fc2c --- /dev/null +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-dateien-tab.cy.ts @@ -0,0 +1,133 @@ +import { registerLocaleData } from '@angular/common'; +import localeDe from '@angular/common/locales/de'; +import localeDeExtra from '@angular/common/locales/extra/de'; +import { AttachmentListE2EComponent } from 'apps/alfa-e2e/src/components/attachment/attachment.e2e.component'; +import { VorgangFormularDatenE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-formular.e2e.component'; +import { VorgangSubnavigationE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-subnavigation'; +import { FileDataE2E } from 'apps/alfa-e2e/src/model/binary-file'; +import { + createJpgAttachment, + createPdfAttachment, + createXmlRepresentation, + initGridFs, +} from 'apps/alfa-e2e/src/support/binary-file-util'; +import { VorgangListE2EComponent } from '../../../components/vorgang/vorgang-list.e2e.component'; +import { EingangE2E, VorgangE2E } from '../../../model/vorgang'; +import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; +import { VorgangPage } from '../../../page-objects/vorgang.po'; +import { + createJpgGridFsData, + createPdfGridFsData, + createXmlGridFsData, +} from '../../../support/binary-file-util'; +import { dropCollections } from '../../../support/cypress-helper'; +import { contains, exist, notExist } from '../../../support/cypress.util'; +import { loginAsSabine } from '../../../support/user-util'; +import { + buildVorgang, + createVorgang, + initVorgaenge, + objectIds, +} from '../../../support/vorgang-util'; + +registerLocaleData(localeDe, 'de', localeDeExtra); + +describe('Dateien Tab', () => { + const mainPage: MainPage = new MainPage(); + const vorgangList: VorgangListE2EComponent = mainPage.getVorgangList(); + + const vorgangPage: VorgangPage = new VorgangPage(); + const vorgangDatenFormular: VorgangFormularDatenE2EComponent = + vorgangPage.getFormularDatenContainer(); + const vorgangSubnavigation: VorgangSubnavigationE2EComponent = vorgangPage.getSubnavigation(); + const attachmentList: AttachmentListE2EComponent = new AttachmentListE2EComponent(); + + const xmlFileName: string = 'XML-Daten.xml'; + const jpgFileName: string = 'win.jpg'; + const pdfFileName: string = 'Anlage_Vollmacht.pdf'; + + const xmlRepresentation: FileDataE2E = createXmlRepresentation(); + + const sonstigeAttachment = { + name: 'datei_sonstiges', + files: [createJpgAttachment(), createPdfAttachment()], + }; + + const eingangWithRepresentation: EingangE2E = { + ...createVorgang().eingangs[0], + numberOfRepresentations: 1, + representations: [xmlRepresentation], + }; + + const vorgangRepresentation: VorgangE2E = { + ...buildVorgang(objectIds[0], 'VorgangWithRepresentation'), + eingangs: [eingangWithRepresentation], + }; + + const eingangWithAttachments: EingangE2E = { + ...createVorgang().eingangs[0], + numberOfRepresentations: 1, + representations: [xmlRepresentation], + numberOfAttachments: 2, + attachments: [sonstigeAttachment], + }; + + const vorgangWithAttachments: VorgangE2E = { + ...buildVorgang(objectIds[1], 'VorgangWithAttachments'), + eingangs: [eingangWithAttachments], + }; + + before(() => { + initGridFs([createXmlGridFsData(), createPdfGridFsData(), createJpgGridFsData()]); + initVorgaenge([vorgangRepresentation, vorgangWithAttachments]); + + loginAsSabine(); + + waitForSpinnerToDisappear(); + exist(vorgangList.getRoot()); + }); + + after(() => { + dropCollections(); + }); + + describe('show Dateien contents', () => { + it('should not show Metadaten or Representation tab', () => { + vorgangList.getListItem(vorgangRepresentation.name).getRoot().click(); + waitForSpinnerToDisappear(); + + notExist(vorgangDatenFormular.getMetadatenTab()); + notExist(vorgangDatenFormular.getRepresentationTab()); + }); + it('should show 1 file in header of Dateien tab', () => { + contains(vorgangDatenFormular.getDateienTab(), '(1)'); + }); + + it('should only show sub-header for Antrag', () => { + exist(vorgangDatenFormular.getFileListHeader()); + notExist(vorgangDatenFormular.getAttachmentListHeader()); + }); + + it('should show 1 XML file for Antragsdetails', () => { + exist(vorgangDatenFormular.getFileElementByName(xmlFileName)); + }); + + it('should show 3 files in header of Dateien tab', () => { + vorgangSubnavigation.getBackButton().click(); + vorgangList.getListItem(vorgangWithAttachments.name).getRoot().click(); + + contains(vorgangDatenFormular.getDateienTab(), '(3)'); + }); + + it('should show sub-headers for Antrag and Anhänge', () => { + exist(vorgangDatenFormular.getFileListHeader()); + exist(vorgangDatenFormular.getAttachmentListHeader()); + }); + + it('should show attachments and download button', () => { + exist(vorgangDatenFormular.getFileElementByName(jpgFileName)); + exist(vorgangDatenFormular.getFileElementByName(pdfFileName)); + exist(attachmentList.getDownloadAttachmentsButton(vorgangDatenFormular.getRoot())); + }); + }); +}); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-exportieren.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-exportieren.cy.ts index 926d415508c42364a82a165d6f33c6fbdab5b246..768acbc0f79869d9a834fd236fcc25232232808e 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-exportieren.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-detailansicht/vorgang-exportieren.cy.ts @@ -24,6 +24,7 @@ import { registerLocaleData } from '@angular/common'; import localeDe from '@angular/common/locales/de'; import localeDeExtra from '@angular/common/locales/extra/de'; +import { SnackBarE2EComponent } from 'apps/alfa-e2e/src/components/ui/snackbar.e2e.component'; import { VorgangFormularButtonsE2EComponent } from 'apps/alfa-e2e/src/components/vorgang/vorgang-formular-buttons.e2e.components'; import { VorgangMoreMenuE2EComponent, @@ -46,6 +47,8 @@ registerLocaleData(localeDe, 'de', localeDeExtra); describe('Vorgang exportieren', () => { const mainPage: MainPage = new MainPage(); + const snackBar: SnackBarE2EComponent = mainPage.getSnackBar(); + const vorgangList: VorgangListE2EComponent = mainPage.getVorgangList(); const vorgangPage: VorgangPage = new VorgangPage(); @@ -100,14 +103,24 @@ describe('Vorgang exportieren', () => { vorgangMoreMenu.getButton().click({ force: true }); }); - it('should show "Herunterladen" button in Abgeschlossen status', () => { + it('should handle snackbar after abschliessen', () => { vorgangFormularButtons.getAbschliessenButton().click(); + waitForSpinnerToDisappear(); + + exist(snackBar.getCloseButton()); + snackBar.getCloseButton().click(); + notExist(snackBar.getMessage()); + }); + it('should show "Herunterladen" button in Abgeschlossen status', () => { vorgangMoreMenu.getButton().click(); + exist(menuItem.getButton()); + vorgangMoreMenu.getButton().click({ force: true }); }); it('should have 1 file in download folder after download', () => { + vorgangMoreMenu.getButton().click(); deleteDownloadFolder().then(() => { menuItem.getButton().click({ force: true }); waitForSpinnerToDisappear(); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-list.search.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-list.search.cy.ts index 2a0e6f115c47059a992399afe671ab931407920f..774264c1145b03c36e535c670df13f69554e857f 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-list.search.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-list.search.cy.ts @@ -67,14 +67,7 @@ import { createWiedervorlageItem, } from 'apps/alfa-e2e/src/support/wiedervorlage-util'; import { MainPage, waitForSpinnerToDisappear } from '../../../page-objects/main.po'; -import { - backspaceOn, - enterWith, - exist, - haveText, - haveValue, - notExist, -} from '../../../support/cypress.util'; +import { backspaceOn, exist, haveText, haveValue, notExist } from '../../../support/cypress.util'; import { UserRoleE2E, getUserSabine, @@ -447,6 +440,7 @@ describe('VorgangList Suche', () => { describe('select vorgang in vorgangdetail preview list', () => { it('should navigate to vorgang on selection', () => { vorgangSearch.getInput().clear().type(vorgangOtherName); + waitForSpinnerToDisappear(); exist(previewListItemVorgangOther.getRoot()); previewListItemVorgangOther.getRoot().click(); @@ -697,13 +691,13 @@ describe('VorgangList Suche', () => { describe('empty search field', () => { it('should not search with empty input', () => { - doSearchWith(''); + vorgangSearch.getInput().clear().type('{enter}'); notExist(vorgangPage.getSpinner()); }); }); function doSearchWith(searchBy: string): void { - enterWith(vorgangSearch.getInput(), searchBy); + vorgangSearch.doSearch(searchBy); } }); diff --git a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-views-filter.cy.ts b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-views-filter.cy.ts index 7ed13f59c0f6caf94b0914eff020cf57fa7630c1..c2d95b6e8e7097f83c3cb9ac6e3ec80db42e8641 100644 --- a/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-views-filter.cy.ts +++ b/alfa-client/apps/alfa-e2e/src/e2e/main-tests/vorgang-list/vorgang-views-filter.cy.ts @@ -24,7 +24,6 @@ import { dropCollections, dropSearchIndex } from 'apps/alfa-e2e/src/support/cypr import { containClass, contains, - enterWith, exist, haveLength, haveText, @@ -770,11 +769,11 @@ describe('Vorgang views and filter', () => { }); it('should unselect Alle filter', () => { - isButtonToggleNotChecked(navigation.getMeineVorgaengeFilter()); + isButtonToggleNotChecked(navigation.getAlleFilter()); }); it('should keep Meine Vorgänge unselected', () => { - isButtonToggleNotChecked(navigation.getAlleFilter()); + isButtonToggleNotChecked(navigation.getMeineVorgaengeFilter()); }); it('should keep view selection', () => { @@ -808,7 +807,7 @@ describe('Vorgang views and filter', () => { it('should keep filter selection', () => { isButtonToggleChecked(navigation.getAlleFilter()); - isButtonToggleNotChecked(navigation.getMeineVorgaengeFilter()); + isButtonToggleNotChecked(navigation.getNichtZugewiesenFilter()); }); it('should unselect Meine Vorgänge filter', () => { @@ -899,7 +898,7 @@ describe('Vorgang views and filter', () => { }); function doSearch(searchString: string): void { - enterWith(vorgangSearch.getInput(), searchString); + vorgangSearch.doSearch(searchString); } function isNotSelected(element) { diff --git a/alfa-client/apps/alfa-e2e/src/support/cypress-helper.ts b/alfa-client/apps/alfa-e2e/src/support/cypress-helper.ts index cc70ec8dbe3806519598d89d2482029f70699f7a..7666dbcaf8155666c509a50bc3f954b4da517961 100644 --- a/alfa-client/apps/alfa-e2e/src/support/cypress-helper.ts +++ b/alfa-client/apps/alfa-e2e/src/support/cypress-helper.ts @@ -40,6 +40,8 @@ enum CypressTasks { INIT_USERMANAGER_DATA = 'initUsermanagerData', COUNT_FILES = 'countFiles', DELETE_FOLDER = 'deleteFolder', + UNZIP_FILE = 'unzipDownloadFile', + GET_DOWNLOAD_FILES = 'getDownloadFiles', } enum MongoCollections { @@ -136,6 +138,14 @@ export function deleteDownloadFolder() { return cy.task(CypressTasks.DELETE_FOLDER, DOWNLOAD_FOLDER); } +export function unzipDownloadFile(file: string) { + return cy.task(CypressTasks.UNZIP_FILE, { folderName: DOWNLOAD_FOLDER, fileName: file }); +} + +export function getDownloadFiles(): Cypress.Chainable<Array<string>> { + return cy.task(CypressTasks.GET_DOWNLOAD_FILES, DOWNLOAD_FOLDER); +} + export function scrollToWindowBottom(): void { cy.window().scrollTo('bottom'); } @@ -178,8 +188,8 @@ export function reload(): void { cy.reload(); } -export function readFileFromDownloads(fileName: string) { - return cy.readFile(`${DOWNLOAD_FOLDER}/${fileName}`); +export function readFileFromDownloads(fileName: string): Cypress.Chainable<any> { + return cy.readFile(`${DOWNLOAD_FOLDER}/${fileName}`, { timeout: 5000 }); } export function pressTab(): void { @@ -202,4 +212,4 @@ export function addSmockerMock(mock: SmockerMocks): void { export function resetSmocker(): void { cy.resetSmocker(); -} \ No newline at end of file +} diff --git a/alfa-client/apps/alfa-e2e/src/support/cypress-tasks.ts b/alfa-client/apps/alfa-e2e/src/support/cypress-tasks.ts index 1e965a342e415f6825afaa78d27a684a8a125bfa..a82b4f736601daf7f1b3164cbb45818a609e29de 100644 --- a/alfa-client/apps/alfa-e2e/src/support/cypress-tasks.ts +++ b/alfa-client/apps/alfa-e2e/src/support/cypress-tasks.ts @@ -1,5 +1,7 @@ import { readdir, remove } from 'fs-extra'; import { Db, Long, MongoClient, ObjectId } from 'mongodb'; +const fs = require('fs'); +const decompress = require('decompress'); const Binary = require('mongodb').Binary; @@ -54,6 +56,15 @@ module.exports = (on: any, config: any) => { deleteFolder(folderName); return 0; }, + getDownloadFiles(folderName: string) { + console.log('get files in folder %s', folderName); + return getDownloadFiles(folderName); + }, + unzipDownloadFile({ folderName, fileName }) { + console.log('unzip file %s in folder %s', fileName, folderName); + unzipFile(folderName, fileName); + return 0; + }, }); // Workaround für Angular 13 und Cypress mit Webpack 4, @@ -333,3 +344,14 @@ function deleteFolder(folderName: string): void { throw new Error(`Failed to delete folder: ${err.message}`); }); } + +function getDownloadFiles(folderName: string): Promise<Array<string>> { + return fs.readdirSync(folderName); +} + +function unzipFile(folderName: string, fileName: string): void { + console.log('File: ' + folderName + fileName); + + decompress(folderName + '/' + fileName, folderName); + return null; +} diff --git a/alfa-client/apps/alfa-e2e/src/support/cypress.util.ts b/alfa-client/apps/alfa-e2e/src/support/cypress.util.ts index 2e22bc2a6f6bc43f622ebac4772364105bfb9ccc..34935393bc51d6b7acf38b8494e7c2ba70f25689 100644 --- a/alfa-client/apps/alfa-e2e/src/support/cypress.util.ts +++ b/alfa-client/apps/alfa-e2e/src/support/cypress.util.ts @@ -22,6 +22,8 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { wait } from './cypress-helper'; + //TODO Naming der Methoden geradeziehen export function containClass(element: any, cssClass: string): void { @@ -113,8 +115,14 @@ export function enter(element: any): void { element.clear().type(CypressKeyboardActions.ENTER); } -export function enterWith(element: any, value: any): void { - element.clear().type(value + CypressKeyboardActions.ENTER); +export function enterWith( + element: Cypress.Chainable<JQuery<HTMLElement>>, + value: string, + delayBeforeEnter: number = 200, +): void { + element.clear().type(value); + wait(delayBeforeEnter); + element.type(CypressKeyboardActions.ENTER); } export function backspaceOn(element: any): void { diff --git a/alfa-client/apps/demo/src/app/app.component.html b/alfa-client/apps/demo/src/app/app.component.html index 4ecd2941d74717e3ef1791e8a0c1e4e039da9b66..81bc4dff284d5e739981af85b8d4e1cc0c4b3844 100644 --- a/alfa-client/apps/demo/src/app/app.component.html +++ b/alfa-client/apps/demo/src/app/app.component.html @@ -15,7 +15,7 @@ </div> <main class="flex-auto bg-background-50 p-6"> <div class="w-96"> - <ods-attachment-container> + <ods-attachment-wrapper> <ods-attachment caption="Mein_2Bescheid.pdf" description="234 kB" @@ -39,7 +39,7 @@ fileType="pdf" > </ods-attachment> - </ods-attachment-container> + </ods-attachment-wrapper> </div> <form id="antrag_bescheiden_form" [formGroup]="exampleForm"> diff --git a/alfa-client/apps/demo/src/app/app.component.ts b/alfa-client/apps/demo/src/app/app.component.ts index 58a09ee4d02b29261200d0f6f0db78b2bda20baa..5d64b15a623f2330d29201455ec298907bcd94fa 100644 --- a/alfa-client/apps/demo/src/app/app.component.ts +++ b/alfa-client/apps/demo/src/app/app.component.ts @@ -5,8 +5,8 @@ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { AttachmentComponent, - AttachmentContainerComponent, AttachmentIconComponent, + AttachmentWrapperComponent, BescheidGenerateIconComponent, BescheidUploadIconComponent, ButtonCardComponent, @@ -34,7 +34,7 @@ import { CustomStepperComponent } from './components/cdk-demo/custom-stepper.com imports: [ CommonModule, AttachmentComponent, - AttachmentContainerComponent, + AttachmentWrapperComponent, ButtonComponent, ButtonCardComponent, FileUploadButtonComponent, diff --git a/alfa-client/libs/admin-settings/src/lib/shared/text-field/text-field.component.ts b/alfa-client/libs/admin-settings/src/lib/shared/text-field/text-field.component.ts index 66db648e5e870b40619f321f04321b35b763e93d..bca9352718c8cbdb0e8f0434095d574215da1577 100644 --- a/alfa-client/libs/admin-settings/src/lib/shared/text-field/text-field.component.ts +++ b/alfa-client/libs/admin-settings/src/lib/shared/text-field/text-field.component.ts @@ -1,6 +1,6 @@ import { isNotNil } from '@alfa-client/tech-shared'; import { Component, Input } from '@angular/core'; -import { FormControlEditorAbstractComponent } from 'libs/ui/src/lib/ui/editor/formcontrol-editor.abstract.component'; +import { FormControlEditorAbstractComponent } from '@ods/component'; @Component({ selector: 'text-field', diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts index 78fe8433b135b716b9882a8c4a7bf4cccd2fef0b..228b24efb9cd33d7d236fbb0a5b4a4499cee4169 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts @@ -1520,4 +1520,14 @@ describe('BescheidService', () => { }); }); }); + + describe('clear attachment upload', () => { + it('should clear stateresource', () => { + service.uploadAttachmentInProgress$.next(createStateResource(createUploadFileInProgress())); + + service.clearAttachmentUpload(); + + expect(service.uploadAttachmentInProgress$.value).toEqual(createEmptyStateResource()); + }); + }); }); diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts index a8994d7ff2c9f8d74952af4e523dd240543e8929..76b74767032eac3979371d834d17c59217323600 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts @@ -589,4 +589,8 @@ export class BescheidService { public getUploadedAttachment(): Observable<StateResource<BinaryFileResource>> { return this.uploadedAttachment$.asObservable(); } + + public clearAttachmentUpload(): void { + this.uploadAttachmentInProgress$.next(createEmptyStateResource()); + } } diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.html index 8c0d2203d740cd0bba3db4dc9d8642178677d0ae..09e3689508466b42cb14f601cef86e5e1e82637c 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.html +++ b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.html @@ -1,5 +1,5 @@ <div class="flex max-w-xl flex-col gap-4"> - <ods-bescheid-container + <ods-bescheid-wrapper *ngFor="let bescheid of bescheidList | toEmbeddedResources: bescheidListLinkRel.BESCHEID_LIST" data-test-id="bescheid-container-in-vorgang" > @@ -23,5 +23,5 @@ [resource]="bescheid" [linkRel]="bescheidLinkRel.ATTACHMENTS" ></alfa-binary-file-list-container> - </ods-bescheid-container> + </ods-bescheid-wrapper> </div> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.spec.ts index e0d873aa1642ddef9bbd08cf7ced139549f24b50..60b406d0b6ac33394f4fbf38e16e8f22bcd0aa31 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.spec.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/bescheid-list-in-vorgang.component.spec.ts @@ -5,7 +5,7 @@ import { existsAsHtmlElement, notExistsAsHtmlElement } from '@alfa-client/test-u import { DatePipe } from '@angular/common'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIcon } from '@angular/material/icon'; -import { BescheidContainerComponent, BescheidStatusTextComponent } from '@ods/system'; +import { BescheidStatusTextComponent, BescheidWrapperComponent } from '@ods/system'; import { createBescheidListResource, createBescheidResource, @@ -37,7 +37,7 @@ describe('BescheidListInVorgangComponent', () => { MockComponent(DocumentInBescheidContainerComponent), MockComponent(BinaryFileListContainerComponent), MockComponent(BescheidStatusTextComponent), - MockComponent(BescheidContainerComponent), + MockComponent(BescheidWrapperComponent), ], }).compileComponents(); diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.html b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.html index aefc4c75af7cfc3bf0669e8c9fb99665f464fde4..464b7a0a4ff8783533e4160bc53d3c007eae5eed 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.html +++ b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.html @@ -1,9 +1,9 @@ <ng-container *ngIf="documentStateResource$ | async as documentStateResource"> - <ods-attachment-container> + <ods-attachment-wrapper> <alfa-binary-file-uri-container *ngIf="documentStateResource.resource | hasLink: documentLinkRel.FILE" data-test-class="binary-file-uri-container" [binaryFileUri]="documentStateResource.resource | getUrl: documentLinkRel.FILE" ></alfa-binary-file-uri-container> - </ods-attachment-container> + </ods-attachment-wrapper> </ng-container> diff --git a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.spec.ts b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.spec.ts index f005143e8fcd38ef21d5fe5a41753b7f98c347a6..6d4e62565ec7e6fa5e80f193cfa8c8c56fc2885d 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.spec.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid-list-in-vorgang-container/bescheid-list-in-vorgang/document-in-bescheid-container/document-in-bescheid-container.component.spec.ts @@ -16,7 +16,7 @@ import { import { ComponentFixture, TestBed } from '@angular/core/testing'; import faker from '@faker-js/faker'; import { ResourceUri, getUrl } from '@ngxp/rest'; -import { AttachmentContainerComponent } from '@ods/system'; +import { AttachmentWrapperComponent } from '@ods/system'; import { DocumentLinkRel } from 'libs/bescheid-shared/src/lib/document.linkrel'; import { DocumentResource } from 'libs/bescheid-shared/src/lib/document.model'; import { getDataTestClassOf } from 'libs/tech-shared/test/data-test'; @@ -45,7 +45,7 @@ describe('DocumentInBescheidContainerComponent', () => { HasLinkPipe, GetUrlPipe, MockComponent(BinaryFileUriContainerComponent), - MockComponent(AttachmentContainerComponent), + MockComponent(AttachmentWrapperComponent), ], providers: [ { diff --git a/alfa-client/libs/bescheid/src/lib/bescheid.module.ts b/alfa-client/libs/bescheid/src/lib/bescheid.module.ts index ab43b96faec19afccbfddc606e2b0b942174c012..e6faf4a67f1e937d0aab66ae583415d8a4030bd9 100644 --- a/alfa-client/libs/bescheid/src/lib/bescheid.module.ts +++ b/alfa-client/libs/bescheid/src/lib/bescheid.module.ts @@ -16,8 +16,8 @@ import { CreateBescheidButtonContainerComponent } from './create-bescheid-button import { CreateBescheidButtonComponent } from './create-bescheid-button-container/create-bescheid-button/create-bescheid-button.component'; import { - BescheidContainerComponent, BescheidStatusTextComponent, + BescheidWrapperComponent, CloseIconComponent, StampIconComponent, } from '@ods/system'; @@ -31,7 +31,7 @@ import { UiModule, CommandSharedModule, BescheidStatusTextComponent, - BescheidContainerComponent, + BescheidWrapperComponent, StampIconComponent, CloseIconComponent, ], diff --git a/alfa-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.ts b/alfa-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.ts index c6fcec9a06a7d81756a40aaa2afe23b226cc12bd..4a29ed56be2585d9746e49526e4977f66dbb2239 100644 --- a/alfa-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.ts +++ b/alfa-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.ts @@ -74,7 +74,7 @@ export class BinaryFileEffects { loadBinaryFileList$ = createEffect(() => this.actions$.pipe( ofType(BinaryFileActions.loadBinaryFileList), - switchMap((props: LoadBinaryFileListProps) => + mergeMap((props: LoadBinaryFileListProps) => this.repository.getFiles(props.resource, props.linkRel).pipe( map((binaryFileList: BinaryFileListResource) => props.successAction(binaryFileList)), catchError((error) => of(props.failureAction(error.error))), diff --git a/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.spec.ts b/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.spec.ts index 9ad810e73de5bb0a2015ad9b012adc2700f705d1..071f1a03138fae246311d53e97be4819b029127b 100644 --- a/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.spec.ts +++ b/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.spec.ts @@ -21,26 +21,34 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { faker } from '@faker-js/faker'; import { + BlobWithFileName, ContentType, GetRequestOptions, HttpErrorHandler, + HttpHeader, ListResource, TechSharedModule, } from '@alfa-client/tech-shared'; import { mock, mockClass, useFromMock } from '@alfa-client/test-utils'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { faker } from '@faker-js/faker'; import { Resource, ResourceFactory, ResourceUri, getUrl } from '@ngxp/rest'; import { cold, hot } from 'jest-marbles'; import { DummyLinkRel } from 'libs/tech-shared/test/dummy'; import { createDummyListResource, createDummyResource } from 'libs/tech-shared/test/resource'; -import { of } from 'rxjs'; -import { createBinaryFileResource } from '../../test/binary-file'; +import { Observable, of } from 'rxjs'; +import { + createBinaryFileResource, + createBlob, + createGetRequestOptions, +} from '../../test/binary-file'; import { BinaryFileLinkRel } from './binary-file.linkrel'; import { BinaryFileResource } from './binary-file.model'; import { BinaryFileRepository } from './binary-file.repository'; +import * as HttpUtil from '../../../tech-shared/src/lib/http.util'; + describe('BinaryFileRepository', () => { let repository: BinaryFileRepository; @@ -181,7 +189,7 @@ describe('BinaryFileRepository', () => { }); function getExpectedRequestOptions(): HttpHeaders { - return new HttpHeaders().set('Accept', [ + return new HttpHeaders().set(HttpHeader.ACCEPT, [ ContentType.APPLICATION_PDF, ContentType.APPLICATION_JSON, ]); @@ -224,4 +232,55 @@ describe('BinaryFileRepository', () => { expect(resourceWrapper.get).toHaveBeenCalledWith(DummyLinkRel.DUMMY); }); }); + + describe('downloadArchive', () => { + const blob: Blob = createBlob(); + const uri: ResourceUri = faker.internet.url(); + + const blobWithFileName: BlobWithFileName = { blob, fileName: faker.name.firstName() }; + + beforeEach(() => { + httpClient.get.mockReturnValue(hot('a', { a: blob })); + jest.spyOn(HttpUtil, 'buildBlobWithFileName').mockReturnValue(blobWithFileName); + }); + + it('should call httpClient', () => { + const requestOptions: GetRequestOptions = createGetRequestOptions(); + repository.buildRequestOptionsForArchive = jest.fn().mockReturnValue(requestOptions); + + repository.downloadArchive(uri); + + expect(httpClient.get).toHaveBeenCalledWith(uri, requestOptions); + }); + + it('should return value', () => { + const result: Observable<BlobWithFileName> = repository.downloadArchive(uri); + + expect(result).toBeObservable(cold('b', { b: blobWithFileName })); + }); + + describe('buildRequestOptionsForArchive', () => { + it('should return httpHeaders', () => { + const result: GetRequestOptions = repository.buildRequestOptionsForArchive(); + + expect(result.responseType).toEqual('blob'); + }); + + it('should return responseType', () => { + const result: GetRequestOptions = repository.buildRequestOptionsForArchive(); + + expect(result.headers).toEqual(buildExpectedRequestOptions()); + }); + + it('should return observe', () => { + const result: GetRequestOptions = repository.buildRequestOptionsForArchive(); + + expect((<any>result).observe).toEqual('response'); + }); + }); + + function buildExpectedRequestOptions(): HttpHeaders { + return new HttpHeaders().set(HttpHeader.ACCEPT, ContentType.APPLICATION_OCTET_STREAM); + } + }); }); diff --git a/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.ts b/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.ts index 9e2d3f4548b00e0fbbe64f7971b4af6c44356479..ef0c1b6762f8ac2177282f36b07d64879f8aac7c 100644 --- a/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.ts +++ b/alfa-client/libs/binary-file-shared/src/lib/binary-file.repository.ts @@ -21,16 +21,18 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { Injectable } from '@angular/core'; import { + BlobWithFileName, ContentType, GetRequestOptions, HttpHeader, SkipInterceptor, + buildBlobWithFileName, } from '@alfa-client/tech-shared'; +import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http'; +import { Injectable } from '@angular/core'; import { Resource, ResourceFactory, ResourceUri, getUrl } from '@ngxp/rest'; -import { Observable } from 'rxjs'; +import { Observable, map } from 'rxjs'; import { BinaryFileLinkRel } from './binary-file.linkrel'; import { BinaryFileListResource, BinaryFileResource } from './binary-file.model'; @@ -41,7 +43,11 @@ export class BinaryFileRepository { private resourceFactory: ResourceFactory, ) {} - public uploadFile(resource: Resource, linkRel: string, file: File): Observable<any> { + public uploadFile( + resource: Resource, + linkRel: string, + file: File, + ): Observable<HttpResponse<Object>> { const formData: FormData = new FormData(); formData.append('file', file, file.name); @@ -89,4 +95,16 @@ export class BinaryFileRepository { public getFiles(resource: Resource, linkRel: string): Observable<BinaryFileListResource> { return this.resourceFactory.from(resource).get(linkRel); } + + public downloadArchive(uri: ResourceUri): Observable<BlobWithFileName> { + return this.httpClient + .get<HttpResponse<Blob>>(uri, this.buildRequestOptionsForArchive()) + .pipe(map(buildBlobWithFileName)); + } + + buildRequestOptionsForArchive(): GetRequestOptions { + let headers = new HttpHeaders(); + headers = headers.set(HttpHeader.ACCEPT, ContentType.APPLICATION_OCTET_STREAM); + return <GetRequestOptions>{ headers, responseType: 'blob' as 'json', observe: 'response' }; + } } diff --git a/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.spec.ts b/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.spec.ts index ff3225272c34a37e6ccb820eaed7b82c39e4ef6a..9d69ae78a151079401a9d94caceff99e8c980675 100644 --- a/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.spec.ts +++ b/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.spec.ts @@ -21,30 +21,32 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { faker } from '@faker-js/faker'; import { + BlobWithFileName, + StateResource, createEmptyStateResource, createStateResource, - StateResource, } from '@alfa-client/tech-shared'; import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; import { SnackBarService } from '@alfa-client/ui'; -import { Resource } from '@ngxp/rest'; +import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; +import { fakeAsync, tick } from '@angular/core/testing'; +import { faker } from '@faker-js/faker'; +import { Resource, ResourceUri } from '@ngxp/rest'; import { cold, hot } from 'jest-marbles'; -import { createBinaryFileResource } from 'libs/binary-file-shared/test/binary-file'; +import { createBinaryFileResource, createBlob } from 'libs/binary-file-shared/test/binary-file'; import { VALIDATION_MESSAGES, ValidationMessageCode, } from 'libs/tech-shared/src/lib/validation/tech.validation.messages'; import { DummyLinkRel } from 'libs/tech-shared/test/dummy'; import { createDummyResource } from 'libs/tech-shared/test/resource'; -import { of } from 'rxjs'; +import { Observable, of } from 'rxjs'; +import { createHttpErrorResponse } from '../../../tech-shared/test/http'; +import { singleHot } from '../../../tech-shared/test/marbles'; import { BinaryFileResource } from './binary-file.model'; import { BinaryFileRepository } from './binary-file.repository'; import { BinaryFileService } from './binary-file.service'; -import { fakeAsync, tick } from '@angular/core/testing'; -import { createHttpErrorResponse } from '../../../tech-shared/test/http'; describe('BinaryFileService', () => { let service: BinaryFileService; @@ -80,7 +82,23 @@ describe('BinaryFileService', () => { expect(repository.download).toHaveBeenCalledWith(binaryFileResource); }); - describe('save file', () => { + it('should return value', () => { + repository.download.mockReturnValue(singleHot(blob, '-a')); + + const returnValue: Observable<StateResource<Blob>> = service.downloadFile( + binaryFileResource, + downloadNamePrefix, + ); + + expect(returnValue).toBeObservable( + cold('ab', { + a: createEmptyStateResource(true), + b: createStateResource(blob), + }), + ); + }); + + describe('save data', () => { describe('on existing response', () => { it('should save file without prefix', () => { service.saveBinaryFile(blob, binaryFileResource, undefined); @@ -122,12 +140,72 @@ describe('BinaryFileService', () => { }); }); + describe('download archive', () => { + const blob: Blob = createBlob(); + const resourceUri: ResourceUri = faker.internet.url(); + + beforeEach(() => { + service.save = jest.fn(); + repository.downloadArchive.mockReturnValue(hot('a', { a: blob })); + }); + + it('should call repository', () => { + service.downloadArchive(resourceUri); + + expect(repository.downloadArchive).toHaveBeenCalledWith(resourceUri); + }); + + it('should return value', () => { + repository.downloadArchive.mockReturnValue(singleHot(blob, '-a')); + service.saveData = jest.fn().mockReturnValue(createStateResource(blob)); + + const returnValue: Observable<StateResource<Blob>> = service.downloadArchive(resourceUri); + + expect(returnValue).toBeObservable( + cold('ab', { + a: createEmptyStateResource(true), + b: createStateResource(blob), + }), + ); + }); + + describe('save data', () => { + const fileName: string = faker.random.word(); + const blobWithFileName: BlobWithFileName = { blob, fileName }; + + describe('on existing response', () => { + it('should save file', () => { + service.saveData(blobWithFileName); + + expect(service.save).toHaveBeenCalledWith(blob, fileName); + }); + + it('should return loaded stateResource', () => { + const result: StateResource<Blob> = service.saveData(blobWithFileName); + + expect(result).toEqual(createStateResource(blob)); + }); + }); + + describe('on non existing response', () => { + it('should return loading stateResource', () => { + const result: StateResource<Blob> = service.saveData({ + ...blobWithFileName, + blob: undefined, + }); + + expect(result).toEqual(createEmptyStateResource(true)); + }); + }); + }); + }); + describe('upload file', () => { const dummyResource: Resource = createDummyResource(); const dummyLinkRel: string = DummyLinkRel.DUMMY; const fileLocation: string = 'fileLocation'; - const uploadFileResponse: HttpResponse<void> = <any>{ + const uploadFileResponse: HttpResponse<Object> = <any>{ response: { headers: { ['Location']: fileLocation } }, }; diff --git a/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.ts b/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.ts index 8e051164991b0bb42bcf1b7c268f23d2307e0516..bb6592cb76931ff30edca949cc28a864686ecfce 100644 --- a/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.ts +++ b/alfa-client/libs/binary-file-shared/src/lib/binary-file.service.ts @@ -22,7 +22,9 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { + BlobWithFileName, EMPTY_STRING, + HttpHeader, StateResource, createEmptyStateResource, createErrorStateResource, @@ -58,7 +60,9 @@ export class BinaryFileService { showValidationErrorSnackBar: boolean = true, ): Observable<StateResource<BinaryFileResource>> { return this.repository.uploadFile(resource, linkRel, file).pipe( - mergeMap((response: HttpResponse<void>) => this.getFile(response.headers.get('Location'))), + mergeMap((response: HttpResponse<Object>) => + this.getFile(response.headers.get(HttpHeader.LOCATION)), + ), catchError((errorResponse) => this.handleError(errorResponse.error, showValidationErrorSnackBar), ), @@ -115,13 +119,29 @@ export class BinaryFileService { return file.name; } + public downloadArchive(uri: ResourceUri): Observable<StateResource<Blob>> { + return this.repository.downloadArchive(uri).pipe( + map((data: BlobWithFileName) => this.saveData(data)), + startWith(createEmptyStateResource<Blob>(true)), + ); + } + + saveData(dataWithFileName: BlobWithFileName): StateResource<Blob> { + const data: Blob = dataWithFileName.blob; + if (isNil(data)) { + return createEmptyStateResource(true); + } + this.save(data, dataWithFileName.fileName); + return createStateResource(data); + } + save(data: any, fileName: string): void { saveAs(data, fileName); } public getFile(uri: ResourceUri): Observable<StateResource<BinaryFileResource>> { return this.repository.getFile(uri).pipe( - map((fileList) => createStateResource(fileList)), + map((fileList: BinaryFileResource) => createStateResource(fileList)), startWith(createEmptyStateResource<BinaryFileResource>(true)), ); } @@ -131,7 +151,7 @@ export class BinaryFileService { linkRel: string, ): Observable<StateResource<BinaryFileListResource>> { return this.repository.getFiles(resource, linkRel).pipe( - map((fileList) => createStateResource(fileList)), + map((fileList: BinaryFileListResource) => createStateResource(fileList)), startWith(createEmptyStateResource<BinaryFileListResource>(true)), ); } diff --git a/alfa-client/libs/binary-file-shared/test/binary-file.ts b/alfa-client/libs/binary-file-shared/test/binary-file.ts index 11ec8db5f9e7d0fea09215377ba9cb3911d93ea9..c776a49793353a3674e60ed40431df7392091509 100644 --- a/alfa-client/libs/binary-file-shared/test/binary-file.ts +++ b/alfa-client/libs/binary-file-shared/test/binary-file.ts @@ -21,7 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { StateResource, createStateResource } from '@alfa-client/tech-shared'; +import { GetRequestOptions, StateResource, createStateResource } from '@alfa-client/tech-shared'; import { faker } from '@faker-js/faker'; import { BinaryFileListLinkRel } from 'libs/binary-file-shared/src/lib/binary-file.linkrel'; import { @@ -64,3 +64,11 @@ export function createLoadingBinaryFileStateResource(): StateResource<BinaryFile export function createLoadedBinaryFileResource(): StateResource<BinaryFileResource> { return createStateResource(createBinaryFileResource()); } + +export function createBlob(): Blob { + return <Blob>{}; +} + +export function createGetRequestOptions(): GetRequestOptions { + return <GetRequestOptions>{}; +} diff --git a/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.html b/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.html index ccb6ff68ca977040b551bbccdae63a27b5122617..bdd7a27733a69d622edeb8e8abff53f5eb8c8d2b 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.html +++ b/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.html @@ -1,5 +1,5 @@ -<ods-attachment-container> +<ods-attachment-wrapper> <alfa-binary-file-list [binaryFileListStateResource]="binaryFileListStateResource$ | async" ></alfa-binary-file-list> -</ods-attachment-container> +</ods-attachment-wrapper> diff --git a/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.spec.ts b/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.spec.ts index 861fc464fac3928e8c5a25ff71fcd93031abfa13..1541adfb3f823192df4745d1534ab7ca2478df01 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.spec.ts +++ b/alfa-client/libs/binary-file/src/lib/binary-file-list-container/binary-file-list-container.component.spec.ts @@ -11,7 +11,7 @@ import { of } from 'rxjs'; import { BinaryFileListContainerComponent } from './binary-file-list-container.component'; import { BinaryFileListComponent } from './binary-file-list/binary-file-list.component'; -import { AttachmentContainerComponent } from '@ods/system'; +import { AttachmentWrapperComponent } from '@ods/system'; describe('BinaryFileListContainerComponent', () => { let component: BinaryFileListContainerComponent; @@ -30,7 +30,7 @@ describe('BinaryFileListContainerComponent', () => { declarations: [ BinaryFileListContainerComponent, MockComponent(BinaryFileListComponent), - MockComponent(AttachmentContainerComponent), + MockComponent(AttachmentWrapperComponent), ], providers: [ { diff --git a/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.html b/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.html index bd452e3e647ee15ed78a06b3e75374873c3f7b8d..156e5a66a1684b330e0a364a7e66a476244bf1cd 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.html +++ b/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.html @@ -1,11 +1,10 @@ <ng-container *ngIf="binaryFileStateResource$ | async as binaryFileStateResource"> - <ods-attachment-container> + <ods-attachment-wrapper> <alfa-binary-file2-container *ngIf="binaryFileStateResource.resource as binaryFile" data-test-class="binary-file-container" [file]="binaryFile" - [isLoading]="binaryFileStateResource.loading" [deletable]="false" ></alfa-binary-file2-container> - </ods-attachment-container> + </ods-attachment-wrapper> </ng-container> diff --git a/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.spec.ts b/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.spec.ts index 34d0358a03a8f651acba5a8335d860df4e8578c8..0080d68eb8b8c6ae8a425340e1422b5055e21fa6 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.spec.ts +++ b/alfa-client/libs/binary-file/src/lib/binary-file-uri-container/binary-file-uri-container.component.spec.ts @@ -21,7 +21,7 @@ import { of } from 'rxjs'; import { BinaryFile2ContainerComponent } from '../binary-file2-container/binary-file2-container.component'; import { BinaryFileUriContainerComponent } from './binary-file-uri-container.component'; -import { AttachmentContainerComponent } from '@ods/system'; +import { AttachmentWrapperComponent } from '@ods/system'; describe('BinaryFileUriContainerComponent', () => { let component: BinaryFileUriContainerComponent; @@ -37,7 +37,7 @@ describe('BinaryFileUriContainerComponent', () => { declarations: [ BinaryFileUriContainerComponent, MockComponent(BinaryFile2ContainerComponent), - MockComponent(AttachmentContainerComponent), + MockComponent(AttachmentWrapperComponent), ], providers: [ { @@ -97,13 +97,6 @@ describe('BinaryFileUriContainerComponent', () => { expect(binaryFileContainerComponent.file).toEqual(binaryFileStateResource.resource); }); - it('should be called with isLoading', () => { - const binaryFileContainerComponent: BinaryFile2ContainerComponent = - getMockComponent<BinaryFile2ContainerComponent>(fixture, BinaryFile2ContainerComponent); - - expect(binaryFileContainerComponent.isLoading).toBe(binaryFileStateResource.loading); - }); - it('should be called with deleteable', () => { const binaryFileContainerComponent: BinaryFile2ContainerComponent = getMockComponent<BinaryFile2ContainerComponent>(fixture, BinaryFile2ContainerComponent); diff --git a/alfa-client/libs/binary-file/src/lib/binary-file.module.ts b/alfa-client/libs/binary-file/src/lib/binary-file.module.ts index b1f7c8e578217084c9419a892fa0edb503846c31..89f78362805623629dbb65e5e9963789da21a337 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file.module.ts +++ b/alfa-client/libs/binary-file/src/lib/binary-file.module.ts @@ -25,9 +25,11 @@ import { TechSharedModule } from '@alfa-client/tech-shared'; import { UiModule } from '@alfa-client/ui'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; +import { DownloadButtonComponent } from '@ods/component'; import { AttachmentComponent, - AttachmentContainerComponent, + AttachmentHeaderComponent, + AttachmentWrapperComponent, CloseIconComponent, SpinnerIconComponent, } from '@ods/system'; @@ -39,6 +41,7 @@ import { BinaryFileListComponent } from './binary-file-list-container/binary-fil import { BinaryFileUriContainerComponent } from './binary-file-uri-container/binary-file-uri-container.component'; import { BinaryFile2ContainerComponent } from './binary-file2-container/binary-file2-container.component'; import { BinaryFile2Component } from './binary-file2-container/binary-file2/binary-file2.component'; +import { DownloadArchiveFileButtonContainerComponent } from './download-archive-file-button-container/download-archive-file-button-container.component'; import { HorizontalBinaryFileListComponent } from './horizontal-binary-file-list/horizontal-binary-file-list.component'; import { VerticalBinaryFileListComponent } from './vertical-binary-file-list/vertical-binary-file-list.component'; @@ -48,9 +51,11 @@ import { VerticalBinaryFileListComponent } from './vertical-binary-file-list/ver UiModule, TechSharedModule, AttachmentComponent, - AttachmentContainerComponent, + AttachmentHeaderComponent, + AttachmentWrapperComponent, SpinnerIconComponent, CloseIconComponent, + DownloadButtonComponent, ], declarations: [ BinaryFileAttachmentContainerComponent, @@ -63,6 +68,7 @@ import { VerticalBinaryFileListComponent } from './vertical-binary-file-list/ver BinaryFileUriContainerComponent, BinaryFileListContainerComponent, BinaryFileListComponent, + DownloadArchiveFileButtonContainerComponent, ], exports: [ BinaryFileAttachmentContainerComponent, diff --git a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.html b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.html index f901ea0826e96e938dab2632af3d9d5c754531bb..41fe9ab51a28ec7cc893e0dfd418cbffb86075c5 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.html +++ b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.html @@ -3,7 +3,6 @@ [file]="file" [stateResource]="fileStateResource$ | async" [deletable]="deletable" - [isLoading]="isLoading" [downloadToken]="downloadToken$ | async" (startDownload)="startDownload($event)" (startDelete)="startDelete.emit($event)" diff --git a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.ts b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.ts index 6461380c6ebe06fa80e8a242440f9acee33051fa..ad052ff94920bcd3eb6f1b974aeeba26b276f675 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.ts +++ b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2-container.component.ts @@ -12,7 +12,6 @@ export class BinaryFile2ContainerComponent { @Input() file: BinaryFileResource; @Input() downloadFileNamePrefix: string; @Input() deletable: boolean = false; - @Input() isLoading: boolean = false; @Output() startDelete: EventEmitter<BinaryFileResource> = new EventEmitter(); diff --git a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.html b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.html index cd64683430db122e9d8411e606f9f80be75d2028..48f6c28798060e009239a18af52f8c24c4944492 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.html +++ b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.html @@ -1,10 +1,12 @@ <ods-attachment [caption]="file.name" + [loadingCaption]="file.name + ' wird geladen...'" [description]="file.size | fileSizePlain" [fileType]="getIconType(file.contentType)" (click)="downloadFile()" [attr.aria-label]="'Anhang: Dateiname: ' + file.name" [isLoading]="isLoading" + data-test-class="download-file-button" > <div close class="flex-shrink self-center"> <button diff --git a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.ts b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.ts index 6118b4d86b30fae7b721cf1f34c44ca190bdc421..c22c2d1c4bc05dd546c8e8e95f990fa359b7a01f 100644 --- a/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.ts +++ b/alfa-client/libs/binary-file/src/lib/binary-file2-container/binary-file2/binary-file2.component.ts @@ -19,7 +19,6 @@ export class BinaryFile2Component { @Input() stateResource: StateResource<Resource>; @Input() deletable: boolean = false; @Input() downloadToken: ApiDownloadToken = <ApiDownloadToken>{}; - @Input() isLoading: boolean = false; readonly fileLinkRel = BinaryFileLinkRel; @@ -33,6 +32,10 @@ export class BinaryFile2Component { return this.getStateResource().loading; } + get isLoading(): boolean { + return this.getStateResource().loading; + } + getStateResource(): StateResource<Resource> { return isNil(this.stateResource) ? createEmptyStateResource<Resource>() : this.stateResource; } diff --git a/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.html b/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.html new file mode 100644 index 0000000000000000000000000000000000000000..a649ed00b6baf0a460ce19e409ad2fc536ebeb24 --- /dev/null +++ b/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.html @@ -0,0 +1,5 @@ +<ods-download-button + [stateResource]="downloadArchiveInProgress$ | async" + data-test-class="download-archive" + (click)="downloadArchive()" +/> diff --git a/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.spec.ts b/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..6b711f3a245de614563b6d1914ea0dbd09a0a73b --- /dev/null +++ b/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.spec.ts @@ -0,0 +1,49 @@ +import { BinaryFileService } from '@alfa-client/binary-file-shared'; +import { Mock, dispatchEventFromFixture, mock } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import faker from '@faker-js/faker'; +import { DownloadButtonComponent } from '@ods/component'; +import { getDataTestClassOf } from 'libs/tech-shared/test/data-test'; +import { MockComponent } from 'ng-mocks'; +import { DownloadArchiveFileButtonContainerComponent } from './download-archive-file-button-container.component'; + +describe('DownloadArchiveFileButtonContainerComponent', () => { + let component: DownloadArchiveFileButtonContainerComponent; + let fixture: ComponentFixture<DownloadArchiveFileButtonContainerComponent>; + + const downloadArchiveButton: string = getDataTestClassOf('download-archive'); + + const binaryFileService: Mock<BinaryFileService> = mock(BinaryFileService); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [DownloadArchiveFileButtonContainerComponent], + imports: [MockComponent(DownloadButtonComponent)], + providers: [ + { + provide: BinaryFileService, + useValue: binaryFileService, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(DownloadArchiveFileButtonContainerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('download attachments', () => { + const downloadUri: string = faker.internet.url(); + + it('should call service to download archive', () => { + component.downloadUri = downloadUri; + dispatchEventFromFixture(fixture, downloadArchiveButton, 'click'); + + expect(binaryFileService.downloadArchive).toHaveBeenCalledWith(downloadUri); + }); + }); +}); diff --git a/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.ts b/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..0c584eeb78164e120a18be156e24703c1c9f87ad --- /dev/null +++ b/alfa-client/libs/binary-file/src/lib/download-archive-file-button-container/download-archive-file-button-container.component.ts @@ -0,0 +1,22 @@ +import { BinaryFileService } from '@alfa-client/binary-file-shared'; +import { StateResource, createEmptyStateResource } from '@alfa-client/tech-shared'; +import { Component, Input } from '@angular/core'; +import { Observable, of } from 'rxjs'; + +@Component({ + selector: 'alfa-download-archive-file-button-container', + templateUrl: './download-archive-file-button-container.component.html', +}) +export class DownloadArchiveFileButtonContainerComponent { + @Input() downloadUri: string; + + public downloadArchiveInProgress$: Observable<StateResource<unknown>> = of( + createEmptyStateResource(), + ); + + constructor(private binaryFileService: BinaryFileService) {} + + public downloadArchive(): void { + this.downloadArchiveInProgress$ = this.binaryFileService.downloadArchive(this.downloadUri); + } +} diff --git a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.html b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.html index fbea31c02eff3f5d30abd9cf1c1ff7a73447f716..187ab55187039b6ca7ea2f9d6b1ab8e0cc456a4a 100644 --- a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.html +++ b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.html @@ -23,17 +23,31 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<ozgcloud-spinner [stateResource]="fileListResource"> - <div class="vertical" data-test-id="file-list"> - <alfa-binary-file-container +<ozgcloud-spinner + *ngIf="binaryFileListStateResource.resource" + [stateResource]="binaryFileListStateResource" +> + <ods-attachment-wrapper [title]="title" data-test-id="file-list"> + <ods-attachment-header [title]="title"> + <alfa-download-archive-file-button-container + *ngIf="archiveDownloadUri" + data-test-class="download-archive-file-button" + [downloadUri]="archiveDownloadUri" + action-buttons + ></alfa-download-archive-file-button-container + ></ods-attachment-header> + <ng-container *ngFor=" - let binaryFileResource of fileListResource.resource + let binaryFileResource of binaryFileListStateResource.resource | toEmbeddedResources: fileListRel.FILE_LIST " - [file]="binaryFileResource" - [deletable]="deletable" - [downloadFileNamePrefix]="downloadFileNamePrefix" > - </alfa-binary-file-container> - </div> + <alfa-binary-file2-container + [file]="binaryFileResource" + [deletable]="deletable" + [downloadFileNamePrefix]="downloadFileNamePrefix" + > + </alfa-binary-file2-container> + </ng-container> + </ods-attachment-wrapper> </ozgcloud-spinner> diff --git a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.scss b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.scss deleted file mode 100644 index 1212c1617eca6dd57a7d70ae7069d282a3611ea7..0000000000000000000000000000000000000000 --- a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.scss +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -.vertical { - display: flex; - flex-direction: column; - max-width: 100%; - align-items: flex-start; - margin: 0 -4px; - margin-top: 1rem; -} diff --git a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.spec.ts b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.spec.ts index 07c0a783352ebfa2a91827c1216b71ca1736baae..a42980975bd6362343a865a3dae296bbd3191728 100644 --- a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.spec.ts +++ b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.spec.ts @@ -21,24 +21,46 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { createEmptyStateResource, ToEmbeddedResourcesPipe } from '@alfa-client/tech-shared'; +import { BinaryFileResource } from '@alfa-client/binary-file-shared'; +import { ToEmbeddedResourcesPipe, createStateResource } from '@alfa-client/tech-shared'; +import { + existsAsHtmlElement, + getMockComponent, + notExistsAsHtmlElement, +} from '@alfa-client/test-utils'; import { SpinnerComponent } from '@alfa-client/ui'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import faker from '@faker-js/faker'; +import { ResourceUri } from '@ngxp/rest'; +import { AttachmentHeaderComponent, AttachmentWrapperComponent } from '@ods/system'; +import { + createBinaryFileListResource, + createBinaryFileResource, +} from 'libs/binary-file-shared/test/binary-file'; +import { getDataTestClassOf } from 'libs/tech-shared/test/data-test'; import { MockComponent } from 'ng-mocks'; -import { BinaryFileContainerComponent } from './../binary-file-container/binary-file-container.component'; +import { BinaryFile2ContainerComponent } from '../binary-file2-container/binary-file2-container.component'; +import { DownloadArchiveFileButtonContainerComponent } from '../download-archive-file-button-container/download-archive-file-button-container.component'; import { VerticalBinaryFileListComponent } from './vertical-binary-file-list.component'; describe('VerticalBinaryFileListComponent', () => { let component: VerticalBinaryFileListComponent; let fixture: ComponentFixture<VerticalBinaryFileListComponent>; + const downloadArchiveFileButton: string = getDataTestClassOf('download-archive-file-button'); + + const binaryFile: BinaryFileResource = createBinaryFileResource(); + beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ VerticalBinaryFileListComponent, ToEmbeddedResourcesPipe, - MockComponent(BinaryFileContainerComponent), + MockComponent(AttachmentWrapperComponent), + MockComponent(AttachmentHeaderComponent), + MockComponent(BinaryFile2ContainerComponent), MockComponent(SpinnerComponent), + MockComponent(DownloadArchiveFileButtonContainerComponent), ], }).compileComponents(); }); @@ -46,11 +68,80 @@ describe('VerticalBinaryFileListComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(VerticalBinaryFileListComponent); component = fixture.componentInstance; - component.fileListResource = createEmptyStateResource(); + component.binaryFileListStateResource = createStateResource( + createBinaryFileListResource([binaryFile]), + ); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + describe('binary file component', () => { + describe('should be called with', () => { + it('file', () => { + const comp: BinaryFile2ContainerComponent = getMockComponent( + fixture, + BinaryFile2ContainerComponent, + ); + + expect(comp.file).toBe(binaryFile); + }); + + it('deletable', () => { + const comp: BinaryFile2ContainerComponent = getMockComponent( + fixture, + BinaryFile2ContainerComponent, + ); + + expect(comp.deletable).toBe(component.deletable); + }); + + it('downloadFileNamePrefix', () => { + const comp: BinaryFile2ContainerComponent = getMockComponent( + fixture, + BinaryFile2ContainerComponent, + ); + + expect(comp.downloadFileNamePrefix).toBe(component.downloadFileNamePrefix); + }); + }); + }); + + describe('download archive button', () => { + const downloadUri: ResourceUri = faker.internet.url(); + + it('should be visible if uri exists', () => { + component.archiveDownloadUri = downloadUri; + + fixture.detectChanges(); + + existsAsHtmlElement(fixture, downloadArchiveFileButton); + }); + + it('should be hidden if uri is undefined', () => { + component.archiveDownloadUri = undefined; + + fixture.detectChanges(); + + notExistsAsHtmlElement(fixture, downloadArchiveFileButton); + }); + + describe('component should be called with', () => { + beforeEach(() => { + component.archiveDownloadUri = downloadUri; + fixture.detectChanges(); + }); + + it('downloadUri', () => { + const comp: DownloadArchiveFileButtonContainerComponent = getMockComponent( + fixture, + DownloadArchiveFileButtonContainerComponent, + ); + + expect(comp.downloadUri).toBe(downloadUri); + }); + }); + }); }); diff --git a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.ts b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.ts index 3eb9096547a466caf4ac3b11ee935f728bee1745..363dc54b3bfb8ff2dc7f7bd5e00570afe9fe5565 100644 --- a/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.ts +++ b/alfa-client/libs/binary-file/src/lib/vertical-binary-file-list/vertical-binary-file-list.component.ts @@ -21,20 +21,22 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Component, Input } from '@angular/core'; import { BinaryFileListLinkRel, BinaryFileListResource } from '@alfa-client/binary-file-shared'; -import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; +import { StateResource, createEmptyStateResource } from '@alfa-client/tech-shared'; +import { Component, Input } from '@angular/core'; +import { ResourceUri } from '@ngxp/rest'; @Component({ selector: 'alfa-vertical-binary-file-list', templateUrl: './vertical-binary-file-list.component.html', - styleUrls: ['./vertical-binary-file-list.component.scss'], }) export class VerticalBinaryFileListComponent { - @Input() fileListResource: StateResource<BinaryFileListResource> = + @Input() binaryFileListStateResource: StateResource<BinaryFileListResource> = createEmptyStateResource<BinaryFileListResource>(); @Input() downloadFileNamePrefix: string; + @Input() title: string = ''; @Input() deletable: boolean = false; + @Input() archiveDownloadUri: ResourceUri; readonly fileListRel = BinaryFileListLinkRel; } diff --git a/alfa-client/libs/design-component/src/index.ts b/alfa-client/libs/design-component/src/index.ts index 8670320bc61acad5f807315ea595229b26da068d..0ed0ab64368e0b3716f3ed6b1ab8d7da0a6fc2d7 100644 --- a/alfa-client/libs/design-component/src/index.ts +++ b/alfa-client/libs/design-component/src/index.ts @@ -1,5 +1,7 @@ export * from './lib/button-with-spinner/button-with-spinner.component'; +export * from './lib/download-button/download-button.component'; export * from './lib/form/file-upload-editor/file-upload-editor.component'; +export * from './lib/form/formcontrol-editor.abstract.component'; export * from './lib/form/single-file-upload-editor/single-file-upload-editor.component'; export * from './lib/form/text-editor/text-editor.component'; export * from './lib/form/textarea-editor/textarea-editor.component'; diff --git a/alfa-client/libs/design-component/src/lib/download-button/download-button.component.spec.ts b/alfa-client/libs/design-component/src/lib/download-button/download-button.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..3b5b17755eea019979b1ec405107272c3f43b692 --- /dev/null +++ b/alfa-client/libs/design-component/src/lib/download-button/download-button.component.spec.ts @@ -0,0 +1,48 @@ +import { createEmptyStateResource } from '@alfa-client/tech-shared'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DownloadButtonComponent } from './download-button.component'; + +describe('DownloadButtonComponent', () => { + let component: DownloadButtonComponent; + let fixture: ComponentFixture<DownloadButtonComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DownloadButtonComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(DownloadButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('isLoading', () => { + it('should return false', () => { + component.stateResource = createEmptyStateResource(); + + const isLoading: boolean = component.isLoading; + + expect(isLoading).toBeFalsy(); + }); + + it('should return true if stateResource is loading', () => { + component.stateResource = createEmptyStateResource(true); + + const isLoading: boolean = component.isLoading; + + expect(isLoading).toBeTruthy(); + }); + + it('should return true if stateResource is reloading', () => { + component.stateResource = { ...createEmptyStateResource(), reload: true }; + + const isLoading: boolean = component.isLoading; + + expect(isLoading).toBeTruthy(); + }); + }); +}); diff --git a/alfa-client/libs/design-component/src/lib/download-button/download-button.component.ts b/alfa-client/libs/design-component/src/lib/download-button/download-button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd69b20129739ad87ed4cd40744a858355b3fefc --- /dev/null +++ b/alfa-client/libs/design-component/src/lib/download-button/download-button.component.ts @@ -0,0 +1,35 @@ +import { CommandResource } from '@alfa-client/command-shared'; +import { StateResource } from '@alfa-client/tech-shared'; +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { ButtonComponent, SaveIconComponent, iconVariants } from '@ods/system'; +import { VariantProps } from 'class-variance-authority'; + +type IconVariants = VariantProps<typeof iconVariants>; + +@Component({ + selector: 'ods-download-button', + standalone: true, + imports: [CommonModule, ButtonComponent, SaveIconComponent], + template: `<ods-button + [dataTestId]="dataTestId" + variant="icon" + size="fit" + spinnerSize="small" + [isLoading]="isLoading" + (click)="clickEmitter.emit()" + > + <ods-save-icon icon [size]="size" class="fill-text"></ods-save-icon> + </ods-button>`, +}) +export class DownloadButtonComponent { + @Input() dataTestId: string = ''; + @Input() size: IconVariants['size'] = 'small'; + @Input() set stateResource(resource: StateResource<CommandResource>) { + this.isLoading = resource.loading || resource.reload; + } + + @Output() public clickEmitter: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>(); + + isLoading: boolean = false; +} diff --git a/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.spec.ts b/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.spec.ts index 64341eff5a4cbcfc8a9b5730559f23e1acfe440b..bcd618c337bd126c861a20360a9ac56633bfa0ec 100644 --- a/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.spec.ts +++ b/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.spec.ts @@ -32,7 +32,6 @@ import { } from '@angular/forms'; import { FileUploadButtonComponent, SpinnerIconComponent } from '@ods/system'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; -import { ValidationErrorComponent } from 'libs/ui/src/lib/ui/validation-error/validation-error.component'; import { MockComponent } from 'ng-mocks'; import { FileUploadEditorComponent } from './file-upload-editor.component'; @@ -51,7 +50,6 @@ describe('FileUploadEditorComponent', () => { await TestBed.configureTestingModule({ declarations: [ FileUploadEditorComponent, - MockComponent(ValidationErrorComponent), MockComponent(SpinnerIconComponent), MockComponent(FileUploadButtonComponent), ], diff --git a/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.ts b/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.ts index d6e927851b53ccb0b3048b0f7562d10af3a75275..3c7992f6f52d3525ad32f74fd4282442d44dd1a8 100644 --- a/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.ts +++ b/alfa-client/libs/design-component/src/lib/form/file-upload-editor/file-upload-editor.component.ts @@ -1,5 +1,4 @@ import { StateResource, TechSharedModule } from '@alfa-client/tech-shared'; -import { FormControlEditorAbstractComponent } from '@alfa-client/ui'; import { NgForOf } from '@angular/common'; import { Component, EventEmitter, HostListener, Input, OnInit, Output } from '@angular/core'; import { @@ -16,6 +15,7 @@ import { SpinnerIconComponent, } from '@ods/system'; import { uniqueId } from 'lodash-es'; +import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; @Component({ selector: 'ods-file-upload-editor', diff --git a/alfa-client/libs/ui/src/lib/ui/editor/formcontrol-editor.abstract.component.ts b/alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts similarity index 91% rename from alfa-client/libs/ui/src/lib/ui/editor/formcontrol-editor.abstract.component.ts rename to alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts index f0270e9c2fea92811bc6b8a1f3c0b2d115c3c240..a023a009d1780be33395159d25234da39285f745 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/formcontrol-editor.abstract.component.ts +++ b/alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts @@ -21,16 +21,18 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Component, Optional, Self } from '@angular/core'; -import { ControlValueAccessor, UntypedFormControl, NgControl } from '@angular/forms'; import { Issue } from '@alfa-client/tech-shared'; +import { Component, OnDestroy, OnInit, Optional, Self } from '@angular/core'; +import { ControlValueAccessor, NgControl, UntypedFormControl } from '@angular/forms'; import { isEmpty } from 'lodash-es'; import { Subscription } from 'rxjs'; @Component({ template: 'NO UI', }) -export abstract class FormControlEditorAbstractComponent implements ControlValueAccessor { +export abstract class FormControlEditorAbstractComponent + implements ControlValueAccessor, OnInit, OnDestroy +{ readonly fieldControl: UntypedFormControl = new UntypedFormControl(); public onChange = (text: string | Date) => undefined; public onTouched = () => undefined; diff --git a/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.spec.ts b/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.spec.ts index 2a7ee6d4fc46926081e1aedbb5bf086f9fc63883..4d44ce0dcbb1165a9ddb6bad46a1537fee601d6d 100644 --- a/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.spec.ts +++ b/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.spec.ts @@ -3,7 +3,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { FileUploadButtonComponent, SpinnerIconComponent } from '@ods/system'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; -import { ValidationErrorComponent } from 'libs/ui/src/lib/ui/validation-error/validation-error.component'; import { MockComponent } from 'ng-mocks'; import { SingleFileUploadEditorComponent } from './single-file-upload-editor.component'; @@ -18,7 +17,6 @@ describe('SingleFileUploadEditorComponent', () => { declarations: [ SingleFileUploadEditorComponent, FileUploadButtonComponent, - MockComponent(ValidationErrorComponent), MockComponent(SpinnerIconComponent), ], imports: [ReactiveFormsModule], diff --git a/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.ts b/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.ts index 36db826a36c473a9012eafc2d3470ce05af011e8..8da41fe6bb90f102b7bad62c44fd230f0eb094c7 100644 --- a/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.ts +++ b/alfa-client/libs/design-component/src/lib/form/single-file-upload-editor/single-file-upload-editor.component.ts @@ -1,20 +1,15 @@ import { TechSharedModule, isNotNil } from '@alfa-client/tech-shared'; -import { FormControlEditorAbstractComponent } from '@alfa-client/ui'; import { Component, EventEmitter, HostListener, Input, Output } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { FileUploadButtonComponent, SpinnerIconComponent } from '@ods/system'; import { uniqueId } from 'lodash-es'; +import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; @Component({ selector: 'ods-single-file-upload-editor', templateUrl: './single-file-upload-editor.component.html', standalone: true, - imports: [ - FileUploadButtonComponent, - SpinnerIconComponent, - ReactiveFormsModule, - TechSharedModule, - ], + imports: [FileUploadButtonComponent, SpinnerIconComponent, ReactiveFormsModule, TechSharedModule], }) export class SingleFileUploadEditorComponent extends FormControlEditorAbstractComponent { @Input() label: string = ''; diff --git a/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.html b/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.html index bd8034d8366f83cf4ddbee1e54c271fb932f2c9a..544b9312b03adba3e0e097e8eba8a20751e5b013 100644 --- a/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.html +++ b/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.html @@ -5,7 +5,7 @@ [autocomplete]="autocomplete" [variant]="variant" [attr.data-test-id]="(label | convertForDataTest) + '-text-editor'" - [required]="required" + [required]="isRequired" [focus]="focus" > <ods-validation-error diff --git a/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.ts b/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.ts index a1881546fe7309af8c1992f7c17645fe3e692ad4..c78a7554800ed6f61eb9d189617811e7f9d0490a 100644 --- a/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.ts +++ b/alfa-client/libs/design-component/src/lib/form/text-editor/text-editor.component.ts @@ -1,9 +1,9 @@ import { TechSharedModule } from '@alfa-client/tech-shared'; -import { FormControlEditorAbstractComponent } from '@alfa-client/ui'; import { CommonModule } from '@angular/common'; -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { TextInputComponent } from '@ods/system'; +import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; import { ValidationErrorComponent } from '../validation-error/validation-error.component'; @Component({ @@ -18,11 +18,11 @@ import { ValidationErrorComponent } from '../validation-error/validation-error.c ], templateUrl: './text-editor.component.html', }) -export class TextEditorComponent extends FormControlEditorAbstractComponent implements OnInit { +export class TextEditorComponent extends FormControlEditorAbstractComponent { @Input({ required: true }) label: string; @Input() autocomplete: 'off' | 'email' = 'off'; @Input() placeholder: string = ''; - @Input() required: boolean = false; + @Input() isRequired: boolean = false; @Input() focus: boolean = false; get variant(): string { diff --git a/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.html b/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.html index 73ea744d07206658aafb6af0e95533b8d106960b..5f3959bd34260ac44c11db6a108f24583986b9b0 100644 --- a/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.html +++ b/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.html @@ -5,7 +5,7 @@ [rows]="rows" [variant]="variant" [attr.data-test-id]="(label | convertForDataTest) + '-textarea-editor'" - [required]="required" + [required]="isRequired" [focus]="focus" > <ods-validation-error diff --git a/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.ts b/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.ts index 1514871a5a9e0d6032e96fd0b85cd3064726ff13..f78ee2c938fdb89b91a17060ba862ecadcff04aa 100644 --- a/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.ts +++ b/alfa-client/libs/design-component/src/lib/form/textarea-editor/textarea-editor.component.ts @@ -1,9 +1,9 @@ import { TechSharedModule } from '@alfa-client/tech-shared'; -import { FormControlEditorAbstractComponent } from '@alfa-client/ui'; import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { TextareaComponent } from '@ods/system'; +import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; import { ValidationErrorComponent } from '../validation-error/validation-error.component'; @Component({ @@ -20,9 +20,9 @@ import { ValidationErrorComponent } from '../validation-error/validation-error.c }) export class TextareaEditorComponent extends FormControlEditorAbstractComponent { @Input({ required: true }) label: string; - @Input() placeholder: string; + @Input() placeholder: string = ''; @Input() rows: number = 10; - @Input() required: boolean = false; + @Input() isRequired: boolean = false; @Input() focus: boolean = false; get variant(): string { diff --git a/alfa-client/libs/design-system/Dockerfile b/alfa-client/libs/design-system/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..db2112bd5fa7878771e6c2a1fac73bd58feb4e09 --- /dev/null +++ b/alfa-client/libs/design-system/Dockerfile @@ -0,0 +1,5 @@ +FROM nginxinc/nginx-unprivileged:stable-alpine + + +COPY dist/storybook /usr/share/nginx/html/ +COPY libs/design-system/nginx.conf /etc/nginx/nginx.conf \ No newline at end of file diff --git a/alfa-client/libs/design-system/main/helm/Chart.yaml b/alfa-client/libs/design-system/main/helm/Chart.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fcdc7922e7386ee8ab01671dd283b5df3778f037 --- /dev/null +++ b/alfa-client/libs/design-system/main/helm/Chart.yaml @@ -0,0 +1,30 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +apiVersion: v1 +appVersion: '1.0' +description: A Helm chart for storybook +name: storybook +version: 0.0.0-MANAGED-BY-JENKINS +icon: https://simpleicons.org/icons/helm.svg diff --git a/alfa-client/libs/design-system/main/helm/templates/_helpers.tpl b/alfa-client/libs/design-system/main/helm/templates/_helpers.tpl new file mode 100644 index 0000000000000000000000000000000000000000..08da97368b065f836fbcc316b69c38c77e7a47a9 --- /dev/null +++ b/alfa-client/libs/design-system/main/helm/templates/_helpers.tpl @@ -0,0 +1,81 @@ + +{{/* error check 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec) */}} +{{/* Namespace */}} +{{- define "app.namespace" -}} +{{- if gt (len (.Release.Namespace)) 63 -}} +{{- fail (printf ".Release.Namespace %s ist zu lang (max. 63 Zeichen)" .Release.Namespace) -}} +{{- end -}} +{{ printf "%s" .Release.Namespace }} +{{- end -}} + +{{/* Chart: Name + Version */}} +{{- define "app.chart" -}} +{{- if gt (len (printf "%s-%s" .Chart.Name .Chart.Version)) 63 -}} +{{- fail (printf ".Chart.Name-.Chart.Version %s-%s ist zu lang (max. 63 Zeichen)" .Chart.Name .Chart.Version) -}} +{{- end -}} +{{ printf "%s-%s" .Chart.Name .Chart.Version }} +{{- end -}} + +{{/* Managed-by -> On Helm, this value is always Helm */}} +{{- define "app.managedBy" -}} +{{- if gt (len (.Release.Service)) 63 -}} +{{- fail (printf ".Release.Service %s ist zu lang (max. 63 Zeichen)" .Release.Service) -}} +{{- end -}} +{{ printf "%s" .Release.Service }} +{{- end -}} + + +{{/* Default Labels: Helm recommended best-practice labels https://helm.sh/docs/chart_best_practices/labels/ */}} +{{- define "app.defaultLabels" }} +app.kubernetes.io/instance: storybook +app.kubernetes.io/managed-by: {{ include "app.managedBy" . }} +app.kubernetes.io/name: {{ .Release.Name }} +app.kubernetes.io/namespace: {{ include "app.namespace" . }} +app.kubernetes.io/part-of: ozgcloud +app.kubernetes.io/version: {{ .Chart.Version }} +helm.sh/chart: {{ include "app.chart" . }} +{{- end -}} + +{{- define "app.matchLabels" }} +app.kubernetes.io/name: {{ .Release.Name }} +app.kubernetes.io/namespace: {{ include "app.namespace" . }} +{{- end -}} + + +{{- define "app.serviceAccountName" -}} +{{ printf "%s" ( (.Values.serviceAccount).name | default "storybook-service-account" ) }} +{{- end -}} + + +{{- define "app.baseDomain" -}} +{{- printf "%s.%s" (include "app.ozgcloudBezeichner" . ) (include "app.baseUrl" . ) }} +{{- end -}} + +{{- define "app.ozgcloudBezeichner" -}} +{{- required "ozgcloud.bezeichner muss angegeben sein" (.Values.ozgcloud).bezeichner -}} +{{- if lt 46 (len (.Values.ozgcloud).bezeichner) -}} +{{ fail (printf "ozgcloud.bezeichner %s ist zu lang (max. 46 Zeichen)" (.Values.ozgcloud).bezeichner) }} +{{- end -}} +{{- end -}} + +{{- define "app.baseUrl" -}} +{{- required "baseUrl muss angegeben sein" .Values.baseUrl }} +{{- end -}} + +{{- define "app.getCustomList" -}} +{{- with (.Values.env).customList -}} +{{- if kindIs "map" . -}} +{{ include "app.dictToList" . }} +{{- else if kindIs "slice" . -}} +{{ . | toYaml }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "app.dictToList" -}} +{{- $customList := list -}} +{{- range $key, $value := . -}} +{{- $customList = append $customList (dict "name" $key "value" $value) }} +{{- end -}} +{{- $customList | toYaml -}} +{{- end -}} \ No newline at end of file diff --git a/alfa-client/libs/design-system/main/helm/templates/deployment.yaml b/alfa-client/libs/design-system/main/helm/templates/deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7daf47c5c1e9f110de1a378b984698d9130c3c69 --- /dev/null +++ b/alfa-client/libs/design-system/main/helm/templates/deployment.yaml @@ -0,0 +1,122 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }} + namespace: {{ include "app.namespace" . }} + labels: + {{- include "app.defaultLabels" . | indent 4 }} +spec: + progressDeadlineSeconds: 600 + replicas: {{ .Values.replicaCount }} + revisionHistoryLimit: 10 + selector: + matchLabels: + {{- include "app.matchLabels" . | indent 6 }} + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + type: RollingUpdate + template: + metadata: + labels: + {{- include "app.defaultLabels" . | indent 8 }} + component: storybook + spec: + {{- if (.Values.serviceAccount).create }} + serviceAccountName: {{ include "app.serviceAccountName" . }} + {{- end }} + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app.kubernetes.io/name: {{ .Release.Name }} + + containers: + - env: + {{- with include "app.getCustomList" . }} +{{ . | indent 8 }} + {{- end }} + + image: "{{ .Values.image.repo }}/{{ .Values.image.name }}:{{ coalesce (.Values.image).tag "latest" }}" + imagePullPolicy: Always + name: storybook + + startupProbe: + httpGet: + path: / + port: 8080 + scheme: HTTP + timeoutSeconds: 3 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 + readinessProbe: + httpGet: + path: / + port: 8080 + scheme: HTTP + timeoutSeconds: 3 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 + + resources: + {{- with .Values.resources }} +{{ toYaml . | indent 10 }} + {{- end }} + securityContext: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: false + runAsNonRoot: true + {{- with (.Values.securityContext).runAsUser }} + runAsUser: {{ . }} + {{- end }} + {{- with (.Values.securityContext).runAsGroup }} + runAsGroup: {{ . }} + {{- end }} + stdin: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + tty: true + + + dnsConfig: {} + dnsPolicy: ClusterFirst + imagePullSecrets: + - name: {{ required "imagePullSecret must be set" .Values.imagePullSecret }} + restartPolicy: Always + {{- with .Values.hostAliases }} + hostAliases: +{{ toYaml . | indent 8 }} + {{- end }} + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 diff --git a/alfa-client/libs/design-system/main/helm/templates/ingress.yaml b/alfa-client/libs/design-system/main/helm/templates/ingress.yaml new file mode 100644 index 0000000000000000000000000000000000000000..05882a4bd3278a2170632c8e70319e2011b1823f --- /dev/null +++ b/alfa-client/libs/design-system/main/helm/templates/ingress.yaml @@ -0,0 +1,39 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Release.Name }} + namespace: {{ include "app.namespace" . }} + annotations: + {{- if (.Values.ingress).certManagerAnnotations -}} + {{- range (.Values.ingress).certManagerAnnotations }} +{{ . | indent 4 }} + {{- end }} + {{- else if (.Values.ingress).use_staging_cert }} + cert-manager.io/cluster-issuer: letsencrypt-staging + {{- else }} + cert-manager.io/cluster-issuer: letsencrypt-prod + {{- end }} +spec: + {{- if and (.Values.ingress).className (ne (.Values).cluster_env "dataport") }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: storybook + port: + number: 8080 + + host: {{ include "app.baseDomain" . }} + tls: + - hosts: + - {{ include "app.baseDomain" . }} + {{- if (.Values.ingress).tlsSecretName }} + secretName: {{ (.Values.ingress).tlsSecretName }} + {{- else if ne (.Values).cluster_env "dataport" }} + secretName: {{ .Values.ozgcloud.bezeichner }}-{{ .Release.Name }}-tls + {{- end }} \ No newline at end of file diff --git a/alfa-client/libs/design-system/main/helm/templates/network_policy.yaml b/alfa-client/libs/design-system/main/helm/templates/network_policy.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9e281036f63a8e3bdaeb45223007d484a80af2c3 --- /dev/null +++ b/alfa-client/libs/design-system/main/helm/templates/network_policy.yaml @@ -0,0 +1,44 @@ +{{- if not (.Values.networkPolicy).disabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: network-policy-storybook + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + {{- include "app.matchLabels" . | indent 6 }} + policyTypes: + - Ingress + - Egress + ingress: + - ports: + - port: 8080 +{{- with (.Values.networkPolicy).additionalIngressConfigLocal }} +{{ toYaml . | indent 2 }} +{{- end }} +{{- with (.Values.networkPolicy).additionalIngressConfigGlobal }} +{{ toYaml . | indent 2 }} +{{- end }} + egress: + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: {{ required "networkPolicy.dnsServerNamespace must be set" (.Values.networkPolicy).dnsServerNamespace }} + ports: + - port: 53 + protocol: UDP + - port: 53 + protocol: TCP + - port: 5353 + protocol: UDP + - port: 5353 + protocol: TCP +{{- with (.Values.networkPolicy).additionalEgressConfigLocal }} +{{ toYaml . | indent 2 }} +{{- end }} +{{- with (.Values.networkPolicy).additionalEgressConfigGlobal }} +{{ toYaml . | indent 2 }} +{{- end }} + +{{- end }} \ No newline at end of file diff --git a/alfa-client/libs/design-system/main/helm/templates/service.yaml b/alfa-client/libs/design-system/main/helm/templates/service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a4a152fab1b685e2df235e8b067db58d5c5a995d --- /dev/null +++ b/alfa-client/libs/design-system/main/helm/templates/service.yaml @@ -0,0 +1,43 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }} + namespace: {{ include "app.namespace" . }} + labels: + {{- include "app.defaultLabels" . | indent 4 }} + component: storybook-service +spec: + type: ClusterIP + ports: + - name: http + port: 8080 + protocol: TCP + targetPort: 8080 + + selector: + {{- include "app.matchLabels" . | indent 4 }} + component: storybook \ No newline at end of file diff --git a/alfa-client/libs/design-system/main/helm/values.yaml b/alfa-client/libs/design-system/main/helm/values.yaml new file mode 100644 index 0000000000000000000000000000000000000000..68df0d24e6ecee39af5ae05d521a2f89dec67f22 --- /dev/null +++ b/alfa-client/libs/design-system/main/helm/values.yaml @@ -0,0 +1,6 @@ +image: + repo: docker.ozg-sh.de + name: storybook + tag: 0.1.0 # [default: latest] +replicaCount: 1 + diff --git a/alfa-client/libs/design-system/nginx.conf b/alfa-client/libs/design-system/nginx.conf new file mode 100644 index 0000000000000000000000000000000000000000..add779e6030ab68ebb8f5dde211d26f25e69a2e4 --- /dev/null +++ b/alfa-client/libs/design-system/nginx.conf @@ -0,0 +1,58 @@ +# Verarbeitungsreihenfolge von location rules: +# -------------------------------------------------------------------------------------------------------------------------------------------- +# Search-Order Modifier Description Match-Type Stops-search-on-match +# -------------------------------------------------------------------------------------------------------------------------------------------- +# 1st = The URI must match the specified pattern exactly Simple-string Yes +# 2nd ^~ The URI must begin with the specified pattern Simple-string Yes +# 3rd (None) The URI must begin with the specified pattern Simple-string No +# 4th ~ The URI must be a case-sensitive match to the specified Rx Perl-Compatible-Rx Yes (first match) +# 4th ~* The URI must be a case-insensitive match to the specified Rx Perl-Compatible-Rx Yes (first match) +# N/A @ Defines a named location block. Simple-string Yes +# -------------------------------------------------------------------------------------------------------------------------------------------- +# +# Regex Matches werden bevorzugt verwendet. +# Mehr: https://stackoverflow.com/a/59846239/1546181 +pid /tmp/nginx.pid; +worker_processes 1; + +events { + worker_connections 1024; +} + +http { + include mime.types; + server_tokens off; + access_log off; + error_log stderr crit; + + server { + listen 8080; + server_name localhost; + + + + location / { + root /usr/share/nginx/html/design-system; + index index.html index.htm; + } + + + + client_body_temp_path /tmp/client_temp; + proxy_temp_path /tmp/proxy_temp_path; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + + gzip on; + gzip_min_length 1000; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root html; + } + } +} diff --git a/alfa-client/libs/design-system/project.json b/alfa-client/libs/design-system/project.json index bcec7a688f84eefe1731c08cc4fa56e21e415975..bc681e80f5d762322adf47a62dba25464c0a7a0d 100644 --- a/alfa-client/libs/design-system/project.json +++ b/alfa-client/libs/design-system/project.json @@ -48,6 +48,21 @@ "quiet": true } } + }, + "container": { + "executor": "@nx-tools/nx-container:build", + "dependsOn": ["build"], + "options": { + "engine": "docker", + "push": false, + "metadata": { + "images": ["docker.ozg-sh.de/storybook"], + "load": true, + "tags": [ + "build-latest" + ] + } + } } } } diff --git a/alfa-client/libs/design-system/run_helm_test.sh b/alfa-client/libs/design-system/run_helm_test.sh new file mode 100755 index 0000000000000000000000000000000000000000..03f7485f84e6da1fd27bb3e24303172c4a09111c --- /dev/null +++ b/alfa-client/libs/design-system/run_helm_test.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -e +set -x + +helm template ./main/helm/ -f ./test/helm-linter-values.yaml +helm lint -f test/helm-linter-values.yaml ./main/helm/ +cd main/helm && helm unittest -f '../../test/helm/*.yaml' . + diff --git a/alfa-client/libs/design-system/src/index.ts b/alfa-client/libs/design-system/src/index.ts index 9f7b60a883c3ad40fc3bb29b3bd56d8a058d9197..57c220dc85cf06f66d1e23edd66bc7d9bff947ea 100644 --- a/alfa-client/libs/design-system/src/index.ts +++ b/alfa-client/libs/design-system/src/index.ts @@ -1,7 +1,8 @@ -export * from './lib/attachment-container/attachment-container.component'; +export * from './lib/attachment-header/attachment-header.component'; +export * from './lib/attachment-wrapper/attachment-wrapper.component'; export * from './lib/attachment/attachment.component'; -export * from './lib/bescheid-container/bescheid-container.component'; export * from './lib/bescheid-status-text/bescheid-status-text.component'; +export * from './lib/bescheid-wrapper/bescheid-wrapper.component'; export * from './lib/button-card/button-card.component'; export * from './lib/button/button.component'; export * from './lib/form/error-message/error-message.component'; @@ -15,6 +16,7 @@ export * from './lib/icons/bescheid-upload-icon/bescheid-upload-icon.component'; export * from './lib/icons/close-icon/close-icon.component'; export * from './lib/icons/exclamation-icon/exclamation-icon.component'; export * from './lib/icons/file-icon/file-icon.component'; +export * from './lib/icons/iconVariants'; export * from './lib/icons/save-icon/save-icon.component'; export * from './lib/icons/send-icon/send-icon.component'; export * from './lib/icons/spinner-icon/spinner-icon.component'; diff --git a/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.component.spec.ts b/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.component.spec.ts deleted file mode 100644 index 5c799383d81b51dd6df2350ed794ed09c999df1f..0000000000000000000000000000000000000000 --- a/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.component.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { AttachmentContainerComponent } from './attachment-container.component'; - -describe('AttachmentContainerComponent', () => { - let component: AttachmentContainerComponent; - let fixture: ComponentFixture<AttachmentContainerComponent>; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [AttachmentContainerComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(AttachmentContainerComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.component.ts b/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.component.ts deleted file mode 100644 index 35de705fee041ebb34196879393dffcaecb76f26..0000000000000000000000000000000000000000 --- a/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.component.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; - -@Component({ - selector: 'ods-attachment-container', - standalone: true, - imports: [CommonModule], - template: `<div - class="border-grayborder block overflow-hidden rounded-md border shadow empty:hidden" - > - <ng-content></ng-content> - </div>`, -}) -export class AttachmentContainerComponent {} diff --git a/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.stories.ts b/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.stories.ts deleted file mode 100644 index dbb89342b65d8059a7eb55cce494d2ec53dac8f6..0000000000000000000000000000000000000000 --- a/alfa-client/libs/design-system/src/lib/attachment-container/attachment-container.stories.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { moduleMetadata, type Meta, type StoryObj } from '@storybook/angular'; - -import { AttachmentComponent } from '../attachment/attachment.component'; -import { AttachmentContainerComponent } from './attachment-container.component'; - -const meta: Meta<AttachmentContainerComponent> = { - title: 'Containers/Attachment container', - component: AttachmentContainerComponent, - parameters: { - docs: { - description: { - component: 'Container for multiple attachments', - }, - }, - }, - decorators: [ - moduleMetadata({ - imports: [AttachmentContainerComponent, AttachmentComponent], - }), - ], - excludeStories: /.*Data$/, - tags: ['autodocs'], -}; - -export default meta; -type Story = StoryObj<AttachmentContainerComponent>; - -export const Default: Story = { - render: () => ({ - template: `<ods-attachment-container> - <ods-attachment caption="Attachment" description="200 kB" fileType="pdf"></ods-attachment> - <ods-attachment caption="Second attachment" description="432 kB" fileType="doc"></ods-attachment> - </ods-attachment-container>`, - }), -}; diff --git a/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.component.spec.ts b/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..b1484d6418804db087a5418a8eb26a8e41114398 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AttachmentHeaderComponent } from './attachment-header.component'; + +describe('AttachmentHeaderComponent', () => { + let component: AttachmentHeaderComponent; + let fixture: ComponentFixture<AttachmentHeaderComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AttachmentHeaderComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(AttachmentHeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.component.ts b/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..dadeccb218137f87612c2d67a12c7087a6286927 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.component.ts @@ -0,0 +1,15 @@ +import { CommonModule } from '@angular/common'; +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'ods-attachment-header', + standalone: true, + imports: [CommonModule], + template: `<div class="flex h-11 items-center justify-between px-3"> + <h4 class="text-sm font-medium text-text">{{ title }}</h4> + <ng-content select="[action-buttons]"></ng-content> + </div>`, +}) +export class AttachmentHeaderComponent { + @Input() title: string = ''; +} diff --git a/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.stories.ts b/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.stories.ts new file mode 100644 index 0000000000000000000000000000000000000000..33a3d93237c69b82a28b08efaf36cc92f5970c5e --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/attachment-header/attachment-header.stories.ts @@ -0,0 +1,45 @@ +import { argsToTemplate, moduleMetadata, type Meta, type StoryObj } from '@storybook/angular'; + +import { DownloadButtonComponent } from '../../../../design-component/src/lib/download-button/download-button.component'; + +import { AttachmentComponent } from '../attachment/attachment.component'; +import { AttachmentHeaderComponent } from './attachment-header.component'; + +const meta: Meta<AttachmentHeaderComponent> = { + title: 'Attachment header', + component: AttachmentHeaderComponent, + parameters: { + docs: { + description: { + component: 'Header may contain title and/or buttons', + }, + }, + }, + decorators: [ + moduleMetadata({ + imports: [AttachmentHeaderComponent, AttachmentComponent, DownloadButtonComponent], + }), + ], + excludeStories: /.*Data$/, + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj<AttachmentHeaderComponent>; + +export const Default: Story = { + args: { + title: 'Anhänge', + }, + argTypes: { + title: { + description: 'Title for group of files', + }, + }, + render: (args) => ({ + props: args, + template: `<ods-attachment-header ${argsToTemplate(args)}> + <ods-download-button action-buttons /> + </ods-attachment-header>`, + }), +}; diff --git a/alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-container.component.spec.ts b/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.component.spec.ts similarity index 50% rename from alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-container.component.spec.ts rename to alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.component.spec.ts index 32b8fc6eadf7d114d8730894bd5d666e33b7bc81..bc771a072c51f229dd9c2d68262a32a2f3db915b 100644 --- a/alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-container.component.spec.ts +++ b/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.component.spec.ts @@ -1,16 +1,16 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { BescheidContainerComponent } from './bescheid-container.component'; +import { AttachmentWrapperComponent } from './attachment-wrapper.component'; -describe('BescheidContainerComponent', () => { - let component: BescheidContainerComponent; - let fixture: ComponentFixture<BescheidContainerComponent>; +describe('AttachmentWrapperComponent', () => { + let component: AttachmentWrapperComponent; + let fixture: ComponentFixture<AttachmentWrapperComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [BescheidContainerComponent], + imports: [AttachmentWrapperComponent], }).compileComponents(); - fixture = TestBed.createComponent(BescheidContainerComponent); + fixture = TestBed.createComponent(AttachmentWrapperComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.component.ts b/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..863cc92dffee0d1f7a1e2fd58636f40a6a39f1b5 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.component.ts @@ -0,0 +1,13 @@ +import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; + +@Component({ + selector: 'ods-attachment-wrapper', + standalone: true, + imports: [CommonModule], + styles: [ + ':host {@apply flex flex-col overflow-hidden rounded-md shadow shadow-grayborder border border-grayborder empty:hidden}', + ], + template: `<ng-content></ng-content>`, +}) +export class AttachmentWrapperComponent {} diff --git a/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.stories.ts b/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.stories.ts new file mode 100644 index 0000000000000000000000000000000000000000..485acda2600b59166e331fe38bffba7392c1e783 --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/attachment-wrapper/attachment-wrapper.stories.ts @@ -0,0 +1,47 @@ +import { argsToTemplate, moduleMetadata, type Meta, type StoryObj } from '@storybook/angular'; + +import { DownloadButtonComponent } from '../../../../design-component/src/lib/download-button/download-button.component'; + +import { AttachmentComponent } from '../attachment/attachment.component'; +import { AttachmentWrapperComponent } from './attachment-wrapper.component'; + +const meta: Meta<AttachmentWrapperComponent> = { + title: 'Wrappers/Attachment wrapper', + component: AttachmentWrapperComponent, + parameters: { + docs: { + description: { + component: 'Wrapper for multiple attachments', + }, + }, + }, + decorators: [ + moduleMetadata({ + imports: [AttachmentWrapperComponent, AttachmentComponent, DownloadButtonComponent], + }), + ], + excludeStories: /.*Data$/, + tags: ['autodocs'], +}; + +export default meta; +type Story = StoryObj<AttachmentWrapperComponent>; + +export const Default: Story = { + args: { + title: 'Anhänge', + }, + argTypes: { + title: { + description: 'Title for group of files', + }, + }, + render: (args) => ({ + props: args, + template: `<ods-attachment-wrapper ${argsToTemplate(args)}> + <ods-download-button action-buttons /> + <ods-attachment caption="Attachment" description="200 kB" fileType="pdf"></ods-attachment> + <ods-attachment caption="Second attachment" description="432 kB" fileType="doc"></ods-attachment> + </ods-attachment-wrapper>`, + }), +}; diff --git a/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.component.spec.ts b/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..741c0bb800a3c711eb0771d36c00fa55feb38dee --- /dev/null +++ b/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BescheidWrapperComponent } from './bescheid-wrapper.component'; + +describe('BescheidWrapperComponent', () => { + let component: BescheidWrapperComponent; + let fixture: ComponentFixture<BescheidWrapperComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BescheidWrapperComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(BescheidWrapperComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-container.component.ts b/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.component.ts similarity index 54% rename from alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-container.component.ts rename to alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.component.ts index 16f18a8cc4c562567f48be52c279bba2aa139ac3..62f2564416b0855318c87c4fa3b0c2b2b9b47912 100644 --- a/alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-container.component.ts +++ b/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.component.ts @@ -2,11 +2,11 @@ import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; @Component({ - selector: 'ods-bescheid-container', + selector: 'ods-bescheid-wrapper', standalone: true, imports: [CommonModule], - template: ` <article class="bg-background-150 flex flex-col gap-4 rounded-lg p-4"> + template: ` <article class="flex flex-col gap-4 rounded-lg bg-background-150 p-4"> <ng-content></ng-content> </article>`, }) -export class BescheidContainerComponent {} +export class BescheidWrapperComponent {} diff --git a/alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-comtainer.stories.ts b/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.stories.ts similarity index 60% rename from alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-comtainer.stories.ts rename to alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.stories.ts index b0fc0da094793d9e46009e818b7f365a0f4f72ff..2d3a4ee07f081623052bb6b16e76cbb42dcd8c40 100644 --- a/alfa-client/libs/design-system/src/lib/bescheid-container/bescheid-comtainer.stories.ts +++ b/alfa-client/libs/design-system/src/lib/bescheid-wrapper/bescheid-wrapper.stories.ts @@ -1,17 +1,17 @@ import { moduleMetadata, type Meta, type StoryObj } from '@storybook/angular'; -import { AttachmentContainerComponent } from '../attachment-container/attachment-container.component'; +import { AttachmentWrapperComponent } from '../attachment-wrapper/attachment-wrapper.component'; import { AttachmentComponent } from '../attachment/attachment.component'; import { BescheidStatusTextComponent } from '../bescheid-status-text/bescheid-status-text.component'; -import { BescheidContainerComponent } from './bescheid-container.component'; +import { BescheidWrapperComponent } from './bescheid-wrapper.component'; -const meta: Meta<BescheidContainerComponent> = { - title: 'Containers/Bescheid container', - component: BescheidContainerComponent, +const meta: Meta<BescheidWrapperComponent> = { + title: 'Wrappers/Bescheid wrapper', + component: BescheidWrapperComponent, parameters: { docs: { description: { - component: 'Container for bescheid information', + component: 'Wrapper for bescheid information', }, }, }, @@ -19,8 +19,8 @@ const meta: Meta<BescheidContainerComponent> = { moduleMetadata({ imports: [ AttachmentComponent, - AttachmentContainerComponent, - BescheidContainerComponent, + AttachmentWrapperComponent, + BescheidWrapperComponent, BescheidStatusTextComponent, ], }), @@ -30,20 +30,20 @@ const meta: Meta<BescheidContainerComponent> = { }; export default meta; -type Story = StoryObj<BescheidContainerComponent>; +type Story = StoryObj<BescheidWrapperComponent>; export const Default: Story = { render: () => ({ - template: `<ods-bescheid-container> + template: `<ods-bescheid-wrapper> <ods-bescheid-status-text bewilligt="true" dateText="13.11.2024" [hasBescheidDraft]="false" ></ods-bescheid-status-text> - <ods-attachment-container> + <ods-attachment-wrapper> <ods-attachment caption="Attachment" description="200 kB" fileType="pdf"></ods-attachment> <ods-attachment caption="Second attachment" description="432 kB" fileType="doc"></ods-attachment> - </ods-attachment-container> - </ods-bescheid-container>`, + </ods-attachment-wrapper> + </ods-bescheid-wrapper>`, }), }; diff --git a/alfa-client/libs/design-system/src/lib/button/button.component.ts b/alfa-client/libs/design-system/src/lib/button/button.component.ts index 19d22368fd3dd405970df6c7a77790783ee19d77..58ba7ec0d96c8c0eb0516c9fbc367b885d686837 100644 --- a/alfa-client/libs/design-system/src/lib/button/button.component.ts +++ b/alfa-client/libs/design-system/src/lib/button/button.component.ts @@ -2,19 +2,22 @@ import { CommonModule } from '@angular/common'; import { Component, EventEmitter, Input, Output } from '@angular/core'; import { VariantProps, cva } from 'class-variance-authority'; +import { IconVariants } from '../icons/iconVariants'; import { SpinnerIconComponent } from '../icons/spinner-icon/spinner-icon.component'; export const buttonVariants = cva( - 'flex cursor-pointer items-center gap-4 rounded-md font-medium disabled:cursor-wait text-sm min-w-32 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ozgblue-800', + 'flex cursor-pointer items-center gap-4 rounded-md font-medium disabled:cursor-wait text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary', { variants: { variant: { primary: 'hover:enabled:bg-primary-hover bg-primary text-white shadow-sm', outline: 'border border-primary bg-background-50 text-primary hover:enabled:bg-background-100', + icon: 'border border-transparent hover:border-primary', }, size: { - medium: 'h-9 py-2 px-4', + medium: 'h-9 py-2 px-4 min-w-32', + fit: 'h-fit p-2', }, }, defaultVariants: { @@ -39,8 +42,8 @@ type ButtonVariants = VariantProps<typeof buttonVariants>; (click)="clickEmitter.emit()" > <ng-content *ngIf="!isLoading" select="[icon]"></ng-content> - <ods-spinner-icon *ngIf="isLoading" size="medium"></ods-spinner-icon> - <div class="flex-grow">{{ text }}</div> + <ods-spinner-icon *ngIf="isLoading" [size]="spinnerSize"></ods-spinner-icon> + <div *ngIf="text" class="flex-grow">{{ text }}</div> </button>`, }) export class ButtonComponent { @@ -49,6 +52,7 @@ export class ButtonComponent { @Input() isLoading: boolean = false; @Input() variant: ButtonVariants['variant']; @Input() size: ButtonVariants['size']; + @Input() spinnerSize: IconVariants['size'] = 'medium'; @Output() public clickEmitter: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>(); diff --git a/alfa-client/libs/design-system/src/lib/button/button.stories.ts b/alfa-client/libs/design-system/src/lib/button/button.stories.ts index 5db5e2e20ad61881bf767c822890d900898544ea..c5ac6ce8b3312deac5a00ce17af2a73f073565e6 100644 --- a/alfa-client/libs/design-system/src/lib/button/button.stories.ts +++ b/alfa-client/libs/design-system/src/lib/button/button.stories.ts @@ -1,6 +1,6 @@ import { argsToTemplate, moduleMetadata, type Meta, type StoryObj } from '@storybook/angular'; -import { FileIconComponent } from '../icons/file-icon/file-icon.component'; +import { SaveIconComponent } from '../icons/save-icon/save-icon.component'; import { ButtonComponent } from './button.component'; const meta: Meta<ButtonComponent> = { @@ -8,7 +8,7 @@ const meta: Meta<ButtonComponent> = { component: ButtonComponent, decorators: [ moduleMetadata({ - imports: [ButtonComponent, FileIconComponent], + imports: [ButtonComponent, SaveIconComponent], }), ], excludeStories: /.*Data$/, @@ -23,6 +23,8 @@ export const Default: Story = { text: 'Hello world!', isLoading: false, variant: 'primary', + size: 'medium', + spinnerSize: 'medium', }, argTypes: { variant: { @@ -37,11 +39,12 @@ export const WithIcon: Story = { args: { text: 'I have an icon', variant: 'outline', + size: 'medium', }, render: (args) => ({ props: args, template: `<ods-button ${argsToTemplate(args)}> - <ods-file-icon icon fileType='pdf' size='small'></ods-file-icon> + <ods-save-icon icon size='small'></ods-save-icon> </ods-button>`, }), }; @@ -50,5 +53,19 @@ export const IsLoading: Story = { args: { text: 'Loading...', isLoading: true, + size: 'medium', }, }; + +export const OnlyIcon: Story = { + args: { + variant: 'icon', + size: 'fit', + }, + render: (args) => ({ + props: args, + template: `<ods-button ${argsToTemplate(args)}> + <ods-save-icon icon size='medium'></ods-save-icon> + </ods-button>`, + }), +}; diff --git a/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts b/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts index 87770f419954a3c32d59424b0aab2a6a6c05a96b..e136a50bb991af6df9384d7ceca287879b3cdcc8 100644 --- a/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts +++ b/alfa-client/libs/design-system/src/lib/form/text-input/text-input.component.ts @@ -6,12 +6,14 @@ import { cva, VariantProps } from 'class-variance-authority'; import { ErrorMessageComponent } from '../error-message/error-message.component'; const textInputVariants = cva( - 'block w-full rounded-lg border bg-background-50 px-3 py-2 text-base leading-5 text-text focus:border-primary focus:ring-primary outline-none', + 'block w-full rounded-lg border bg-background-50 px-3 py-2 text-base leading-5 text-text focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2', { variants: { variant: { - default: 'border-primary-600/50', - error: 'border-error', + default: + 'border-primary-600/50 focus-visible:outline-focus focus-visible:border-background-200 hover:border-primary-hover', + error: + 'border-error/50 hover:border-error focus-visible:outline-error focus-visible:border-background-200', }, }, defaultVariants: { @@ -27,9 +29,8 @@ type TextInputVariants = VariantProps<typeof textInputVariants>; imports: [CommonModule, ErrorMessageComponent, ReactiveFormsModule, TechSharedModule], template: ` <div> - <label [for]="id" class="text-md mb-2 block font-medium text-text" - >{{ label }} - <ng-container *ngIf="required"><i aria-hidden="true">*</i></ng-container> + <label [for]="id" class="text-md mb-2 block font-medium text-text"> + {{ inputLabel }}<ng-container *ngIf="required"><i aria-hidden="true">*</i></ng-container> </label> <div class="mt-2"> <input @@ -39,9 +40,9 @@ type TextInputVariants = VariantProps<typeof textInputVariants>; [ngClass]="textInputVariants({ variant })" [placeholder]="placeholder" [autocomplete]="autocomplete" - [required]="required" + [attr.aria-required]="required" [attr.aria-invalid]="variant === 'error'" - [attr.data-test-id]="(label | convertForDataTest) + '-text-input'" + [attr.data-test-id]="(inputLabel | convertForDataTest) + '-text-input'" #inputElement /> </div> @@ -52,7 +53,10 @@ type TextInputVariants = VariantProps<typeof textInputVariants>; export class TextInputComponent { @ViewChild('inputElement') inputElement: ElementRef; - @Input({ required: true }) label: string; + @Input({ required: true }) set label(label: string) { + this.inputLabel = label; + this.id = convertForDataTest(label); + } @Input() placeholder: string = ''; @Input() autocomplete: string = 'off'; @Input() variant: TextInputVariants['variant']; @@ -65,9 +69,7 @@ export class TextInputComponent { } } + inputLabel: string; + id: string; textInputVariants = textInputVariants; - - get id(): string { - return convertForDataTest(this.label); - } } diff --git a/alfa-client/libs/design-system/src/lib/form/textarea/textarea.component.ts b/alfa-client/libs/design-system/src/lib/form/textarea/textarea.component.ts index 21e6c863afcc41c876cdfd0fa45704770ce39d56..90a4fa85f7dcb0b5a55f6a926a0d08ab63e332a9 100644 --- a/alfa-client/libs/design-system/src/lib/form/textarea/textarea.component.ts +++ b/alfa-client/libs/design-system/src/lib/form/textarea/textarea.component.ts @@ -1,16 +1,18 @@ -import { convertForDataTest, TechSharedModule } from '@alfa-client/tech-shared'; +import { TechSharedModule, convertForDataTest } from '@alfa-client/tech-shared'; import { CommonModule } from '@angular/common'; import { Component, ElementRef, Input, ViewChild } from '@angular/core'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; -import { cva, VariantProps } from 'class-variance-authority'; +import { VariantProps, cva } from 'class-variance-authority'; const textareaVariants = cva( - 'block w-full rounded-lg border bg-background-50 px-3 py-2 text-base text-text leading-5 focus:border-primary focus:ring-primary outline-none', + 'block w-full rounded-lg border bg-background-50 px-3 py-2 text-base text-text leading-5 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2', { variants: { variant: { - default: 'border-primary-600/50', - error: 'border-error', + default: + 'border-primary-600/50 focus-visible:outline-focus focus-visible:border-background-200 hover:border-primary-hover', + error: + 'border-error/50 hover:border-error focus-visible:outline-error focus-visible:border-background-200', }, }, defaultVariants: { @@ -26,9 +28,8 @@ type TextareaVariants = VariantProps<typeof textareaVariants>; imports: [CommonModule, ReactiveFormsModule, TechSharedModule], template: ` <div class="mt-2"> - <label [for]="id" class="text-md mb-2 block font-medium text-text" - >{{ label }} - <ng-container *ngIf="required"><i aria-hidden="true">*</i></ng-container> + <label [for]="id" class="text-md mb-2 block font-medium text-text"> + {{ inputLabel }}<ng-container *ngIf="required"><i aria-hidden="true">*</i></ng-container> </label> <textarea [id]="id" @@ -37,9 +38,9 @@ type TextareaVariants = VariantProps<typeof textareaVariants>; [ngClass]="textareaVariants({ variant })" [placeholder]="placeholder" [autocomplete]="autocomplete" - [required]="required" + [attr.aria-required]="required" [attr.aria-invalid]="variant === 'error'" - [attr.data-test-id]="(label | convertForDataTest) + '-textarea'" + [attr.data-test-id]="(inputLabel | convertForDataTest) + '-textarea'" #textAreaElement ></textarea> <ng-content select="[error]"></ng-content> @@ -49,7 +50,10 @@ type TextareaVariants = VariantProps<typeof textareaVariants>; export class TextareaComponent { @ViewChild('textAreaElement') textAreaElement: ElementRef; - @Input({ required: true }) label!: string; + @Input({ required: true }) set label(label: string) { + this.inputLabel = label; + this.id = convertForDataTest(label); + } @Input({ required: true }) placeholder!: string; @Input() error: string; @Input() rows: number = 3; @@ -64,9 +68,7 @@ export class TextareaComponent { } } + inputLabel: string; + id: string; textareaVariants = textareaVariants; - - get id(): string { - return convertForDataTest(this.label); - } } diff --git a/alfa-client/libs/design-system/src/lib/icons/attachment-icon/attachment-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/attachment-icon/attachment-icon.component.ts index dea522781c4adc45ddfa582fd86fa7f52f5774f5..30a34056cee8da2a6e01385790c7c8e48cee4431 100644 --- a/alfa-client/libs/design-system/src/lib/icons/attachment-icon/attachment-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/attachment-icon/attachment-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-attachment-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/bescheid-generate-icon/bescheid-generate-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/bescheid-generate-icon/bescheid-generate-icon.component.ts index 3a7f86c81501c7dc5722b5f868bf1c12fa05c615..d9b829b2269a676de6cf15fed9e50e7cdd9081fa 100644 --- a/alfa-client/libs/design-system/src/lib/icons/bescheid-generate-icon/bescheid-generate-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/bescheid-generate-icon/bescheid-generate-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-bescheid-generate-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/bescheid-upload-icon/bescheid-upload-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/bescheid-upload-icon/bescheid-upload-icon.component.ts index 3947b891b5f3d07cd29fd9a6c0f3a2bb9c2f6089..cc265fdbb039365e1eeae63fc69b339578493eba 100644 --- a/alfa-client/libs/design-system/src/lib/icons/bescheid-upload-icon/bescheid-upload-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/bescheid-upload-icon/bescheid-upload-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-bescheid-upload-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/close-icon/close-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/close-icon/close-icon.component.ts index 81c6ce8e91c4ab7fab1ef6e4e9f0bcf6dfc8f7c2..5de688d73e98d67b1296d1519332fd7e3b7c9660 100644 --- a/alfa-client/libs/design-system/src/lib/icons/close-icon/close-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/close-icon/close-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-close-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/exclamation-icon/exclamation-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/exclamation-icon/exclamation-icon.component.ts index 9dda95ea08ca28bee431be67e9400e6ccaa6f0ec..c425c77aaa40b0efa0d0e2dd5b14decbad216f5e 100644 --- a/alfa-client/libs/design-system/src/lib/icons/exclamation-icon/exclamation-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/exclamation-icon/exclamation-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-exclamation-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/file-icon/file-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/file-icon/file-icon.component.ts index 544495d4fab1c914f1126ffa182911cdb23d7a10..e387e2d121ea54836748f7ee9d04ba3923ad1f56 100644 --- a/alfa-client/libs/design-system/src/lib/icons/file-icon/file-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/file-icon/file-icon.component.ts @@ -4,7 +4,7 @@ import { VariantProps, cva } from 'class-variance-authority'; import { SpinnerIconComponent } from '../spinner-icon/spinner-icon.component'; -const fileiconVariants = cva('', { +const fileiconVariants = cva('fill-ozggray-300', { variants: { fileType: { doc: 'fill-doc', diff --git a/alfa-client/libs/design-system/src/lib/icons/IconClasses.ts b/alfa-client/libs/design-system/src/lib/icons/iconVariants.ts similarity index 100% rename from alfa-client/libs/design-system/src/lib/icons/IconClasses.ts rename to alfa-client/libs/design-system/src/lib/icons/iconVariants.ts diff --git a/alfa-client/libs/design-system/src/lib/icons/save-icon/save-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/save-icon/save-icon.component.ts index 405d1199cd3837498a19164e319107b1b5a706d7..b2d22480f7100aefc09ed77c3d2d499eebef01bf 100644 --- a/alfa-client/libs/design-system/src/lib/icons/save-icon/save-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/save-icon/save-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-save-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/send-icon/send-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/send-icon/send-icon.component.ts index 60205dd2942b8c0a47fd75f9ffe699a731c33061..6a34bb7ebb5b3b019a8254bf2f723eb93ac2b58e 100644 --- a/alfa-client/libs/design-system/src/lib/icons/send-icon/send-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/send-icon/send-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-send-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/spinner-icon/spinner-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/spinner-icon/spinner-icon.component.ts index 05aaf8d9aab8a37f8caa3f078f47b9dab1e96117..321829613a72fd018fbadd3b9117c3f16e119cc8 100644 --- a/alfa-client/libs/design-system/src/lib/icons/spinner-icon/spinner-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/spinner-icon/spinner-icon.component.ts @@ -1,7 +1,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-spinner-icon', diff --git a/alfa-client/libs/design-system/src/lib/icons/stamp-icon/stamp-icon.component.ts b/alfa-client/libs/design-system/src/lib/icons/stamp-icon/stamp-icon.component.ts index 7b71c7b5d4dc15511d67441582c2539c7abea06b..26bb89c266ab88212c87bf6b0beba4874843fb2c 100644 --- a/alfa-client/libs/design-system/src/lib/icons/stamp-icon/stamp-icon.component.ts +++ b/alfa-client/libs/design-system/src/lib/icons/stamp-icon/stamp-icon.component.ts @@ -2,7 +2,7 @@ import { NgClass } from '@angular/common'; import { Component, Input } from '@angular/core'; import { twMerge } from 'tailwind-merge'; -import { IconVariants, iconVariants } from '../IconClasses'; +import { IconVariants, iconVariants } from '../iconVariants'; @Component({ selector: 'ods-stamp-icon', diff --git a/alfa-client/libs/design-system/src/lib/tailwind-preset/root.css b/alfa-client/libs/design-system/src/lib/tailwind-preset/root.css index ef516cdcbe6c4023552a81647ff66826ae0c9ab9..7e997ffe4f9bfb19811dc99c99a708dd8d92e741 100644 --- a/alfa-client/libs/design-system/src/lib/tailwind-preset/root.css +++ b/alfa-client/libs/design-system/src/lib/tailwind-preset/root.css @@ -7,6 +7,7 @@ :root { --warning: 38 92% 50%; --color-error: 0 71% 49%; + --color-focus: 212 64% 47%; --color-background-secondary: 0 0% 98%; --color-mainbg: 0 0% 100%; @@ -20,7 +21,7 @@ --color-background-150: 0 0% 95%; --color-background-200: 0 0% 90%; - --color-grayborder: 0 0% 75%; + --color-grayborder: 0 0% 92%; --color-bewilligt-100: 122 100% 92%; --color-bewilligt-700: 122 100% 29%; @@ -36,6 +37,7 @@ .dark { --color-error: 0 99% 72%; + --color-focus: 43 100% 48%; --color-background-secondary: 0 0% 16%; --color-mainbg: 0 0% 14%; diff --git a/alfa-client/libs/design-system/src/lib/tailwind-preset/tailwind.config.js b/alfa-client/libs/design-system/src/lib/tailwind-preset/tailwind.config.js index a207c9b06a2b19caa92f6abda9a783ae20ea61cd..1c7b298d5afa17aae60ab57b36b9152cec514121 100644 --- a/alfa-client/libs/design-system/src/lib/tailwind-preset/tailwind.config.js +++ b/alfa-client/libs/design-system/src/lib/tailwind-preset/tailwind.config.js @@ -98,6 +98,7 @@ module.exports = { whitetext: 'hsl(var(--color-text-white) / <alpha-value>)', warning: 'hsl(var(--warning))', error: 'hsl(var(--color-error))', + focus: 'hsl(var(--color-focus))', }, }, }, diff --git a/alfa-client/libs/design-system/test/helm-linter-values.yaml b/alfa-client/libs/design-system/test/helm-linter-values.yaml new file mode 100644 index 0000000000000000000000000000000000000000..88a8eb17a808af1c3314ac178a0db55672280ae6 --- /dev/null +++ b/alfa-client/libs/design-system/test/helm-linter-values.yaml @@ -0,0 +1,36 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +baseUrl: test.sh.ozg-cloud.de + +ozgcloud: + bundesland: sh + bezeichner: helm + environment: test + +imagePullSecret: test-image-secret + +networkPolicy: + dnsServerNamespace: dummy-dns + diff --git a/alfa-client/libs/design-system/test/helm/deployment_63_char_test.yaml b/alfa-client/libs/design-system/test/helm/deployment_63_char_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..09d03963a7eeb081a86cc2e28590feaf82c7385f --- /dev/null +++ b/alfa-client/libs/design-system/test/helm/deployment_63_char_test.yaml @@ -0,0 +1,53 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: test deployment less than 63 chars +release: + name: storybook + namespace: sh-helm-test + +templates: + - templates/deployment.yaml +set: + imagePullSecret: test-image-secret + +tests: + - it: should fail on .Release.Namespace length longer than 63 characters + release: + namespace: test1234567890123123456789012345678901234567890123456789012345678901234567890123456789012345678904567890 + asserts: + - failedTemplate: + errorMessage: .Release.Namespace test1234567890123123456789012345678901234567890123456789012345678901234567890123456789012345678904567890 ist zu lang (max. 63 Zeichen) + - it: should not fail on .Release.Namespace length less than 63 characters + asserts: + - notFailedTemplate: {} + - it: should fail on .Chart.Name-.Chart.Version length longer than 63 characters + chart: + version: 1.0-test1234567890123123456789012345678901234567890123456789012345678901234567890123456789012345678904567890 + asserts: + - failedTemplate: + errorMessage: .Chart.Name-.Chart.Version storybook-1.0-test1234567890123123456789012345678901234567890123456789012345678901234567890123456789012345678904567890 ist zu lang (max. 63 Zeichen) + - it: should not fail on .Chart.Name-.Chart.Version length less than 63 characters + asserts: + - notFailedTemplate: {} \ No newline at end of file diff --git a/alfa-client/libs/design-system/test/helm/deployment_env_test.yaml b/alfa-client/libs/design-system/test/helm/deployment_env_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..006e6c299885077afddf976b14b59bb0718dbd9a --- /dev/null +++ b/alfa-client/libs/design-system/test/helm/deployment_env_test.yaml @@ -0,0 +1,70 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: test deployment container environments +templates: + - templates/deployment.yaml +set: + imagePullSecret: test-image-secret + +tests: + - it: check customList as list + set: + env.customList: + - name: my_test_environment_name + value: "A test value" + - name: test_environment + value: "B test value" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: my_test_environment_name + value: "A test value" + - contains: + path: spec.template.spec.containers[0].env + content: + name: test_environment + value: "B test value" + - it: check customList as dict + set: + env.customList: + my_test_environment_name: "A test value" + test_environment: "B test value" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: my_test_environment_name + value: "A test value" + - contains: + path: spec.template.spec.containers[0].env + content: + name: test_environment + value: "B test value" + + - it: no env exist by default + asserts: + - isEmpty: + path: spec.template.spec.containers[0].env diff --git a/alfa-client/libs/design-system/test/helm/deployment_host_aliases_test.yaml b/alfa-client/libs/design-system/test/helm/deployment_host_aliases_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2df27950bdc08c277a14b952b82ebb96cb08e9b3 --- /dev/null +++ b/alfa-client/libs/design-system/test/helm/deployment_host_aliases_test.yaml @@ -0,0 +1,52 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: deployment host aliases +release: + name: storybook + namespace: sh-helm-test +templates: + - templates/deployment.yaml +set: + imagePullSecret: test-image-secret +tests: + - it: should not set spec.template.spec.hostAliases + asserts: + - isNull: + path: spec.template.spec.hostAliases + - it: should set spec.template.spec.hostAliases + set: + hostAliases: + - ip: "127.0.0.1" + hostname: + - "eins" + - "zwei" + asserts: + - contains: + path: spec.template.spec.hostAliases + content: + ip: "127.0.0.1" + hostname: + - "eins" + - "zwei" diff --git a/alfa-client/libs/design-system/test/helm/deployment_ozgcloud_base_values_test.yaml b/alfa-client/libs/design-system/test/helm/deployment_ozgcloud_base_values_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8c4e08d29e5cd804dc358818f84be4b36caae501 --- /dev/null +++ b/alfa-client/libs/design-system/test/helm/deployment_ozgcloud_base_values_test.yaml @@ -0,0 +1,47 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: test ozgcloud base values +release: + name: storybook + namespace: sh-helm-test +templates: + - templates/deployment.yaml + + +tests: + - it: should fail on missing imagePullSecret + set: + ozgcloud: + environment: dev + asserts: + - failedTemplate: + errorMessage: imagePullSecret must be set + - it: should not fail on given environment + set: + ozgcloud: + environment: dev + imagePullSecret: test-image-secret + asserts: + - notFailedTemplate: {} diff --git a/alfa-client/libs/design-system/test/helm/deployment_resources_test.yaml b/alfa-client/libs/design-system/test/helm/deployment_resources_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c1823ac3b5de1ccb782f5cf89d76853def332c98 --- /dev/null +++ b/alfa-client/libs/design-system/test/helm/deployment_resources_test.yaml @@ -0,0 +1,60 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: test deployment container resources +release: + name: storybook +templates: + - templates/deployment.yaml +set: + imagePullSecret: test-image-secret + +tests: + - it: should generate resources when values set + set: + resources: + limits: + cpu: 11m + memory: 22Mi + requests: + cpu: 33m + memory: 44Mi + asserts: + - equal: + path: spec.template.spec.containers[0].resources.limits.cpu + value: 11m + - equal: + path: spec.template.spec.containers[0].resources.limits.memory + value: 22Mi + - equal: + path: spec.template.spec.containers[0].resources.requests.cpu + value: 33m + - equal: + path: spec.template.spec.containers[0].resources.requests.memory + value: 44Mi + - it: should not generate resources when values not set + asserts: + - isEmpty: + path: spec.template.spec.containers[0].resources + diff --git a/alfa-client/libs/design-system/test/helm/deployment_test.yaml b/alfa-client/libs/design-system/test/helm/deployment_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1c76b61107630a97e9d5f69cb4f52b5aed907bcb --- /dev/null +++ b/alfa-client/libs/design-system/test/helm/deployment_test.yaml @@ -0,0 +1,250 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: deployment_test +release: + name: storybook + namespace: storybook +templates: + - templates/deployment.yaml +set: + imagePullSecret: image-pull-secret +tests: + - it: validate deployment root configuration + asserts: + - isAPIVersion: + of: apps/v1 + - isKind: + of: Deployment + - it: validate metadata + asserts: + - equal: + path: metadata.name + value: storybook + - equal: + path: metadata.namespace + value: storybook + - equal: + path: metadata.labels["app.kubernetes.io/instance"] + value: storybook + - equal: + path: metadata.labels["app.kubernetes.io/name"] + value: storybook + - equal: + path: metadata.labels["app.kubernetes.io/namespace"] + value: storybook + - equal: + path: metadata.labels["app.kubernetes.io/part-of"] + value: ozgcloud + - equal: + path: metadata.labels["app.kubernetes.io/version"] + value: 0.0.0-MANAGED-BY-JENKINS + - equal: + path: metadata.labels["helm.sh/chart"] + value: storybook-0.0.0-MANAGED-BY-JENKINS + - it: validate spec root configuration + asserts: + - equal: + path: spec.progressDeadlineSeconds + value: 600 + - equal: + path: spec.replicas + value: 1 + - equal: + path: spec.revisionHistoryLimit + value: 10 + - it: validate spec selector + asserts: + - equal: + path: spec.selector.matchLabels["app.kubernetes.io/name"] + value: storybook + - equal: + path: spec.selector.matchLabels["app.kubernetes.io/namespace"] + value: storybook + - it: validate rolling updates + asserts: + - equal: + path: spec.strategy.rollingUpdate.maxSurge + value: 1 + - equal: + path: spec.strategy.rollingUpdate.maxUnavailable + value: 0 + - equal: + path: spec.strategy.type + value: RollingUpdate + - it: validate spec template metadata + asserts: + - equal: + path: spec.template.metadata.labels.component + value: storybook + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/instance"] + value: storybook + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/name"] + value: storybook + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/namespace"] + value: storybook + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/part-of"] + value: ozgcloud + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/version"] + value: 0.0.0-MANAGED-BY-JENKINS + - equal: + path: spec.template.metadata.labels["helm.sh/chart"] + value: storybook-0.0.0-MANAGED-BY-JENKINS + - it: validate spec template spec topology spread constraints + asserts: + - equal: + path: spec.template.spec.topologySpreadConstraints[0].maxSkew + value: 1 + - equal: + path: spec.template.spec.topologySpreadConstraints[0].topologyKey + value: kubernetes.io/hostname + - equal: + path: spec.template.spec.topologySpreadConstraints[0].whenUnsatisfiable + value: ScheduleAnyway + - equal: + path: spec.template.spec.topologySpreadConstraints[0].labelSelector.matchLabels["app.kubernetes.io/name"] + value: storybook + - it: validate image name + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "docker.ozg-sh.de/storybook:0.1.0" + - isAPIVersion: + of: apps/v1 + - it: validate image pull policy + asserts: + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: Always + - it: validate container name + asserts: + - equal: + path: spec.template.spec.containers[0].name + value: storybook + - it: validate readiness probe + asserts: + - equal: + path: spec.template.spec.containers[0].readinessProbe.failureThreshold + value: 3 + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.path + value: / + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.port + value: 8080 + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.scheme + value: HTTP + - equal: + path: spec.template.spec.containers[0].readinessProbe.periodSeconds + value: 10 + - equal: + path: spec.template.spec.containers[0].readinessProbe.successThreshold + value: 1 + - equal: + path: spec.template.spec.containers[0].readinessProbe.timeoutSeconds + value: 3 + - it: validate startup probe + asserts: + - equal: + path: spec.template.spec.containers[0].startupProbe.failureThreshold + value: 3 + - equal: + path: spec.template.spec.containers[0].startupProbe.httpGet.path + value: / + - equal: + path: spec.template.spec.containers[0].startupProbe.httpGet.port + value: 8080 + - equal: + path: spec.template.spec.containers[0].startupProbe.httpGet.scheme + value: HTTP + - equal: + path: spec.template.spec.containers[0].startupProbe.failureThreshold + value: 3 + - equal: + path: spec.template.spec.containers[0].startupProbe.periodSeconds + value: 10 + - equal: + path: spec.template.spec.containers[0].startupProbe.successThreshold + value: 1 + - equal: + path: spec.template.spec.containers[0].startupProbe.timeoutSeconds + value: 3 + - it: validate container security context + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation + value: false + - equal: + path: spec.template.spec.containers[0].securityContext.privileged + value: false + - equal: + path: spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem + value: false + - equal: + path: spec.template.spec.containers[0].securityContext.runAsNonRoot + value: true + + - it: validate container root config + asserts: + - equal: + path: spec.template.spec.containers[0].stdin + value: true + - equal: + path: spec.template.spec.containers[0].terminationMessagePath + value: /dev/termination-log + - equal: + path: spec.template.spec.containers[0].terminationMessagePolicy + value: File + - equal: + path: spec.template.spec.containers[0].tty + value: true + - it: validate spec template spec root config + asserts: + - equal: + path: spec.template.spec.dnsConfig + value: {} + - equal: + path: spec.template.spec.dnsPolicy + value: ClusterFirst + - equal: + path: spec.template.spec.imagePullSecrets[0].name + value: image-pull-secret + - equal: + path: spec.template.spec.restartPolicy + value: Always + - equal: + path: spec.template.spec.schedulerName + value: default-scheduler + - equal: + path: spec.template.spec.securityContext + value: {} + - equal: + path: spec.template.spec.terminationGracePeriodSeconds + value: 30 \ No newline at end of file diff --git a/alfa-client/libs/design-system/test/helm/ingress_tests.yaml b/alfa-client/libs/design-system/test/helm/ingress_tests.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a6d662510d9e6d214d958042372987813bffb840 --- /dev/null +++ b/alfa-client/libs/design-system/test/helm/ingress_tests.yaml @@ -0,0 +1,101 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: test ingress.yaml +release: + name: storybook + namespace: sh-helm-test +templates: + - templates/ingress.yaml +set: + ozgcloud: + bezeichner: helm + baseUrl: test.by.ozg-cloud.de + +tests: + - it: check ingress kind + asserts: + - isKind: + of: Ingress + - it: should create ingress tls + asserts: + - equal: + path: spec.tls[0].secretName + value: helm-storybook-tls + + - it: should not create ingress tls/ingressClass + set: + cluster_env: dataport + asserts: + - isNull: + path: spec.ingressClassName + - isNull: + path: spec.tls[0].secretName + + - it: should use default letsencrypt-prod cluster-issuer + asserts: + - equal: + path: metadata.annotations["cert-manager.io/cluster-issuer"] + value: letsencrypt-prod + + - it: should use letsencrypt-staging cluster-issuer + set: + ingress.use_staging_cert: true + asserts: + - equal: + path: metadata.annotations["cert-manager.io/cluster-issuer"] + value: letsencrypt-staging + + - it: should use letsencrypt-prod cluster-issuer + set: + ingress.use_staging_cert: false + asserts: + - equal: + path: metadata.annotations["cert-manager.io/cluster-issuer"] + value: letsencrypt-prod + + - it: should create tls hosts name correctly + asserts: + - equal: + path: spec.tls[0].hosts[0] + value: helm.test.by.ozg-cloud.de + + + - equal: + path: spec.rules[0].http.paths[0] + value: + path: / + pathType: Prefix + backend: + service: + name: storybook + port: + number: 8080 + + + - it: should set hostname + asserts: + - equal: + path: spec.rules[0].host + value: helm.test.by.ozg-cloud.de \ No newline at end of file diff --git a/alfa-client/libs/design-system/test/helm/network_policy_test.yaml b/alfa-client/libs/design-system/test/helm/network_policy_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7820c1acdcdfafa48427c049c6292757ba2ae6f5 --- /dev/null +++ b/alfa-client/libs/design-system/test/helm/network_policy_test.yaml @@ -0,0 +1,179 @@ +# +# Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: network policy storybook test +release: + name: storybook + namespace: by-helm-test +templates: + - templates/network_policy.yaml + +tests: + - it: should match apiVersion + set: + networkPolicy: + dnsServerNamespace: kube-system + asserts: + - isAPIVersion: + of: networking.k8s.io/v1 + - it: should match kind + set: + networkPolicy: + dnsServerNamespace: kube-system + asserts: + - isKind: + of: NetworkPolicy + - it: validate metadata + set: + networkPolicy: + dnsServerNamespace: kube-system + asserts: + - equal: + path: metadata + value: + name: network-policy-storybook + namespace: by-helm-test + + + - it: should add egress rule to dns service + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace + asserts: + - contains: + path: spec.egress + content: + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: test-dns-namespace + ports: + - port: 53 + protocol: UDP + - port: 53 + protocol: TCP + - port: 5353 + protocol: UDP + - port: 5353 + protocol: TCP + + - it: should add additionalIngressConfig local + set: + networkPolicy: + dnsServerNamespace: test-namespace-dns + additionalIngressConfigLocal: + - from: + - podSelector: + matchLabels: + component: client2 + asserts: + - contains: + path: spec.ingress + content: + from: + - podSelector: + matchLabels: + component: client2 + - it: should add additionalIngressConfig global + set: + networkPolicy: + dnsServerNamespace: test-namespace-dns + additionalIngressConfigGlobal: + - from: + - podSelector: + matchLabels: + component: client2 + asserts: + - contains: + path: spec.ingress + content: + from: + - podSelector: + matchLabels: + component: client2 + + - it: should add additionalEgressConfig local + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace + additionalEgressConfigLocal: + - to: + - ipBlock: + cidr: 1.2.3.4/32 + asserts: + - contains: + path: spec.egress + content: + to: + - ipBlock: + cidr: 1.2.3.4/32 + + - it: should add additionalEgressConfig global + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace + additionalEgressConfigGlobal: + - to: + - ipBlock: + cidr: 1.2.3.4/32 + asserts: + - contains: + path: spec.egress + content: + to: + - ipBlock: + cidr: 1.2.3.4/32 + + + - it: test network policy disabled + set: + networkPolicy: + disabled: true + asserts: + - hasDocuments: + count: 0 + + - it: test network policy unset should be disabled + set: + networkPolicy: + disabled: false + dnsServerNamespace: test-dns-server-namespace + asserts: + - hasDocuments: + count: 1 + - it: test network policy dnsServerNamespace must be set message + set: + networkPolicy: + disabled: false + asserts: + - failedTemplate: + errorMessage: networkPolicy.dnsServerNamespace must be set + + - it: test network policy should be enabled by default + set: + networkPolicy: + dnsServerNamespace: test-dns-server-namespace + asserts: + - hasDocuments: + count: 1 \ No newline at end of file diff --git a/alfa-client/libs/design-system/test/helm/service_test.yaml b/alfa-client/libs/design-system/test/helm/service_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fd3ae830b640f34e4540828d884dd4fb0aa2df46 --- /dev/null +++ b/alfa-client/libs/design-system/test/helm/service_test.yaml @@ -0,0 +1,66 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: test service +release: + name: storybook + namespace: sh-helm-test +templates: + - templates/service.yaml +tests: + - it: should have the label component with correct value + asserts: + - isKind: + of: Service + - isAPIVersion: + of: v1 + - equal: + path: metadata.labels.component + value: storybook-service + - it: should be of type ClusterIP + asserts: + - equal: + path: spec.type + value: ClusterIP + + - it: selector should contain the component label with correct value + asserts: + - equal: + path: spec.selector.component + value: storybook + + - it: selector should contain helm recommended labels name and namespace + asserts: + - equal: + path: spec.selector + value: + app.kubernetes.io/name: storybook + app.kubernetes.io/namespace: sh-helm-test + component: storybook + + - it: check component label for service + asserts: + - equal: + path: metadata.labels["component"] + value: storybook-service \ No newline at end of file diff --git a/alfa-client/libs/kommentar-shared/src/lib/kommentar.model.ts b/alfa-client/libs/kommentar-shared/src/lib/kommentar.model.ts index 3155c2596134d40deb75c95edad1f86b3a3b9816..f0c5a719ce32a08dafa05c712c5ad694d4a9dc71 100644 --- a/alfa-client/libs/kommentar-shared/src/lib/kommentar.model.ts +++ b/alfa-client/libs/kommentar-shared/src/lib/kommentar.model.ts @@ -21,7 +21,6 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { CreateCommand } from '@alfa-client/command-shared'; import { ListResource } from '@alfa-client/tech-shared'; import { Resource, ResourceUri } from '@ngxp/rest'; @@ -34,7 +33,3 @@ export interface Kommentar { export interface KommentarResource extends Kommentar, Resource {} export interface KommentarListResource extends ListResource {} - -export interface CreateKommentarCommand extends CreateCommand { - kommentar: Kommentar; -} diff --git a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts index 0478aadb64501cee92b4900b6275eb8cd122a96d..a7bff9a03fa653434c6ae6b0627e3decce20c587 100644 --- a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts +++ b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.spec.ts @@ -22,14 +22,19 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { BinaryFileService } from '@alfa-client/binary-file-shared'; -import { CommandOrder, CommandResource, CommandService } from '@alfa-client/command-shared'; +import { + CommandOrder, + CommandResource, + CommandService, + CreateCommand, +} from '@alfa-client/command-shared'; import { NavigationService } from '@alfa-client/navigation-shared'; import { + StateResource, createEmptyStateResource, createStateResource, - StateResource, } from '@alfa-client/tech-shared'; -import { mock, Mock, useFromMock } from '@alfa-client/test-utils'; +import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; import { VorgangService, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; import { cold, hot } from 'jest-marbles'; import { CommandLinkRel } from 'libs/command-shared/src/lib/command.linkrel'; @@ -42,12 +47,7 @@ import { import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { of } from 'rxjs'; import { KommentarLinkRel, KommentarListLinkRel } from './kommentar.linkrel'; -import { - CreateKommentarCommand, - Kommentar, - KommentarListResource, - KommentarResource, -} from './kommentar.model'; +import { Kommentar, KommentarListResource, KommentarResource } from './kommentar.model'; import { KommentarRepository } from './kommentar.repository'; import { KommentarService } from './kommentar.service'; @@ -158,9 +158,9 @@ describe('KommentarService', () => { }); it('should create CreateKommentarCommand', () => { - var command: CreateKommentarCommand = service.createCreateKommentarCommand(kommentar); + var command: CreateCommand = service.createCreateKommentarCommand(kommentar); - expect(command).toEqual({ order: CommandOrder.CREATE_KOMMENTAR, kommentar, body: null }); + expect(command).toEqual({ order: CommandOrder.CREATE_KOMMENTAR, body: kommentar }); }); }); }); @@ -212,9 +212,9 @@ describe('KommentarService', () => { }); it('should create CreateKommentarCommand', () => { - var command: CreateKommentarCommand = service.createEditKommentarCommand(kommentar); + var command: CreateCommand = service.createEditKommentarCommand(kommentar); - expect(command).toEqual({ order: CommandOrder.EDIT_KOMMENTAR, kommentar, body: null }); + expect(command).toEqual({ order: CommandOrder.EDIT_KOMMENTAR, body: kommentar }); }); }); }); diff --git a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.ts b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.ts index b1be2ecc5b41cd6504ea878f9297ea4ecc1a7099..1e183c3c45934214bd145fd8c28a6606eb563dd9 100644 --- a/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.ts +++ b/alfa-client/libs/kommentar-shared/src/lib/kommentar.service.ts @@ -21,8 +21,6 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Injectable } from '@angular/core'; -import { Params } from '@angular/router'; import { BinaryFileListResource, BinaryFileService } from '@alfa-client/binary-file-shared'; import { CommandOrder, @@ -33,23 +31,20 @@ import { } from '@alfa-client/command-shared'; import { NavigationService } from '@alfa-client/navigation-shared'; import { + StateResource, createEmptyStateResource, createStateResource, doIfLoadingRequired, - StateResource, } from '@alfa-client/tech-shared'; import { VorgangResource, VorgangService } from '@alfa-client/vorgang-shared'; -import { hasLink, Resource } from '@ngxp/rest'; +import { Injectable } from '@angular/core'; +import { Params } from '@angular/router'; +import { Resource, hasLink } from '@ngxp/rest'; import { isNil } from 'lodash-es'; -import { BehaviorSubject, Observable, of, Subscription } from 'rxjs'; +import { BehaviorSubject, Observable, Subscription, of } from 'rxjs'; import { map, startWith, tap } from 'rxjs/operators'; import { KommentarLinkRel, KommentarListLinkRel } from './kommentar.linkrel'; -import { - CreateKommentarCommand, - Kommentar, - KommentarListResource, - KommentarResource, -} from './kommentar.model'; +import { Kommentar, KommentarListResource, KommentarResource } from './kommentar.model'; import { KommentarRepository } from './kommentar.repository'; @Injectable({ providedIn: 'root' }) @@ -154,8 +149,8 @@ export class KommentarService { ); } - createCreateKommentarCommand(kommentar: Kommentar): CreateKommentarCommand { - return { order: CommandOrder.CREATE_KOMMENTAR, kommentar, body: null }; + createCreateKommentarCommand(kommentar: Kommentar): CreateCommand { + return { order: CommandOrder.CREATE_KOMMENTAR, body: kommentar }; } public editKommentar( @@ -169,8 +164,8 @@ export class KommentarService { ); } - createEditKommentarCommand(kommentar: Kommentar): CreateKommentarCommand { - return { order: CommandOrder.EDIT_KOMMENTAR, kommentar, body: null }; + createEditKommentarCommand(kommentar: Kommentar): CreateCommand { + return { order: CommandOrder.EDIT_KOMMENTAR, body: kommentar }; } createKommentarCommand( diff --git a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-attachments/postfach-mail-attachments.component.html b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-attachments/postfach-mail-attachments.component.html index 05831b3b1608abfbd8d66d39c61a4e4e929e8775..a06ec149adf4dcaf0ed23c913dba68d720bbdfbb 100644 --- a/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-attachments/postfach-mail-attachments.component.html +++ b/alfa-client/libs/postfach/src/lib/postfach-mail-list-container/postfach-mail-list/postfach-mail/postfach-mail-attachments/postfach-mail-attachments.component.html @@ -23,5 +23,5 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<alfa-vertical-binary-file-list [fileListResource]="attachments$ | async"> +<alfa-vertical-binary-file-list [binaryFileListStateResource]="attachments$ | async"> </alfa-vertical-binary-file-list> diff --git a/alfa-client/libs/tech-shared/src/lib/error/error.util.spec.ts b/alfa-client/libs/tech-shared/src/lib/error/error.util.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..118b8869a0f9a95cba5d73c84b0eb6be5f94bfb8 --- /dev/null +++ b/alfa-client/libs/tech-shared/src/lib/error/error.util.spec.ts @@ -0,0 +1,132 @@ +import { HttpErrorResponse } from '@angular/common/http'; +import { createApiError, createHttpErrorResponse, createIssue } from 'libs/tech-shared/test/error'; +import { MessageCode } from '../message-code'; +import { ApiError, Issue } from '../tech.model'; +import { + getApiErrorFromHttpErrorResponse, + isApiError, + isNotApiError, + isResourceNotFoundError, + isServiceUnavailableMessageCode, +} from './error.util'; + +describe('error.util', () => { + describe('isApiError', () => { + it('should return true if parameter is ApiError', () => { + const apiError: ApiError = createApiError(); + + const result: boolean = isApiError(apiError); + + expect(result).toBeTruthy(); + }); + + it('should return false if empty ApiError.Issues property', () => { + const apiError: ApiError = createApiError([]); + + const result: boolean = isApiError(apiError); + + expect(result).toBeFalsy(); + }); + + it('should return false if parameter is no ApiError', () => { + const httpErrorResponse: HttpErrorResponse = createHttpErrorResponse(); + + const result: boolean = isApiError(httpErrorResponse); + + expect(result).toBeFalsy(); + }); + }); + + describe('isNotApiError', () => { + it('should return true if parameter is no ApiError', () => { + const result: boolean = isNotApiError(createHttpErrorResponse()); + + expect(result).toBeTruthy(); + }); + + it('should return false if parameter is ApiError', () => { + const issue: Issue = createIssue(); + const apiError: ApiError = createApiError([issue]); + + const result: boolean = isNotApiError(apiError); + + expect(result).toBeFalsy(); + }); + }); + + describe('isServiceUnavailableMessageCode', () => { + it('should return true if SERVICE_UNAVAILABLE error code', () => { + const issue: Issue = { ...createIssue(), messageCode: MessageCode.SERVICE_UNAVAILABLE }; + const apiError: ApiError = createApiError([issue]); + + const result: boolean = isServiceUnavailableMessageCode(apiError); + + expect(result).toBeTruthy(); + }); + + it('should return false if not SERVICE_UNAVAILABLE error code', () => { + const issue: Issue = createIssue(); + const apiError: ApiError = createApiError([issue]); + + const result: boolean = isServiceUnavailableMessageCode(apiError); + + expect(result).toBeFalsy(); + }); + + it('should return false if input parameter is no ApiError', () => { + const httpErrorResponse: HttpErrorResponse = new HttpErrorResponse({}); + + const result: boolean = isServiceUnavailableMessageCode(httpErrorResponse); + + expect(result).toBeFalsy(); + }); + }); + + describe('isResourceNotFoundError', () => { + it('should return true if RESOURCE_NOT_FOUND error code', () => { + const issue: Issue = { ...createIssue(), messageCode: MessageCode.RESOURCE_NOT_FOUND }; + const apiError: ApiError = createApiError([issue]); + + const result: boolean = isResourceNotFoundError(apiError); + + expect(result).toBeTruthy(); + }); + + it('should return false if not RESOURCE_NOT_FOUND error code', () => { + const issue: Issue = createIssue(); + const apiError: ApiError = createApiError([issue]); + + const result: boolean = isResourceNotFoundError(apiError); + + expect(result).toBeFalsy(); + }); + + it('should return false if input parameter is no ApiError', () => { + const httpErrorResponse: HttpErrorResponse = new HttpErrorResponse({}); + + const result: boolean = isResourceNotFoundError(httpErrorResponse); + + expect(result).toBeFalsy(); + }); + }); + + describe('getApiErrorFromHttpErrorResponse', () => { + it('should return ApiError', () => { + const issue: Issue = createIssue(); + const apiError: ApiError = createApiError([issue]); + const httpErrorResponse: HttpErrorResponse = createHttpErrorResponse(apiError); + + const result: ApiError = getApiErrorFromHttpErrorResponse(httpErrorResponse); + + expect(result).toBe(apiError); + }); + + it('should return undefined if no ApiError', () => { + const httpErrorResponse: HttpErrorResponse = new HttpErrorResponse({}); + + const result: ApiError = getApiErrorFromHttpErrorResponse(httpErrorResponse); + + expect(result).toBeUndefined(); + }); + }); +}); diff --git a/alfa-client/libs/tech-shared/src/lib/error/error.util.ts b/alfa-client/libs/tech-shared/src/lib/error/error.util.ts index 81c2a8d62e817d5e4907c194bf9dd62bfa2fd3a1..00271229b4f5fe102b8ebb7ebe92d43a55465d3d 100644 --- a/alfa-client/libs/tech-shared/src/lib/error/error.util.ts +++ b/alfa-client/libs/tech-shared/src/lib/error/error.util.ts @@ -22,16 +22,32 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { HttpErrorResponse } from '@angular/common/http'; -import { isNil } from 'lodash-es'; +import { has } from 'lodash-es'; import { MessageCode } from '../message-code'; -import { ApiError, HttpError } from '../tech.model'; +import { ApiError } from '../tech.model'; -export function isApiError(value: any): boolean { - return !isNil(value?.issues) && !isNil(value.issues[0]); +export function isApiError(error: unknown): boolean { + return has(error, 'issues[0]'); } -export function isServiceUnavailableMessageCode(error: ApiError): boolean { - return error.issues[0].messageCode == MessageCode.SERVICE_UNAVAILABLE; +export function isNotApiError(error: unknown): boolean { + return !isApiError(error); +} + +export function isServiceUnavailableMessageCode(error: unknown): boolean { + if (isNotApiError(error)) { + return false; + } + + return (error as ApiError).issues[0].messageCode === MessageCode.SERVICE_UNAVAILABLE; +} + +export function isResourceNotFoundError(error: unknown): boolean { + if (isNotApiError(error)) { + return false; + } + + return (error as ApiError).issues[0].messageCode === MessageCode.RESOURCE_NOT_FOUND; } export function getApiErrorFromHttpErrorResponse(httpErrorResponse: HttpErrorResponse): ApiError { diff --git a/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.spec.ts b/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.spec.ts index e7ff1676a507c878018db9eb3cc4e1de38b2d972..10709bf89427a487644c718947970ac429a1a6ba 100644 --- a/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.spec.ts @@ -2,32 +2,31 @@ import { Resource } from '@ngxp/rest'; import { createDummyResource } from 'libs/tech-shared/test/resource'; import { HasAnyLinkPipe } from './has-any-link.pipe'; +import * as TechUtil from '../tech.util'; + describe('HasAnyLinkPipe', () => { const oneLink: string = 'one'; const anotherLink: string = 'another'; const resource: Resource = createDummyResource([oneLink, anotherLink]); const pipe: HasAnyLinkPipe = new HasAnyLinkPipe(); - it('should return true if resource has at least on link', () => { - const result: boolean = pipe.transform(resource, oneLink, 'notExists'); + describe('transform', () => { + let hasAnyLinkSpy: jest.SpyInstance<boolean>; - expect(result).toBe(true); - }); - it('resource return true if resoure has multiple links', () => { - const result: boolean = pipe.transform(resource, oneLink, anotherLink); + beforeEach(() => { + hasAnyLinkSpy = jest.spyOn(TechUtil, 'hasAnyLink').mockReturnValue(true); + }); - expect(result).toBe(true); - }); + it('should call has any link', () => { + pipe.transform(resource, oneLink); - it('should return false if resource has no of given links', () => { - const result: boolean = pipe.transform(resource, 'notExists', 'notExistsToo'); - - expect(result).toBe(false); - }); + expect(hasAnyLinkSpy).toHaveBeenCalledWith(resource, oneLink); + }); - it('should return false if resource is null', () => { - const result: boolean = pipe.transform(null, 'notExists', 'notExistsToo'); + it('should return value', () => { + const result: boolean = pipe.transform(resource, oneLink, anotherLink); - expect(result).toBe(false); + expect(result).toBe(true); + }); }); }); diff --git a/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.ts b/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.ts index a6627c5fd1072763525025f304082f42749be211..b2827a5cbad9d558ed5666ed506cb9cfa38411ea 100644 --- a/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.ts +++ b/alfa-client/libs/tech-shared/src/lib/pipe/has-any-link.pipe.ts @@ -1,12 +1,11 @@ import { Pipe, PipeTransform } from '@angular/core'; -import { hasLink, Resource } from '@ngxp/rest'; -import { isEmpty } from 'lodash-es'; +import { Resource } from '@ngxp/rest'; +import { LinkRelationName } from '../resource/resource.model'; +import { hasAnyLink } from '../tech.util'; @Pipe({ name: 'hasAnyLink' }) export class HasAnyLinkPipe implements PipeTransform { - transform(resource: Resource, ...links: string[]) { - return !isEmpty( - links.map((link) => hasLink(resource, link)).filter((hasLink) => hasLink === true), - ); + transform(resource: Resource, ...links: LinkRelationName[]): boolean { + return hasAnyLink(resource, ...links); } } diff --git a/alfa-client/libs/tech-shared/src/lib/pipe/not-has-any-link.pipe.spec.ts b/alfa-client/libs/tech-shared/src/lib/pipe/not-has-any-link.pipe.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c3353c6cdb3b8957c4c6ae71f849c4a504b700cd --- /dev/null +++ b/alfa-client/libs/tech-shared/src/lib/pipe/not-has-any-link.pipe.spec.ts @@ -0,0 +1,32 @@ +import { Resource } from '@ngxp/rest'; +import { createDummyResource } from 'libs/tech-shared/test/resource'; +import { NotHasAnyLinkPipe } from './not-has-any-link.pipe'; + +import * as TechUtil from '../tech.util'; + +describe('NotHasAnyLinkPipe', () => { + const oneLink: string = 'one'; + const anotherLink: string = 'another'; + const resource: Resource = createDummyResource([oneLink, anotherLink]); + const pipe: NotHasAnyLinkPipe = new NotHasAnyLinkPipe(); + + describe('transform', () => { + let notHasAnyLinkSpy: jest.SpyInstance<boolean>; + + beforeEach(() => { + notHasAnyLinkSpy = jest.spyOn(TechUtil, 'notHasAnyLink').mockReturnValue(true); + }); + + it('should call not has any link', () => { + pipe.transform(resource, oneLink); + + expect(notHasAnyLinkSpy).toHaveBeenCalledWith(resource, oneLink); + }); + + it('should return value', () => { + const result: boolean = pipe.transform(resource, oneLink, anotherLink); + + expect(result).toBe(true); + }); + }); +}); diff --git a/alfa-client/libs/tech-shared/src/lib/pipe/not-has-any-link.pipe.ts b/alfa-client/libs/tech-shared/src/lib/pipe/not-has-any-link.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..f207bca8edccd5f6229207b9cfb0c8d9204e3b92 --- /dev/null +++ b/alfa-client/libs/tech-shared/src/lib/pipe/not-has-any-link.pipe.ts @@ -0,0 +1,11 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { Resource } from '@ngxp/rest'; +import { LinkRelationName } from '../resource/resource.model'; +import { notHasAnyLink } from '../tech.util'; + +@Pipe({ name: 'notHasAnyLink' }) +export class NotHasAnyLinkPipe implements PipeTransform { + transform(resource: Resource, ...links: LinkRelationName[]): boolean { + return notHasAnyLink(resource, ...links); + } +} diff --git a/alfa-client/libs/tech-shared/src/lib/tech-shared.module.ts b/alfa-client/libs/tech-shared/src/lib/tech-shared.module.ts index ecb138d23695a7111038967cf2d80147cbac5206..331031b298487fcfa77df7c10041a33cfedec7bb 100644 --- a/alfa-client/libs/tech-shared/src/lib/tech-shared.module.ts +++ b/alfa-client/libs/tech-shared/src/lib/tech-shared.module.ts @@ -40,6 +40,7 @@ import { FormatToPrettyDatePipe } from './pipe/format-to-pretty-date.pipe'; import { GetUrlPipe } from './pipe/get-url.pipe'; import { HasAnyLinkPipe } from './pipe/has-any-link.pipe'; import { HasLinkPipe } from './pipe/has-link.pipe'; +import { NotHasAnyLinkPipe } from './pipe/not-has-any-link.pipe'; import { NotHasLinkPipe } from './pipe/not-has-link.pipe'; import { ToEmbeddedResourcesPipe } from './pipe/to-embedded-resource.pipe'; import { ToResourceUriPipe } from './pipe/to-resource-uri.pipe'; @@ -57,6 +58,7 @@ import { ToTrafficLightPipe } from './pipe/to-traffic-light.pipe'; HasLinkPipe, HasAnyLinkPipe, NotHasLinkPipe, + NotHasAnyLinkPipe, ToResourceUriPipe, ToTrafficLightPipe, ToTrafficLightTooltipPipe, @@ -77,6 +79,7 @@ import { ToTrafficLightPipe } from './pipe/to-traffic-light.pipe'; HasLinkPipe, HasAnyLinkPipe, NotHasLinkPipe, + NotHasAnyLinkPipe, ToResourceUriPipe, ToTrafficLightPipe, ToTrafficLightTooltipPipe, diff --git a/alfa-client/libs/tech-shared/src/lib/tech.model.ts b/alfa-client/libs/tech-shared/src/lib/tech.model.ts index 3d9ca01c2225d3550fd9b8832a8feac5a3b4e50e..c85852676e5184152aa17856cca07bd33b3a3223 100644 --- a/alfa-client/libs/tech-shared/src/lib/tech.model.ts +++ b/alfa-client/libs/tech-shared/src/lib/tech.model.ts @@ -68,6 +68,7 @@ export enum HttpMethod { export enum HttpHeader { ACCEPT = 'Accept', CONTENT_DISPOSITION = 'content-disposition', + LOCATION = 'Location', } export interface BlobWithFileName { diff --git a/alfa-client/libs/tech-shared/src/lib/tech.util.spec.ts b/alfa-client/libs/tech-shared/src/lib/tech.util.spec.ts index 446e4961c800953f06a005d7a36c2eb2e643ef91..6184389586142f4acbdc09bb20ffe793ac3424ff 100644 --- a/alfa-client/libs/tech-shared/src/lib/tech.util.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/tech.util.spec.ts @@ -23,6 +23,9 @@ */ import { faker } from '@faker-js/faker'; +import { Resource } from '@ngxp/rest'; +import { createDummyResource } from 'libs/tech-shared/test/resource'; +import { LinkRelationName } from './resource/resource.model'; import { EMPTY_STRING, allEmpty, @@ -32,11 +35,13 @@ import { encodeUrlForEmbedding, getFirstLetter, getStringValue, + hasAnyLink, hasContent, hasMinLength, isNotEmpty, isNotNil, isNotNull, + notHasAnyLink, replaceAllWhitespaces, replacePlaceholder, replacePlaceholders, @@ -363,4 +368,50 @@ describe('TechUtil', () => { expect(value).toBeUndefined(); }); }); + + describe('has any link', () => { + const oneLink: string = 'one'; + const anotherLink: string = 'another'; + const resource: Resource = createDummyResource([oneLink, anotherLink]); + + it('should return true if resource has at least on link', () => { + const result: boolean = hasAnyLink(resource, oneLink, 'notExists'); + + expect(result).toBe(true); + }); + + it('resource return true if resoure has multiple links', () => { + const result: boolean = hasAnyLink(resource, oneLink, anotherLink); + + expect(result).toBe(true); + }); + + it('should return false if resource has no of given links', () => { + const result: boolean = hasAnyLink(resource, 'notExists', 'notExistsToo'); + + expect(result).toBe(false); + }); + + it('should return false if resource is null', () => { + const result: boolean = hasAnyLink(null, 'notExists', 'notExistsToo'); + + expect(result).toBe(false); + }); + }); + + describe('not has any link', () => { + const link: LinkRelationName = 'one'; + const otherLink: LinkRelationName = 'two'; + + it('should return false if at least one link exists', () => { + const result: boolean = notHasAnyLink(createDummyResource([link]), link, otherLink); + + expect(result).toBe(false); + }); + it('should return true if no link exists', () => { + const result: boolean = notHasAnyLink(createDummyResource(), link, otherLink); + + expect(result).toBe(true); + }); + }); }); diff --git a/alfa-client/libs/tech-shared/src/lib/tech.util.ts b/alfa-client/libs/tech-shared/src/lib/tech.util.ts index 0d227ab5dd449507c3b3b9da22b753c8d82f3799..55e881a99ac582741b7bea58ddde81b00c5c11b0 100644 --- a/alfa-client/libs/tech-shared/src/lib/tech.util.ts +++ b/alfa-client/libs/tech-shared/src/lib/tech.util.ts @@ -21,9 +21,11 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { Resource, hasLink } from '@ngxp/rest'; import { Base64 } from 'js-base64'; import { isEmpty, isNil, isNull, isUndefined } from 'lodash-es'; import { sanitize } from 'sanitize-filename-ts'; +import { LinkRelationName } from './resource/resource.model'; import { ApiError } from './tech.model'; export const EMPTY_STRING: string = ''; @@ -143,3 +145,15 @@ export function convertToBoolean(booleanStr: string): boolean { export function joinAndTrim(inputStrings: string[], delimiter: string = ' ') { return inputStrings.join(delimiter).trim(); } + +export function notHasAnyLink(resource: Resource, ...links: LinkRelationName[]): boolean { + return !hasAnyLink(resource, ...links); +} + +export function hasAnyLink(resource: Resource, ...links: LinkRelationName[]): boolean { + return !isEmpty( + links + .map((link: LinkRelationName) => hasLink(resource, link)) + .filter((hasLink: boolean) => hasLink === true), + ); +} diff --git a/alfa-client/libs/ui/src/index.ts b/alfa-client/libs/ui/src/index.ts index 4a57b047ecb1d1e9bd7fc7a6a85bbca76fb12447..18cbad77e78dfb553c9cc351c0f6a084446b41a0 100644 --- a/alfa-client/libs/ui/src/index.ts +++ b/alfa-client/libs/ui/src/index.ts @@ -37,7 +37,6 @@ export * from './lib/ui/editor/checkbox-enum-editor/checkbox-enum-editor.model'; export * from './lib/ui/editor/date-editor/date-editor.component'; export * from './lib/ui/editor/enum-editor/enum-editor.component'; export * from './lib/ui/editor/enum-editor/enum-editor.model'; -export * from './lib/ui/editor/formcontrol-editor.abstract.component'; export * from './lib/ui/editor/text-editor/text-editor.component'; export * from './lib/ui/editor/textarea-editor/textarea-editor.component'; export * from './lib/ui/expansion-panel/expansion-panel.component'; diff --git a/alfa-client/libs/ui/src/lib/ui/editor/autocomplete-editor/autocomplete-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/autocomplete-editor/autocomplete-editor.component.ts index 814dc618b5a9b4cd8d9d0e9fd1f7690110e7d495..99a9eb92b61246dbcc5ded540b949153ffea8a2f 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/autocomplete-editor/autocomplete-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/autocomplete-editor/autocomplete-editor.component.ts @@ -31,8 +31,8 @@ import { ViewChild, } from '@angular/core'; import { ResourceUri } from '@ngxp/rest'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; import { isEmpty, isEqual, isNil } from 'lodash-es'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; @Component({ selector: 'ozgcloud-autocomplete-editor', diff --git a/alfa-client/libs/ui/src/lib/ui/editor/checkbox-enum-editor/checkbox-enum-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/checkbox-enum-editor/checkbox-enum-editor.component.ts index d896c82a1626ffd500382b34f58481fd1a794124..0d76de506f2b3e1a7114b0984a780bed9f505fd8 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/checkbox-enum-editor/checkbox-enum-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/checkbox-enum-editor/checkbox-enum-editor.component.ts @@ -1,7 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; -import { CheckboxEnumEditorItem } from './checkbox-enum-editor.model'; import { MatCheckboxChange } from '@angular/material/checkbox'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; +import { CheckboxEnumEditorItem } from './checkbox-enum-editor.model'; /** * Ein Checkbox, dessen Wert beim FormControl statt boolean als string aus dem enum editor item diff --git a/alfa-client/libs/ui/src/lib/ui/editor/date-editor/date-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/date-editor/date-editor.component.ts index 27619ab5f03eb534053f871ae817dcacd07f5fe3..ea6c64bf12eff1271c4c2ac016bd73333e9e6308 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/date-editor/date-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/date-editor/date-editor.component.ts @@ -25,7 +25,7 @@ import { add2000Years } from '@alfa-client/tech-shared'; import { Component, Input } from '@angular/core'; import { MatDatepickerInputEvent } from '@angular/material/datepicker'; import { isDate } from 'date-fns'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; @Component({ selector: 'ozgcloud-date-editor', diff --git a/alfa-client/libs/ui/src/lib/ui/editor/enum-editor/enum-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/enum-editor/enum-editor.component.ts index 58a4a6f3413d95da58da14ec6b0cf0126eecfa76..4e4451f0621feb2c6c1472eedd2d18f27a2da348 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/enum-editor/enum-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/enum-editor/enum-editor.component.ts @@ -22,7 +22,7 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { Component, Input, OnInit } from '@angular/core'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; import { EnumEditorItem } from './enum-editor.model'; @Component({ diff --git a/alfa-client/libs/ui/src/lib/ui/editor/file-upload-editor/file-upload-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/file-upload-editor/file-upload-editor.component.ts index 172a8deb368e10e86648c95cd7ddb40d18d51d86..5e9fde7e1eb22fb4ec2929181c3bd122693f7558 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/file-upload-editor/file-upload-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/file-upload-editor/file-upload-editor.component.ts @@ -21,6 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { StateResource } from '@alfa-client/tech-shared'; import { Component, ElementRef, @@ -33,13 +34,12 @@ import { } from '@angular/core'; import { ControlContainer, + FormGroupDirective, UntypedFormArray, UntypedFormControl, - FormGroupDirective, } from '@angular/forms'; -import { StateResource } from '@alfa-client/tech-shared'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; import { uniqueId } from 'lodash-es'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; @Component({ selector: 'ozgcloud-file-upload-editor', diff --git a/alfa-client/libs/ui/src/lib/ui/editor/text-editor/text-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/text-editor/text-editor.component.ts index 017dab82a8ba476c585b0af20b9f7c3080e0ff7b..5bb6b3a96e9f06d8770c527bd80dd202b9c3e06c 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/text-editor/text-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/text-editor/text-editor.component.ts @@ -24,8 +24,8 @@ import { hasContent } from '@alfa-client/tech-shared'; import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; import { SubscriptSizing } from '@angular/material/form-field'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; import { Observable, map, startWith } from 'rxjs'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; @Component({ selector: 'ozgcloud-text-editor', diff --git a/alfa-client/libs/ui/src/lib/ui/editor/textarea-editor/textarea-editor.component.ts b/alfa-client/libs/ui/src/lib/ui/editor/textarea-editor/textarea-editor.component.ts index ff008c8ba924f7ff54291ecf8f0130bda01aeea5..12d1e2d4906205137a55508042d63b95c9a7e857 100644 --- a/alfa-client/libs/ui/src/lib/ui/editor/textarea-editor/textarea-editor.component.ts +++ b/alfa-client/libs/ui/src/lib/ui/editor/textarea-editor/textarea-editor.component.ts @@ -23,7 +23,7 @@ */ import { CdkTextareaAutosize } from '@angular/cdk/text-field'; import { AfterViewInit, Component, Input, ViewChild } from '@angular/core'; -import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; +import { FormControlEditorAbstractComponent } from 'libs/design-component/src/lib/form/formcontrol-editor.abstract.component'; @Component({ selector: 'ozgcloud-textarea-editor', diff --git a/alfa-client/libs/ui/src/lib/ui/messages.ts b/alfa-client/libs/ui/src/lib/ui/messages.ts index 35f77d1e3c0b4cb1134b96853eaead712319de2b..49891db3e6afb98e20be5f33e612a2054bb2f41b 100644 --- a/alfa-client/libs/ui/src/lib/ui/messages.ts +++ b/alfa-client/libs/ui/src/lib/ui/messages.ts @@ -22,6 +22,7 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ export enum Messages { - HTTP_STATUS_FORBIDDEN = 'Die Aktion konnte wegen fehlender Berechtigungen nicht durchgeführt werden', + HTTP_STATUS_FORBIDDEN = 'Die Aktion konnte wegen fehlender Berechtigungen nicht durchgeführt werden.', HTTP_USER_MANAGER_SERVICE_UNAVAILABLE = 'Der UserManager ist zurzeit leider nicht verfügbar.', + HTTP_STATUS_VORGANG_NOT_FOUND = 'Der aufgerufene Vorgang wurde nicht gefunden.', } diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.html deleted file mode 100644 index 0c6b5172652511b4c53e0ace77438a6a55b0728a..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.html +++ /dev/null @@ -1,30 +0,0 @@ -<!-- - - Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - Ministerpräsidenten des Landes Schleswig-Holstein - Staatskanzlei - Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - - Lizenziert unter der EUPL, Version 1.2 oder - sobald - diese von der Europäischen Kommission genehmigt wurden - - Folgeversionen der EUPL ("Lizenz"); - Sie dürfen dieses Werk ausschließlich gemäß - dieser Lizenz nutzen. - Eine Kopie der Lizenz finden Sie hier: - - https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - - Sofern nicht durch anwendbare Rechtsvorschriften - gefordert oder in schriftlicher Form vereinbart, wird - die unter der Lizenz verbreitete Software "so wie sie - ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - ausdrücklich oder stillschweigend - verbreitet. - Die sprachspezifischen Genehmigungen und Beschränkungen - unter der Lizenz sind dem Lizenztext zu entnehmen. - ---> -<alfa-vertical-binary-file-list - [fileListResource]="fileListResource$ | async" - [downloadFileNamePrefix]="vorgangWithEingang.nummer" -> -</alfa-vertical-binary-file-list> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.scss b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.scss deleted file mode 100644 index 9a08a5aabce6cc4cdbb268c4190a8d67f82f19e5..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.spec.ts deleted file mode 100644 index a9ec25ceade2c4416584d658de61791a3db9c645..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { VerticalBinaryFileListComponent } from '@alfa-client/binary-file'; -import { mock } from '@alfa-client/test-utils'; -import { - VorgangService, - VorgangWithEingangLinkRel, - VorgangWithEingangResource, -} from '@alfa-client/vorgang-shared'; -import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { MockComponent } from 'ng-mocks'; -import { VorgangDetailAttachmentListComponent } from './vorgang-detail-attachment-list.component'; - -describe('VorgangDetailAttachmentListComponent', () => { - let component: VorgangDetailAttachmentListComponent; - let fixture: ComponentFixture<VorgangDetailAttachmentListComponent>; - - const vorgangService = mock(VorgangService); - const vorgangWithAttachments: VorgangWithEingangResource = createVorgangWithEingangResource([ - VorgangWithEingangLinkRel.ATTACHMENTS, - ]); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ - VorgangDetailAttachmentListComponent, - MockComponent(VerticalBinaryFileListComponent), - ], - providers: [ - { - provide: VorgangService, - useValue: vorgangService, - }, - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(VorgangDetailAttachmentListComponent); - component = fixture.componentInstance; - component.vorgangWithEingang = createVorgangWithEingangResource(); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('on vorgang with "attachment" link', () => { - it('should call vorgangService methods', () => { - component.vorgangWithEingang = vorgangWithAttachments; - - component.ngOnInit(); - - expect(vorgangService.getAttachments).toHaveBeenCalled(); - }); - }); -}); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.ts deleted file mode 100644 index f1d39c242772553f9b49f790df9b3220d3afea05..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { Component, Input, OnInit } from '@angular/core'; -import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; -import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; -import { VorgangService, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; -import { Observable, of } from 'rxjs'; - -@Component({ - selector: 'alfa-vorgang-detail-attachment-list', - templateUrl: './vorgang-detail-attachment-list.component.html', - styleUrls: ['./vorgang-detail-attachment-list.component.scss'], -}) -export class VorgangDetailAttachmentListComponent implements OnInit { - @Input() vorgangWithEingang: VorgangWithEingangResource; - - fileListResource$: Observable<StateResource<BinaryFileListResource>> = of( - createEmptyStateResource<BinaryFileListResource>(), - ); - - constructor(private vorgangService: VorgangService) {} - - ngOnInit(): void { - this.fileListResource$ = this.vorgangService.getAttachments(); - } -} diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.html new file mode 100644 index 0000000000000000000000000000000000000000..085d1d818ee3dbe414ea74dde833aa1ce01366c7 --- /dev/null +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.html @@ -0,0 +1,29 @@ +<div class="flex w-full flex-col gap-6 py-4 xl:flex-row"> + <div class="flex-1"> + <ng-container + *ngIf="representationListStateResource$ | async as representationListStateResource" + > + <alfa-vertical-binary-file-list + *ngIf="representationListStateResource.resource" + title="Antrag" + data-test-id="representation-list" + [binaryFileListStateResource]="representationListStateResource" + [downloadFileNamePrefix]="downloadPrefix" + > + </alfa-vertical-binary-file-list> + </ng-container> + </div> + <div class="flex-1"> + <ng-container *ngIf="attachmentListStateResource$ | async as attachmentListStateResource"> + <alfa-vertical-binary-file-list + *ngIf="attachmentListStateResource.resource" + title="Anhänge" + data-test-id="attachment-list" + [binaryFileListStateResource]="attachmentListStateResource" + [downloadFileNamePrefix]="downloadPrefix" + [archiveDownloadUri]="attachmentsDownloadUri" + > + </alfa-vertical-binary-file-list> + </ng-container> + </div> +</div> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..141729a2b4ec449a4746a6b97968e44bbf4f8499 --- /dev/null +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.spec.ts @@ -0,0 +1,223 @@ +import { VerticalBinaryFileListComponent } from '@alfa-client/binary-file'; +import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; +import { StateResource, createStateResource } from '@alfa-client/tech-shared'; +import { Mock, getDebugElementFromFixtureByCss, mock } from '@alfa-client/test-utils'; +import { + VorgangService, + VorgangWithEingangLinkRel, + VorgangWithEingangResource, +} from '@alfa-client/vorgang-shared'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import faker from '@faker-js/faker'; +import { getUrl } from '@ngxp/rest'; +import { createBinaryFileListResource } from 'libs/binary-file-shared/test/binary-file'; +import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; +import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; +import { MockComponent } from 'ng-mocks'; +import { of } from 'rxjs'; +import { VorgangDetailDateienContainerComponent } from './vorgang-detail-dateien-container.component'; + +describe('VorgangDetailDateienContainerComponent', () => { + let component: VorgangDetailDateienContainerComponent; + let fixture: ComponentFixture<VorgangDetailDateienContainerComponent>; + + const attachmentList: string = getDataTestIdOf('attachment-list'); + const representationList: string = getDataTestIdOf('representation-list'); + + const vorgang: VorgangWithEingangResource = createVorgangWithEingangResource(); + + const vorgangService: Mock<VorgangService> = mock(VorgangService); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [VorgangDetailDateienContainerComponent], + declarations: [ + VorgangDetailDateienContainerComponent, + MockComponent(VerticalBinaryFileListComponent), + ], + providers: [ + { + provide: VorgangService, + useValue: vorgangService, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(VorgangDetailDateienContainerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should call handleVorgangChange on set new vorgang', () => { + component.handleVorgangChange = jest.fn(); + + component.vorgangWithEingang = createVorgangWithEingangResource(); + + expect(component.handleVorgangChange).toHaveBeenCalled(); + }); + + describe('handle vorgang change', () => { + it('should set downloadPrefix', () => { + component.handleVorgangChange(vorgang); + + expect(component.downloadPrefix).toEqual(vorgang.nummer); + }); + + it('should call handle attachements', () => { + component.handleAttachments = jest.fn(); + + component.handleVorgangChange(vorgang); + + expect(component.handleAttachments).toHaveBeenCalledWith(vorgang); + }); + + it('should call get representations if exists', () => { + component.getRepresentationsIfExists = jest.fn(); + + component.handleVorgangChange(vorgang); + + expect(component.getRepresentationsIfExists).toHaveBeenCalledWith(vorgang); + }); + }); + + describe('get representations if exists', () => { + it('should call vorgangService if link exists', () => { + component.getRepresentationsIfExists( + createVorgangWithEingangResource([VorgangWithEingangLinkRel.REPRESENTATIONS]), + ); + + expect(vorgangService.getRepresentations).toHaveBeenCalled(); + }); + }); + + describe('handle attachments', () => { + it('should call set download uri', () => { + component.setAttachmentsDownloadUri = jest.fn(); + + component.handleAttachments(vorgang); + + expect(component.setAttachmentsDownloadUri).toHaveBeenCalledWith(vorgang); + }); + + it('should call vorgangService methods', () => { + component.handleVorgangChange( + createVorgangWithEingangResource([VorgangWithEingangLinkRel.ATTACHMENTS]), + ); + + expect(vorgangService.getAttachments).toHaveBeenCalled(); + }); + + it('should call get attachments if exists', () => { + component.getAttachmentsIfExists = jest.fn(); + + component.handleVorgangChange(vorgang); + + expect(component.getAttachmentsIfExists).toHaveBeenCalledWith(vorgang); + }); + }); + + describe('get attachments if exists', () => { + it('should call vorgangService if link exists', () => { + component.getAttachmentsIfExists( + createVorgangWithEingangResource([VorgangWithEingangLinkRel.ATTACHMENTS]), + ); + + expect(vorgangService.getAttachments).toHaveBeenCalled(); + }); + }); + + describe('download attachments link', () => { + it('should be set if exists', () => { + const vorganWithLink: VorgangWithEingangResource = createVorgangWithEingangResource([ + VorgangWithEingangLinkRel.DOWNLOAD_ATTACHMENTS, + ]); + + component.handleVorgangChange(vorganWithLink); + + expect(component.attachmentsDownloadUri).toBe( + getUrl(vorganWithLink, VorgangWithEingangLinkRel.DOWNLOAD_ATTACHMENTS), + ); + }); + + it('should NOT be set if missing', () => { + const vorganWithLink: VorgangWithEingangResource = createVorgangWithEingangResource(); + + component.handleVorgangChange(vorganWithLink); + + expect(component.attachmentsDownloadUri).toBeUndefined(); + }); + }); + + describe('on existing attachments', () => { + describe('binary file list component should be called with', () => { + const binaryFileListResource: BinaryFileListResource = createBinaryFileListResource(); + const binaryFileListStateResource: StateResource<BinaryFileListResource> = + createStateResource(binaryFileListResource); + + beforeEach(() => { + component.attachmentListStateResource$ = of(binaryFileListStateResource); + fixture.detectChanges(); + }); + + it('binaryFileListStateResource', () => { + const binaryFileListComp: VerticalBinaryFileListComponent = + getVerticalBinaryFileListComponent(attachmentList); + expect(binaryFileListComp.binaryFileListStateResource).toEqual(binaryFileListStateResource); + }); + + it('downloadFileNamePrefix', () => { + const binaryFileListComp: VerticalBinaryFileListComponent = + getVerticalBinaryFileListComponent(attachmentList); + expect(binaryFileListComp.downloadFileNamePrefix).toEqual(component.downloadPrefix); + }); + + it('archiveDownloadUri', () => { + component.attachmentsDownloadUri = faker.internet.url(); + + fixture.detectChanges(); + + const binaryFileListComp: VerticalBinaryFileListComponent = + getVerticalBinaryFileListComponent(attachmentList); + expect(binaryFileListComp.archiveDownloadUri).toEqual(component.attachmentsDownloadUri); + }); + }); + }); + + describe('On existing representations', () => { + describe('binary file list component should be called with', () => { + const binaryFileListResource: BinaryFileListResource = createBinaryFileListResource(); + const binaryFileListStateResource: StateResource<BinaryFileListResource> = + createStateResource(binaryFileListResource); + + beforeEach(() => { + component.representationListStateResource$ = of(binaryFileListStateResource); + fixture.detectChanges(); + }); + + it('binaryFileListStateResource', () => { + const binaryFileListComp: VerticalBinaryFileListComponent = + getVerticalBinaryFileListComponent(representationList); + expect(binaryFileListComp.binaryFileListStateResource).toEqual(binaryFileListStateResource); + }); + + it('downloadFileNamePrefix', () => { + const binaryFileListComp: VerticalBinaryFileListComponent = + getVerticalBinaryFileListComponent(representationList); + expect(binaryFileListComp.downloadFileNamePrefix).toEqual(component.downloadPrefix); + }); + }); + }); + + function getVerticalBinaryFileListComponent( + dataTestIdElement: string, + ): VerticalBinaryFileListComponent { + return getDebugElementFromFixtureByCss( + fixture, + 'alfa-vertical-binary-file-list' + dataTestIdElement, + ).componentInstance; + } +}); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..55c4a9368bb1a8a89b0b1f8cecf7185682c2ccaf --- /dev/null +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component.ts @@ -0,0 +1,67 @@ +import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; +import { StateResource, createEmptyStateResource } from '@alfa-client/tech-shared'; +import { + VorgangService, + VorgangWithEingangLinkRel, + VorgangWithEingangResource, +} from '@alfa-client/vorgang-shared'; +import { Component, Input } from '@angular/core'; +import { getUrl, hasLink } from '@ngxp/rest'; +import { Observable, of } from 'rxjs'; + +@Component({ + selector: 'alfa-vorgang-detail-dateien-container', + templateUrl: './vorgang-detail-dateien-container.component.html', +}) +export class VorgangDetailDateienContainerComponent { + @Input() set vorgangWithEingang(vorgangWithEingang: VorgangWithEingangResource) { + this.handleVorgangChange(vorgangWithEingang); + } + + public attachmentListStateResource$: Observable<StateResource<BinaryFileListResource>> = of( + createEmptyStateResource<BinaryFileListResource>(), + ); + public representationListStateResource$: Observable<StateResource<BinaryFileListResource>> = of( + createEmptyStateResource<BinaryFileListResource>(), + ); + + public downloadPrefix: string; + public attachmentsDownloadUri: string; + + public readonly vorgangWithEingangLinkRel = VorgangWithEingangLinkRel; + + constructor(private vorgangService: VorgangService) {} + + handleVorgangChange(vorgangWithEingang: VorgangWithEingangResource): void { + this.downloadPrefix = vorgangWithEingang.nummer; + + this.handleAttachments(vorgangWithEingang); + this.getRepresentationsIfExists(vorgangWithEingang); + } + + handleAttachments(vorgangWithEingang: VorgangWithEingangResource): void { + this.setAttachmentsDownloadUri(vorgangWithEingang); + this.getAttachmentsIfExists(vorgangWithEingang); + } + + setAttachmentsDownloadUri(vorgangWithEingang: VorgangWithEingangResource): void { + if (hasLink(vorgangWithEingang, VorgangWithEingangLinkRel.DOWNLOAD_ATTACHMENTS)) { + this.attachmentsDownloadUri = getUrl( + vorgangWithEingang, + VorgangWithEingangLinkRel.DOWNLOAD_ATTACHMENTS, + ); + } + } + + getRepresentationsIfExists(vorgangWithEingang: VorgangWithEingangResource): void { + if (hasLink(vorgangWithEingang, VorgangWithEingangLinkRel.REPRESENTATIONS)) { + this.representationListStateResource$ = this.vorgangService.getRepresentations(); + } + } + + getAttachmentsIfExists(vorgangWithEingang: VorgangWithEingangResource): void { + if (hasLink(vorgangWithEingang, VorgangWithEingangLinkRel.ATTACHMENTS)) { + this.attachmentListStateResource$ = this.vorgangService.getAttachments(); + } + } +} diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.html index fdb75c0c568b35de0627f2b6295459812f1d2b8f..1d4d2f566f9ea57e41318a3f59f7b40f0c19b308 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.html @@ -40,32 +40,20 @@ [antragsData]="antragsData" ></alfa-vorgang-detail-antrag-data> </mat-tab> - <mat-tab label="Metadaten" [disabled]="!metaData"> - <alfa-vorgang-detail-meta-data - data-test-id="tab-meta-data" - [metaData]="metaData" - ></alfa-vorgang-detail-meta-data> - </mat-tab> - <mat-tab - label="Datenrepräsentation ({{ vorgangWithEingang.eingang.numberOfRepresentations }})" - [disabled]="vorgangWithEingang | notHasLink: vorgangWithEingangLinkRel.REPRESENTATIONS" - > - <ng-template matTabContent> - <alfa-vorgang-detail-representation-list - data-test-id="tab-representations-list" - [vorgangWithEingang]="vorgangWithEingang" - ></alfa-vorgang-detail-representation-list> - </ng-template> - </mat-tab> <mat-tab - label="Anhänge ({{ vorgangWithEingang.eingang.numberOfAttachments }})" - [disabled]="vorgangWithEingang | notHasLink: vorgangWithEingangLinkRel.ATTACHMENTS" + label="Dateien ({{ filesNumber }})" + [disabled]=" + vorgangWithEingang + | notHasAnyLink + : vorgangWithEingangLinkRel.REPRESENTATIONS + : vorgangWithEingangLinkRel.ATTACHMENTS + " > <ng-template matTabContent> - <alfa-vorgang-detail-attachment-list - data-test-id="tab-attachments-list" + <alfa-vorgang-detail-dateien-container + *ngIf="vorgangWithEingang" [vorgangWithEingang]="vorgangWithEingang" - ></alfa-vorgang-detail-attachment-list> + ></alfa-vorgang-detail-dateien-container> </ng-template> </mat-tab> <mat-tab diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.spec.ts index dfe0151ba8bf70bb061b862471ec6b678e1b1952..83a309ba164e11fc7debead039783ae81e8aa925 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.spec.ts @@ -21,30 +21,27 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MatTabsModule } from '@angular/material/tabs'; -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { HistorieContainerComponent } from '@alfa-client/historie'; -import { HasLinkPipe, NotHasLinkPipe } from '@alfa-client/tech-shared'; +import { HasLinkPipe } from '@alfa-client/tech-shared'; import { getElementFromFixture } from '@alfa-client/test-utils'; import { ExpansionPanelComponent } from '@alfa-client/ui'; import { VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatTabsModule } from '@angular/material/tabs'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { NotHasAnyLinkPipe } from 'libs/tech-shared/src/lib/pipe/not-has-any-link.pipe'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { MockComponent } from 'ng-mocks'; import { VorgangDetailAntragDataComponent } from './vorgang-detail-antrag-data/vorgang-detail-antrag-data.component'; -import { VorgangDetailAttachmentListComponent } from './vorgang-detail-attachment-list/vorgang-detail-attachment-list.component'; +import { VorgangDetailDateienContainerComponent } from './vorgang-detail-dateien-container/vorgang-detail-dateien-container.component'; import { VorgangDetailFormularDatenComponent } from './vorgang-detail-formular-daten.component'; -import { VorgangDetailMetaDataComponent } from './vorgang-detail-meta-data/vorgang-detail-meta-data.component'; -import { VorgangDetailRepresentationListComponent } from './vorgang-detail-representation-list/vorgang-detail-representation-list.component'; describe('VorgangDetailFormularDatenComponent', () => { let component: VorgangDetailFormularDatenComponent; let fixture: ComponentFixture<VorgangDetailFormularDatenComponent>; - const tabMetaData: string = getTabByIndex(2); - const tabRepresentations: string = getTabByIndex(3); - const tabAttachments: string = getTabByIndex(4); - const tabHistorie: string = getTabByIndex(5); + const tabDateien: string = getTabByIndex(2); + const tabHistorie: string = getTabByIndex(3); function getTabByIndex(index: number): string { return `div[role=tab]:nth-child(${index})`; @@ -62,12 +59,10 @@ describe('VorgangDetailFormularDatenComponent', () => { imports: [NoopAnimationsModule, MatTabsModule], declarations: [ HasLinkPipe, - NotHasLinkPipe, + NotHasAnyLinkPipe, VorgangDetailFormularDatenComponent, MockComponent(VorgangDetailAntragDataComponent), - MockComponent(VorgangDetailMetaDataComponent), - MockComponent(VorgangDetailAttachmentListComponent), - MockComponent(VorgangDetailRepresentationListComponent), + MockComponent(VorgangDetailDateienContainerComponent), MockComponent(ExpansionPanelComponent), MockComponent(HistorieContainerComponent), ], @@ -85,98 +80,82 @@ describe('VorgangDetailFormularDatenComponent', () => { expect(component).toBeTruthy(); }); - describe('Metadaten', () => { - it('should show tab enabled if Vorgang contains metadata', () => { - component.metaData = { test: 'testValue' }; + describe('Tag Dateien', () => { + describe('Datenrepräsentation', () => { + const vorgangWithRepresentations: VorgangWithEingangResource = + createVorgangWithEingangResource([VorgangWithEingangLinkRel.REPRESENTATIONS]); - fixture.detectChanges(); - const metaDataTab = getElementFromFixture(fixture, tabMetaData); + it('should be enabled and selected if representations links is present', () => { + component.vorgangWithEingang = vorgangWithRepresentations; - expect(metaDataTab).not.toHaveClass(disabledTabClass); - }); + fixture.detectChanges(); - it('should disable tab if Vorgang contains no metadata', () => { - fixture.detectChanges(); - const metaDataTab = getElementFromFixture(fixture, tabMetaData); + const tab: HTMLElement = getElementFromFixture(fixture, tabDateien); + expect(tab).not.toHaveClass(disabledTabClass); + expect(tab).toHaveAttribute('aria-selected', 'true'); + }); - expect(metaDataTab).toHaveClass(disabledTabClass); - }); - - it('should show MetaData label', () => { - fixture.detectChanges(); - const metaDataTab = getElementFromFixture(fixture, tabMetaData); + it('should be disable and not selected if representations link is NOT present', () => { + component.vorgangWithEingang = vorgangWithEingang; - expect(metaDataTab).toHaveTextContent('Metadaten'); - }); - }); + fixture.detectChanges(); - describe('Tab Datenrepräsentation', () => { - const vorgangWithRepresentations: VorgangWithEingangResource = createVorgangWithEingangResource( - [VorgangWithEingangLinkRel.REPRESENTATIONS], - ); + const tab: HTMLElement = getElementFromFixture(fixture, tabDateien); + expect(tab).toHaveClass(disabledTabClass); + expect(tab).toHaveAttribute('aria-selected', 'false'); + }); - it('should be enabled and selected if representations links is present', () => { - component.vorgangWithEingang = vorgangWithRepresentations; + it('should show number of Representations in the label', () => { + component.vorgangWithEingang = { + ...vorgangWithRepresentations, + eingang: { ...vorgangWithRepresentations.eingang, numberOfAttachments: 0 }, + }; - fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabRepresentations); + fixture.detectChanges(); - expect(tab).not.toHaveClass(disabledTabClass); - expect(tab).toHaveAttribute('aria-selected', 'true'); + const tab: HTMLElement = getElementFromFixture(fixture, tabDateien); + expect(tab).toHaveTextContent( + `Dateien (${vorgangWithRepresentations.eingang.numberOfRepresentations})`, + ); + }); }); - it('should be disable and not selected if representations link is NOT present', () => { - component.vorgangWithEingang = vorgangWithEingang; + describe('Anhänge', () => { + const vorgangWithAttachments: VorgangWithEingangResource = createVorgangWithEingangResource([ + VorgangWithEingangLinkRel.ATTACHMENTS, + ]); - fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabRepresentations); + it('should be enabled if attachments link is present', () => { + component.vorgangWithEingang = vorgangWithAttachments; - expect(tab).toHaveClass(disabledTabClass); - expect(tab).toHaveAttribute('aria-selected', 'false'); - }); - - it('should show number of Representations in the label', () => { - component.vorgangWithEingang = vorgangWithRepresentations; - - fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabRepresentations); - const tabLabel = `Datenrepräsentation (${vorgangWithRepresentations.eingang.numberOfRepresentations})`; - - expect(tab).toHaveTextContent(tabLabel); - }); - }); - - describe('Tab Anhänge', () => { - const vorgangWithAttachments: VorgangWithEingangResource = createVorgangWithEingangResource([ - VorgangWithEingangLinkRel.ATTACHMENTS, - ]); + fixture.detectChanges(); - it('should be enabled if attachments link is present', () => { - component.vorgangWithEingang = vorgangWithAttachments; + const tab: HTMLElement = getElementFromFixture(fixture, tabDateien); + expect(tab).not.toHaveClass(disabledTabClass); + }); - fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabAttachments); - - expect(tab).not.toHaveClass(disabledTabClass); - }); - - it('should be disable if attachments link is NOT present', () => { - component.vorgangWithEingang = vorgangWithEingang; + it('should be disable if attachments link is NOT present', () => { + component.vorgangWithEingang = vorgangWithEingang; - fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabAttachments); + fixture.detectChanges(); - expect(tab).toHaveClass(disabledTabClass); - }); + const tab: HTMLElement = getElementFromFixture(fixture, tabDateien); + expect(tab).toHaveClass(disabledTabClass); + }); - it('should show number of Attachments in the label', () => { - component.vorgangWithEingang = vorgangWithAttachments; + it('should show number of Attachments in the label', () => { + component.vorgangWithEingang = { + ...vorgangWithAttachments, + eingang: { ...vorgangWithAttachments.eingang, numberOfRepresentations: 0 }, + }; - fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabAttachments); - const tabLabel = `Anhänge (${vorgangWithAttachments.eingang.numberOfAttachments})`; + fixture.detectChanges(); - expect(tab).toHaveTextContent(tabLabel); + const tab: HTMLElement = getElementFromFixture(fixture, tabDateien); + expect(tab).toHaveTextContent( + `Dateien (${vorgangWithAttachments.eingang.numberOfAttachments})`, + ); + }); }); }); @@ -185,8 +164,8 @@ describe('VorgangDetailFormularDatenComponent', () => { component.vorgangWithEingang = vorgangWithHistorie; fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabHistorie); + const tab: HTMLElement = getElementFromFixture(fixture, tabHistorie); expect(tab).toBeInTheDocument(); }); @@ -194,8 +173,8 @@ describe('VorgangDetailFormularDatenComponent', () => { component.vorgangWithEingang = vorgangWithEingang; fixture.detectChanges(); - const tab = getElementFromFixture(fixture, tabHistorie); + const tab: HTMLElement = getElementFromFixture(fixture, tabHistorie); expect(tab).not.toBeInTheDocument(); }); @@ -203,8 +182,8 @@ describe('VorgangDetailFormularDatenComponent', () => { component.vorgangWithEingang = vorgangWithHistorie; fixture.detectChanges(); - const metaDataTab = getElementFromFixture(fixture, tabHistorie); + const metaDataTab: HTMLElement = getElementFromFixture(fixture, tabHistorie); expect(metaDataTab).toHaveTextContent('Historie'); }); }); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.ts index 89c9894be1cd94b8e61ce7981abf3f4688c33caf..69a869b9ccdb4a363b3f9e0d75388f018cc1d22e 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component.ts @@ -21,8 +21,8 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Component, Input, OnInit } from '@angular/core'; import { VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; +import { Component, Input, OnInit } from '@angular/core'; import { hasLink } from '@ngxp/rest'; @Component({ @@ -33,7 +33,6 @@ import { hasLink } from '@ngxp/rest'; export class VorgangDetailFormularDatenComponent implements OnInit { @Input() vorgangWithEingang: VorgangWithEingangResource; - metaData: object; antragsData: object; readonly vorgangWithEingangLinkRel = VorgangWithEingangLinkRel; @@ -43,6 +42,13 @@ export class VorgangDetailFormularDatenComponent implements OnInit { } get defaultSelection(): number { - return hasLink(this.vorgangWithEingang, this.vorgangWithEingangLinkRel.REPRESENTATIONS) ? 2 : 0; + return hasLink(this.vorgangWithEingang, this.vorgangWithEingangLinkRel.REPRESENTATIONS) ? 1 : 0; + } + + get filesNumber(): number { + return ( + this.vorgangWithEingang.eingang.numberOfAttachments + + this.vorgangWithEingang.eingang.numberOfRepresentations + ); } } diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.html deleted file mode 100644 index 993f0b128ebd152b9b054e1f2c76acd5c54fd198..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.html +++ /dev/null @@ -1,29 +0,0 @@ -<!-- - - Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - Ministerpräsidenten des Landes Schleswig-Holstein - Staatskanzlei - Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - - Lizenziert unter der EUPL, Version 1.2 oder - sobald - diese von der Europäischen Kommission genehmigt wurden - - Folgeversionen der EUPL ("Lizenz"); - Sie dürfen dieses Werk ausschließlich gemäß - dieser Lizenz nutzen. - Eine Kopie der Lizenz finden Sie hier: - - https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - - Sofern nicht durch anwendbare Rechtsvorschriften - gefordert oder in schriftlicher Form vereinbart, wird - die unter der Lizenz verbreitete Software "so wie sie - ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - ausdrücklich oder stillschweigend - verbreitet. - Die sprachspezifischen Genehmigungen und Beschränkungen - unter der Lizenz sind dem Lizenztext zu entnehmen. - ---> -<alfa-vorgang-detail-form-data-table - data-test-id="metadaten" - [formData]="metaData" -></alfa-vorgang-detail-form-data-table> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.scss b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.scss deleted file mode 100644 index b571b3c71b2625b19658760f31f072dc3134468d..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.scss +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -:host { - display: inline-block; - margin-top: 0.5rem; - overflow: hidden; -} diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.spec.ts deleted file mode 100644 index 9a253e89f86b3dd11bfe9811f02f0839b141e315..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MockComponent } from 'ng-mocks'; -import { VorgangDetailFormDataTableComponent } from '../vorgang-detail-form-data-table/vorgang-detail-form-data-table.component'; -import { VorgangDetailMetaDataComponent } from './vorgang-detail-meta-data.component'; - -describe('VorgangDetailMetaDataComponent', () => { - let component: VorgangDetailMetaDataComponent; - let fixture: ComponentFixture<VorgangDetailMetaDataComponent>; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ - VorgangDetailMetaDataComponent, - MockComponent(VorgangDetailFormDataTableComponent), - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(VorgangDetailMetaDataComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.ts deleted file mode 100644 index 1ab47d3436611b4542506e51920f2e3dda936b6a..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { Component, Input } from '@angular/core'; - -@Component({ - selector: 'alfa-vorgang-detail-meta-data', - templateUrl: './vorgang-detail-meta-data.component.html', - styleUrls: ['./vorgang-detail-meta-data.component.scss'], -}) -export class VorgangDetailMetaDataComponent { - @Input() metaData: object; -} diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.html deleted file mode 100644 index 0c6b5172652511b4c53e0ace77438a6a55b0728a..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.html +++ /dev/null @@ -1,30 +0,0 @@ -<!-- - - Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - Ministerpräsidenten des Landes Schleswig-Holstein - Staatskanzlei - Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - - Lizenziert unter der EUPL, Version 1.2 oder - sobald - diese von der Europäischen Kommission genehmigt wurden - - Folgeversionen der EUPL ("Lizenz"); - Sie dürfen dieses Werk ausschließlich gemäß - dieser Lizenz nutzen. - Eine Kopie der Lizenz finden Sie hier: - - https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - - Sofern nicht durch anwendbare Rechtsvorschriften - gefordert oder in schriftlicher Form vereinbart, wird - die unter der Lizenz verbreitete Software "so wie sie - ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - ausdrücklich oder stillschweigend - verbreitet. - Die sprachspezifischen Genehmigungen und Beschränkungen - unter der Lizenz sind dem Lizenztext zu entnehmen. - ---> -<alfa-vertical-binary-file-list - [fileListResource]="fileListResource$ | async" - [downloadFileNamePrefix]="vorgangWithEingang.nummer" -> -</alfa-vertical-binary-file-list> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.scss b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.scss deleted file mode 100644 index 9a08a5aabce6cc4cdbb268c4190a8d67f82f19e5..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.spec.ts deleted file mode 100644 index 04a6e0a8f98a6568cade6dadaa49deeab04ebeab..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { VerticalBinaryFileListComponent } from '@alfa-client/binary-file'; -import { mock } from '@alfa-client/test-utils'; -import { - VorgangService, - VorgangWithEingangLinkRel, - VorgangWithEingangResource, -} from '@alfa-client/vorgang-shared'; -import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { MockComponent } from 'ng-mocks'; -import { VorgangDetailRepresentationListComponent } from './vorgang-detail-representation-list.component'; - -describe('VorgangDetailRepresentationListComponent', () => { - let component: VorgangDetailRepresentationListComponent; - let fixture: ComponentFixture<VorgangDetailRepresentationListComponent>; - - const vorgangService = mock(VorgangService); - const vorgangWithRepresentations: VorgangWithEingangResource = createVorgangWithEingangResource([ - VorgangWithEingangLinkRel.REPRESENTATIONS, - ]); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ - VorgangDetailRepresentationListComponent, - MockComponent(VerticalBinaryFileListComponent), - ], - providers: [ - { - provide: VorgangService, - useValue: vorgangService, - }, - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(VorgangDetailRepresentationListComponent); - component = fixture.componentInstance; - component.vorgangWithEingang = createVorgangWithEingangResource(); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('on vorgang with "representation" link', () => { - it('should call vorgangService methods', () => { - component.vorgangWithEingang = vorgangWithRepresentations; - - component.ngOnInit(); - - expect(vorgangService.getRepresentations).toHaveBeenCalled(); - }); - }); -}); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.ts deleted file mode 100644 index 2007d106199389a23bd6370bee91fd36f5cfca2f..0000000000000000000000000000000000000000 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -import { Component, Input, OnInit } from '@angular/core'; -import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; -import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; -import { VorgangService, VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; -import { Observable, of } from 'rxjs'; - -@Component({ - selector: 'alfa-vorgang-detail-representation-list', - templateUrl: './vorgang-detail-representation-list.component.html', - styleUrls: ['./vorgang-detail-representation-list.component.scss'], -}) -export class VorgangDetailRepresentationListComponent implements OnInit { - @Input() vorgangWithEingang: VorgangWithEingangResource; - - fileListResource$: Observable<StateResource<BinaryFileListResource>> = of( - createEmptyStateResource<BinaryFileListResource>(), - ); - - constructor(private vorgangService: VorgangService) {} - - ngOnInit(): void { - this.fileListResource$ = this.vorgangService.getRepresentations(); - } -} diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.spec.ts index 9e29f9d8172e162e37e5187cb5729efe2dd45eaf..5c631ffeb6e6c4007028f95655df19047482b1b9 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.spec.ts @@ -9,10 +9,10 @@ import { import { BinaryFileResource } from '@alfa-client/binary-file-shared'; import { CommandLinkRel, CommandResource } from '@alfa-client/command-shared'; import { - createStateResource, EMPTY_STRING, - formatForDatabase, StateResource, + createStateResource, + formatForDatabase, } from '@alfa-client/tech-shared'; import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; @@ -114,6 +114,22 @@ describe('BescheidenFormService', () => { expect(service.sendByManual.value).toBeFalsy(); }); + it('should not emit if value not changed', () => { + service.sendByManual.next(true); + service.sendByManual.next = jest.fn(); + + service.updateSendByManual(BescheidSendBy.MANUAL); + + expect(service.sendByManual.next).not.toHaveBeenCalled(); + }); + it('should emit on value change', () => { + service.sendByManual.next(false); + service.sendByManual.next = jest.fn(); + + service.updateSendByManual(BescheidSendBy.MANUAL); + + expect(service.sendByManual.next).toHaveBeenCalled(); + }); }); describe('initializeFormChanges', () => { diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.ts index d9e05716a5c15264f7f5b671ecec6a8f61cc1ca1..b079362062868d5fd121503655948db80538f466 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/bescheiden.formservice.ts @@ -10,14 +10,14 @@ import { BinaryFileResource } from '@alfa-client/binary-file-shared'; import { CommandResource, tapOnCommandSuccessfullyDone } from '@alfa-client/command-shared'; import { AbstractFormService, - convertToBoolean, EMPTY_STRING, - formatForDatabase, HttpError, + StateResource, + convertToBoolean, + formatForDatabase, isLoaded, isNotEmpty, isNotNil, - StateResource, } from '@alfa-client/tech-shared'; import { VorgangWithEingangResource } from '@alfa-client/vorgang-shared'; import { Injectable, OnDestroy } from '@angular/core'; @@ -28,18 +28,18 @@ import { UntypedFormControl, UntypedFormGroup, } from '@angular/forms'; -import { getUrl, hasLink, Resource, ResourceUri } from '@ngxp/rest'; +import { Resource, ResourceUri, getUrl, hasLink } from '@ngxp/rest'; import { isEmpty, isNil, isUndefined } from 'lodash-es'; import { BehaviorSubject, + Observable, + Subject, + Subscription, combineLatest, filter, first, map, - Observable, startWith, - Subject, - Subscription, } from 'rxjs'; @Injectable() @@ -92,7 +92,8 @@ export class BescheidenFormService extends AbstractFormService implements OnDest } updateSendByManual(sendBy: BescheidSendBy): void { - this.sendByManual.next(sendBy === BescheidSendBy.MANUAL); + const isSendByManual = sendBy === BescheidSendBy.MANUAL; + if (isSendByManual !== this.sendByManual.value) this.sendByManual.next(isSendByManual); } initializeFormChanges(): void { diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.html index 95e424d51d4542bce8bc97612cd8f632bca51b48..ff4e542e0d9181e9ee7bc98b468e62b2a1d23482 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.html @@ -1,4 +1,4 @@ -<ods-attachment-container> +<ods-attachment-wrapper> <alfa-binary-file2-container *ngFor="let attachment of existingAttachments" [file]="attachment" @@ -27,4 +27,4 @@ [isLoading]="uploadFileInProgress.loading" ></ods-attachment> </ng-container> -</ods-attachment-container> +</ods-attachment-wrapper> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.spec.ts index 38a32fe726c44d6f7c0152e0caec38bfb8c90791..63758c537759f45fbe750b6c286b6b2e5778864d 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-attachments/vorgang-detail-bescheiden-result-attachments.component.spec.ts @@ -14,11 +14,7 @@ import { existsAsHtmlElement, Mock, mock, notExistsAsHtmlElement } from '@alfa-c import { OzgcloudSvgIconComponent, SpinnerComponent } from '@alfa-client/ui'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIcon } from '@angular/material/icon'; -import { - AttachmentComponent, - AttachmentContainerComponent, - SpinnerIconComponent, -} from '@ods/system'; +import { AttachmentComponent, AttachmentWrapperComponent, SpinnerIconComponent } from '@ods/system'; import { MockComponent, MockPipe } from 'ng-mocks'; import { BehaviorSubject, EMPTY, Observable, of, Subscription } from 'rxjs'; import { createUploadFileInProgress } from '../../../../../../../bescheid-shared/src/test/bescheid'; @@ -58,7 +54,7 @@ describe('VorgangDetailBescheidenResultAttachmentsComponent', () => { MockPipe(ConvertApiErrorToErrorMessagesPipe), MockComponent(OzgcloudSvgIconComponent), MockComponent(SpinnerComponent), - MockComponent(AttachmentContainerComponent), + MockComponent(AttachmentWrapperComponent), MockComponent(BinaryFile2ContainerComponent), MockComponent(SpinnerIconComponent), MockComponent(AttachmentComponent), diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.html index 2080ba5b2a7b977a0837a437c97ae3d1579ba396..bb762af88d7b449d222804be6eccd2be60690f5e 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.html @@ -5,7 +5,7 @@ > Bitte fügen Sie ein Bescheiddokument hinzu. </p> -<ods-attachment-container> +<ods-attachment-wrapper> <ng-container *ngIf="bescheidDocumentFile.resource"> <alfa-binary-file2-container *ngIf=" @@ -39,4 +39,4 @@ [errorMessages]="createDocumentErrorMessages" data-test-id="create-bescheid-document-attachment" ></ods-attachment> -</ods-attachment-container> +</ods-attachment-wrapper> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.spec.ts index 15a99f1bea6f85d4287f1ca9d0a85fffc4144dbc..c61b90e368bb525ef644dfd2e95d156c06338d22 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-dokument/vorgang-detail-bescheiden-result-dokument.component.spec.ts @@ -3,14 +3,14 @@ import { BinaryFile2ContainerComponent } from '@alfa-client/binary-file'; import { CommandResource } from '@alfa-client/command-shared'; import { ConvertApiErrorToErrorMessagesPipe, + StateResource, createEmptyStateResource, createStateResource, - StateResource, } from '@alfa-client/tech-shared'; -import { existsAsHtmlElement, Mock, mock, notExistsAsHtmlElement } from '@alfa-client/test-utils'; +import { Mock, existsAsHtmlElement, mock, notExistsAsHtmlElement } from '@alfa-client/test-utils'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { getUrl } from '@ngxp/rest'; -import { AttachmentComponent, AttachmentContainerComponent } from '@ods/system'; +import { AttachmentComponent, AttachmentWrapperComponent } from '@ods/system'; import { createBescheidResource } from 'libs/bescheid-shared/src/test/bescheid'; import { createBinaryFileResource } from 'libs/binary-file-shared/test/binary-file'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; @@ -47,7 +47,7 @@ describe('VorgangDetailBescheidenResultDokumentComponent', () => { VorgangDetailBescheidenResultDokumentComponent, MockComponent(BinaryFile2ContainerComponent), MockComponent(AttachmentComponent), - MockComponent(AttachmentContainerComponent), + MockComponent(AttachmentWrapperComponent), MockPipe(ConvertApiErrorToErrorMessagesPipe), ], providers: [ diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-nachricht/vorgang-detail-bescheiden-result-nachricht.component.html b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-nachricht/vorgang-detail-bescheiden-result-nachricht.component.html index 0b4b30cf95db4e50dbaec098d73c7b5fe1d0399b..5f5be409ae38611e4d72d5712e7beaae28677299 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-nachricht/vorgang-detail-bescheiden-result-nachricht.component.html +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result-nachricht/vorgang-detail-bescheiden-result-nachricht.component.html @@ -12,6 +12,7 @@ label="Betreff" placeholder="Betreff hier eingeben" [focus]="focusBetreff" + [isRequired]="true" > </ods-text-editor> @@ -20,6 +21,7 @@ label="Text" placeholder="Nachrichtentext hier eingeben" [focus]="focusNachricht" + [isRequired]="true" > </ods-textarea-editor> </div> diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.spec.ts index 63d46924cd75604ec3975f452e36034aecd854c0..22332fc5187c9ce1bc318ceb77b4b7ad0e30e833 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.spec.ts @@ -59,6 +59,7 @@ describe('VorgangDetailBescheidenResultComponent', () => { new BehaviorSubject({ beschiedenAm: new Date(), bewilligt: false }), ); formService.getActiveStep.mockReturnValue(EMPTY); + formService.isSendByManual.mockReturnValue(EMPTY); await TestBed.configureTestingModule({ declarations: [ @@ -213,42 +214,18 @@ describe('VorgangDetailBescheidenResultComponent', () => { expect(bescheidService.getBescheidDocument).toHaveBeenCalled(); }); - it('should call formservice to get active step', () => { - component.ngOnInit(); - - expect(formService.getActiveStep).toHaveBeenCalled(); - }); - - it.each([1, 2])('should reset save and send in progress in step %d', (step: number) => { - formService.getActiveStep.mockReturnValue(of(step)); - component.resetSend = jest.fn(); - - component.ngOnInit(); - - component.activeStep$.subscribe(); - expect(component.resetSend).toHaveBeenCalled(); - }); - - it('should not reset save and send in progress in last step', () => { - formService.getActiveStep.mockReturnValue(of(3)); - component.resetSend = jest.fn(); - - component.ngOnInit(); - - component.activeStep$.subscribe(); - expect(component.resetSend).not.toHaveBeenCalled(); - }); - it('should call formservice to get current bescheid/formular', () => { component.ngOnInit(); expect(formService.getBescheidChanges).toHaveBeenCalled(); }); - it('should call formservice to get sendByManual', () => { + it('should get is send by manual', () => { + component.getIsSendManually = jest.fn(); + component.ngOnInit(); - expect(formService.isSendByManual).toHaveBeenCalled(); + expect(component.getIsSendManually).toHaveBeenCalled(); }); it('should call service to get upload bescheid document in progress', () => { @@ -263,6 +240,14 @@ describe('VorgangDetailBescheidenResultComponent', () => { expect(bescheidService.getCreateBescheidDocumentInProgress).toHaveBeenCalled(); }); + it('should get active step', () => { + component.getActiveStep = jest.fn(); + + component.ngOnInit(); + + expect(component.getActiveStep).toHaveBeenCalled(); + }); + describe('canSave$', () => { it('should emit true', () => { bescheidService.getBescheidDraft.mockReturnValue( @@ -620,21 +605,93 @@ describe('VorgangDetailBescheidenResultComponent', () => { }); }); - describe('ifNotLastStep', () => { - it.each([1, 2])('should do it in step %d', (step: number) => { - const doIt = jest.fn(); + describe('get active step', () => { + it('should call formService', () => { + component.getActiveStep().pipe(first()).subscribe(); + + expect(formService.getActiveStep).toHaveBeenCalled(); + }); + + it.each([1, VorgangDetailBescheidenResultComponent.BESCHEID_VERSENDEN_STEP])( + 'should call bescheidService to clear attachment upload on step %s', + (step: number) => { + formService.getActiveStep.mockReturnValue(of(step)); + + component.getActiveStep().pipe(first()).subscribe(); + + expect(bescheidService.clearAttachmentUpload).toHaveBeenCalled(); + }, + ); + + describe('reset send', () => { + beforeEach(() => { + component.resetSend = jest.fn(); + }); + + it.each([1, VorgangDetailBescheidenResultComponent.ADD_DOCUMENTS_STEP])( + 'should be called on step %s', + (step: number) => { + formService.getActiveStep.mockReturnValue(of(step)); + + component.getActiveStep().pipe(first()).subscribe(); + + expect(component.resetSend).toHaveBeenCalled(); + }, + ); + + it('should not be called on last step', () => { + formService.getActiveStep.mockReturnValue(of(3)); + + component.getActiveStep().pipe(first()).subscribe(); + + expect(component.resetSend).not.toHaveBeenCalled(); + }); + }); + }); + + describe('reset send', () => { + it('should clear saveAndSendInProgress', (done) => { + component.saveAndSendInProgress$ = of(createCommandStateResource()); - of(step).pipe(component.ifNotLastStep(doIt)).subscribe(); + component.resetSend(); - expect(doIt).toHaveBeenCalled(); + component.saveAndSendInProgress$ + .pipe(first()) + .subscribe((saveAndSendInProgress: StateResource<CommandResource>) => { + expect(saveAndSendInProgress).toEqual(createEmptyStateResource()); + done(); + }); }); - it('should not do it in last step', () => { - const doIt = jest.fn(); + it('should clear sendWithNachricht', () => { + component.sendWithNachricht$.next(createCommandStateResource()); + + component.resetSend(); - of(3).pipe(component.ifNotLastStep(doIt)).subscribe(); + expect(component.sendWithNachricht$.value).toEqual(createEmptyStateResource()); + }); + }); - expect(doIt).not.toHaveBeenCalled(); + describe('getIsSendManually', () => { + beforeEach(() => { + component.resetSend = jest.fn(); }); + + it('should get is send by manual from from service', () => { + component.getIsSendManually(); + + expect(formService.isSendByManual).toHaveBeenCalled(); + }); + + it.each([true, false])( + 'should reset send if send by manual is %s', + (isSendByManual: boolean) => { + formService.isSendByManual.mockReturnValue(of(isSendByManual)); + + component.getIsSendManually().subscribe(); + + expect(component.resetSend).toHaveBeenCalled(); + }, + ); }); }); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.ts index 277c06197a06582d354c5f19e4985d97e7cef6c7..5b423f9fdb6be53a986356f5bab275490a479dc7 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-bescheiden/vorgang-detail-bescheiden-result/vorgang-detail-bescheiden-result.component.ts @@ -28,7 +28,8 @@ type sendBescheid = ( templateUrl: './vorgang-detail-bescheiden-result.component.html', }) export class VorgangDetailBescheidenResultComponent implements OnInit { - private static readonly LAST_STEP: number = 3; + static readonly ADD_DOCUMENTS_STEP: number = 2; + static readonly BESCHEID_VERSENDEN_STEP: number = 3; @Output() closeDialog: EventEmitter<void> = new EventEmitter(); @@ -81,11 +82,9 @@ export class VorgangDetailBescheidenResultComponent implements OnInit { this.bescheidService.getCreateBescheidDocumentInProgress(); this.bescheidDocument$ = this.bescheidService.getBescheidDocument(); - this.activeStep$ = this.formService - .getActiveStep() - .pipe(this.ifNotLastStep(() => this.resetSend())); + this.activeStep$ = this.getActiveStep(); this.bescheid$ = this.formService.getBescheidChanges(); - this.sendByManual$ = this.formService.isSendByManual(); + this.sendByManual$ = this.getIsSendManually(); this.canSave$ = this.bescheidDraftStateResource$.pipe( filter(isLoaded), @@ -102,16 +101,27 @@ export class VorgangDetailBescheidenResultComponent implements OnInit { this.showMissingBescheidDocumentError$ = this.formService.getShowMissingBescheidDocumentError(); } - ifNotLastStep(doIt: () => void): OperatorFunction<number, number> { - return (source: Observable<number>) => { - return source.pipe( - tap((step: number) => { - if (step < VorgangDetailBescheidenResultComponent.LAST_STEP) { - doIt(); - } - }), - ); - }; + getActiveStep(): Observable<number> { + return this.formService + .getActiveStep() + .pipe(tap((step: number) => this.resetStateOnStepChange(step))); + } + + getIsSendManually(): Observable<boolean> { + return this.formService.isSendByManual().pipe(tap(() => this.resetSend())); + } + + private resetStateOnStepChange(step: number): void { + if (this.isNotLast(step)) this.resetSend(); + if (this.shouldClearAttachmentInProgress(step)) this.bescheidService.clearAttachmentUpload(); + } + + private isNotLast(step: number): boolean { + return step < VorgangDetailBescheidenResultComponent.BESCHEID_VERSENDEN_STEP; + } + + private shouldClearAttachmentInProgress(step: number): boolean { + return step != VorgangDetailBescheidenResultComponent.ADD_DOCUMENTS_STEP; } resetSend(): void { diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.spec.ts index 7466616259f69afe3069e61596a3af74acb2b891..a3554bb8bab782f85f2d0924eeb5dd51c9a906d5 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.spec.ts @@ -22,9 +22,20 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { LoeschAnforderungService } from '@alfa-client/loesch-anforderung-shared'; -import { StateResource, createStateResource } from '@alfa-client/tech-shared'; +import { + Issue, + MessageCode, + StateResource, + createErrorStateResource, + createStateResource, +} from '@alfa-client/tech-shared'; import { Mock, existsAsHtmlElement, mock, notExistsAsHtmlElement } from '@alfa-client/test-utils'; -import { SpinnerComponent, SpinnerTransparencyComponent } from '@alfa-client/ui'; +import { + Messages, + SnackBarService, + SpinnerComponent, + SpinnerTransparencyComponent, +} from '@alfa-client/ui'; import { VorgangCommandService, VorgangService, @@ -34,11 +45,12 @@ import { import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIcon } from '@angular/material/icon'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; +import { createApiError, createIssue } from 'libs/tech-shared/test/error'; import { ProgressBarComponent } from 'libs/ui/src/lib/ui/progress-bar/progress-bar.component'; import { SubnavigationComponent } from 'libs/ui/src/lib/ui/subnavigation/subnavigation.component'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { MockComponent } from 'ng-mocks'; -import { Subject } from 'rxjs'; +import { EMPTY, Subject, of } from 'rxjs'; import { VorgangDetailActionButtonsComponent } from './vorgang-detail-area/vorgang-detail-action-buttons/vorgang-detail-action-buttons.component'; import { VorgangDetailAreaComponent } from './vorgang-detail-area/vorgang-detail-area.component'; import { VorgangDetailFormularButtonsComponent } from './vorgang-detail-area/vorgang-detail-formular-buttons/vorgang-detail-formular-buttons.component'; @@ -46,6 +58,9 @@ import { VorgangDetailBackButtonContainerComponent } from './vorgang-detail-back import { VorgangDetailMoreMenuComponent } from './vorgang-detail-more-menu/vorgang-detail-more-menu.component'; import { VorgangDetailPageComponent } from './vorgang-detail-page.component'; +import { NavigationService } from '@alfa-client/navigation-shared'; +import * as ErrorUtil from 'libs/tech-shared/src/lib/error/error.util'; + type withGetVorgang = { getVorgangWithEingang: () => Subject<StateResource<VorgangWithEingang>>; }; @@ -58,6 +73,8 @@ describe('VorgangDetailPageComponent', () => { let vorgangService: Mock<VorgangService> | withGetVorgang; const vorgangCommandService = mock(VorgangCommandService); const loeschAnforderungService = mock(LoeschAnforderungService); + const navigationService: Mock<NavigationService> = mock(NavigationService); + const snackbarService: Mock<SnackBarService> = mock(SnackBarService); const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); @@ -100,6 +117,14 @@ describe('VorgangDetailPageComponent', () => { provide: LoeschAnforderungService, useValue: loeschAnforderungService, }, + { + provide: NavigationService, + useValue: navigationService, + }, + { + provide: SnackBarService, + useValue: snackbarService, + }, ], }); @@ -114,7 +139,7 @@ describe('VorgangDetailPageComponent', () => { describe('ngOnInit', () => { it('should get vorgangWithEingang', () => { - vorgangService.getVorgangWithEingang = jest.fn(); + vorgangService.getVorgangWithEingang = jest.fn().mockReturnValue(of(EMPTY)); component.ngOnInit(); @@ -197,4 +222,62 @@ describe('VorgangDetailPageComponent', () => { existsAsHtmlElement(fixture, detailArea); }); }); + + describe('handleApiError', () => { + afterEach(() => { + snackbarService.showInfo.mockClear(); + navigationService.navigateToVorgangList.mockClear(); + }); + + describe('with ApiError RESOURCE_NOT_FOUND', () => { + const issue: Issue = { ...createIssue(), messageCode: MessageCode.RESOURCE_NOT_FOUND }; + const resource: StateResource<any> = createErrorStateResource(createApiError([issue])); + + it('should call isResourceNotFoundError', () => { + const isResourceNotFoundError: jest.SpyInstance<boolean> = jest + .spyOn(ErrorUtil, 'isResourceNotFoundError') + .mockClear(); + + component.handleApiError(resource); + + expect(isResourceNotFoundError).toHaveBeenCalled(); + }); + + it('should call showSnackBarInfo', () => { + component.handleApiError(resource); + + expect(snackbarService.showInfo).toHaveBeenCalledWith( + Messages.HTTP_STATUS_VORGANG_NOT_FOUND, + ); + }); + + it('should call navigateToVorgangList', () => { + component.handleApiError(resource); + + expect(navigationService.navigateToVorgangList).toHaveBeenCalled(); + }); + }); + + describe('without ApiError', () => { + it('should not call snackbarService.showInfo', () => { + const resource: StateResource<any> = createStateResource( + createVorgangWithEingangResource(), + ); + + component.handleApiError(resource); + + expect(snackbarService.showInfo).not.toHaveBeenCalled(); + }); + + it('should not call navigationService.navigateToVorgangList', () => { + const resource: StateResource<any> = createStateResource( + createVorgangWithEingangResource(), + ); + + component.handleApiError(resource); + + expect(navigationService.navigateToVorgangList).not.toHaveBeenCalled(); + }); + }); + }); }); diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.ts index 8d51e40d64b5f8a1cc56566ff9a5bdcf733cfebb..97e2bae42d5480038b639184224643bf1b440318 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail-page/vorgang-detail-page.component.ts @@ -23,14 +23,16 @@ */ import { CommandResource } from '@alfa-client/command-shared'; import { LoeschAnforderungService } from '@alfa-client/loesch-anforderung-shared'; -import { StateResource } from '@alfa-client/tech-shared'; +import { NavigationService } from '@alfa-client/navigation-shared'; +import { StateResource, isResourceNotFoundError } from '@alfa-client/tech-shared'; +import { Messages, SnackBarService } from '@alfa-client/ui'; import { VorgangCommandService, VorgangService, VorgangWithEingangResource, } from '@alfa-client/vorgang-shared'; import { Component, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Observable, tap } from 'rxjs'; import { BescheidenFormService } from './vorgang-detail-bescheiden/bescheiden.formservice'; @Component({ @@ -48,12 +50,25 @@ export class VorgangDetailPageComponent implements OnInit { private vorgangService: VorgangService, private vorgangCommandService: VorgangCommandService, private loeschAnforderungService: LoeschAnforderungService, + public snackbarService: SnackBarService, + public navigationService: NavigationService, ) {} ngOnInit(): void { - this.vorgangStateResource$ = this.vorgangService.getVorgangWithEingang(); + this.vorgangStateResource$ = this.vorgangService + .getVorgangWithEingang() + .pipe( + tap((resource: StateResource<VorgangWithEingangResource>) => this.handleApiError(resource)), + ); this.revokeCommandStateResource$ = this.vorgangCommandService.getRevokeCommand(); this.vorgangLoeschenCommandStateResource$ = this.loeschAnforderungService.getEndgueltigLoeschenCommand(); } + + handleApiError(resource: StateResource<VorgangWithEingangResource>) { + if (isResourceNotFoundError(resource.error)) { + this.snackbarService.showInfo(Messages.HTTP_STATUS_VORGANG_NOT_FOUND); + this.navigationService.navigateToVorgangList(); + } + } } diff --git a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail.module.ts b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail.module.ts index 910757039e1fdfe3e1b5ff885a2491687d92cfa2..5a6d84231e9b41aa3bc51655315edd6ff5e4e531 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail.module.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/vorgang-detail.module.ts @@ -49,7 +49,7 @@ import { } from '@ods/component'; import { AttachmentComponent, - AttachmentContainerComponent, + AttachmentWrapperComponent, BescheidGenerateIconComponent, BescheidStatusTextComponent, BescheidUploadIconComponent, @@ -80,11 +80,9 @@ import { VorgangDetailAreaComponent } from './vorgang-detail-page/vorgang-detail import { VorgangDetailFormularButtonsComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-buttons/vorgang-detail-formular-buttons.component'; import { VorgangDetailAntragDataComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-antrag-data/vorgang-detail-antrag-data.component'; import { VorgangDetailEingangHeaderComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-antrag-data/vorgang-detail-eingang-header/vorgang-detail-eingang-header.component'; -import { VorgangDetailAttachmentListComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-attachment-list/vorgang-detail-attachment-list.component'; +import { VorgangDetailDateienContainerComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-dateien-container/vorgang-detail-dateien-container.component'; import { VorgangDetailFormDataTableComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-form-data-table/vorgang-detail-form-data-table.component'; import { VorgangDetailFormularDatenComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-formular-daten.component'; -import { VorgangDetailMetaDataComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-meta-data/vorgang-detail-meta-data.component'; -import { VorgangDetailRepresentationListComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-formular-daten/vorgang-detail-representation-list/vorgang-detail-representation-list.component'; import { VorgangDetailHeaderComponent } from './vorgang-detail-page/vorgang-detail-area/vorgang-detail-header/vorgang-detail-header.component'; import { VorgangDetailBackButtonContainerComponent } from './vorgang-detail-page/vorgang-detail-back-button-container/vorgang-detail-back-button-container.component'; import { VorgangDetailBackButtonComponent } from './vorgang-detail-page/vorgang-detail-back-button-container/vorgang-detail-back-button/vorgang-detail-back-button.component'; @@ -155,7 +153,7 @@ const routes: Routes = [ BescheidUploadIconComponent, BescheidGenerateIconComponent, AttachmentComponent, - AttachmentContainerComponent, + AttachmentWrapperComponent, ButtonWithSpinnerComponent, FileUploadEditorComponent, SingleFileUploadEditorComponent, @@ -185,9 +183,6 @@ const routes: Routes = [ VorgangDetailBackButtonContainerComponent, VorgangDetailFormularDatenComponent, VorgangDetailAntragDataComponent, - VorgangDetailMetaDataComponent, - VorgangDetailAttachmentListComponent, - VorgangDetailRepresentationListComponent, VorgangDetailMoreMenuComponent, VorgangExportContainerComponent, ProcessVorgangContainerComponent, @@ -216,13 +211,11 @@ const routes: Routes = [ VorgangDetailBescheidenResultAttachmentsComponent, VorgangDetailBescheidenBescheidVersendenComponent, VorgangDetailBescheidenResultNachrichtComponent, + VorgangDetailDateienContainerComponent, ], exports: [ VorgangDetailAntragstellerComponent, VorgangDetailAntragDataComponent, - VorgangDetailMetaDataComponent, - VorgangDetailAttachmentListComponent, - VorgangDetailRepresentationListComponent, VorgangDetailBescheidenComponent, ], }) diff --git a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.spec.ts b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.spec.ts index f84d501a719fb4fe3a75a86a22a2ed92757f2850..e9d054d5197c95f9912ce6b5a787df923694d446 100644 --- a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.spec.ts +++ b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.spec.ts @@ -21,9 +21,6 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { FormControl, UntypedFormBuilder } from '@angular/forms'; -import { Params } from '@angular/router'; -import { faker } from '@faker-js/faker'; import { NavigationService } from '@alfa-client/navigation-shared'; import { EMPTY_STRING, encodeUrlForEmbedding } from '@alfa-client/tech-shared'; import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; @@ -32,6 +29,9 @@ import { VorgangListService, VorgangResource, } from '@alfa-client/vorgang-shared'; +import { FormControl, UntypedFormBuilder } from '@angular/forms'; +import { Params } from '@angular/router'; +import { faker } from '@faker-js/faker'; import { ResourceUri, getUrl } from '@ngxp/rest'; import { createVorgangResource } from 'libs/vorgang-shared/test/vorgang'; import { Observable, of } from 'rxjs'; @@ -131,15 +131,6 @@ describe('VorgangSearchFormService', () => { expect(shouldSearch).toBeFalsy(); }); - - it('on searchString is equals lastsearchString', () => { - formService.lastSearchString = VALID_SEARCH_STRING; - formService.isSearchInputNotPristine = jest.fn().mockReturnValue(true); - - const shouldSearch: boolean = formService.shouldSearchForPreview(VALID_SEARCH_STRING); - - expect(shouldSearch).toBeFalsy(); - }); }); describe('should return true', () => { @@ -148,7 +139,7 @@ describe('VorgangSearchFormService', () => { formService.isSearchInputNotPristine = jest.fn().mockReturnValue(true); }); - it('on searchInput is NOT pristine, has minLength and is not lastSearchString', () => { + it('on searchInput is NOT pristine and has minLength', () => { const shouldSearch: boolean = formService.shouldSearchForPreview(VALID_SEARCH_STRING); expect(shouldSearch).toBeTruthy(); diff --git a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.ts b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.ts index 3a65e9250ae11831cf30e208f7898e93a78ffeaf..bf88a1381c9a827c61a71dc204f5cbaf820ca63a 100644 --- a/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.ts +++ b/alfa-client/libs/vorgang-shared-ui/src/lib/vorgang-search-container/vorgang-search/vorgang-search.formservice.ts @@ -21,9 +21,6 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Injectable, OnDestroy } from '@angular/core'; -import { FormGroup, UntypedFormBuilder, UntypedFormControl } from '@angular/forms'; -import { Params } from '@angular/router'; import { NavigationService } from '@alfa-client/navigation-shared'; import { EMPTY_STRING, @@ -37,7 +34,10 @@ import { VorgangListService, VorgangResource, } from '@alfa-client/vorgang-shared'; -import { isEmpty, isEqual } from 'lodash-es'; +import { Injectable, OnDestroy } from '@angular/core'; +import { FormGroup, UntypedFormBuilder, UntypedFormControl } from '@angular/forms'; +import { Params } from '@angular/router'; +import { isEmpty } from 'lodash-es'; import { Observable, Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged, first, tap } from 'rxjs/operators'; @@ -100,9 +100,7 @@ export class VorgangSearchFormService implements OnDestroy { shouldSearchForPreview(value: string): boolean { return ( - this.isSearchInputNotPristine() && - hasMinLength(value, this.PREVIEW_SEARCH_STRING_MIN_LENGTH) && - !isEqual(this.lastSearchString, value) + this.isSearchInputNotPristine() && hasMinLength(value, this.PREVIEW_SEARCH_STRING_MIN_LENGTH) ); } diff --git a/alfa-client/libs/vorgang-shared/src/lib/vorgang.linkrel.ts b/alfa-client/libs/vorgang-shared/src/lib/vorgang.linkrel.ts index 508ab793ded8f30eb496de410dea11af6e79b417..57cfeeab38cb013afb230703102924d436b2acd0 100644 --- a/alfa-client/libs/vorgang-shared/src/lib/vorgang.linkrel.ts +++ b/alfa-client/libs/vorgang-shared/src/lib/vorgang.linkrel.ts @@ -64,6 +64,7 @@ export enum VorgangWithEingangLinkRel { BESCHEID_DRAFT = 'bescheidDraft', BESCHEIDE = 'bescheide', UEBERSPRINGEN_UND_ABSCHLIESSEN = 'ueberspringen_und_abschliessen', + DOWNLOAD_ATTACHMENTS = 'downloadAttachments', } export enum LoeschAnforderungLinkRel { diff --git a/alfa-client/libs/vorgang/src/lib/vorgang-list-container/vorgang-list/vorgang-list-item/vorgang-list-item.component.html b/alfa-client/libs/vorgang/src/lib/vorgang-list-container/vorgang-list/vorgang-list-item/vorgang-list-item.component.html index f0c81c71c4fc151f86c17011eabc9193f48f2cef..4ae91cb40e746eef978cc1682f431ec3ab17e9f1 100644 --- a/alfa-client/libs/vorgang/src/lib/vorgang-list-container/vorgang-list/vorgang-list-item/vorgang-list-item.component.html +++ b/alfa-client/libs/vorgang/src/lib/vorgang-list-container/vorgang-list/vorgang-list-item/vorgang-list-item.component.html @@ -52,7 +52,7 @@ </div> </div> <div class="flex min-w-0 flex-1 flex-col gap-2"> - <div class="text-base font-medium">{{ vorgang.name }}</div> + <div data-test-id="name" class="text-base font-medium">{{ vorgang.name }}</div> <div> <alfa-vorgang-nummer class="vorgang-nummer" [vorgang]="vorgang"></alfa-vorgang-nummer> </div> diff --git a/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.model.ts b/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.model.ts index b24c2d66850741f13a5f0b63a6f7b9284704a756..3029e68a26c92c943b277675a974bc2e1a47391d 100644 --- a/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.model.ts +++ b/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.model.ts @@ -22,7 +22,6 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { BinaryFileListResource } from '@alfa-client/binary-file-shared'; -import { CommandOrder, CreateCommand } from '@alfa-client/command-shared'; import { ListResource, StateResource } from '@alfa-client/tech-shared'; import { Resource, ResourceUri } from '@ngxp/rest'; import { BehaviorSubject } from 'rxjs'; @@ -39,11 +38,6 @@ export interface Wiedervorlage { export interface WiedervorlageResource extends Wiedervorlage, Resource {} export interface WiedervorlageListResource extends ListResource {} -export interface CreateWiedervorlageCommand extends CreateCommand { - order: CommandOrder; - wiedervorlage: Wiedervorlage; -} - export interface BinaryFileListByWiedervorlageUri { [uri: ResourceUri]: BehaviorSubject<StateResource<BinaryFileListResource>>; } diff --git a/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts b/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts index a6fda05d4aae0d6cbc82268e5e0ac05f1655af0b..70080850adac9584f26c3c39f14134fa589a7e0f 100644 --- a/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts +++ b/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.spec.ts @@ -283,7 +283,7 @@ describe('WiedervorlageService', () => { expect(commandService.createCommand).toHaveBeenCalledWith( wiedervorlageResource, WiedervorlageLinkRel.EDIT, - { order: CommandOrder.EDIT_WIEDERVORLAGE, wiedervorlage, body: null }, + { order: CommandOrder.EDIT_WIEDERVORLAGE, body: wiedervorlage }, ); }); @@ -339,7 +339,7 @@ describe('WiedervorlageService', () => { it('should call command service', () => { service.createWiedervorlage(wiedervorlage).subscribe(); - const command = { order: CommandOrder.CREATE_WIEDERVORLAGE, wiedervorlage, body: null }; + const command = { order: CommandOrder.CREATE_WIEDERVORLAGE, body: wiedervorlage }; expect(commandService.createCommand).toHaveBeenCalledWith( service.wiedervorlageList$.value.resource, WiedervorlageListLinkRel.CREATE_WIEDERVORLAGE, diff --git a/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.ts b/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.ts index c9633abfedc9521786c49655796b3304e4a3972e..821e6e7d19a57ef6dfe5b77cd0603985b3149296 100644 --- a/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.ts +++ b/alfa-client/libs/wiedervorlage-shared/src/lib/wiedervorlage.service.ts @@ -23,7 +23,6 @@ */ import { BinaryFileListResource, BinaryFileService } from '@alfa-client/binary-file-shared'; import { - Command, CommandOrder, CommandResource, CommandService, @@ -54,7 +53,6 @@ import { WiedervorlageLinkRel, WiedervorlageListLinkRel } from './wiedervorlage. import { WiedervorlageMessages } from './wiedervorlage.message'; import { BinaryFileListByWiedervorlageUri, - CreateWiedervorlageCommand, Wiedervorlage, WiedervorlageListResource, WiedervorlageResource, @@ -274,8 +272,8 @@ export class WiedervorlageService implements OnDestroy { ); } - private buildCreateWiedervorlageCommand(wiedervorlage: Wiedervorlage): Command { - return <any>{ order: CommandOrder.CREATE_WIEDERVORLAGE, wiedervorlage, body: null }; + private buildCreateWiedervorlageCommand(wiedervorlage: Wiedervorlage): CreateCommand { + return { order: CommandOrder.CREATE_WIEDERVORLAGE, body: wiedervorlage }; } public saveWiedervorlage( @@ -311,8 +309,8 @@ export class WiedervorlageService implements OnDestroy { ); } - private buildEditWiedervorlageCommand(wiedervorlage: Wiedervorlage): CreateWiedervorlageCommand { - return { order: CommandOrder.EDIT_WIEDERVORLAGE, wiedervorlage, body: null }; + private buildEditWiedervorlageCommand(wiedervorlage: Wiedervorlage): CreateCommand { + return { order: CommandOrder.EDIT_WIEDERVORLAGE, body: wiedervorlage }; } proceedAfterReceiveCommand( diff --git a/alfa-client/libs/wiedervorlage/src/lib/wiedervorlage-list-in-vorgang-container/wiedervorlage-list-in-vorgang/wiedervorlage-in-vorgang/wiedervorlage-attachment-list-container/wiedervorlage-attachment-list-container.component.html b/alfa-client/libs/wiedervorlage/src/lib/wiedervorlage-list-in-vorgang-container/wiedervorlage-list-in-vorgang/wiedervorlage-in-vorgang/wiedervorlage-attachment-list-container/wiedervorlage-attachment-list-container.component.html index 62717bd101780a3e2c58031f1a1404640613ffcb..c472eaa9ff02c349f6db5a95cef29902f0a5a76a 100644 --- a/alfa-client/libs/wiedervorlage/src/lib/wiedervorlage-list-in-vorgang-container/wiedervorlage-list-in-vorgang/wiedervorlage-in-vorgang/wiedervorlage-attachment-list-container/wiedervorlage-attachment-list-container.component.html +++ b/alfa-client/libs/wiedervorlage/src/lib/wiedervorlage-list-in-vorgang-container/wiedervorlage-list-in-vorgang/wiedervorlage-in-vorgang/wiedervorlage-attachment-list-container/wiedervorlage-attachment-list-container.component.html @@ -24,7 +24,7 @@ --> <alfa-vertical-binary-file-list - [fileListResource]="attachments$ | async" + [binaryFileListStateResource]="attachments$ | async" [deletable]="false" data-test-id="wiedervorlage-attachment-list" > diff --git a/alfa-client/package-lock.json b/alfa-client/package-lock.json index d98128ff37fbc9a75656282a0e5652ee59966c91..949f8b38d13465282b7f59f829cf3981fbc8c6bc 100644 --- a/alfa-client/package-lock.json +++ b/alfa-client/package-lock.json @@ -1,12 +1,12 @@ { "name": "alfa", - "version": "0.3.0-SNAPSHOT", + "version": "0.6.0-SNAPSHOT", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "alfa", - "version": "0.3.0-SNAPSHOT", + "version": "0.6.0-SNAPSHOT", "license": "MIT", "dependencies": { "@angular/animations": "17.3.10", @@ -6191,11 +6191,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -6219,17 +6214,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -6480,6 +6464,14 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { "version": "4.1.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/find-up/-/find-up-4.1.0.tgz", @@ -6492,6 +6484,18 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { "version": "5.0.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/locate-path/-/locate-path-5.0.0.tgz", @@ -16124,6 +16128,26 @@ "node": ">=14.15.0" } }, + "node_modules/@yarnpkg/parsers/node_modules/argparse": { + "version": "1.0.10", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@yarnpkg/parsers/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@zkochan/js-yaml": { "version": "0.0.6", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz", @@ -16135,11 +16159,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@zkochan/js-yaml/node_modules/argparse": { - "version": "2.0.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/abab": { "version": "2.0.6", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/abab/-/abab-2.0.6.tgz", @@ -16498,12 +16517,9 @@ "devOptional": true }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/aria-hidden": { "version": "1.2.4", @@ -20907,11 +20923,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -20995,17 +21006,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -26076,12 +26076,11 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -27588,13 +27587,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "peer": true - }, "node_modules/mocha/node_modules/chokidar": { "version": "3.5.3", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/chokidar/-/chokidar-3.5.3.tgz", @@ -27689,19 +27681,6 @@ "node": ">=8" } }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "peer": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/mocha/node_modules/minimatch": { "version": "5.0.1", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/minimatch/-/minimatch-5.0.1.tgz", @@ -29491,11 +29470,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/nx/node_modules/argparse": { - "version": "2.0.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/nx/node_modules/chalk": { "version": "4.1.2", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/chalk/-/chalk-4.1.2.tgz", @@ -29530,17 +29504,6 @@ "node": ">=8" } }, - "node_modules/nx/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/nx/node_modules/jsonc-parser": { "version": "3.2.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/jsonc-parser/-/jsonc-parser-3.2.0.tgz", @@ -31062,11 +31025,6 @@ } } }, - "node_modules/postcss-loader/node_modules/argparse": { - "version": "2.0.1", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, "node_modules/postcss-loader/node_modules/cosmiconfig": { "version": "9.0.0", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/cosmiconfig/-/cosmiconfig-9.0.0.tgz", @@ -31092,17 +31050,6 @@ } } }, - "node_modules/postcss-loader/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/postcss-logical": { "version": "5.0.4", "resolved": "http://nexus.ozg-sh.de/repository/npm-proxy/postcss-logical/-/postcss-logical-5.0.4.tgz", diff --git a/alfa-client/package.json b/alfa-client/package.json index 941615e48c676d90e015854377c3441e18a98a3e..7ce0965ffbc9ed9db9ee23a7450cf7a26db2ec77 100644 --- a/alfa-client/package.json +++ b/alfa-client/package.json @@ -1,6 +1,6 @@ { "name": "alfa", - "version": "0.3.0-SNAPSHOT", + "version": "0.7.0-SNAPSHOT", "license": "MIT", "scripts": { "start": "nx run alfa:serve --port 4300 --disable-host-check", @@ -43,7 +43,8 @@ "cypress:install": "npx cypress install", "cypress:open": "npx cypress open --project apps/alfa-e2e", "workspace-generator": "nx workspace-generator", - "storybook": "nx storybook design-system" + "storybook": "nx storybook design-system", + "ci-storybook": "nx build-storybook design-system && nx container design-system" }, "private": true, "dependencies": { @@ -70,6 +71,7 @@ "angular-oauth2-oidc-jwks": "17.0.2", "class-variance-authority": "^0.7.0", "date-fns": "^2.30.0", + "decompress": "^4.2.1", "file-saver": "2.0.5", "include-media": "^1.4.10", "js-base64": "^3.7.7", @@ -159,4 +161,4 @@ "ts-node": "10.9.1", "typescript": "5.4.5" } -} \ No newline at end of file +} diff --git a/alfa-client/pom.xml b/alfa-client/pom.xml index 57beed7ab6d0d58e675a3475c8bf14a5603b8923..31c038a64e63e52771d87ecb1085e907e9af2cb8 100644 --- a/alfa-client/pom.xml +++ b/alfa-client/pom.xml @@ -29,7 +29,7 @@ <parent> <groupId>de.ozgcloud.alfa</groupId> <artifactId>alfa</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.11.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> diff --git a/alfa-server/pom.xml b/alfa-server/pom.xml index 3b98d77c4c8185451f105def421e6d428750e002..5124be2c496ebd8623a1445b16fdcb0a2074c4f0 100644 --- a/alfa-server/pom.xml +++ b/alfa-server/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>de.ozgcloud.alfa</groupId> <artifactId>alfa</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.11.0-SNAPSHOT</version> </parent> <artifactId>alfa-server</artifactId> diff --git a/alfa-server/src/main/resources/application-dev.yml b/alfa-server/src/main/resources/application-dev.yml index d34dd2855b8c828fec781e29e2dc07185ab53753..52ca56a22d8fa5ded4dcecfae01b3e22553c2da1 100644 --- a/alfa-server/src/main/resources/application-dev.yml +++ b/alfa-server/src/main/resources/application-dev.yml @@ -9,7 +9,6 @@ server: ozgcloud: feature: - vorgang-export: true reply-always-allowed: true production: false stage: diff --git a/alfa-server/src/main/resources/application-e2e.yml b/alfa-server/src/main/resources/application-e2e.yml index 2e755e6882c2aaa8cf67db3bce71e5ad2968ce76..7aa16cc6e02921c1fa3157a05b185fcbf08c623d 100644 --- a/alfa-server/src/main/resources/application-e2e.yml +++ b/alfa-server/src/main/resources/application-e2e.yml @@ -8,7 +8,6 @@ keycloak: ozgcloud: feature: - vorgang-export: true createBescheid: true reply-always-allowed: true forwarding: diff --git a/alfa-server/src/main/resources/application-local.yml b/alfa-server/src/main/resources/application-local.yml index e3b8ec1628d0820ff24226fc3905c2b58e9c8786..b113a7052d27746ebf67fdd3600b381e7e658dd5 100644 --- a/alfa-server/src/main/resources/application-local.yml +++ b/alfa-server/src/main/resources/application-local.yml @@ -17,7 +17,6 @@ grpc: ozgcloud: feature: - vorgang-export: true reply-always-allowed: true production: false user-assistance: @@ -27,10 +26,6 @@ ozgcloud: url: http://localhost:9092 profile-template: /api/userProfiles/%s search-template: /api/userProfiles/?searchBy={searchBy} - xdomea: - behoerdenschluessel: "behoerdenschluesselWirdÜberHelmGesetzt" - behoerdenschluesselUri: "behoerdenschluesselUriWirdÜberHelmGesetzt" - behoerdenschluesselVersion: "behoerdenschluesselVersionWirdÜberHelmGesetzt" keycloak: auth-server-url: http://localhost:8088 diff --git a/alfa-server/src/main/resources/application.yml b/alfa-server/src/main/resources/application.yml index 8e17178fea49083f2e1a5035d0b27d50c6670e50..ac930de36a83c9b7d962e72dca5ef29eae2fdd57 100644 --- a/alfa-server/src/main/resources/application.yml +++ b/alfa-server/src/main/resources/application.yml @@ -91,4 +91,8 @@ ozgcloud: - image/jpeg user-manager: profile-template: /api/userProfiles/%s - search-template: /api/userProfiles/?searchBy={searchBy} \ No newline at end of file + search-template: /api/userProfiles/?searchBy={searchBy} + xdomea: + behoerdenschluessel: + behoerdenschluessel-uri: + behoerdenschluessel-version: diff --git a/alfa-service/pom.xml b/alfa-service/pom.xml index f57d5f690a4cabf8e13bb94d2c7fd98a24a2949e..f2e9a4141f8dc1c75e3769a1671b1ca511714d8c 100644 --- a/alfa-service/pom.xml +++ b/alfa-service/pom.xml @@ -31,7 +31,7 @@ <parent> <groupId>de.ozgcloud.alfa</groupId> <artifactId>alfa</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.11.0-SNAPSHOT</version> </parent> <artifactId>alfa-service</artifactId> diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/attachment/AttachmentController.java b/alfa-service/src/main/java/de/ozgcloud/alfa/attachment/AttachmentController.java index e708645828f0bd692f4763859586a3725db45f12..1211543468abb21bc9b39b98408cd8075adc1710 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/attachment/AttachmentController.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/attachment/AttachmentController.java @@ -23,33 +23,70 @@ */ package de.ozgcloud.alfa.attachment; -import org.springframework.beans.factory.annotation.Autowired; +import java.io.OutputStream; + import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import de.ozgcloud.alfa.common.binaryfile.BinaryFileModelAssembler; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.common.file.OzgFileService; +import de.ozgcloud.alfa.common.zipdownload.ZipDownloadService; +import de.ozgcloud.alfa.vorgang.VorgangController; +import lombok.RequiredArgsConstructor; @RestController -@RequestMapping(AttachmentController.ATTACHMENT_PATH) +@RequestMapping(AttachmentController.PATH) +@RequiredArgsConstructor public class AttachmentController { - static final String ATTACHMENT_PATH = "/api/attachments"; // NOSONAR + static final String PATH = "/api/attachments"; // NOSONAR static final String PARAM_EINGANG_ID = "eingangId"; - @Autowired - private OzgFileService fileService; - @Autowired - private BinaryFileModelAssembler modelAssembler; + private final OzgFileService fileService; + private final BinaryFileModelAssembler modelAssembler; @GetMapping(params = PARAM_EINGANG_ID) public CollectionModel<EntityModel<OzgFile>> getAllByEingang(@RequestParam String eingangId) { return modelAssembler.toCollectionModel(fileService.getAttachmentsByEingang(eingangId)); } + + @RestController + @RequestMapping(AttachmentsByVorgangController.PATH) + @RequiredArgsConstructor + public static class AttachmentsByVorgangController { + static final String PATH = "/api/vorgang/{vorgangId}/attachments"; // NOSONAR + + private final OzgFileService fileService; + private final ZipDownloadService zipDownloadService; + private final VorgangController vorgangController; + + @GetMapping(params = PARAM_EINGANG_ID, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public ResponseEntity<StreamingResponseBody> download(@PathVariable String vorgangId, @RequestParam String eingangId) { + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s", buildZipName(vorgangId))) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(out -> createZipFile(out, eingangId)); + } + + String buildZipName(String vorgangId) { + return vorgangController.getVorgang(vorgangId).getNummer() + "_Anhaenge.zip"; + } + + void createZipFile(OutputStream out, String eingangId) { + var ozgFiles = fileService.getAttachmentsByEingang(eingangId); + zipDownloadService.write(ozgFiles.toList(), out); + } + } + } \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessor.java index 3a134ac79f67c11e6168fe0116bcebbbfe94b24f..836adfdc95b6ca002dc00e842398b34b93279560 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessor.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessor.java @@ -39,6 +39,9 @@ class BescheidHistorieProcessor implements HistorieProcessor { private static final Predicate<Command> IS_NOT_UPDATE_ATTACHED_ITEM = Predicate .not(command -> command.getCommandOrder() == CommandOrder.UPDATE_ATTACHED_ITEM && IS_BESCHEID_ATTACHED_ITEM.test(command)); + private static final Predicate<Command> IS_NOT_PATCH_ATTACHED_ITEM = Predicate + .not(command -> command.getCommandOrder() == CommandOrder.PATCH_ATTACHED_ITEM + && IS_BESCHEID_ATTACHED_ITEM.test(command)); @Override public List<Command> process(List<Command> commands) { @@ -52,6 +55,7 @@ class BescheidHistorieProcessor implements HistorieProcessor { .filter(IS_NOT_DELETE_BESCHEID_COMMAND) .filter(IS_NOT_CREATE_ATTACHED_ITEM) .filter(IS_NOT_UPDATE_ATTACHED_ITEM) + .filter(IS_NOT_PATCH_ATTACHED_ITEM) .toList(); } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/FeatureToggleProperties.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/FeatureToggleProperties.java index 1b516773c30d287d3a1571cb75f3c4009bee0873..b06e637da196c9b9d5dfb11a5d51a72fb505da7d 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/FeatureToggleProperties.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/FeatureToggleProperties.java @@ -12,11 +12,6 @@ import lombok.Setter; @ConfigurationProperties(prefix = "ozgcloud.feature") public class FeatureToggleProperties { - /** - * Enable vorgang export(xdomea) feature. - */ - private boolean vorgangExport = false; - /** * Enable/Disable bescheid creation feature. */ diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandBody.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandBody.java index 55b885acd5287b15b723de70261722f444ddce02..8bdf5bbab7eee1d9c6040bdc844cc568546e88bd 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandBody.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandBody.java @@ -44,8 +44,10 @@ import de.ozgcloud.alfa.wiedervorlage.Wiedervorlage; @Type(value = PostfachMail.class, name = "RESEND_POSTFACH_MAIL"), @Type(value = AssignUserCommandBody.class, name = "ASSIGN_USER"), @Type(value = RedirectRequest.class, name = "REDIRECT_VORGANG"), - @Type(value = Wiedervorlage.class, name = "WIEDERVORLAGE"), - @Type(value = Kommentar.class, name = "KOMMENTAR"), + @Type(value = Wiedervorlage.class, name = "CREATE_WIEDERVORLAGE"), + @Type(value = Wiedervorlage.class, name = "EDIT_WIEDERVORLAGE"), + @Type(value = Kommentar.class, name = "CREATE_KOMMENTAR"), + @Type(value = Kommentar.class, name = "EDIT_KOMMENTAR"), @Type(value = LoeschAnforderung.class, name = "LOESCH_ANFORDERUNG"), @Type(value = DeleteLoeschAnforderung.class, name = "DELETE_LOESCH_ANFORDERUNG"), @Type(value = Bescheid.class, name = "CREATE_BESCHEID"), diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/zipdownload/ZipDownloadService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/zipdownload/ZipDownloadService.java new file mode 100644 index 0000000000000000000000000000000000000000..46ddd81e4099efc42772a94ce7e3fd33f1198d3d --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/zipdownload/ZipDownloadService.java @@ -0,0 +1,58 @@ +package de.ozgcloud.alfa.common.zipdownload; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.springframework.stereotype.Service; + +import de.ozgcloud.alfa.common.binaryfile.BinaryFileService; +import de.ozgcloud.alfa.common.file.OzgFile; +import de.ozgcloud.common.binaryfile.TempFileUtils; +import de.ozgcloud.common.errorhandling.TechnicalException; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class ZipDownloadService { + + private final BinaryFileService binaryFileService; + + public void write(List<OzgFile> ozgFiles, OutputStream out) { + writeZipFileContent(createZipFile(ozgFiles), out); + } + + File createZipFile(List<OzgFile> ozgFiles) { + var file = TempFileUtils.createTmpFile().toFile(); + try (var zipOutputStream = new ZipOutputStream(new FileOutputStream(file))) { + ozgFiles.forEach(ozgFile -> addOzgFileToZip(ozgFile, zipOutputStream)); + return file; + } catch (Exception e) { + throw new TechnicalException("Error creating zip file", e); + } + } + + void addOzgFileToZip(OzgFile ozgFile, ZipOutputStream zipOutputStream) { + try { + zipOutputStream.putNextEntry(new ZipEntry(ozgFile.getName())); + binaryFileService.writeFileContent(ozgFile.getId(), zipOutputStream); + zipOutputStream.closeEntry(); + } catch (IOException e) { + throw new TechnicalException("Cannot add file to ZIP.", e); + } + } + + void writeZipFileContent(File zipFile, OutputStream outputStream) { + try (var fileInputStream = new FileInputStream(zipFile)) { + fileInputStream.transferTo(outputStream); + } catch (Exception e) { + throw new TechnicalException("Error writing zip file to output stream", e); + } + } + +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarCommand.java b/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarCommand.java deleted file mode 100644 index 5612b0af7dca140289899ef1ecbda0ba7e89dcee..0000000000000000000000000000000000000000 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarCommand.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -package de.ozgcloud.alfa.kommentar; - -import jakarta.validation.Valid; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -import de.ozgcloud.alfa.common.command.CommandStatus; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Builder -@Getter -@NoArgsConstructor(access = AccessLevel.PRIVATE) -@AllArgsConstructor(access = AccessLevel.PACKAGE) -@EqualsAndHashCode -class KommentarCommand { - - @JsonIgnore - private String id; - - private String order; - - @JsonIgnore - private CommandStatus status; - - @Valid - private Kommentar kommentar; -} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarCommandController.java b/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarCommandController.java index a11c25251682cbc0f3792bb58a810deeee0fd900..85cdc5c7545d2d467f53352c7290b2e2e80264a9 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarCommandController.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarCommandController.java @@ -35,6 +35,7 @@ import org.springframework.web.bind.annotation.RestController; import de.ozgcloud.alfa.common.command.Command; import de.ozgcloud.alfa.common.command.CommandController; +import de.ozgcloud.alfa.common.command.CreateCommand; @RestController @RequestMapping(KommentarCommandController.KOMMENTAR_COMMANDS) @@ -46,9 +47,9 @@ public class KommentarCommandController { private KommentarService service; @PostMapping - public ResponseEntity<Void> editKommentar(@RequestBody KommentarCommand kommentarCommand, @PathVariable String kommentarId, + public ResponseEntity<Void> editKommentar(@RequestBody CreateCommand command, @PathVariable String kommentarId, @PathVariable long kommentarVersion) { - var createdCommand = service.editKommentar(kommentarCommand.getKommentar(), kommentarId, kommentarVersion); + var createdCommand = service.editKommentar((Kommentar) command.getBody(), kommentarId, kommentarVersion); return buildResponseLink(createdCommand); } @@ -67,8 +68,8 @@ public class KommentarCommandController { private KommentarService service; @PostMapping - public ResponseEntity<Void> createKommentar(@RequestBody KommentarCommand command, @PathVariable String vorgangId) { - var createdCommand = service.createKommentar(command.getKommentar(), vorgangId); + public ResponseEntity<Void> createKommentar(@RequestBody CreateCommand command, @PathVariable String vorgangId) { + var createdCommand = service.createKommentar((Kommentar) command.getBody(), vorgangId); return ResponseEntity.created(linkTo(CommandController.class).slash(createdCommand.getId()).toUri()).build(); } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarMapper.java b/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarMapper.java index d95bb89ce7b65a0167269a71cc1e245d0bc8efb5..57e5fc50b48e34a9bc789f293b4baa7b380d8093 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarMapper.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/kommentar/KommentarMapper.java @@ -27,16 +27,12 @@ import java.time.ZonedDateTime; import java.util.Map; import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingConstants; import org.mapstruct.NullValueCheckStrategy; import org.mapstruct.ReportingPolicy; -import org.mapstruct.ValueMapping; import org.springframework.beans.factory.annotation.Autowired; import de.ozgcloud.alfa.attachment.AttachmentMapper; import de.ozgcloud.vorgang.common.grpc.GrpcObjectMapper; -import de.ozgcloud.vorgang.grpc.command.GrpcCommand; import de.ozgcloud.vorgang.vorgangAttachedItem.GrpcVorgangAttachedItem; @Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR, nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS) @@ -51,12 +47,6 @@ abstract class KommentarMapper { @Autowired private GrpcObjectMapper grpcObjectMapper; - @Mapping(target = "kommentar", ignore = true) - @Mapping(target = "order", source = "orderString") - @ValueMapping(source = "UNRECOGNIZED", target = MappingConstants.NULL) - @ValueMapping(source = "UNDEFINED", target = MappingConstants.NULL) - abstract KommentarCommand toKommentarCommand(GrpcCommand command); - public Kommentar fromItem(GrpcVorgangAttachedItem vorgangAttachedItem) { return fromItemMap(grpcObjectMapper.mapFromGrpc(vorgangAttachedItem.getItem()), vorgangAttachedItem.getVersion()); } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/representation/RepresentationController.java b/alfa-service/src/main/java/de/ozgcloud/alfa/representation/RepresentationController.java index ec181a8640f7f38d35ab43d06714ab8afd48411d..28913930a067740c12c8bf214423b59426f22c54 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/representation/RepresentationController.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/representation/RepresentationController.java @@ -23,7 +23,6 @@ */ package de.ozgcloud.alfa.representation; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; import org.springframework.web.bind.annotation.GetMapping; @@ -34,22 +33,23 @@ import org.springframework.web.bind.annotation.RestController; import de.ozgcloud.alfa.common.binaryfile.BinaryFileModelAssembler; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.common.file.OzgFileService; +import lombok.RequiredArgsConstructor; @RestController -@RequestMapping(RepresentationController.REPRESENTATIONS_PATH) +@RequestMapping(RepresentationController.PATH) +@RequiredArgsConstructor public class RepresentationController { - static final String REPRESENTATIONS_PATH = "/api/representations"; // NOSONAR + static final String PATH = "/api/representations"; // NOSONAR static final String PARAM_EINGANG_ID = "eingangId"; - @Autowired - private OzgFileService fileService; - @Autowired - private BinaryFileModelAssembler modelAssembler; + private final OzgFileService fileService; + private final BinaryFileModelAssembler modelAssembler; @GetMapping(params = PARAM_EINGANG_ID) public CollectionModel<EntityModel<OzgFile>> getAllByEingang(@RequestParam String eingangId) { return modelAssembler.toCollectionModel(fileService.getRepresentationsByEingang(eingangId)); } + } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessor.java index 78b09e77437d01710618ee0351819412a198a274..94edbda19108796dfe1b0518e3cea960fa3aa1d5 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessor.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessor.java @@ -31,7 +31,6 @@ import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkRelation; @@ -39,6 +38,7 @@ import org.springframework.hateoas.server.RepresentationModelProcessor; import org.springframework.stereotype.Component; import de.ozgcloud.alfa.attachment.AttachmentController; +import de.ozgcloud.alfa.attachment.AttachmentController.AttachmentsByVorgangController; import de.ozgcloud.alfa.common.FeatureToggleProperties; import de.ozgcloud.alfa.common.ModelBuilder; import de.ozgcloud.alfa.common.command.CommandController.CommandByRelationController; @@ -51,12 +51,15 @@ import de.ozgcloud.alfa.postfach.PostfachMailController; import de.ozgcloud.alfa.representation.RepresentationController; import de.ozgcloud.alfa.vorgang.VorgangProperties.VorgangProperty; import de.ozgcloud.alfa.vorgang.forwarding.ForwardingController; +import lombok.RequiredArgsConstructor; @Component +@RequiredArgsConstructor class VorgangWithEingangProcessor implements RepresentationModelProcessor<EntityModel<VorgangWithEingang>> { static final LinkRelation REL_KOMMENTARE = LinkRelation.of("kommentare"); static final LinkRelation REL_ATTACHMENTS = LinkRelation.of("attachments"); + static final LinkRelation REL_DOWNLOAD_ATTACHMENTS = LinkRelation.of("downloadAttachments"); static final LinkRelation REL_REPRESENTATIONS = LinkRelation.of("representations"); static final LinkRelation REL_POSTFACH_MAILS = LinkRelation.of("postfachMails"); static final LinkRelation REL_VORGANG_FORWARDING = LinkRelation.of("forwarding"); @@ -68,19 +71,13 @@ class VorgangWithEingangProcessor implements RepresentationModelProcessor<Entity static final String USER_PROFILE_SEARCH_DELETED_PARAM = "deleted"; static final String USER_PROFILE_SEARCH_ORGANISATIONS_EINHEIT_ID_PARAM = "organisationsEinheitId"; - @Autowired - private PostfachMailController postfachMailController; - @Autowired - private CurrentUserService userService; - @Autowired - private UserManagerUrlProvider userManagerUrlProvider; + private final PostfachMailController postfachMailController; + private final CurrentUserService userService; + private final UserManagerUrlProvider userManagerUrlProvider; - @Autowired - private FeatureToggleProperties featureToggleProperties; - @Autowired - private VorgangProperties vorgangProperties; - @Autowired - private VorgangProcessorProperties vorgangProcessorProperties; + private final FeatureToggleProperties featureToggleProperties; + private final VorgangProperties vorgangProperties; + private final VorgangProcessorProperties vorgangProcessorProperties; private static final Predicate<VorgangWithEingang> HAS_ATTACHMENTS = vorgangWithEingang -> vorgangWithEingang.getEingang() .getNumberOfAttachments() > 0; @@ -99,20 +96,22 @@ class VorgangWithEingangProcessor implements RepresentationModelProcessor<Entity return ModelBuilder.fromModel(model) .addLink(linkTo(KommentarByVorgangController.class).slash(vorgang.getId()).slash("kommentars").withRel(REL_KOMMENTARE)) .ifMatch(HAS_ATTACHMENTS) - .addLink(vorgangWithEingang -> linkTo(methodOn(AttachmentController.class).getAllByEingang(vorgangWithEingang.getEingang().getId())) - .withRel(REL_ATTACHMENTS)) + .addLinks(linkTo(methodOn(AttachmentController.class).getAllByEingang(vorgang.getEingang().getId())) + .withRel(REL_ATTACHMENTS), + linkTo(methodOn(AttachmentsByVorgangController.class).download(vorgang.getId(), vorgang.getEingang().getId())) + .withRel(REL_DOWNLOAD_ATTACHMENTS)) .ifMatch(HAS_REPRESENTATIONS) .addLink(vorgangWithEingang -> linkTo( methodOn(RepresentationController.class).getAllByEingang(vorgangWithEingang.getEingang().getId())) - .withRel(REL_REPRESENTATIONS)) + .withRel(REL_REPRESENTATIONS)) .ifMatch(this::isPostfachConfigured) .addLink(linkTo(methodOn(PostfachMailController.class).getAll(vorgang.getId())).withRel(REL_POSTFACH_MAILS)) .ifMatch(this::isEinheitlicherAnsprechpartner) .addLink(linkTo(methodOn(ForwardingController.class).findByVorgangId(vorgang.getId())).withRel(REL_VORGANG_FORWARDING)) .addLink(linkTo(methodOn(HistorieController.class).getAll(vorgang.getId())).withRel(REL_HISTORIE)) .ifMatch(() -> userManagerUrlProvider.isConfiguredForSearchUserProfile() && hasOrganisationsEinheitId(vorgang)) - .addLink(() -> buildSearchUserProfilesLink(vorgang)) - .ifMatch(() -> isCreateBescheidEnabled(vorgang)) + .addLink(this::buildSearchUserProfilesLink) + .ifMatch(this::isCreateBescheidEnabled) .addLink(linkTo(methodOn(CommandByRelationController.class).createCommand(vorgang.getId(), vorgang.getId(), vorgang.getVersion(), null)).withRel(REL_BESCHEID)) .ifMatch(this::isProcessable) diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommand.java b/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommand.java deleted file mode 100644 index a9cb0e0ea5aa30f4958672fbb168341d2976d0d1..0000000000000000000000000000000000000000 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommand.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -package de.ozgcloud.alfa.wiedervorlage; - -import jakarta.validation.Valid; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -import de.ozgcloud.alfa.common.command.CommandStatus; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Builder(toBuilder = true) -@Getter -@NoArgsConstructor(access = AccessLevel.PRIVATE) -@AllArgsConstructor(access = AccessLevel.PACKAGE) -@EqualsAndHashCode -class WiedervorlageCommand { - - @JsonIgnore - private String id; - - private String order; - - @JsonIgnore - private CommandStatus status; - - @Valid - private Wiedervorlage wiedervorlage; -} \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandController.java b/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandController.java index 9435cef625d6ce8c64681ea60bc92ec1e73d6091..d621a01dd99a332de8fe0dd6b2b754ccdc9a351a 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandController.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandController.java @@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.RestController; import de.ozgcloud.alfa.common.command.Command; import de.ozgcloud.alfa.common.command.CommandController; +import de.ozgcloud.alfa.common.command.CreateCommand; import de.ozgcloud.alfa.common.command.LegacyOrder; import de.ozgcloud.common.errorhandling.TechnicalException; import lombok.RequiredArgsConstructor; @@ -48,7 +49,7 @@ public class WiedervorlageCommandController { private final WiedervorlageService service; @PostMapping - public ResponseEntity<Void> updateWiedervorlage(@RequestBody WiedervorlageCommand command, @PathVariable String wiedervorlageId, + public ResponseEntity<Void> updateWiedervorlage(@RequestBody CreateCommand command, @PathVariable String wiedervorlageId, @PathVariable long wiedervorlageVersion) { var wiedervorlage = service.getById(wiedervorlageId); var createdCommand = createCommand(wiedervorlage, command); @@ -58,7 +59,7 @@ public class WiedervorlageCommandController { return ResponseEntity.created(linkTo(CommandController.class).slash(createdCommand.getId()).toUri()).build(); } - Command createCommand(Wiedervorlage wiedervorlage, WiedervorlageCommand command) { + Command createCommand(Wiedervorlage wiedervorlage, CreateCommand command) { switch (command.getOrder()) { case LegacyOrder.WIEDERVORLAGE_ERLEDIGEN: { return service.erledigen(wiedervorlage); @@ -67,7 +68,8 @@ public class WiedervorlageCommandController { return service.wiedereroeffnen(wiedervorlage); } case LegacyOrder.EDIT_WIEDERVORLAGE: { - return service.editWiedervorlage(updateWiedervorlageByCommand(wiedervorlage, command), wiedervorlage.getId(), + return service.editWiedervorlage(updateWiedervorlageByCommand(wiedervorlage, (Wiedervorlage) command.getBody()), + wiedervorlage.getId(), wiedervorlage.getVersion()); } default: @@ -75,12 +77,12 @@ public class WiedervorlageCommandController { } } - Wiedervorlage updateWiedervorlageByCommand(Wiedervorlage wiedervorlage, WiedervorlageCommand command) { + Wiedervorlage updateWiedervorlageByCommand(Wiedervorlage wiedervorlage, Wiedervorlage requestBody) { return wiedervorlage.toBuilder() - .clearAttachments().attachments(command.getWiedervorlage().getAttachments()) - .betreff(command.getWiedervorlage().getBetreff()) - .beschreibung(command.getWiedervorlage().getBeschreibung()) - .frist(command.getWiedervorlage().getFrist()).build(); + .clearAttachments().attachments(requestBody.getAttachments()) + .betreff(requestBody.getBetreff()) + .beschreibung(requestBody.getBeschreibung()) + .frist(requestBody.getFrist()).build(); } @RestController @@ -93,8 +95,8 @@ public class WiedervorlageCommandController { private final WiedervorlageService service; @PostMapping - public ResponseEntity<Void> createWiedervorlage(@RequestBody WiedervorlageCommand command, @PathVariable String vorgangId) { - var createdCommand = service.createWiedervorlage(command.getWiedervorlage(), vorgangId); + public ResponseEntity<Void> createWiedervorlage(@RequestBody CreateCommand command, @PathVariable String vorgangId) { + var createdCommand = service.createWiedervorlage((Wiedervorlage) command.getBody(), vorgangId); service.updateNextFrist(vorgangId); diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentControllerTest.java index 801e18a6bde57ae9c7d1a6c12a9190a438cee89b..7e7b3a6e20a4aa2ca117acb79ece432c7d5cf57c 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentControllerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentControllerTest.java @@ -36,24 +36,29 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import de.ozgcloud.alfa.common.binaryfile.BinaryFileModelAssembler; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.common.file.OzgFileService; import de.ozgcloud.alfa.common.file.OzgFileTestFactory; +import de.ozgcloud.alfa.common.zipdownload.ZipDownloadService; +import lombok.SneakyThrows; class AttachmentControllerTest { - private final String PATH = AttachmentController.ATTACHMENT_PATH; - + @Spy @InjectMocks private AttachmentController controller; @Mock private BinaryFileModelAssembler modelAssembler; @Mock private OzgFileService fileService; + @Mock + private ZipDownloadService zipDownloadService; private MockMvc mockMvc; @@ -70,27 +75,33 @@ class AttachmentControllerTest { when(fileService.getAttachmentsByEingang(anyString())).thenReturn(Stream.of(OzgFileTestFactory.create())); } + @SneakyThrows @Test - void shouldCallEndpoint() throws Exception { - callEndpoint(); + void shouldResponseWithStatusOk() { + var result = callEndpoint(); + + result.andExpect(status().isOk()); } + @SneakyThrows @Test - void shoudlCallFileService() throws Exception { + void shoudlCallFileService() { callEndpoint(); verify(fileService).getAttachmentsByEingang(any()); } + @SneakyThrows @Test - void shouldCallModelAssembler() throws Exception { + void shouldCallModelAssembler() { callEndpoint(); verify(modelAssembler).toCollectionModel(ArgumentMatchers.<Stream<OzgFile>>any()); } - private void callEndpoint() throws Exception { - mockMvc.perform(get(PATH).param(AttachmentController.PARAM_EINGANG_ID, "1")).andExpect(status().isOk()); + @SneakyThrows + private ResultActions callEndpoint() { + return mockMvc.perform(get(AttachmentController.PATH).param(AttachmentController.PARAM_EINGANG_ID, "1")); } } } \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentsByVorgangControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentsByVorgangControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..54638cb8a8390236ab8fc1c155d8ff3e33759779 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/attachment/AttachmentsByVorgangControllerTest.java @@ -0,0 +1,179 @@ +package de.ozgcloud.alfa.attachment; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.io.OutputStream; +import java.util.List; +import java.util.UUID; +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.alfa.attachment.AttachmentController.AttachmentsByVorgangController; +import de.ozgcloud.alfa.common.binaryfile.BinaryFileModelAssembler; +import de.ozgcloud.alfa.common.file.OzgFileService; +import de.ozgcloud.alfa.common.file.OzgFileTestFactory; +import de.ozgcloud.alfa.common.zipdownload.ZipDownloadService; +import de.ozgcloud.alfa.vorgang.EingangTestFactory; +import de.ozgcloud.alfa.vorgang.VorgangController; +import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; +import de.ozgcloud.alfa.vorgang.VorgangWithEingang; +import de.ozgcloud.alfa.vorgang.VorgangWithEingangTestFactory; +import lombok.SneakyThrows; + +class AttachmentsByVorgangControllerTest { + + @Spy + @InjectMocks + private AttachmentsByVorgangController controller; + @Mock + private BinaryFileModelAssembler modelAssembler; + @Mock + private OzgFileService fileService; + @Mock + private ZipDownloadService zipDownloadService; + @Mock + private VorgangController vorgangController; + + private MockMvc mockMvc; + + @BeforeEach + void initTest() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @DisplayName("Download") + @Nested + class TestDownload { + private final String filename = LoremIpsum.getInstance().getWords(1); + + @BeforeEach + void setUpMock() { + doReturn(filename).when(controller).buildZipName(VorgangHeaderTestFactory.ID); + } + + @SneakyThrows + @Test + void shouldRespondWithStatusOk() { + var response = doRequest(); + + response.andExpect(status().isOk()); + } + + @SneakyThrows + @Test + void shouldBuildZipName() { + doRequest(); + + verify(controller).buildZipName(VorgangHeaderTestFactory.ID); + } + + @SneakyThrows + @Test + void shouldHaveContentDispositonHeader() { + var response = doRequest(); + + response.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename)); + } + + @SneakyThrows + @Test + void shouldHaveContentType() { + var response = doRequest(); + + response.andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE)); + } + + @SneakyThrows + @Test + void shouldWriteZipFile() { + doRequest(); + + verify(controller).createZipFile(any(), eq(EingangTestFactory.ID)); + } + + @SneakyThrows + private ResultActions doRequest() { + return mockMvc.perform(asyncDispatch( + mockMvc.perform(get(AttachmentsByVorgangController.PATH.replace("{vorgangId}", VorgangHeaderTestFactory.ID) + "?eingangId=" + + EingangTestFactory.ID) + .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_OCTET_STREAM_VALUE)) + .andReturn())) + .andExpect(status().isOk()); + } + } + + @Nested + class TestBuildZipName { + private final String vorgangId = UUID.randomUUID().toString(); + + private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + + @BeforeEach + void mockVorgangController() { + when(vorgangController.getVorgang(vorgangId)).thenReturn(vorgang); + } + + @Test + void shouldCallVorgangController() { + callController(); + + verify(vorgangController).getVorgang(vorgangId); + } + + @Test + void shouldReturnFileName() { + var resultFileName = callController(); + + assertThat(resultFileName).isEqualTo(vorgang.getNummer() + "_Anhaenge.zip"); + } + + private String callController() { + return controller.buildZipName(vorgangId); + } + } + + @Nested + class TestCreateZipFile { + @Mock + private OutputStream outputStream; + + @Test + void shouldGetAttachments() { + callController(); + + verify(fileService).getAttachmentsByEingang(EingangTestFactory.ID); + } + + @Test + void shouldCallZipDownloadService() { + var ozgFile = OzgFileTestFactory.create(); + when(fileService.getAttachmentsByEingang(EingangTestFactory.ID)).thenReturn(Stream.of(ozgFile)); + + callController(); + + verify(zipDownloadService).write(List.of(ozgFile), outputStream); + } + + private void callController() { + controller.createZipFile(outputStream, EingangTestFactory.ID); + } + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessorTest.java index ac60d058d4b2b3a315ac2312cae6375a160275d6..b3a68ed0ad5e3d441a2705cf6181e802882d0e55 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessorTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/bescheid/BescheidHistorieProcessorTest.java @@ -79,7 +79,7 @@ class BescheidHistorieProcessorTest { @ParameterizedTest @EnumSource(mode = Mode.INCLUDE, names = { "CREATE_BESCHEID", "DELETE_BESCHEID" }) void shouldFilterCommandWithOrder(CommandOrder order) { - var command = CommandTestFactory.createBuilder().order(order.toString()).build(); + var command = CommandTestFactory.createBuilder().order(order.name()).build(); var commands = processor.filterByOrder(Collections.singletonList(command)); @@ -89,30 +89,19 @@ class BescheidHistorieProcessorTest { @ParameterizedTest @EnumSource(mode = Mode.EXCLUDE, names = { "CREATE_BESCHEID", "DELETE_BESCHEID" }) void shouldKeepCommandWithOrder(CommandOrder order) { - var command = CommandTestFactory.createBuilder().order(order.toString()).build(); + var command = CommandTestFactory.createBuilder().order(order.name()).build(); var commands = processor.filterByOrder(Collections.singletonList(command)); assertThat(commands).containsExactly(command); } - @DisplayName("should filter bescheid related create attached item command") - @Test - void shouldFilterCreateAttachedItemCommand() { - var command = CommandTestFactory.createBuilder().order(CommandOrder.CREATE_ATTACHED_ITEM.toString()) - .body(Map.of(VorgangAttachedItem.FIELD_ITEM_NAME, BescheidHistorieProcessor.BESCHEID_ITEM_NAME)) - .build(); - - var commands = processor.filterByOrder(Collections.singletonList(command)); - - assertThat(commands).isEmpty(); - } - - @DisplayName("should filter bescheid related update attached item command") - @Test - void shouldFilterUpdateAttachedItemCommand() { - var command = CommandTestFactory.createBuilder().order(CommandOrder.UPDATE_ATTACHED_ITEM.toString()) - .body(Map.of(VorgangAttachedItem.FIELD_ITEM_NAME, BescheidHistorieProcessor.BESCHEID_ITEM_NAME)) + @ParameterizedTest + @EnumSource(mode = Mode.INCLUDE, names = { "CREATE_ATTACHED_ITEM", "UPDATE_ATTACHED_ITEM", "PATCH_ATTACHED_ITEM" }) + @DisplayName("should filter bescheid related command") + void shouldFilterAttachedItemCommand(CommandOrder order) { + var command = CommandTestFactory.createBuilder().order(order.name()) + .body(Collections.singletonMap(VorgangAttachedItem.FIELD_ITEM_NAME, BescheidHistorieProcessor.BESCHEID_ITEM_NAME)) .build(); var commands = processor.filterByOrder(Collections.singletonList(command)); @@ -156,17 +145,17 @@ class BescheidHistorieProcessorTest { private final Command sendBescheidCommand = CommandTestFactory.createBuilder() .id(UUID.randomUUID().toString()) .relationId(BescheidTestFactory.ID) - .order(CommandOrder.SEND_BESCHEID.toString()) + .order(CommandOrder.SEND_BESCHEID.name()) .build(); private final Command updateBescheidCommand = CommandTestFactory.createBuilder() .id(UUID.randomUUID().toString()) .relationId(BescheidTestFactory.ID) - .order(CommandOrder.UPDATE_BESCHEID.toString()) + .order(CommandOrder.UPDATE_BESCHEID.name()) .build(); private final Command updateBescheidCommandOfBescheidNotSent = CommandTestFactory.createBuilder() .id(UUID.randomUUID().toString()) .relationId(UUID.randomUUID().toString()) - .order(CommandOrder.UPDATE_BESCHEID.toString()) + .order(CommandOrder.UPDATE_BESCHEID.name()) .build(); private final List<Command> commands = List.of(sendBescheidCommand, updateBescheidCommand, updateBescheidCommandOfBescheidNotSent); @@ -197,7 +186,7 @@ class BescheidHistorieProcessorTest { private final Command sendBescheidCommand = CommandTestFactory.createBuilder() .id(UUID.randomUUID().toString()) .relationId(BescheidTestFactory.ID) - .order(CommandOrder.SEND_BESCHEID.toString()).build(); + .order(CommandOrder.SEND_BESCHEID.name()).build(); private final Command relatedUpdateBescheidCommand = CommandTestFactory.createBuilder().id(UUID.randomUUID().toString()) .createdAt(ZonedDateTime.now()).build(); @@ -293,7 +282,6 @@ class BescheidHistorieProcessorTest { private Map<String, Object> createBescheidMap(SendBy sendBy) { var item = BescheidTestFactory.createAsMap(); - item.remove(Bescheid.SEND_BY_FIELD); item.put(Bescheid.SEND_BY_FIELD, sendBy); return item; } @@ -317,7 +305,7 @@ class BescheidHistorieProcessorTest { @ParameterizedTest @EnumSource(mode = Mode.INCLUDE, names = { "UPDATE_BESCHEID" }) void shouldKeepUpdateBescheidCommandsOnly(CommandOrder order) { - var commandWithNonMatchingOrder = CommandTestFactory.createBuilder().order(order.toString()).build(); + var commandWithNonMatchingOrder = CommandTestFactory.createBuilder().order(order.name()).build(); var relatedCommands = processor.getRelatedUpdateBescheidCommands(command, Collections.singletonList(commandWithNonMatchingOrder)); @@ -327,7 +315,7 @@ class BescheidHistorieProcessorTest { @ParameterizedTest @EnumSource(mode = Mode.EXCLUDE, names = { "UPDATE_BESCHEID" }) void shouldFilterNonUpdateBescheidCommands(CommandOrder order) { - var commandWithNonMatchingOrder = CommandTestFactory.createBuilder().order(order.toString()).build(); + var commandWithNonMatchingOrder = CommandTestFactory.createBuilder().order(order.name()).build(); var relatedCommands = processor.getRelatedUpdateBescheidCommands(command, Collections.singletonList(commandWithNonMatchingOrder)); @@ -381,7 +369,8 @@ class BescheidHistorieProcessorTest { @Test void shouldThrowOnMissingSendBy() { - assertThatThrownBy(() -> processor.isManualSent(Map.of())).isInstanceOf(NullPointerException.class); + var bescheidItemMap = Collections.<String, Object>emptyMap(); + assertThatThrownBy(() -> processor.isManualSent(bescheidItemMap)).isInstanceOf(NullPointerException.class); } } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/TestUtils.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/AlfaTestUtils.java similarity index 96% rename from alfa-service/src/test/java/de/ozgcloud/alfa/common/TestUtils.java rename to alfa-service/src/test/java/de/ozgcloud/alfa/common/AlfaTestUtils.java index a11339d1baaf6b7e7afd691970664dbc28416e58..9792108f3aabc21d4a8bf09e1aa3477ce6b93144 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/TestUtils.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/AlfaTestUtils.java @@ -10,7 +10,7 @@ import java.util.stream.Stream; import org.apache.commons.lang3.RandomStringUtils; -public class TestUtils { +public class AlfaTestUtils { public static final String UUID_REGEX = "[0-9a-f]{8,8}-[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]{12,12}"; diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/file/OzgFileTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/file/OzgFileTestFactory.java index e4ccd038bdafc2decf7f352cdb8a5f042a30c5c1..66615affbbf4b7af0970568c6bb8453aaf56c244 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/file/OzgFileTestFactory.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/file/OzgFileTestFactory.java @@ -23,7 +23,7 @@ */ package de.ozgcloud.alfa.common.file; -import static de.ozgcloud.alfa.common.TestUtils.*; +import static de.ozgcloud.alfa.common.AlfaTestUtils.*; import de.ozgcloud.alfa.common.binaryfile.BinaryFileTestFactory; import de.ozgcloud.alfa.common.binaryfile.FileId; diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/zipdownload/ZipDownloadServiceTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/zipdownload/ZipDownloadServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..943488ca0faf1f231ab479314884ed9c522d4de4 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/zipdownload/ZipDownloadServiceTest.java @@ -0,0 +1,262 @@ +package de.ozgcloud.alfa.common.zipdownload; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.Spy; + +import de.ozgcloud.alfa.common.binaryfile.BinaryFileService; +import de.ozgcloud.alfa.common.file.OzgFile; +import de.ozgcloud.alfa.common.file.OzgFileTestFactory; +import de.ozgcloud.common.errorhandling.TechnicalException; +import lombok.SneakyThrows; + +public class ZipDownloadServiceTest { + + @Spy + @InjectMocks + private ZipDownloadService service; + + @Mock + private BinaryFileService binaryFileService; + + @Nested + class TestWrite { + private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + private final List<OzgFile> ozgFiles = List.of(OzgFileTestFactory.create()); + @Mock + private File zipFile; + + @Test + void shouldCreateZipFile() { + callService(); + + verify(service).createZipFile(ozgFiles); + } + + @Test + void shouldWriteZipFileContent() { + doReturn(zipFile).when(service).createZipFile(ozgFiles); + doNothing().when(service).writeZipFileContent(zipFile, outputStream); + + callService(); + + verify(service).writeZipFileContent(zipFile, outputStream); + } + + private void callService() { + service.write(ozgFiles, outputStream); + } + } + + @Nested + class TestCreateZipFile { + private final OzgFile file1 = OzgFileTestFactory.createWithUniqueId(); + private final OzgFile file2 = OzgFileTestFactory.createWithUniqueId(); + private final List<OzgFile> ozgFiles = List.of(file1, file2); + + private MockedConstruction<FileOutputStream> mockFileOutputStream; + private MockedConstruction<ZipOutputStream> mockZipOutputStream; + private File file; + private FileOutputStream fileOutputStream; + + @BeforeEach + void setUpMocks() { + mockFileOutputStream = mockConstruction(FileOutputStream.class, + (mock, context) -> { + file = (File) context.arguments().get(0); + }); + mockZipOutputStream = mockConstruction(ZipOutputStream.class, + (mock, context) -> { + fileOutputStream = (FileOutputStream) context.arguments().get(0); + }); + } + + @AfterEach + void cleanUp() { + mockFileOutputStream.close(); + mockZipOutputStream.close(); + } + + @Test + void shouldCreateZipOutputStream() { + callService(); + + assertThat(mockZipOutputStream.constructed()).hasSize(1); + assertThat(fileOutputStream).isEqualTo(mockFileOutputStream.constructed().get(0)); + } + + @Test + void shouldAddOzgFilesToZip() { + callService(); + + verify(service).addOzgFileToZip(file1, mockZipOutputStream.constructed().get(0)); + verify(service).addOzgFileToZip(file2, mockZipOutputStream.constructed().get(0)); + } + + @Test + void shouldReturnFile() { + var resultFile = callService(); + + assertThat(resultFile).isEqualTo(file); + } + + @Test + void shouldThrowTechnicalException() { + doThrow(TechnicalException.class).when(service).addOzgFileToZip(any(), any()); + + assertThrows(TechnicalException.class, this::callService); + } + + private File callService() { + return service.createZipFile(ozgFiles); + } + } + + @Nested + class TestAddOzgFileToZip { + private final OzgFile ozgFile = OzgFileTestFactory.create(); + + @Mock + private ZipOutputStream zipOutputStream; + + private MockedConstruction<ZipEntry> mockZipEntry; + private String entryName; + + @BeforeEach + void setUpMocks() { + mockZipEntry = mockConstruction(ZipEntry.class, + (mock, context) -> { + entryName = (String) context.arguments().get(0); + }); + } + + @AfterEach + void cleanUp() { + mockZipEntry.close(); + } + + @Test + void shouldCreateZipEntry() { + callService(); + + assertThat(mockZipEntry.constructed()).hasSize(1); + } + + @Test + void shouldSetZipEntryName() { + callService(); + + assertThat(entryName).isEqualTo(ozgFile.getName()); + } + + @Test + @SneakyThrows + void shouldPutNextEntry() { + callService(); + + verify(zipOutputStream).putNextEntry(mockZipEntry.constructed().get(0)); + } + + @Test + @SneakyThrows + void shouldWriteContent() { + callService(); + + verify(binaryFileService).writeFileContent(ozgFile.getId(), zipOutputStream); + } + + @Test + @SneakyThrows + void shouldCloseEntry() { + callService(); + + verify(zipOutputStream).closeEntry(); + } + + @Test + @SneakyThrows + void shouldThrowTechnicalException() { + doThrow(IOException.class).when(zipOutputStream).putNextEntry(any()); + + assertThrows(TechnicalException.class, this::callService); + } + + private void callService() { + service.addOzgFileToZip(ozgFile, zipOutputStream); + } + } + + @Nested + class TestWriteZipFileContent { + private MockedConstruction<FileInputStream> mockFileInputStream; + + private File argumentFile; + @Mock + private File file; + + @Mock + private FileOutputStream fileOutputStream; + + @AfterEach + void cleanUp() { + mockFileInputStream.close(); + } + + @Test + void shouldCreateFileInputStreamWithFileArgument() { + mockFileInputStream = mockConstruction(FileInputStream.class, + (mock, context) -> { + argumentFile = (File) context.arguments().get(0); + }); + + callService(); + + assertThat(mockFileInputStream.constructed()).hasSize(1); + assertThat(argumentFile).isEqualTo(file); + } + + @Test + @SneakyThrows + void shouldTransferToOutputStream() { + mockFileInputStream = mockConstruction(FileInputStream.class); + + callService(); + + verify(mockFileInputStream.constructed().get(0)).transferTo(fileOutputStream); + } + + @Test + void shouldThrowTechnicalException() { + mockFileInputStream = mockConstruction(FileInputStream.class, + (mock, context) -> { + when(mock.transferTo(fileOutputStream)).thenThrow(IOException.class); + }); + + assertThrows(TechnicalException.class, this::callService); + } + + private void callService() { + service.writeZipFileContent(file, fileOutputStream); + } + + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryServiceTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryServiceTest.java index 451b3e9b0d1fceb0cfcced85d257679f7466b837..1e02f31be3dc179716b1b3eae020820a9a8c4d89 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryServiceTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/VorgangChangeHistoryServiceTest.java @@ -1,6 +1,6 @@ package de.ozgcloud.alfa.historie; -import static de.ozgcloud.alfa.common.TestUtils.*; +import static de.ozgcloud.alfa.common.AlfaTestUtils.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandControllerTest.java index 31cde4a9e360d9affef6708551760353f0f19ce6..b8e51c4a1f9bb378e5989c7cf3716959b4dacbb9 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandControllerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandControllerTest.java @@ -23,7 +23,6 @@ */ package de.ozgcloud.alfa.kommentar; -import static de.ozgcloud.alfa.kommentar.KommentarCommandTestFactory.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; @@ -44,8 +43,10 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import de.ozgcloud.alfa.common.command.CommandTestFactory; import de.ozgcloud.alfa.common.command.CreateCommand; +import de.ozgcloud.alfa.common.command.LegacyOrder; import de.ozgcloud.alfa.kommentar.KommentarCommandController.KommentarCommandByVorgangController; import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; +import lombok.SneakyThrows; class KommentarCommandControllerTest { @@ -70,22 +71,25 @@ class KommentarCommandControllerTest { when(service.createKommentar(any(), any())).thenReturn(CommandTestFactory.create()); } + @SneakyThrows @Test - void shouldCallService() throws Exception { + void shouldCallService() { doRequest(); verify(service).createKommentar(any(Kommentar.class), eq(VorgangHeaderTestFactory.ID)); } + @SneakyThrows @Test - void shouldReturnCreated() throws Exception { + void shouldReturnCreated() { doRequest().andExpect(status().isCreated()); } - private ResultActions doRequest() throws Exception { + @SneakyThrows + private ResultActions doRequest() { return mockMvc.perform( post(KommentarCommandByVorgangController.KOMMENTAR_COMMANDS_BY_VORGANG, VorgangHeaderTestFactory.ID) - .content(createValidRequestContent()).contentType(MediaType.APPLICATION_JSON)) + .content(createRequestContent()).contentType(MediaType.APPLICATION_JSON)) .andExpect(status().is2xxSuccessful()); } } @@ -93,7 +97,7 @@ class KommentarCommandControllerTest { @Nested class TestCreateEditKommentarCommand { - private final static String REPONSE_HEADER = "http://localhost/api/commands/" + KommentarCommandTestFactory.ID; + private final static String REPONSE_HEADER = "http://localhost/api/commands/" + CommandTestFactory.ID; @Spy @InjectMocks @@ -111,27 +115,35 @@ class KommentarCommandControllerTest { } @Test - void shouldCallService() throws Exception { + void shouldCallService() { doRequest(); verify(service).editKommentar(any(Kommentar.class), eq(KommentarTestFactory.ID), eq(KommentarTestFactory.VERSION)); } + @SneakyThrows @Test - void shouldReturnCreated() throws Exception { + void shouldReturnCreated() { doRequest().andExpect(status().isCreated()); } + @SneakyThrows @Test - void shouldRespondWithLocationHeader() throws Exception { + void shouldRespondWithLocationHeader() { doRequest().andExpect(header().string("Location", REPONSE_HEADER)); } - private ResultActions doRequest() throws Exception { - String content = createValidRequestContent(); + @SneakyThrows + private ResultActions doRequest() { return mockMvc.perform(post(KommentarCommandController.KOMMENTAR_COMMANDS, KommentarTestFactory.ID, KommentarTestFactory.VERSION) - .content(content).contentType(MediaType.APPLICATION_JSON)) + .content(createRequestContent()).contentType(MediaType.APPLICATION_JSON)) .andExpect(status().is2xxSuccessful()); } } + + private String createRequestContent() { + return KommentarTestFactory + .createRequestContent(CommandTestFactory.createCreateCommandBuilder().body(KommentarTestFactory.create()) + .order(LegacyOrder.CREATE_KOMMENTAR).build()); + } } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandITCase.java index 5cf825978a7a5f179f9c67cf8c635764665ee293..87d5be0da2eae911f865f47380307fe8a7fa61b5 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandITCase.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandITCase.java @@ -23,7 +23,6 @@ */ package de.ozgcloud.alfa.kommentar; -import static de.ozgcloud.alfa.kommentar.KommentarCommandTestFactory.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; @@ -46,7 +45,6 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import de.ozgcloud.alfa.common.ValidationMessageCodes; -import de.ozgcloud.alfa.common.command.CommandOrder; import de.ozgcloud.alfa.common.command.CommandRemoteService; import de.ozgcloud.alfa.common.command.CommandTestFactory; import de.ozgcloud.alfa.common.command.LegacyOrder; @@ -54,6 +52,7 @@ import de.ozgcloud.alfa.common.user.CurrentUserService; import de.ozgcloud.alfa.common.user.UserProfileTestFactory; import de.ozgcloud.alfa.kommentar.KommentarCommandController.KommentarCommandByVorgangController; import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; +import lombok.SneakyThrows; @AutoConfigureMockMvc @SpringBootTest @@ -80,21 +79,31 @@ class KommentarCommandITCase { when(commandRemoteService.createCommand(any())).thenReturn(CommandTestFactory.create()); } + @SneakyThrows @Test - void shouldCreateCommand() throws Exception { + void shouldCreateCommand() { doRequestByKommentarId(createValidRequestContent()).andExpect(status().isCreated()); verify(commandRemoteService).createCommand(any()); } + private String createValidRequestContent() { + return KommentarTestFactory + .createRequestContent(CommandTestFactory.createCreateCommandBuilder() + .order(LegacyOrder.CREATE_KOMMENTAR) + .body(KommentarTestFactory.create()) + .build()); + } + @WithMockUser @DisplayName("should return validation error") @Nested class TestValidation { + @SneakyThrows @DisplayName("for null Text") @Test - void createCommandWithInvalidText() throws Exception { + void createCommandWithInvalidText() { String content = buildContentWithText(null); doRequestByKommentarId(content).andExpect(status().isUnprocessableEntity()) @@ -103,9 +112,10 @@ class KommentarCommandITCase { .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); } + @SneakyThrows @DisplayName("for empty String in Text") @Test - void createcommandWithShortText() throws Exception { + void createcommandWithShortText() { String content = buildContentWithText(StringUtils.EMPTY); doRequestByKommentarId(content).andExpect(status().isUnprocessableEntity()) @@ -113,9 +123,10 @@ class KommentarCommandITCase { } + @SneakyThrows @DisplayName("for invalid text should have parameter") @Test - void minMaxParameter() throws Exception { + void minMaxParameter() { String content = buildContentWithText(StringUtils.EMPTY); doRequestByKommentarId(content).andExpect(status().isUnprocessableEntity()) @@ -123,14 +134,17 @@ class KommentarCommandITCase { } private String buildContentWithText(String text) { - return createRequestContent(KommentarCommandTestFactory.createBuilder() - .order(CommandOrder.CREATE_ATTACHED_ITEM.name()) - .kommentar(KommentarTestFactory.createBuilder().text(text).build()) - .build()); + var kommentar = KommentarTestFactory.createBuilder().text(text).build(); + var createCommand = CommandTestFactory.createCreateCommandBuilder() + .order(LegacyOrder.CREATE_KOMMENTAR) + .body(kommentar) + .build(); + return KommentarTestFactory.createRequestContent(createCommand); } } - private ResultActions doRequestByKommentarId(String content) throws Exception { + @SneakyThrows + private ResultActions doRequestByKommentarId(String content) { return mockMvc .perform(post(KommentarCommandController.KOMMENTAR_COMMANDS, KommentarTestFactory.ID, KommentarTestFactory.VERSION).with(csrf()) .contentType(MediaType.APPLICATION_JSON) @@ -152,9 +166,14 @@ class KommentarCommandITCase { doReturn(UserProfileTestFactory.ID).when(userService).getUserId(); } + @SneakyThrows @Test - void shouldCreateCommand() throws Exception { - doRequestByVorgangId(createValidRequestContent()).andExpect(status().isCreated()); + void shouldCreateCommand() { + var content = KommentarTestFactory + .createRequestContent(CommandTestFactory.createCreateCommandBuilder() + .order(LegacyOrder.CREATE_KOMMENTAR) + .body(KommentarTestFactory.create()).build()); + doRequestByVorgangId(content).andExpect(status().isCreated()); verify(commandRemoteService).createCommand(any()); } @@ -164,9 +183,10 @@ class KommentarCommandITCase { @Nested class TestValidation { + @SneakyThrows @DisplayName("for null Text") @Test - void createCommandWithInvalidText() throws Exception { + void createCommandWithInvalidText() { String content = buildContentWithText(null); doRequestByVorgangId(content).andExpect(status().isUnprocessableEntity()) @@ -175,9 +195,10 @@ class KommentarCommandITCase { .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); } + @SneakyThrows @DisplayName("for empty String in Text") @Test - void createcommandWithShortText() throws Exception { + void createcommandWithShortText() { String content = buildContentWithText(StringUtils.EMPTY); doRequestByVorgangId(content).andExpect(status().isUnprocessableEntity()) @@ -185,9 +206,10 @@ class KommentarCommandITCase { } + @SneakyThrows @DisplayName("for invalid text should have parameter") @Test - void minMaxParameter() throws Exception { + void minMaxParameter() { String content = buildContentWithText(StringUtils.EMPTY); doRequestByVorgangId(content).andExpect(status().isUnprocessableEntity()) @@ -195,14 +217,17 @@ class KommentarCommandITCase { } private String buildContentWithText(String text) { - return createRequestContent(KommentarCommandTestFactory.createBuilder() + var kommentar = KommentarTestFactory.createBuilder().text(text).build(); + var createCommand = CommandTestFactory.createCreateCommandBuilder() .order(LegacyOrder.CREATE_KOMMENTAR) - .kommentar(KommentarTestFactory.createBuilder().text(text).build()) - .build()); + .body(kommentar) + .build(); + return KommentarTestFactory.createRequestContent(createCommand); } } - private ResultActions doRequestByVorgangId(String content) throws Exception { + @SneakyThrows + private ResultActions doRequestByVorgangId(String content) { return mockMvc.perform(post(KommentarCommandByVorgangController.KOMMENTAR_COMMANDS_BY_VORGANG, VorgangHeaderTestFactory.ID, RELATION_ID_ON_CREATE).with(csrf()) .contentType(MediaType.APPLICATION_JSON) diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandTestFactory.java deleted file mode 100644 index d9bdbad1c831c6f39681b446748faf0fac54fd28..0000000000000000000000000000000000000000 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarCommandTestFactory.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -package de.ozgcloud.alfa.kommentar; - -import java.util.Objects; - -import de.ozgcloud.alfa.common.command.CommandTestFactory; -import de.ozgcloud.alfa.common.command.LegacyOrder; -import de.ozgcloud.common.test.TestUtils; - -public class KommentarCommandTestFactory { - - public static final String ID = CommandTestFactory.ID; - public static final String ORDER = LegacyOrder.CREATE_KOMMENTAR; - - public static KommentarCommand create() { - return createBuilder().build(); - } - - public static KommentarCommand.KommentarCommandBuilder createBuilder() { - return KommentarCommand.builder() - .id(ID) - .order(ORDER) - .kommentar(KommentarTestFactory.create()); - } - - public static String createValidRequestContent() { - return createRequestContent(create()); - } - - public static String createRequestContent(KommentarCommand command) { - return TestUtils.loadTextFile("jsonTemplates/command/createCommandWithKommentar.json.tmpl", - command.getOrder(), - addTuedelchen(command.getKommentar().getText())); - } - - private static String addTuedelchen(String str) { - return Objects.isNull(str) ? null : String.format("\"%s\"", str); - } -} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarTestFactory.java index dc82e3ab1ca130feea7c764a1685e10d5f18c7c1..d7d48c46bda778c523aca310648828cddf27efe1 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarTestFactory.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/kommentar/KommentarTestFactory.java @@ -31,17 +31,19 @@ import java.util.Map; import com.thedeanda.lorem.Lorem; import com.thedeanda.lorem.LoremIpsum; -import de.ozgcloud.alfa.common.TestUtils; +import de.ozgcloud.alfa.common.AlfaTestUtils; import de.ozgcloud.alfa.common.binaryfile.BinaryFileTestFactory; import de.ozgcloud.alfa.common.binaryfile.FileId; +import de.ozgcloud.alfa.common.command.CreateCommand; import de.ozgcloud.alfa.common.user.UserProfileTestFactory; import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; +import de.ozgcloud.common.test.TestUtils; public class KommentarTestFactory { private static Lorem lorem = LoremIpsum.getInstance(); - public static final String ID = TestUtils.createMongoDbObjectId(); + public static final String ID = AlfaTestUtils.createMongoDbObjectId(); public static final long VERSION = 73; public static final String CREATED_BY = UserProfileTestFactory.ID.toString(); @@ -76,4 +78,10 @@ public class KommentarTestFactory { KommentarMapper.ATTACHMENTS, ATTACHMENTS); } -} + public static String createRequestContent(CreateCommand command) { + var kommentar = (Kommentar) command.getBody(); + return TestUtils.loadTextFile("jsonTemplates/command/createCommandWithKommentar.json.tmpl", + command.getOrder(), + TestUtils.addQuote(kommentar.getText())); + } +} \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/representation/RepresentationControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/representation/RepresentationControllerTest.java index 689c5630291732fc004ab4529f3a97ca83d8b3b1..baa2ecbbff4cb2b1ddf4e43ac192d10109088069 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/representation/RepresentationControllerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/representation/RepresentationControllerTest.java @@ -36,6 +36,7 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -43,11 +44,11 @@ import de.ozgcloud.alfa.common.binaryfile.BinaryFileModelAssembler; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.common.file.OzgFileService; import de.ozgcloud.alfa.common.file.OzgFileTestFactory; +import lombok.SneakyThrows; class RepresentationControllerTest { - private final String PATH = RepresentationController.REPRESENTATIONS_PATH; - + @Spy @InjectMocks private RepresentationController controller; @Mock @@ -70,27 +71,32 @@ class RepresentationControllerTest { when(fileService.getRepresentationsByEingang(anyString())).thenReturn(Stream.of(OzgFileTestFactory.create())); } + @SneakyThrows @Test - void shouldCallEndpoint() throws Exception { + void shouldCallEndpoint() { callEndpoint(); } + @SneakyThrows @Test - void shoudlCallFileService() throws Exception { + void shouldCallFileService() { callEndpoint(); verify(fileService).getRepresentationsByEingang(any()); } + @SneakyThrows @Test - void shouldCallModelAssembler() throws Exception { + void shouldCallModelAssembler() { callEndpoint(); verify(modelAssembler).toCollectionModel(ArgumentMatchers.<Stream<OzgFile>>any()); } - private void callEndpoint() throws Exception { - mockMvc.perform(get(PATH).param(RepresentationController.PARAM_EINGANG_ID, "1")).andExpect(status().isOk()); + @SneakyThrows + private void callEndpoint() { + mockMvc.perform(get(RepresentationController.PATH).param(RepresentationController.PARAM_EINGANG_ID, "1")).andExpect(status().isOk()); } } + } \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangHeaderTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangHeaderTestFactory.java index b557d223d8864f48d72a0b65978ab00083293b95..ffe87bd60e28af75e1c0ee6e815ffaeeb3ff0727 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangHeaderTestFactory.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangHeaderTestFactory.java @@ -23,7 +23,7 @@ */ package de.ozgcloud.alfa.vorgang; -import static de.ozgcloud.alfa.common.TestUtils.*; +import static de.ozgcloud.alfa.common.AlfaTestUtils.*; import java.time.ZonedDateTime; diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessorTest.java index 1022eb3a27b1e8dfc262aafc25ab99b4d80883c0..05306f1e29f313ec547dee7e7a8fc5ec1d652614 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessorTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangWithEingangProcessorTest.java @@ -77,30 +77,59 @@ class VorgangWithEingangProcessorTest { private UserProfileUrlProvider urlProvider = new UserProfileUrlProvider(); + @DisplayName("Attachments") @Nested - class TestAttachmentsLink { - - private final LinkRelation linkRel = VorgangWithEingangProcessor.REL_ATTACHMENTS; - private final String PATH = "/api/attachments?eingangId=" + EingangTestFactory.ID; + class TestAttachments { @BeforeEach void init() { initUserProfileUrlProvider(urlProvider); } - @DisplayName("should be present on numberOfAttachments > 0") - @Test - void shouldBePresentByAttachments() { - var link = processor.process(buildModelWithAttachments(1)).getLink(linkRel); + @DisplayName("link") + @Nested + class TestLink { - assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(PATH); + private static final String PATH = "/api/attachments?eingangId=" + EingangTestFactory.ID; + private final LinkRelation linkRel = VorgangWithEingangProcessor.REL_ATTACHMENTS; + + @DisplayName("should be present if attachments exists") + @Test + void shouldBePresent() { + var link = processor.process(buildModelWithAttachments(1)).getLink(linkRel); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(PATH); + } + + @Test + void shouldNotBePresent() { + var link = processor.process(buildModelWithAttachments(0)).getLink(linkRel); + + assertThat(link).isNotPresent(); + } } - @Test - void shouldNotBePresent() { - var link = processor.process(buildModelWithAttachments(0)).getLink(linkRel); + @DisplayName("download link") + @Nested + class TestDownloadLink { - assertThat(link).isNotPresent(); + private static final String PATH = "/api/vorgang/" + VorgangHeaderTestFactory.ID + "/attachments?eingangId=" + EingangTestFactory.ID; + private final LinkRelation linkRel = VorgangWithEingangProcessor.REL_DOWNLOAD_ATTACHMENTS; + + @DisplayName("should be present if attachments exists") + @Test + void shouldBePresent() { + var link = processor.process(buildModelWithAttachments(1)).getLink(linkRel); + + assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(PATH); + } + + @Test + void shouldNotBePresent() { + var link = processor.process(buildModelWithAttachments(0)).getLink(linkRel); + + assertThat(link).isNotPresent(); + } } private EntityModel<VorgangWithEingang> buildModelWithAttachments(int numberOfAttachments) { @@ -114,15 +143,22 @@ class VorgangWithEingangProcessorTest { } } + @DisplayName("Representations") @Nested class TestRepresentationsLink { + @BeforeEach + void init() { + initUserProfileUrlProvider(urlProvider); + } + + private static final String PATH = "/api/representations?eingangId=" + EingangTestFactory.ID; + private final LinkRelation linkRel = VorgangWithEingangProcessor.REL_REPRESENTATIONS; - private final String PATH = "/api/representations?eingangId=" + EingangTestFactory.ID; - @DisplayName("should be present on numberOfRepresentations > 0") + @DisplayName("should be present if representations exist") @Test - void shouldBePresentByRepresentations() { + void shouldBePresent() { var link = processor.process(buildModelWithRepresentation(1)).getLink(linkRel); assertThat(link).isPresent().get().extracting(Link::getHref).isEqualTo(PATH); @@ -389,7 +425,6 @@ class VorgangWithEingangProcessorTest { private VorgangWithEingang createVorgang(Eingang eingang) { return VorgangWithEingangTestFactory.createBuilder().eingang(eingang).build(); } - } @Nested diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandByVorgangControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandByVorgangControllerTest.java index decfe1c9660d52cc4116c2468c7842eaec89b1ba..74d31babe720c7877812b0422b00cb1d76b377c2 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandByVorgangControllerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandByVorgangControllerTest.java @@ -47,9 +47,11 @@ import de.ozgcloud.alfa.common.binaryfile.BinaryFileTestFactory; import de.ozgcloud.alfa.common.command.CommandController.CommandByRelationController; import de.ozgcloud.alfa.common.command.CommandTestFactory; import de.ozgcloud.alfa.common.command.CreateCommand; +import de.ozgcloud.alfa.common.command.LegacyOrder; import de.ozgcloud.alfa.common.user.CurrentUserService; import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; import de.ozgcloud.alfa.wiedervorlage.WiedervorlageCommandController.WiedervorlageCommandByVorgangController; +import lombok.SneakyThrows; class WiedervorlageCommandByVorgangControllerTest { @@ -88,33 +90,42 @@ class WiedervorlageCommandByVorgangControllerTest { class ServiceMethod { @Test - void shouldCallService() throws Exception { + void shouldCallService() { doRequest(); verify(service).createWiedervorlage(wiedervorlageCaptor.capture(), eq(VorgangHeaderTestFactory.ID)); - assertThat(wiedervorlageCaptor.getValue().getAttachments().get(0)).isEqualTo(BinaryFileTestFactory.FILE_ID); + assertThat(wiedervorlageCaptor.getValue().getAttachments().getFirst()).isEqualTo(BinaryFileTestFactory.FILE_ID); } @Test - void shouldCallServiceToUpdateNextFrist() throws Exception { + void shouldCallServiceToUpdateNextFrist() { doRequest(); verify(service).updateNextFrist(VorgangHeaderTestFactory.ID); } + @SneakyThrows @Test - void returnReturnCreated() throws Exception { + void returnReturnCreated() { doRequest().andExpect(status().isCreated()); } } } - private ResultActions doRequest() throws Exception { + @SneakyThrows + private ResultActions doRequest() { return mockMvc.perform( post(WiedervorlageCommandByVorgangController.WIEDERVORLAGE_COMMANDS_BY_VORGANG, VorgangHeaderTestFactory.ID) - .content(WiedervorlageCommandTestFactory.createValidRequestContent()) + .content(createRequestContent()) .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().is2xxSuccessful()); } + + private String createRequestContent() { + var createCommand = CommandTestFactory.createCreateCommandBuilder().body(WiedervorlageTestFactory.create()) + .order(LegacyOrder.EDIT_WIEDERVORLAGE) + .build(); + return WiedervorlageTestFactory.createRequestContent(createCommand); + } } \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandControllerTest.java index 00536e559a99dec6a358f7304669248bb5e2da8e..74bc2df86a8f6f8d98dc1fe34365654ef44c2e0e 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandControllerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandControllerTest.java @@ -35,8 +35,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; @@ -54,6 +52,7 @@ import de.ozgcloud.alfa.common.command.CreateCommand; import de.ozgcloud.alfa.common.command.LegacyOrder; import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; import de.ozgcloud.common.errorhandling.TechnicalException; +import lombok.SneakyThrows; class WiedervorlageCommandControllerTest { @@ -64,10 +63,6 @@ class WiedervorlageCommandControllerTest { private CommandService commandService; @Mock private WiedervorlageService service; - @Captor - private ArgumentCaptor<WiedervorlageCommand> commandCaptor; - @Captor - private ArgumentCaptor<CreateCommand> createCommandCaptor; private MockMvc mockMvc; @@ -80,7 +75,7 @@ class WiedervorlageCommandControllerTest { @Nested class TestUpdateWiedervorlage { - private static final String RESPONSE_HEADER = "http://localhost/api/commands/" + WiedervorlageCommandTestFactory.ID; + private static final String RESPONSE_HEADER = "http://localhost/api/commands/" + CommandTestFactory.ID; @Nested class ControllerMethods { @@ -93,43 +88,60 @@ class WiedervorlageCommandControllerTest { .body(WiedervorlageTestFactory.createAsMap()).build()); } + @SneakyThrows @Test - void shouldCallServiceGetById() throws Exception { + void shouldCallServiceGetById() { doRequest(); verify(service).getById(WiedervorlageTestFactory.ID); } @Test - void shouldCallServiceUpdateNextFrist() throws Exception { + void shouldCallServiceUpdateNextFrist() { doRequest(); verify(service).updateNextFrist(VorgangHeaderTestFactory.ID); } + @SneakyThrows @Test - void shouldReturnCreatedStatus() throws Exception { - doRequest().andExpect(status().isCreated()); + void shouldReturnCreatedStatus() { + var response = doRequest(); + + response.andExpect(status().isCreated()); } + @SneakyThrows @Test - void shouldResponseWithLocationHeader() throws Exception { - doRequest().andExpect(header().string("Location", RESPONSE_HEADER)); + void shouldResponseWithLocationHeader() { + var response = doRequest(); + + response.andExpect(header().string("Location", RESPONSE_HEADER)); } @Test - void shouldCreateCommand() throws Exception { + void shouldCreateCommand() { doRequest(); - verify(controller).createCommand(any(Wiedervorlage.class), any(WiedervorlageCommand.class)); + verify(controller).createCommand(any(Wiedervorlage.class), any(CreateCommand.class)); } - private ResultActions doRequest() throws Exception { + @SneakyThrows + private ResultActions doRequest() { return mockMvc.perform( post(WiedervorlageCommandController.WIEDERVORLAGE_COMMANDS, WiedervorlageTestFactory.ID, WiedervorlageTestFactory.VERSION) - .content(WiedervorlageCommandTestFactory.createValidRequestContent()).contentType(MediaType.APPLICATION_JSON)) + .content(createRequestContent()).contentType(MediaType.APPLICATION_JSON)) .andExpect(status().is2xxSuccessful()); } + + private String createRequestContent() { + return WiedervorlageTestFactory + .createRequestContent( + CommandTestFactory.createCreateCommandBuilder() + .order("EDIT_WIEDERVORLAGE") + .body(WiedervorlageTestFactory.create()) + .build()); + } } @DisplayName("create command") @@ -176,10 +188,10 @@ class WiedervorlageCommandControllerTest { @Nested class TestUpdateWiedervorlageByCommand { - private final FileId ATTACHMENT_ID = FileId.from("73"); - private final String BETREFF = "Neuer Betreff"; - private final String BESCHREIBUNG = "Neuer Beschreibung"; - private final LocalDate FRIST = LocalDate.now(); + private final static FileId ATTACHMENT_ID = FileId.from("73"); + private final static String BETREFF = "Neuer Betreff"; + private final static String BESCHREIBUNG = "Neuer Beschreibung"; + private final static LocalDate FRIST = LocalDate.now(); @Test void shouldHaveSetAttachments() { @@ -211,17 +223,16 @@ class WiedervorlageCommandControllerTest { } private Wiedervorlage update() { - return controller.updateWiedervorlageByCommand(WiedervorlageTestFactory.create(), buildWiedervorlageCommand()); + return controller.updateWiedervorlageByCommand(WiedervorlageTestFactory.create(), buildWiedervorlage()); } - private WiedervorlageCommand buildWiedervorlageCommand() { - var commandWiedervorlage = WiedervorlageTestFactory.createBuilder() + private Wiedervorlage buildWiedervorlage() { + return WiedervorlageTestFactory.createBuilder() .clearAttachments().attachment(ATTACHMENT_ID) .betreff(BETREFF) .beschreibung(BESCHREIBUNG) .frist(FRIST) .build(); - return WiedervorlageCommandTestFactory.createBuilder().wiedervorlage(commandWiedervorlage).build(); } } } @@ -232,7 +243,9 @@ class WiedervorlageCommandControllerTest { @Test void shouldThrowException() { var wiedervorlage = WiedervorlageTestFactory.create(); - var command = WiedervorlageCommandTestFactory.createBuilder().order(CommandOrder.CREATE_ATTACHED_ITEM.name()).build(); + var command = CommandTestFactory.createCreateCommandBuilder() + .order(CommandOrder.CREATE_ATTACHED_ITEM.name()) + .body(wiedervorlage).build(); assertThatExceptionOfType(TechnicalException.class) .isThrownBy(() -> controller.createCommand(wiedervorlage, command)); @@ -241,7 +254,9 @@ class WiedervorlageCommandControllerTest { private Command callCreateCommand(String order) { return controller.createCommand(WiedervorlageTestFactory.create(), - WiedervorlageCommandTestFactory.createBuilder().order(order).build()); + CommandTestFactory.createCreateCommandBuilder().body(WiedervorlageTestFactory.create()) + .order(order) + .build()); } } } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandITCase.java index fe84f65889cd73f0b065003e7e3b1dffb07964f2..88a17b72ba185204edb3514e4a2bfcd90bcf53a6 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandITCase.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandITCase.java @@ -23,7 +23,6 @@ */ package de.ozgcloud.alfa.wiedervorlage; -import static de.ozgcloud.alfa.wiedervorlage.WiedervorlageCommandTestFactory.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; @@ -50,11 +49,13 @@ import org.springframework.test.web.servlet.ResultActions; import de.ozgcloud.alfa.common.ValidationMessageCodes; import de.ozgcloud.alfa.common.command.CommandRemoteService; import de.ozgcloud.alfa.common.command.CommandTestFactory; +import de.ozgcloud.alfa.common.command.CreateCommand; import de.ozgcloud.alfa.common.command.LegacyOrder; import de.ozgcloud.alfa.common.user.CurrentUserService; import de.ozgcloud.alfa.common.user.UserProfileTestFactory; import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; import de.ozgcloud.alfa.wiedervorlage.WiedervorlageCommandController.WiedervorlageCommandByVorgangController; +import lombok.SneakyThrows; @AutoConfigureMockMvc @SpringBootTest @@ -82,9 +83,10 @@ class WiedervorlageCommandITCase { when(commandRemoteService.createCommand(any())).thenReturn(CommandTestFactory.create()); } + @SneakyThrows @Test - void shouldCreateCommand() throws Exception { - doRequest(WiedervorlageCommandTestFactory.createValidRequestContent()).andExpect(status().isCreated()); + void shouldCreateCommand() { + doRequest(createEditWiedervorlageRequestContent()).andExpect(status().isCreated()); verify(commandRemoteService).createCommand(any()); } @@ -94,9 +96,10 @@ class WiedervorlageCommandITCase { @Nested class TestValidation { + @SneakyThrows @DisplayName("for null Betreff") @Test - void createCommandWithInvalidBetreff() throws Exception { + void createCommandWithInvalidBetreff() { String content = buildContentWithBetreff(null); doRequest(content).andExpect(status().isUnprocessableEntity()) @@ -105,9 +108,10 @@ class WiedervorlageCommandITCase { .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); } + @SneakyThrows @DisplayName("for only 1 character in Betreff") @Test - void createcommandWithShortBetreff() throws Exception { + void createcommandWithShortBetreff() { String content = buildContentWithBetreff("a"); doRequest(content).andExpect(status().isUnprocessableEntity()) @@ -115,17 +119,19 @@ class WiedervorlageCommandITCase { } + @SneakyThrows @DisplayName("for invalid betreff should have paramter") @Test - void minMaxParameter() throws Exception { + void minMaxParameter() { String content = buildContentWithBetreff("a"); doRequest(content).andExpect(status().isUnprocessableEntity()).andExpect(jsonPath("$.issues[0].parameters.length()").value(2)); } + @SneakyThrows @DisplayName("for 41 character in Betreff") @Test - void createCommandWithLongBetreff() throws Exception { + void createCommandWithLongBetreff() { var content = buildContentWithBetreff(RandomStringUtils.random(41)); doRequest(content).andExpect(status().isUnprocessableEntity()); @@ -135,9 +141,10 @@ class WiedervorlageCommandITCase { return createEditContent(WiedervorlageTestFactory.createBuilder().betreff(betreff).build()); } + @SneakyThrows @DisplayName("for frist in past") @Test - void createCommandWithInvalidDate() throws Exception { + void createCommandWithInvalidDate() { String content = createEditContent(WiedervorlageTestFactory.createBuilder().frist(LocalDate.parse("2020-01-01")).build()); doRequest(content).andExpect(status().isUnprocessableEntity()) @@ -146,9 +153,10 @@ class WiedervorlageCommandITCase { .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_DATE_PAST)); } + @SneakyThrows @DisplayName("for empty frist") @Test - void createCommandWithoutDate() throws Exception { + void createCommandWithoutDate() { String content = createEditContent(WiedervorlageTestFactory.createBuilder().frist(null).build()); doRequest(content).andExpect(status().isUnprocessableEntity()) @@ -158,12 +166,14 @@ class WiedervorlageCommandITCase { } private String createEditContent(Wiedervorlage wiedervorlage) { - return createRequestContent(createWithWiedervorlage(wiedervorlage).toBuilder().order(LegacyOrder.EDIT_WIEDERVORLAGE).build()); + return WiedervorlageTestFactory + .createRequestContent(createWithWiedervorlage(wiedervorlage).toBuilder().order(LegacyOrder.EDIT_WIEDERVORLAGE).build()); } } - private ResultActions doRequest(String content) throws Exception { + @SneakyThrows + private ResultActions doRequest(String content) { return mockMvc.perform( post(WiedervorlageCommandController.WIEDERVORLAGE_COMMANDS, WiedervorlageTestFactory.ID, WiedervorlageTestFactory.VERSION) .with(csrf()) @@ -187,21 +197,31 @@ class WiedervorlageCommandITCase { doReturn(UserProfileTestFactory.ID).when(userService).getUserId(); } + @SneakyThrows @Test - void shouldCreateCommand() throws Exception { - doRequest(createValidRequestContent()).andExpect(status().isCreated()); + void shouldCallCreateCommand() { + doRequest(createEditWiedervorlageRequestContent()); verify(commandRemoteService).createCommand(any()); } + @SneakyThrows + @Test + void shouldReturnCreatedStatus() { + var response = doRequest(createEditWiedervorlageRequestContent()); + + response.andExpect(status().isCreated()); + } + @WithMockUser @DisplayName("should return validation error") @Nested class TestValidation { + @SneakyThrows @DisplayName("for null Betreff") @Test - void createCommandWithInvalidBetreff() throws Exception { + void createCommandWithInvalidBetreff() { String content = buildContentWithBetreff(null); doRequest(content).andExpect(status().isUnprocessableEntity()) @@ -210,9 +230,10 @@ class WiedervorlageCommandITCase { .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_IS_EMPTY)); } + @SneakyThrows @DisplayName("for only 1 character in Betreff") @Test - void createcommandWithShortBetreff() throws Exception { + void createcommandWithShortBetreff() { String content = buildContentWithBetreff("a"); doRequest(content).andExpect(status().isUnprocessableEntity()) @@ -220,30 +241,34 @@ class WiedervorlageCommandITCase { } + @SneakyThrows @DisplayName("for invalid betreff should have paramter") @Test - void minMaxParameter() throws Exception { + void minMaxParameter() { String content = buildContentWithBetreff("a"); doRequest(content).andExpect(status().isUnprocessableEntity()).andExpect(jsonPath("$.issues[0].parameters.length()").value(2)); } + @SneakyThrows @DisplayName("for 41 character in Betreff") @Test - void createCommandWithLongBetreff() throws Exception { + void createCommandWithLongBetreff() { var content = buildContentWithBetreff(RandomStringUtils.randomAlphanumeric(41)); doRequest(content).andExpect(status().isUnprocessableEntity()); } private String buildContentWithBetreff(String betreff) { - return createRequestContent(createWithWiedervorlage(WiedervorlageTestFactory.createBuilder().betreff(betreff).build())); + return WiedervorlageTestFactory + .createRequestContent(createWithWiedervorlage(WiedervorlageTestFactory.createBuilder().betreff(betreff).build())); } + @SneakyThrows @DisplayName("for frist in past") @Test - void createCommandWithInvalidDate() throws Exception { - String content = createRequestContent( + void createCommandWithInvalidDate() { + String content = WiedervorlageTestFactory.createRequestContent( createWithWiedervorlage(WiedervorlageTestFactory.createBuilder().frist(LocalDate.parse("2020-01-01")).build())); doRequest(content).andExpect(status().isUnprocessableEntity()) @@ -252,10 +277,11 @@ class WiedervorlageCommandITCase { .andExpect(jsonPath("$.issues.[0].messageCode").value(ValidationMessageCodes.FIELD_DATE_PAST)); } + @SneakyThrows @DisplayName("for empty frist") @Test - void createCommandWithoutDate() throws Exception { - String content = createRequestContent( + void createCommandWithoutDate() { + String content = WiedervorlageTestFactory.createRequestContent( createWithWiedervorlage(WiedervorlageTestFactory.createBuilder().frist(null).build())); doRequest(content).andExpect(status().isUnprocessableEntity()) @@ -266,7 +292,8 @@ class WiedervorlageCommandITCase { } - private ResultActions doRequest(String content) throws Exception { + @SneakyThrows + private ResultActions doRequest(String content) { return mockMvc.perform(post(WiedervorlageCommandByVorgangController.WIEDERVORLAGE_COMMANDS_BY_VORGANG, VorgangHeaderTestFactory.ID, RELATION_ID_ON_CREATE) .with(csrf()) @@ -274,4 +301,17 @@ class WiedervorlageCommandITCase { .content(content)); } } + + private String createEditWiedervorlageRequestContent() { + var createCommand = CommandTestFactory.createCreateCommandBuilder().body(WiedervorlageTestFactory.create()) + .order(LegacyOrder.EDIT_WIEDERVORLAGE).build(); + return WiedervorlageTestFactory.createRequestContent(createCommand); + } + + private CreateCommand createWithWiedervorlage(Wiedervorlage wiedervorlage) { + return CommandTestFactory.createCreateCommandBuilder() + .body(wiedervorlage) + .order("EDIT_WIEDERVORLAGE") + .build(); + } } \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandTestFactory.java deleted file mode 100644 index 2b2d67f630017f8219c40b3168bea482fdc95ee5..0000000000000000000000000000000000000000 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageCommandTestFactory.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -package de.ozgcloud.alfa.wiedervorlage; - -import java.util.Objects; -import java.util.Optional; - -import org.apache.commons.lang3.StringUtils; - -import de.ozgcloud.alfa.common.binaryfile.BinaryFileTestFactory; -import de.ozgcloud.alfa.common.command.CommandOrder; -import de.ozgcloud.alfa.common.command.CommandTestFactory; -import de.ozgcloud.alfa.common.command.LegacyOrder; -import de.ozgcloud.common.test.TestUtils; - -public class WiedervorlageCommandTestFactory { - - public static final String ID = CommandTestFactory.ID; - public static final String ORDER = LegacyOrder.EDIT_WIEDERVORLAGE; - - public static WiedervorlageCommand create() { - return createBuilder().build(); - } - - public static WiedervorlageCommand createWithWiedervorlage(Wiedervorlage wiedervorlage) { - return createBuilder().wiedervorlage(wiedervorlage).build(); - } - - public static WiedervorlageCommand.WiedervorlageCommandBuilder createBuilder() { - return WiedervorlageCommand.builder() - .id(ID) - .order(ORDER) - .wiedervorlage(WiedervorlageTestFactory.create()); - } - - public static String createValidRequestContent() { - return createRequestContent(create()); - } - - public static String createRequestContent(WiedervorlageCommand command) { - return TestUtils.loadTextFile("jsonTemplates/command/createCommandWithWiedervorlage.json.tmpl", - command.getOrder(), - addTuedelchen(command.getWiedervorlage().getBetreff()), - command.getWiedervorlage().getBeschreibung(), - Optional.ofNullable(command.getWiedervorlage().getFrist()).map(Object::toString).orElse(StringUtils.EMPTY), - "/api/binaryFiles/" + BinaryFileTestFactory.FILE_ID); - } - - private static String addTuedelchen(String str) { - return Objects.isNull(str) ? null : String.format("\"%s\"", str); - } - - public static String createRequestContent(CommandOrder order) { - return TestUtils.loadTextFile("jsonTemplates/command/createWiedervorlageOrderCommand.json.tmpl", order.name()); - } -} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageTestFactory.java index 06078c97e7b7def4a61368ca6eb6f97740f78a94..be94298693d45e299a297c108c5fb5df5ccc1ac5 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageTestFactory.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/wiedervorlage/WiedervorlageTestFactory.java @@ -26,12 +26,17 @@ package de.ozgcloud.alfa.wiedervorlage; import java.time.LocalDate; import java.time.ZonedDateTime; import java.util.Map; +import java.util.Optional; import java.util.UUID; +import org.apache.commons.lang3.StringUtils; + import com.thedeanda.lorem.LoremIpsum; import de.ozgcloud.alfa.common.binaryfile.BinaryFileTestFactory; +import de.ozgcloud.alfa.common.command.CreateCommand; import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; +import de.ozgcloud.common.test.TestUtils; public class WiedervorlageTestFactory { @@ -78,4 +83,14 @@ public class WiedervorlageTestFactory { WiedervorlageMapper.CREATED_AT, CREATED_AT_STR, WiedervorlageMapper.ATTACHMENTS, BinaryFileTestFactory.ID); } + + public static String createRequestContent(CreateCommand command) { + var wiedervorlage = (Wiedervorlage) command.getBody(); + return TestUtils.loadTextFile("jsonTemplates/command/createCommandWithWiedervorlage.json.tmpl", + command.getOrder(), + TestUtils.addQuote(wiedervorlage.getBetreff()), + wiedervorlage.getBeschreibung(), + Optional.ofNullable(wiedervorlage.getFrist()).map(String::valueOf).orElse(StringUtils.EMPTY), + "/api/binaryFiles/" + BinaryFileTestFactory.FILE_ID); + } } \ No newline at end of file diff --git a/alfa-service/src/test/resources/jsonTemplates/command/createCommandWithKommentar.json.tmpl b/alfa-service/src/test/resources/jsonTemplates/command/createCommandWithKommentar.json.tmpl index fbe55525a6dd55f400f08c0f8baec92b15af7c72..072af9c1c4afb4c6546c741b4fe0f0479be53e11 100644 --- a/alfa-service/src/test/resources/jsonTemplates/command/createCommandWithKommentar.json.tmpl +++ b/alfa-service/src/test/resources/jsonTemplates/command/createCommandWithKommentar.json.tmpl @@ -1,7 +1,7 @@ { "order": "%s", - "kommentar": { + "body": { "text": %s, - "attachments": [] + "attachments": [] } } \ No newline at end of file diff --git a/alfa-service/src/test/resources/jsonTemplates/command/createCommandWithWiedervorlage.json.tmpl b/alfa-service/src/test/resources/jsonTemplates/command/createCommandWithWiedervorlage.json.tmpl index fa1665155401142608bf6e6f08b1502a358ae8ca..4ef1fb4b5dbf2c90689c192c210ed32157bece63 100644 --- a/alfa-service/src/test/resources/jsonTemplates/command/createCommandWithWiedervorlage.json.tmpl +++ b/alfa-service/src/test/resources/jsonTemplates/command/createCommandWithWiedervorlage.json.tmpl @@ -1,6 +1,6 @@ { "order": "%s", - "wiedervorlage": { + "body": { "betreff": %s, "beschreibung": "%s", "frist": "%s", diff --git a/alfa-service/src/test/resources/jsonTemplates/command/createWiedervorlageOrderCommand.json.tmpl b/alfa-service/src/test/resources/jsonTemplates/command/createWiedervorlageOrderCommand.json.tmpl deleted file mode 100644 index 64d27c8846dd5cd0654e7af502fded4231eb7b4e..0000000000000000000000000000000000000000 --- a/alfa-service/src/test/resources/jsonTemplates/command/createWiedervorlageOrderCommand.json.tmpl +++ /dev/null @@ -1,3 +0,0 @@ -{ - "order": "%s" -} \ No newline at end of file diff --git a/alfa-xdomea/pom.xml b/alfa-xdomea/pom.xml index ee34595691b9d2da7c53e3e963a12708ace55e4e..6f92cd2bc38e598b1dc2ab97a546ab4c331c7139 100644 --- a/alfa-xdomea/pom.xml +++ b/alfa-xdomea/pom.xml @@ -31,7 +31,7 @@ <parent> <groupId>de.ozgcloud.alfa</groupId> <artifactId>alfa</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.11.0-SNAPSHOT</version> </parent> <artifactId>alfa-xdomea</artifactId> diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/DefaultXdomeaProperties.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/DefaultXdomeaProperties.java deleted file mode 100644 index 32861d97bc2c68ae07e572d05b8316b526aaec90..0000000000000000000000000000000000000000 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/DefaultXdomeaProperties.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.ozgcloud.alfa.export; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Configuration; - -@Configuration -@ConditionalOnProperty(value = "ozgcloud.feature.vorgang-export", havingValue = "false", matchIfMissing = true) -public class DefaultXdomeaProperties implements XdomeaProperties { - - private static final String EXCEPTION_MESSAGE = "The feature vorgang-export must be active to access this property."; - - @Override - public String getBehoerdenschluessel() { - throw new UnsupportedOperationException(EXCEPTION_MESSAGE); - } - - @Override - public String getBehoerdenschluesselUri() { - throw new UnsupportedOperationException(EXCEPTION_MESSAGE); - } - - @Override - public String getBehoerdenschluesselVersion() { - throw new UnsupportedOperationException(EXCEPTION_MESSAGE); - } -} diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportConfiguration.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportConfiguration.java index 8840c0ac344b8d8d84fde3d7d48a96ec76794267..1ad1ea37461edf34756e2f478e3b56cc74e2bd88 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportConfiguration.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportConfiguration.java @@ -3,12 +3,12 @@ package de.ozgcloud.alfa.export; import java.util.HashMap; import java.util.Map; -import jakarta.xml.bind.Marshaller; - import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.oxm.jaxb.Jaxb2Marshaller; +import org.springframework.validation.Validator; +import jakarta.xml.bind.Marshaller; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @@ -21,6 +21,11 @@ class ExportConfiguration { private final XdomeaNamespacePrefixMapper prefixMapper; + @Bean + static Validator configurationPropertiesValidator() { + return new XdomeaPropertiesValidator(); + } + @Bean Jaxb2Marshaller marshaller() { var marshaller = new Jaxb2Marshaller(); diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportVorgangProcessor.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportVorgangProcessor.java index cd93a4d4c15df7e7f57bcc02668076f4c2efdd15..b7621e342cb52f19593b07ee03384b25285feb6e 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportVorgangProcessor.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExportVorgangProcessor.java @@ -10,7 +10,6 @@ import org.springframework.hateoas.LinkRelation; import org.springframework.hateoas.server.RepresentationModelProcessor; import org.springframework.stereotype.Component; -import de.ozgcloud.alfa.common.FeatureToggleProperties; import de.ozgcloud.alfa.common.ModelBuilder; import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus; import de.ozgcloud.alfa.vorgang.VorgangWithEingang; @@ -20,13 +19,10 @@ import lombok.RequiredArgsConstructor; @Component class ExportVorgangProcessor implements RepresentationModelProcessor<EntityModel<VorgangWithEingang>> { - private final Predicate<VorgangWithEingang> isExportEnabled = vorgang -> isExportEnabled(); private static final Predicate<VorgangWithEingang> IS_VORGANG_ABGESCHLOSSEN = vorgang -> vorgang.getStatus() == VorgangStatus.ABGESCHLOSSEN; static final LinkRelation REL_EXPORT = LinkRelation.of("export"); - private final FeatureToggleProperties featureToggleProperties; - @Override public EntityModel<VorgangWithEingang> process(EntityModel<VorgangWithEingang> model) { var vorgang = model.getContent(); @@ -36,12 +32,8 @@ class ExportVorgangProcessor implements RepresentationModelProcessor<EntityModel } return ModelBuilder.fromModel(model) - .ifMatch(isExportEnabled.and(IS_VORGANG_ABGESCHLOSSEN)) + .ifMatch(IS_VORGANG_ABGESCHLOSSEN) .addLink(linkTo(methodOn(ExportVorgangController.class).exportToXdomea(vorgang.getId())).withRel(REL_EXPORT)) .buildModel(); } - - private boolean isExportEnabled() { - return featureToggleProperties.isVorgangExport(); - } } diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExternalXdomeaProperties.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExternalXdomeaProperties.java deleted file mode 100644 index 924bc8c28890c805e728af255a10a80b0c2cc575..0000000000000000000000000000000000000000 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/ExternalXdomeaProperties.java +++ /dev/null @@ -1,24 +0,0 @@ -package de.ozgcloud.alfa.export; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -import jakarta.validation.constraints.NotNull; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@Configuration -@ConfigurationProperties("ozgcloud.xdomea") -@ConditionalOnProperty(value = "ozgcloud.feature.vorgang-export", havingValue = "true") -public class ExternalXdomeaProperties implements XdomeaProperties { - - @NotNull - private String behoerdenschluessel; - @NotNull - private String behoerdenschluesselUri; - @NotNull - private String behoerdenschluesselVersion; -} diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaProperties.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaProperties.java index bedb3877aeafe1401e8ca5a8e82727e2da1e963a..2d2157c690d1b1c354875b477ec6163752cc1234 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaProperties.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaProperties.java @@ -1,10 +1,26 @@ package de.ozgcloud.alfa.export; -public interface XdomeaProperties { +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.annotation.Validated; - String getBehoerdenschluessel(); +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; - String getBehoerdenschluesselUri(); +@Configuration +@ConfigurationProperties("ozgcloud.xdomea") +@Validated +@Builder +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class XdomeaProperties { - String getBehoerdenschluesselVersion(); + private String behoerdenschluessel; + private String behoerdenschluesselUri; + private String behoerdenschluesselVersion; } diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaPropertiesValidator.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaPropertiesValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..8cc320ecb6346fbc79bd42a2826b08bec38aaff3 --- /dev/null +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/export/XdomeaPropertiesValidator.java @@ -0,0 +1,44 @@ +package de.ozgcloud.alfa.export; + +import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; + +import de.ozgcloud.common.errorhandling.TechnicalException; + +public class XdomeaPropertiesValidator implements Validator { + + @Override + public boolean supports(Class<?> clazz) { + return XdomeaProperties.class.isAssignableFrom(clazz); + } + + @Override + public void validate(Object target, Errors errors) { + var properties = (XdomeaProperties) target; + validateBehoerdenschluesselProperties(errors, properties); + } + + private void validateBehoerdenschluesselProperties(Errors errors, XdomeaProperties properties) { + if (StringUtils.isNotBlank(properties.getBehoerdenschluessel())) { + validateNotBlank("behoerdenschluesselUri", properties, errors); + validateNotBlank("behoerdenschluesselVersion", properties, errors); + } + } + + private void validateNotBlank(String propertyName, XdomeaProperties properties, Errors errors) { + getPropertyValue(propertyName, properties); + if (StringUtils.isBlank(getPropertyValue(propertyName, properties))) { + errors.rejectValue(propertyName, String.format("ozgcloud.xdomea.%s.empty", propertyName), String.format("%s must be set", propertyName)); + } + } + + private static String getPropertyValue(String fieldName, XdomeaProperties properties) { + try { + return BeanUtils.getSimpleProperty(properties, fieldName); + } catch (Exception e) { + throw new TechnicalException("Property does not exist", e); + } + } +} diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/historie/ExportHistorieService.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/historie/ExportHistorieService.java index 827cfa4b00d8a52024ae35e562f539e54cb64f61..4f3e62cffd7e5a5e07f7dccc55a2f8ca59e092f7 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/historie/ExportHistorieService.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/historie/ExportHistorieService.java @@ -42,17 +42,22 @@ public class ExportHistorieService { } String createValueBeforeChange(VorgangChange vorgangChange) { - if (vorgangChange.getOrder().equals(CommandOrder.ASSIGN_USER.name())) { - return appendOrganisationseinheitenID(vorgangChange.getValueBeforeChange(), vorgangChange.getOrganisationseinheitenID()); - } - return vorgangChange.getValueBeforeChange(); + return createValueChange(vorgangChange, vorgangChange.getValueBeforeChange()); } String createValueAfterChange(VorgangChange vorgangChange) { - if (vorgangChange.getOrder().equals(CommandOrder.ASSIGN_USER.name())) { - return appendOrganisationseinheitenID(vorgangChange.getValueAfterChange(), vorgangChange.getOrganisationseinheitenID()); + return createValueChange(vorgangChange, vorgangChange.getValueAfterChange()); + } + + private String createValueChange(VorgangChange vorgangChange, String valueChange) { + if (isAssignUserOrder(vorgangChange)) { + return appendOrganisationseinheitenID(valueChange, vorgangChange.getOrganisationseinheitenID()); } - return vorgangChange.getValueAfterChange(); + return valueChange; + } + + private boolean isAssignUserOrder(VorgangChange vorgangChange) { + return vorgangChange.getOrder().equals(CommandOrder.ASSIGN_USER.name()); } String appendOrganisationseinheitenID(String text, String organisationseinheitenID) { diff --git a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/vorgang/KopfCreator.java b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/vorgang/KopfCreator.java index f23b06bdff34f335838f388ca0da9e389af0eedc..5305519177c60d83ac4de118977b4fbd4c549fa2 100644 --- a/alfa-xdomea/src/main/java/de/ozgcloud/alfa/vorgang/KopfCreator.java +++ b/alfa-xdomea/src/main/java/de/ozgcloud/alfa/vorgang/KopfCreator.java @@ -15,6 +15,7 @@ import de.xoev.xdomea.NachrichtentypCodeType; import de.xoev.xdomea.NkAbgabeType; import de.xoev.xdomea.OrganisationseinheitType; import de.xoev.xdomea.SystemType; +import io.micrometer.common.util.StringUtils; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @@ -26,7 +27,7 @@ class KopfCreator { static final String NACHRICHTENTYP_ABGABE_ABGABE_TYPE_CODE = "0401"; static final String PRODUKT_NAME = "OZG-Cloud"; - private final XdomeaProperties xDomeaProperties; + private final XdomeaProperties xdomeaProperties; public NkAbgabeType createKopf(VorgangWithEingang vorgang) { var nkAbgabeType = new NkAbgabeType(); @@ -57,7 +58,9 @@ class KopfCreator { KontaktType createKontaktType() { var kontakt = new KontaktType(); - kontakt.setBehoerdenkennung(createBehoerdenkennung()); + if (StringUtils.isNotBlank(xdomeaProperties.getBehoerdenschluessel())) { + kontakt.setBehoerdenkennung(createBehoerdenkennung()); + } return kontakt; } @@ -75,9 +78,9 @@ class KopfCreator { Code createBehoerdenschluessel() { var behoerdenschluessel = new Code(); - behoerdenschluessel.setCode(xDomeaProperties.getBehoerdenschluessel()); - behoerdenschluessel.setListURI(xDomeaProperties.getBehoerdenschluesselUri()); - behoerdenschluessel.setListVersionID(xDomeaProperties.getBehoerdenschluesselVersion()); + behoerdenschluessel.setCode(xdomeaProperties.getBehoerdenschluessel()); + behoerdenschluessel.setListURI(xdomeaProperties.getBehoerdenschluesselUri()); + behoerdenschluessel.setListVersionID(xdomeaProperties.getBehoerdenschluesselVersion()); return behoerdenschluessel; } diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportFilenameGeneratorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportFilenameGeneratorTest.java index 1b85ad584d8f2bea60f283e6f20be763731173de..0cf25b49436886e9478b67c4e204ff824c9abed9 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportFilenameGeneratorTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportFilenameGeneratorTest.java @@ -10,7 +10,7 @@ import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import de.ozgcloud.alfa.common.ExportFilenameGenerator; -import de.ozgcloud.alfa.common.TestUtils; +import de.ozgcloud.alfa.common.AlfaTestUtils; import de.ozgcloud.alfa.common.UUIDConverter; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.common.file.OzgFileTestFactory; @@ -48,7 +48,7 @@ class ExportFilenameGeneratorTest { void shouldGenerate() { var filename = callGenerator(); - assertThat(filename).matches(String.format("^%s_%s$", TestUtils.UUID_REGEX, ozgFile.getName())); + assertThat(filename).matches(String.format("^%s_%s$", AlfaTestUtils.UUID_REGEX, ozgFile.getName())); } private String callGenerator() { diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceITCase.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceITCase.java index 4cf6d583e155be78b4d3d0020b700cf9723c6232..a55f463cb200af363bfef7d3a08e39c15066b0d2 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceITCase.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceITCase.java @@ -1,6 +1,6 @@ package de.ozgcloud.alfa.export; -import static de.ozgcloud.alfa.common.TestUtils.*; +import static de.ozgcloud.alfa.common.AlfaTestUtils.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceTest.java index df101b3da9a0289adfc7f9322e2e30a122b62715..35bc4766a34a31b28701ce62ee9ec29a4033bf53 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportServiceTest.java @@ -1,6 +1,6 @@ package de.ozgcloud.alfa.export; -import static de.ozgcloud.alfa.common.TestUtils.*; +import static de.ozgcloud.alfa.common.AlfaTestUtils.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; @@ -31,7 +31,7 @@ import de.ozgcloud.alfa.bescheid.BescheidExportDataTestFactory; import de.ozgcloud.alfa.bescheid.ExportBescheidService; import de.ozgcloud.alfa.common.ExportFilenameGenerator; import de.ozgcloud.alfa.common.HistorienProtokollInformationTypeTestFactory; -import de.ozgcloud.alfa.common.TestUtils; +import de.ozgcloud.alfa.common.AlfaTestUtils; import de.ozgcloud.alfa.common.file.OzgFile; import de.ozgcloud.alfa.common.file.OzgFileTestFactory; import de.ozgcloud.alfa.file.ExportFileService; @@ -397,7 +397,7 @@ class ExportServiceTest { void shouldMatchPattern() { var filename = service.buildXmlFilename(UUID.randomUUID().toString()); - assertThat(filename).matches(TestUtils.uuidRegexWithSuffix(ExportService.EXPORT_FILENAME_SUFFIX)); + assertThat(filename).matches(AlfaTestUtils.uuidRegexWithSuffix(ExportService.EXPORT_FILENAME_SUFFIX)); } } diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportVorgangControllerTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportVorgangControllerTest.java index 61ee1269d53784bb31df5c85fdc192c667cdf338..e83d251ba893e0efd9df548cdbe29846ebd4240c 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportVorgangControllerTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportVorgangControllerTest.java @@ -21,7 +21,7 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import de.ozgcloud.alfa.common.TestUtils; +import de.ozgcloud.alfa.common.AlfaTestUtils; import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; class ExportVorgangControllerTest { @@ -47,7 +47,7 @@ class ExportVorgangControllerTest { void shouldMatchPattern() { var filename = controller.buildZipFilename(UUID.randomUUID().toString()); - assertThat(filename).matches(TestUtils.uuidRegexWithSuffix("_Abgabe.Abgabe.0401.xdomea")); + assertThat(filename).matches(AlfaTestUtils.uuidRegexWithSuffix("_Abgabe.Abgabe.0401.xdomea")); } } @@ -74,7 +74,7 @@ class ExportVorgangControllerTest { doRequest(); verify(xDomeaService).writeExport(eq(VorgangHeaderTestFactory.ID), filenameIdArgumentCaptor.capture(), any()); - assertThat(filenameIdArgumentCaptor.getValue()).matches(TestUtils.UUID_REGEX); + assertThat(filenameIdArgumentCaptor.getValue()).matches(AlfaTestUtils.UUID_REGEX); } @Test @@ -83,7 +83,7 @@ class ExportVorgangControllerTest { verify(controller).buildZipFilename(filenameIdArgumentCaptor.capture()); - assertThat(filenameIdArgumentCaptor.getValue()).matches(TestUtils.UUID_REGEX); + assertThat(filenameIdArgumentCaptor.getValue()).matches(AlfaTestUtils.UUID_REGEX); } diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportVorgangProcessorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportVorgangProcessorTest.java index 27ed62377f845026eb3265b2a23f0694188c2579..1443f94482daadc8ab02a10a9ebeaa17a3045457 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportVorgangProcessorTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/ExportVorgangProcessorTest.java @@ -2,7 +2,6 @@ package de.ozgcloud.alfa.export; import static de.ozgcloud.alfa.common.UserProfileUrlProviderTestFactory.*; import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -11,11 +10,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.mockito.InjectMocks; -import org.mockito.Mock; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; -import de.ozgcloud.alfa.common.FeatureToggleProperties; import de.ozgcloud.alfa.common.UserProfileUrlProvider; import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus; import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; @@ -27,9 +24,6 @@ class ExportVorgangProcessorTest { @InjectMocks private ExportVorgangProcessor processor; - @Mock - private FeatureToggleProperties featureToggleProperties; - private final UserProfileUrlProvider urlProvider = new UserProfileUrlProvider(); @Nested @@ -45,8 +39,7 @@ class ExportVorgangProcessorTest { } @Test - void shouldAddLinkWhenExportIsEnabledAndVorgangIsAbgeschlossen() { - when(featureToggleProperties.isVorgangExport()).thenReturn(true); + void shouldAddLinkWhenVorgangIsAbgeschlossen() { var vorgang = vorgangInStatus(VorgangStatus.ABGESCHLOSSEN); var model = processor.process(EntityModel.of(vorgang)); @@ -58,7 +51,6 @@ class ExportVorgangProcessorTest { @ParameterizedTest @EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "ABGESCHLOSSEN") void shouldNotAddLinkWhenVorgangIsNotAbgeschlossen(VorgangStatus status) { - when(featureToggleProperties.isVorgangExport()).thenReturn(true); var vorgang = vorgangInStatus(status); var model = processor.process(EntityModel.of(vorgang)); @@ -66,16 +58,6 @@ class ExportVorgangProcessorTest { assertThat(model.getLink(ExportVorgangProcessor.REL_EXPORT)).isEmpty(); } - @Test - void shouldNotAddLinkWhenExportIsDisabled() { - when(featureToggleProperties.isVorgangExport()).thenReturn(false); - var vorgang = vorgangInStatus(VorgangStatus.ABGESCHLOSSEN); - - var model = processor.process(EntityModel.of(vorgang)); - - assertThat(model.getLink(ExportVorgangProcessor.REL_EXPORT)).isEmpty(); - } - private VorgangWithEingang vorgangInStatus(VorgangStatus status) { return VorgangWithEingangTestFactory.createBuilder().status(status).build(); } diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/UUIDConverterTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/UUIDConverterTest.java index f3da0c5f65cf046199af868553c39e19c413ce03..2af0c16013bf75ec4f81c938aad8092afc884083 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/UUIDConverterTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/UUIDConverterTest.java @@ -5,7 +5,7 @@ import static org.assertj.core.api.Assertions.*; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import de.ozgcloud.alfa.common.TestUtils; +import de.ozgcloud.alfa.common.AlfaTestUtils; import de.ozgcloud.alfa.common.UUIDConverter; class UUIDConverterTest { @@ -17,7 +17,7 @@ class UUIDConverterTest { void shouldReturnUUID() { var result = UUIDConverter.fromObjectId("64a820d36285172ac02826d0"); - assertThat(result).isEqualTo("64a820d3-6285-172a-c028-0000000026d0").matches(TestUtils.UUID_REGEX); + assertThat(result).isEqualTo("64a820d3-6285-172a-c028-0000000026d0").matches(AlfaTestUtils.UUID_REGEX); } @Test diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesTest.java deleted file mode 100644 index e44fadad0ec3d14b3d261b45fe5b8dc30cbb7669..0000000000000000000000000000000000000000 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesTest.java +++ /dev/null @@ -1,70 +0,0 @@ -package de.ozgcloud.alfa.export; - -import static org.assertj.core.api.AssertionsForClassTypes.*; - -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.TestPropertySource; - -public class XdomeaPropertiesTest { - - @Nested - @SpringBootTest(properties = {"ozgcloud.feature.vorgang-export=false"}) - class OnMissingVorgangExportFeature { - - @Autowired - XdomeaProperties xdomeaProperties; - - @Test - void shouldPropertiesBeOfTypeDefault(){ - assertThat(xdomeaProperties).isInstanceOf(DefaultXdomeaProperties.class); - } - - @Test - void shouldBehoerdenschluesselThrowException(){ - assertThatThrownBy(() -> xdomeaProperties.getBehoerdenschluessel()).isInstanceOf(UnsupportedOperationException.class); - } - - @Test - void shouldBehoerdenschluesselUriThrowException(){ - assertThatThrownBy(() -> xdomeaProperties.getBehoerdenschluesselUri()).isInstanceOf(UnsupportedOperationException.class); - } - - @Test - void shouldBehoerdenschluesselVersionThrowException(){ - assertThatThrownBy(() -> xdomeaProperties.getBehoerdenschluesselVersion()).isInstanceOf(UnsupportedOperationException.class); - } - } - - @Nested - @SpringBootTest - @ActiveProfiles({"itcase"}) - class OnActiveVorgangExportFeature { - - @Autowired - XdomeaProperties xdomeaProperties; - - @Test - void shouldPropertiesBeOfTypeConfig(){ - assertThat(xdomeaProperties).isInstanceOf(ExternalXdomeaProperties.class); - } - - @Test - void shouldBehoerdenschluesselBeSet(){ - assertThat(xdomeaProperties.getBehoerdenschluessel()).isEqualTo("ABC"); - } - - @Test - void shouldBehoerdenschluesselUriBeSet(){ - assertThat(xdomeaProperties.getBehoerdenschluesselUri()).isEqualTo("http://meine.behoer.de"); - } - - @Test - void shouldBehoerdenschluesselVersionBeSet(){ - assertThat(xdomeaProperties.getBehoerdenschluesselVersion()).isEqualTo("1.0.15b"); - } - } -} diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesTestFactory.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..78d786011aeedec05d51b6407305c2fb3769a626 --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesTestFactory.java @@ -0,0 +1,23 @@ +package de.ozgcloud.alfa.export; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.alfa.export.XdomeaProperties.XdomeaPropertiesBuilder; + +public class XdomeaPropertiesTestFactory { + + public static final String BEHOERDENSCHLUESSEL = LoremIpsum.getInstance().getWords(1); + public static final String BEHOERDENSCHLUESSEL_URI = LoremIpsum.getInstance().getUrl(); + public static final String BEHOERDENSCHLUESSEL_VERSION = LoremIpsum.getInstance().getWords(1); + + public static XdomeaProperties create() { + return createBuilder().build(); + } + + public static XdomeaPropertiesBuilder createBuilder() { + return new XdomeaPropertiesBuilder() + .behoerdenschluessel(BEHOERDENSCHLUESSEL) + .behoerdenschluesselUri(BEHOERDENSCHLUESSEL_URI) + .behoerdenschluesselVersion(BEHOERDENSCHLUESSEL_VERSION); + } +} diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesValidatorITCase.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesValidatorITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..451d2c7c49059af03d5e26034ad45f7bc6929998 --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesValidatorITCase.java @@ -0,0 +1,17 @@ +package de.ozgcloud.alfa.export; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.springframework.context.ApplicationContext; + +import de.ozgcloud.common.test.ITCase; + +@ITCase +class XdomeaPropertiesValidatorITCase { + + @Test + void shouldExistInApplicationContext(ApplicationContext context) { + assertThat(context.getBean("configurationPropertiesValidator")).isNotNull().isInstanceOf(XdomeaPropertiesValidator.class); + } +} diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesValidatorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesValidatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..cd6cf19672ab8118254ded34a4b2444d921f8f55 --- /dev/null +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/export/XdomeaPropertiesValidatorTest.java @@ -0,0 +1,138 @@ +package de.ozgcloud.alfa.export; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.mockito.Spy; +import org.springframework.validation.Errors; + +class XdomeaPropertiesValidatorTest { + + @Spy + private XdomeaPropertiesValidator validator; + + @Nested + class TestSupports { + + @Test + void shouldSupportXdomeaProperties() { + var supports = validator.supports(XdomeaProperties.class); + + assertThat(supports).isTrue(); + } + } + + @Nested + class TestValidate { + + @Test + void shouldBeValidIfAllBehoerdenschluesselPropertiesAreSet() { + var properties = XdomeaPropertiesTestFactory.create(); + + var errors = validate(properties); + + assertThat(errors.hasErrors()).isFalse(); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldBeValidIfBehoerdenschluesselIsNotSet(String behoerdenschluessel) { + var properties = XdomeaPropertiesTestFactory.createBuilder().behoerdenschluessel(behoerdenschluessel).build(); + + var errors = validate(properties); + + assertThat(errors.hasErrors()).isFalse(); + } + + @Nested + class OnUriNotSet { + + private static final String PROPERTY_NAME = "behoerdenschluesselUri"; + public static final String PROPERTY_PATH = "ozgcloud.xdomea." + PROPERTY_NAME; + public static final String ERROR_CODE = PROPERTY_PATH + ".empty"; + + @ParameterizedTest + @NullAndEmptySource + void shouldHaveFieldErrors(String uri) { + var properties = withBehoerdenschluesselUri(uri); + + var errors = validate(properties); + + assertThat(errors.hasFieldErrors(PROPERTY_NAME)).isTrue(); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldHaveMeaningfulErrorMessage(String uri) { + var properties = withBehoerdenschluesselUri(uri); + + var errors = validate(properties); + + assertThat(errors.getFieldError(PROPERTY_NAME).getDefaultMessage()).contains(PROPERTY_NAME); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldHaveErrorCode(String uri) { + var properties = withBehoerdenschluesselUri(uri); + + var errors = validate(properties); + + assertThat(errors.getFieldError(PROPERTY_NAME).getCode()).isEqualTo(ERROR_CODE); + } + + private XdomeaProperties withBehoerdenschluesselUri(String uri) { + return XdomeaPropertiesTestFactory.createBuilder().behoerdenschluesselUri(uri).build(); + } + } + + @Nested + class OnVersionNotSet { + + public static final String PROPERTY_NAME = "behoerdenschluesselVersion"; + public static final String PROPERTY_PATH = "ozgcloud.xdomea." + PROPERTY_NAME; + public static final String ERROR_CODE = PROPERTY_PATH + ".empty"; + + @ParameterizedTest + @NullAndEmptySource + void shouldHaveFieldErrors(String version) { + var properties = withBehoerdenschluesselVersion(version); + + var errors = validate(properties); + + assertThat(errors.hasFieldErrors(PROPERTY_NAME)).isTrue(); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldHaveMeaningfulErrorMessage(String version) { + var properties = withBehoerdenschluesselVersion(version); + + var errors = validate(properties); + + assertThat(errors.getFieldError(PROPERTY_NAME).getDefaultMessage()).contains(PROPERTY_NAME); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldHaveErrorCode(String version) { + var properties = withBehoerdenschluesselVersion(version); + + var errors = validate(properties); + + assertThat(errors.getFieldError(PROPERTY_NAME).getCode()).isEqualTo(ERROR_CODE); + } + + private XdomeaProperties withBehoerdenschluesselVersion(String version) { + return XdomeaPropertiesTestFactory.createBuilder().behoerdenschluesselVersion(version).build(); + } + } + + private Errors validate(XdomeaProperties properties) { + return validator.validateObject(properties); + } + } +} diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/vorgang/KopfCreatorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/vorgang/KopfCreatorTest.java index 0c72b5ad9ff5d21840af73074a8f244c3899b0ab..ad2a07d492a41a25956fe6032f940df5b82c0f9e 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/vorgang/KopfCreatorTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/vorgang/KopfCreatorTest.java @@ -1,5 +1,6 @@ package de.ozgcloud.alfa.vorgang; +import static de.ozgcloud.alfa.export.XdomeaPropertiesTestFactory.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; @@ -13,6 +14,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; @@ -38,9 +41,7 @@ class KopfCreatorTest { private KopfCreator creator; @Mock - private XdomeaProperties xDomeaProperties; - @Mock - private DateConverter dateConverter; + private XdomeaProperties xdomeaProperties; @Nested class TestCreateKopf { @@ -170,9 +171,6 @@ class KopfCreatorTest { @Nested class TestCreateAbsender { - @Mock - private BehoerdenkennungType behoerdenkennungType; - @Test void shouldCallCreateKontaktType() { creator.createAbsender(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEITEN_ID); @@ -194,15 +192,54 @@ class KopfCreatorTest { @Nested class TestCreateKontaktType { - @Test - void shouldSetBehoerdenkennungType() { - var expectedValue = new BehoerdenkennungType(); - doReturn(expectedValue).when(creator).createBehoerdenkennung(); + @Nested + class OnBehoerdenschluesselNotSet { + + @ParameterizedTest + @NullAndEmptySource + void shouldNotCreateBehoerdenkennung(String behoerdenschluessel) { + when(xdomeaProperties.getBehoerdenschluessel()).thenReturn(behoerdenschluessel); + + creator.createKontaktType(); + + verify(creator, never()).createBehoerdenkennung(); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldNotSetBehoerdenkennung(String behoerdenschluessel) { + when(xdomeaProperties.getBehoerdenschluessel()).thenReturn(behoerdenschluessel); + + var kontaktType = creator.createKontaktType(); - var kontaktType = creator.createAbsender(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEITEN_ID); + assertThat(kontaktType.getBehoerdenkennung()).isNull(); + } + } + + @Nested + class OnBehoerdenschluesselSet { + + private static final BehoerdenkennungType expectedValue = new BehoerdenkennungType(); + + @BeforeEach + void init() { + when(xdomeaProperties.getBehoerdenschluessel()).thenReturn(BEHOERDENSCHLUESSEL); + doReturn(expectedValue).when(creator).createBehoerdenkennung(); + } + + @Test + void shouldCreateBehoerdenkennung() { + creator.createKontaktType(); + + verify(creator).createBehoerdenkennung(); + } - assertThat(kontaktType.getBehoerdenkennung()).isEqualTo(expectedValue); + @Test + void shouldSetBehoerdenkennung() { + var kontaktType = creator.createKontaktType(); + assertThat(kontaktType.getBehoerdenkennung()).isEqualTo(expectedValue); + } } } @@ -220,11 +257,22 @@ class KopfCreatorTest { @Nested class TestCreateBehoerdenkennung { - @Test - void shouldSetBehoerdenschluessel() { - var expectedBehoerdenschluessel = new Code(); + private final Code expectedBehoerdenschluessel = new Code(); + + @BeforeEach + void init() { doReturn(expectedBehoerdenschluessel).when(creator).createBehoerdenschluessel(); + } + + @Test + void shouldcreateBehoerdenschluessel() { + creator.createBehoerdenkennung(); + + verify(creator).createBehoerdenschluessel(); + } + @Test + void shouldSetBehoerdenschluessel() { var behoerdenkennungType = creator.createBehoerdenkennung(); assertThat(behoerdenkennungType.getBehoerdenschluessel()).isEqualTo(expectedBehoerdenschluessel); @@ -234,34 +282,32 @@ class KopfCreatorTest { @Nested class TestCreateBehoerdenschlussel { + @BeforeEach + void init() { + when(xdomeaProperties.getBehoerdenschluessel()).thenReturn(BEHOERDENSCHLUESSEL); + when(xdomeaProperties.getBehoerdenschluesselUri()).thenReturn(BEHOERDENSCHLUESSEL_URI); + when(xdomeaProperties.getBehoerdenschluesselVersion()).thenReturn(BEHOERDENSCHLUESSEL_VERSION); + } + @Test void shouldSetCode() { - var expectedBehoerdenschluessel = LoremIpsum.getInstance().getWords(1); - when(xDomeaProperties.getBehoerdenschluessel()).thenReturn(expectedBehoerdenschluessel); - var behoerdenschlussel = creator.createBehoerdenschluessel(); - assertThat(behoerdenschlussel.getCode()).isEqualTo(expectedBehoerdenschluessel); + assertThat(behoerdenschlussel.getCode()).isEqualTo(BEHOERDENSCHLUESSEL); } @Test void shouldSetListURI() { - var expectedBehoerdenschluesselUri = LoremIpsum.getInstance().getUrl(); - when(xDomeaProperties.getBehoerdenschluesselUri()).thenReturn(expectedBehoerdenschluesselUri); - var behoerdenschlussel = creator.createBehoerdenschluessel(); - assertThat(behoerdenschlussel.getListURI()).isEqualTo(expectedBehoerdenschluesselUri); + assertThat(behoerdenschlussel.getListURI()).isEqualTo(BEHOERDENSCHLUESSEL_URI); } @Test void shouldSetListVersionID() { - var expectedBehoerdenschluesselVersion = LoremIpsum.getInstance().getWords(1); - when(xDomeaProperties.getBehoerdenschluesselVersion()).thenReturn(expectedBehoerdenschluesselVersion); - var behoerdenschlussel = creator.createBehoerdenschluessel(); - assertThat(behoerdenschlussel.getListVersionID()).isEqualTo(expectedBehoerdenschluesselVersion); + assertThat(behoerdenschlussel.getListVersionID()).isEqualTo(BEHOERDENSCHLUESSEL_VERSION); } } diff --git a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/vorgang/VorgangTypeCreatorTest.java b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/vorgang/VorgangTypeCreatorTest.java index 88dc05b51d179cc817b47252c57c3cb20ab81497..47ec3761ac84e0351a9cae1dcea008c995cb80a3 100644 --- a/alfa-xdomea/src/test/java/de/ozgcloud/alfa/vorgang/VorgangTypeCreatorTest.java +++ b/alfa-xdomea/src/test/java/de/ozgcloud/alfa/vorgang/VorgangTypeCreatorTest.java @@ -12,7 +12,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import de.ozgcloud.alfa.common.TestUtils; +import de.ozgcloud.alfa.common.AlfaTestUtils; import de.ozgcloud.alfa.export.AllgemeineMetadatenTypeTestFactory; import de.ozgcloud.alfa.export.IdentifikationObjektTypeTestFactory; import de.xoev.xdomea.AllgemeineMetadatenType; @@ -183,7 +183,7 @@ class VorgangTypeCreatorTest { void shouldHaveId() { var identifikation = creator.createIdentifikation(); - assertThat(identifikation.getID()).matches(TestUtils.UUID_REGEX); + assertThat(identifikation.getID()).matches(AlfaTestUtils.UUID_REGEX); } @Test diff --git a/alfa-xdomea/src/test/resources/application-itcase.yaml b/alfa-xdomea/src/test/resources/application-itcase.yaml index 8637dea80e839d9c9b817da9756bf1e394ac958d..c6f85b1e94d61d1ee6c5992b97cbac70b7260e24 100644 --- a/alfa-xdomea/src/test/resources/application-itcase.yaml +++ b/alfa-xdomea/src/test/resources/application-itcase.yaml @@ -1,6 +1,4 @@ ozgcloud: - feature: - vorgang-export: true xdomea: behoerdenschluessel: ABC behoerdenschluesselUri: http://meine.behoer.de diff --git a/pom.xml b/pom.xml index 814bc554e80a557f0b17d1e15e5f5c12df36f530..9f392394780ae740b80a1a4c48cc613ae8061467 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ <groupId>de.ozgcloud.alfa</groupId> <artifactId>alfa</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.11.0-SNAPSHOT</version> <name>Alfa Parent</name> <packaging>pom</packaging> diff --git a/src/main/helm/templates/deployment.yaml b/src/main/helm/templates/deployment.yaml index cad88f7ba2e55da7b791497853c8ff633af6aaac..b7221ad6bd6407099a01862c2ffb85bb5199d49d 100644 --- a/src/main/helm/templates/deployment.yaml +++ b/src/main/helm/templates/deployment.yaml @@ -98,7 +98,7 @@ spec: {{- if ((.Values.ozgcloud).vorgang).bescheid}} {{- range $index, $bescheid := ((.Values.ozgcloud).vorgang).bescheid }} - name: ozgcloud_vorgang_bescheid_{{ $index }}_formId - value: {{ $bescheid.formId }} + value: {{ $bescheid.formId | quote }} - name: ozgcloud_vorgang_bescheid_{{ $index }}_formEngineName value: {{ $bescheid.formEngineName }} {{- end }} diff --git a/src/test/helm/deployment_defaults_env_test.yaml b/src/test/helm/deployment_defaults_env_test.yaml index 99d108c95f63fc674e22cf2cc369af4db57c0ae5..0bf3a7adbf32e293791d0af1a2804bf7c8c4d6df 100644 --- a/src/test/helm/deployment_defaults_env_test.yaml +++ b/src/test/helm/deployment_defaults_env_test.yaml @@ -92,7 +92,7 @@ tests: - formEngineName: AFM formId: form_id_1 - formEngineName: FormSolutions - formId: form_id_2 + formId: 2290 asserts: - contains: path: spec.template.spec.containers[0].env @@ -108,7 +108,7 @@ tests: path: spec.template.spec.containers[0].env content: name: ozgcloud_vorgang_bescheid_1_formId - value: form_id_2 + value: "2290" - contains: path: spec.template.spec.containers[0].env content: diff --git a/src/test/helm/network_policy_test.yaml b/src/test/helm/network_policy_test.yaml index c74aa40bad0792d6da348b499eae6a22315ea6fa..af65c804e62b7fb9aebb26f65f2322b536f71550 100644 --- a/src/test/helm/network_policy_test.yaml +++ b/src/test/helm/network_policy_test.yaml @@ -28,21 +28,30 @@ release: namespace: by-helm-test templates: - templates/network_policy.yaml -set: - networkPolicy: - dnsServerNamespace: kube-system - ssoPublicIp: 1.1.1.1/32 + tests: - it: should match apiVersion + set: + networkPolicy: + dnsServerNamespace: kube-system + ssoPublicIp: 1.1.1.1/32 asserts: - isAPIVersion: of: networking.k8s.io/v1 - it: should match kind + set: + networkPolicy: + dnsServerNamespace: kube-system + ssoPublicIp: 1.1.1.1/32 asserts: - isKind: of: NetworkPolicy - it: validate metadata + set: + networkPolicy: + dnsServerNamespace: kube-system + ssoPublicIp: 1.1.1.1/32 asserts: - equal: path: metadata @@ -187,3 +196,31 @@ tests: asserts: - hasDocuments: count: 1 + + - it: test network policy dnsServerNamespace must be set message + set: + networkPolicy: + disabled: false + ssoPublicIp: 1.1.1.1/32 + asserts: + - failedTemplate: + errorMessage: networkPolicy.dnsServerNamespace must be set + + + - it: test network policy ssoPublicIp must be set message + set: + networkPolicy: + disabled: false + dnsServerNamespace: test-dns-server-namespace + asserts: + - failedTemplate: + errorMessage: networkPolicy.ssoPublicIp must be set + + - it: test network policy should be enabled by default + set: + networkPolicy: + ssoPublicIp: 1.1.1.1 + dnsServerNamespace: test-dns-server-namespace + asserts: + - hasDocuments: + count: 1 \ No newline at end of file