From fcacb8c4d0f16203c9a01e7340165c546880d37a Mon Sep 17 00:00:00 2001 From: Gematik Date: Fri, 19 Jun 2020 10:41:39 +0200 Subject: [PATCH 1/5] - Funktionality for add directory entries\n - Funktionality for delete directory entries\n - Funktionality for read directory entries\n --- .gitignore | 28 + LICENSE | 13 + README.md | 21 + ReleaseNotes.md | 6 + bspData/Beispiel_Command_Datei.xml | 157 ++ bspData/Beispiel_Config_Datei.txt | 3 + bspData/Beispiel_Credential_Datei.txt | 2 + bspData/startVzdC_Log.bat | 27 + bspData/startVzdClient.bat | 24 + bspData/vorlage_commands_data.xml | 152 ++ build.gradle | 149 ++ doc/javadoc/config.adoc | 24 + doc/userguide/ReadmeConfig.adoc | 29 + doc/userguide/VZDCL_API.adoc | 5 + doc/userguide/VZDCL_GettingStarted.adoc | 8 + doc/userguide/VZDCL_Introduction.adoc | 7 + doc/userguide/VZDCL_License.adoc | 4 + doc/userguide/VZDCL_Main.adoc | 7 + doc/userguide/VZDCL_Overview.adoc | 4 + doc/userguide/config.adoc | 27 + docs/AdministrationApi.md | 134 ++ docs/Author.md | 21 + docs/AuthorInstitution.md | 13 + docs/BaseDirectoryEntry.md | 27 + docs/CertificateAdministrationApi.md | 307 ++++ docs/ChangeProviderRequestDTO.md | 15 + docs/ConfigurationApi.md | 136 ++ docs/ConfigurationEntry.md | 33 + docs/CreateDirectoryEntry.md | 13 + docs/DefaultApi.md | 126 ++ docs/DeletePermissionDTO.md | 13 + docs/DirectoryApi.md | 134 ++ docs/DirectoryEntry.md | 14 + docs/DirectoryEntryAdministrationApi.md | 321 ++++ docs/DirectoryEntryWithCertificates.md | 13 + docs/DistinguishedName.md | 15 + docs/Document.md | 12 + docs/DocumentWithMetadata.md | 51 + docs/DocumentsApi.md | 260 ++++ docs/DocumentsRequestDTO.md | 13 + docs/Error.md | 13 + docs/FAD1.md | 13 + docs/Fachdaten.md | 13 + docs/FindDirectoryRequestDTO.md | 13 + docs/FindDirectoryResponseDTO.md | 14 + docs/FindDocumentMetaData.md | 33 + docs/FindDocumentsRequestDTO.md | 29 + docs/FindDocumentsResponseDTO.md | 14 + docs/Login.md | 16 + docs/NotificationInformationRequestDTO.md | 13 + docs/NotificationInformationWithId.md | 13 + docs/PermissionEntry.md | 14 + docs/PermissionHcpoDTO.md | 16 + docs/PermissionInsuranceDTO.md | 15 + docs/PermissionLeiProp.md | 15 + docs/PermissionRepresentativeDTO.md | 15 + docs/PermissionsApi.md | 515 ++++++ docs/PermissionsResponseDTO.md | 14 + docs/ProtocolApi.md | 71 + docs/ProtocolEntry.md | 22 + docs/ProtocolResponseDTO.md | 14 + docs/RequestDTO.md | 12 + docs/ResponseDTO.md | 13 + docs/ResponsePingDTO.md | 14 + docs/ResponseProductInformationDTO.md | 14 + docs/RetrieveDocumentsResponseDTO.md | 14 + docs/StoreDocumentRequestDTO.md | 14 + docs/SubmissionSetMetaData.md | 18 + docs/SubmissionSetWithDocumentWithMetadata.md | 13 + docs/UserApi.md | 134 ++ docs/UserCertificate.md | 28 + gradle.properties | 2 + settings.gradle | 1 + .../api/CertificateAdministrationApi.java | 736 +++++++++ .../api/DirectoryEntryAdministrationApi.java | 829 ++++++++++ .../epa/vzd/client/invoker/ApiCallback.java | 77 + .../ti/epa/vzd/client/invoker/ApiClient.java | 1384 +++++++++++++++++ .../epa/vzd/client/invoker/ApiException.java | 109 ++ .../epa/vzd/client/invoker/ApiResponse.java | 76 + .../epa/vzd/client/invoker/Configuration.java | 54 + .../invoker/GzipRequestInterceptor.java | 105 ++ .../ti/epa/vzd/client/invoker/JSON.java | 406 +++++ .../ti/epa/vzd/client/invoker/Pair.java | 74 + .../client/invoker/ProgressRequestBody.java | 87 ++ .../client/invoker/ProgressResponseBody.java | 86 + .../ti/epa/vzd/client/invoker/StringUtil.java | 77 + .../vzd/client/invoker/auth/ApiKeyAuth.java | 93 ++ .../client/invoker/auth/Authentication.java | 46 + .../client/invoker/auth/HttpBasicAuth.java | 67 + .../client/invoker/auth/HttpBearerAuth.java | 76 + .../ti/epa/vzd/client/invoker/auth/OAuth.java | 55 + .../vzd/client/invoker/auth/OAuthFlow.java | 34 + .../invoker/auth/OAuthOkHttpClient.java | 84 + .../client/invoker/auth/RetryingOAuth.java | 187 +++ .../vzd/client/model/BaseDirectoryEntry.java | 531 +++++++ .../client/model/CreateDirectoryEntry.java | 149 ++ .../epa/vzd/client/model/DirectoryEntry.java | 187 +++ .../vzd/client/model/DistinguishedName.java | 217 +++ .../ti/epa/vzd/client/model/Error.java | 139 ++ .../gematik/ti/epa/vzd/client/model/FAD1.java | 128 ++ .../ti/epa/vzd/client/model/Fachdaten.java | 148 ++ .../epa/vzd/client/model/UserCertificate.java | 336 ++++ .../epa/vzd/gemClient/CommandNamesEnum.java | 48 + .../ti/epa/vzd/gemClient/GemStringUtils.java | 56 + .../de/gematik/ti/epa/vzd/gemClient/Main.java | 58 + .../api/GemCertificateAdministrationApi.java | 230 +++ .../GemDirectoryEntryAdministrationApi.java | 260 ++++ .../gemClient/command/CommandsBuilder.java | 88 ++ .../command/ExecutionController.java | 134 ++ .../vzd/gemClient/command/Transformer.java | 119 ++ .../AddDirEntryCertExecution.java | 176 +++ .../AddDirEntryExecution.java | 144 ++ .../DeleteDirEntryCertExecution.java | 151 ++ .../DeleteDirEntryExecution.java | 114 ++ .../commandExecutions/ExecutionBase.java | 161 ++ .../ExecutionCollection.java | 120 ++ .../ModifyDirEntryCertExecution.java | 117 ++ .../ModifyDirEntryExecution.java | 138 ++ .../ReadDirEntryCertExecution.java | 126 ++ .../ReadDirEntryExecution.java | 146 ++ .../exceptions/CommandException.java | 42 + .../exceptions/GemClientException.java | 39 + .../gemClient/exceptions/ReadException.java | 43 + .../vzd/gemClient/invoker/ConfigHandler.java | 167 ++ .../vzd/gemClient/invoker/GemApiClient.java | 248 +++ src/main/resources/jaxb-binding/binding.xml | 10 + src/main/resources/log4j2.xml | 27 + src/main/resources/xsd/commands.xsd | 58 + .../api/CertificateAdministrationApiTest.java | 126 ++ .../DirectoryEntryAdministrationApiTest.java | 135 ++ .../client/model/BaseDirectoryEntryTest.java | 190 +++ .../model/CreateDirectoryEntryTest.java | 79 + .../vzd/client/model/DirectoryEntryTest.java | 88 ++ .../client/model/DistinguishedNameTest.java | 93 ++ .../ti/epa/vzd/client/model/ErrorTest.java | 75 + .../ti/epa/vzd/client/model/FAD1Test.java | 78 + .../epa/vzd/client/model/FachdatenTest.java | 79 + .../vzd/client/model/UserCertificateTest.java | 118 ++ .../command/CommandsBuilderTest.java | 65 + .../AddDirEntryExecutionTest.java | 69 + .../DeleteDirEntryExecutionTest.java | 47 + .../ModifyDirEntryExecutionTest.java | 65 + .../ReadDirEntryExecutionTest.java | 164 ++ .../gemClient/invoker/ConfigHandlerTest.java | 88 ++ src/test/resources/config/Commands.xml | 35 + .../config/CommandsMissingOperationName.xml | 34 + .../config/CommandsWrongFormated.xml | 35 + src/test/resources/config/Config.txt | 3 + .../config/ConfigMissingCommands.txt | 3 + .../config/ConfigMissingOperationname.txt | 3 + .../config/ConfigWrongFormattedCommands.txt | 3 + src/test/resources/config/Credentials.txt | 2 + version.txt | 1 + 153 files changed, 14815 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 ReleaseNotes.md create mode 100644 bspData/Beispiel_Command_Datei.xml create mode 100644 bspData/Beispiel_Config_Datei.txt create mode 100644 bspData/Beispiel_Credential_Datei.txt create mode 100644 bspData/startVzdC_Log.bat create mode 100644 bspData/startVzdClient.bat create mode 100644 bspData/vorlage_commands_data.xml create mode 100644 build.gradle create mode 100644 doc/javadoc/config.adoc create mode 100644 doc/userguide/ReadmeConfig.adoc create mode 100644 doc/userguide/VZDCL_API.adoc create mode 100644 doc/userguide/VZDCL_GettingStarted.adoc create mode 100644 doc/userguide/VZDCL_Introduction.adoc create mode 100644 doc/userguide/VZDCL_License.adoc create mode 100644 doc/userguide/VZDCL_Main.adoc create mode 100644 doc/userguide/VZDCL_Overview.adoc create mode 100644 doc/userguide/config.adoc create mode 100644 docs/AdministrationApi.md create mode 100644 docs/Author.md create mode 100644 docs/AuthorInstitution.md create mode 100644 docs/BaseDirectoryEntry.md create mode 100644 docs/CertificateAdministrationApi.md create mode 100644 docs/ChangeProviderRequestDTO.md create mode 100644 docs/ConfigurationApi.md create mode 100644 docs/ConfigurationEntry.md create mode 100644 docs/CreateDirectoryEntry.md create mode 100644 docs/DefaultApi.md create mode 100644 docs/DeletePermissionDTO.md create mode 100644 docs/DirectoryApi.md create mode 100644 docs/DirectoryEntry.md create mode 100644 docs/DirectoryEntryAdministrationApi.md create mode 100644 docs/DirectoryEntryWithCertificates.md create mode 100644 docs/DistinguishedName.md create mode 100644 docs/Document.md create mode 100644 docs/DocumentWithMetadata.md create mode 100644 docs/DocumentsApi.md create mode 100644 docs/DocumentsRequestDTO.md create mode 100644 docs/Error.md create mode 100644 docs/FAD1.md create mode 100644 docs/Fachdaten.md create mode 100644 docs/FindDirectoryRequestDTO.md create mode 100644 docs/FindDirectoryResponseDTO.md create mode 100644 docs/FindDocumentMetaData.md create mode 100644 docs/FindDocumentsRequestDTO.md create mode 100644 docs/FindDocumentsResponseDTO.md create mode 100644 docs/Login.md create mode 100644 docs/NotificationInformationRequestDTO.md create mode 100644 docs/NotificationInformationWithId.md create mode 100644 docs/PermissionEntry.md create mode 100644 docs/PermissionHcpoDTO.md create mode 100644 docs/PermissionInsuranceDTO.md create mode 100644 docs/PermissionLeiProp.md create mode 100644 docs/PermissionRepresentativeDTO.md create mode 100644 docs/PermissionsApi.md create mode 100644 docs/PermissionsResponseDTO.md create mode 100644 docs/ProtocolApi.md create mode 100644 docs/ProtocolEntry.md create mode 100644 docs/ProtocolResponseDTO.md create mode 100644 docs/RequestDTO.md create mode 100644 docs/ResponseDTO.md create mode 100644 docs/ResponsePingDTO.md create mode 100644 docs/ResponseProductInformationDTO.md create mode 100644 docs/RetrieveDocumentsResponseDTO.md create mode 100644 docs/StoreDocumentRequestDTO.md create mode 100644 docs/SubmissionSetMetaData.md create mode 100644 docs/SubmissionSetWithDocumentWithMetadata.md create mode 100644 docs/UserApi.md create mode 100644 docs/UserCertificate.md create mode 100644 gradle.properties create mode 100644 settings.gradle create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApi.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApi.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiCallback.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiClient.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiException.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiResponse.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/Configuration.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/GzipRequestInterceptor.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/JSON.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/Pair.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressRequestBody.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressResponseBody.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/StringUtil.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/ApiKeyAuth.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/Authentication.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBasicAuth.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBearerAuth.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuth.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthFlow.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthOkHttpClient.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/RetryingOAuth.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntry.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntry.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntry.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/model/DistinguishedName.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/model/Error.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/model/FAD1.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/model/Fachdaten.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/client/model/UserCertificate.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/CommandNamesEnum.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/GemStringUtils.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/Main.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemCertificateAdministrationApi.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemDirectoryEntryAdministrationApi.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilder.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/ExecutionController.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/Transformer.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryCertExecution.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecution.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecution.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecution.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBase.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionCollection.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryCertExecution.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecution.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryCertExecution.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecution.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/CommandException.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/GemClientException.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/ReadException.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandler.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/GemApiClient.java create mode 100644 src/main/resources/jaxb-binding/binding.xml create mode 100644 src/main/resources/log4j2.xml create mode 100644 src/main/resources/xsd/commands.xsd create mode 100644 src/test/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApiTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApiTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntryTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntryTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntryTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/client/model/DistinguishedNameTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/client/model/ErrorTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/client/model/FAD1Test.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/client/model/FachdatenTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/client/model/UserCertificateTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilderTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecutionTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecutionTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecutionTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecutionTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandlerTest.java create mode 100644 src/test/resources/config/Commands.xml create mode 100644 src/test/resources/config/CommandsMissingOperationName.xml create mode 100644 src/test/resources/config/CommandsWrongFormated.xml create mode 100644 src/test/resources/config/Config.txt create mode 100644 src/test/resources/config/ConfigMissingCommands.txt create mode 100644 src/test/resources/config/ConfigMissingOperationname.txt create mode 100644 src/test/resources/config/ConfigWrongFormattedCommands.txt create mode 100644 src/test/resources/config/Credentials.txt create mode 100644 version.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40bb3a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# exclude jar for gradle wrapper +!gradle/wrapper/*.jar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# build files +**/target +target +.gradle +build + +# generated # +generated/jaxb/* + +#testData# +.bsp*Command.xml +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..734a90c --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2020 gematik GmbH + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md index e69de29..7fb2c2a 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,21 @@ +# VZD-Client + +## Introduction + +This part describes the VZD-Client functionalities and structure. + +## API Documentation + +Generated API docs are available at . + +## License + +Licensed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0). + +## Overview + +## Getting Started + +### Build setup + +soon diff --git a/ReleaseNotes.md b/ReleaseNotes.md new file mode 100644 index 0000000..227c5a9 --- /dev/null +++ b/ReleaseNotes.md @@ -0,0 +1,6 @@ +# Release 0.5.0 +- Funktionality for add directory entries + - Funktionality for delete directory entries + - Funktionality for read directory entries + + diff --git a/bspData/Beispiel_Command_Datei.xml b/bspData/Beispiel_Command_Datei.xml new file mode 100644 index 0000000..c1482dc --- /dev/null +++ b/bspData/Beispiel_Command_Datei.xml @@ -0,0 +1,157 @@ + + + + modifyDirectoryEntries + + ModTest + cn + ou + ou2 + dc + dc2 + + displayName + otherName + streetAddress + postalCode + localityName + stateOrProvinceName + title + organization + specialization + domainID + + + modifyDirectoryEntries + + ModTest2 + + displayName2 + otherName2 + streetAddress2 + postalCode2 + localityName2 + stateOrProvinceName2 + title2 + organization2 + specialization2 + domainID2 + + + readDirectoryEntries + + ReadTest + cn + ou + ou2 + dc + dc2 + + displayName + otherName + streetAddress + postalCode + localityName + stateOrProvinceName + title + organization + specialization + domainID + + + readDirectoryEntries + + ReadTest + + displayName2 + otherName2 + streetAddress2 + postalCode2 + localityName2 + stateOrProvinceName2 + title2 + organization2 + specialization2 + domainID2 + + + addDirectoryEntries + + AddTest + cn + ou + ou2 + dc + dc2 + + displayName + otherName + streetAddress + postalCode + localityName + stateOrProvinceName + title + organization + specialization + domainID + + SomeTelematikid + + + + addDirectoryEntries + + AddTest2 + + displayName2 + otherName2 + streetAddress2 + postalCode2 + localityName2 + stateOrProvinceName2 + title2 + organization2 + specialization2 + domainID2 + + SomeUserCertificate + + + + deleteDirectoryEntries + + DelTest + cn + ou + ou2 + dc + dc2 + + displayName + otherName + streetAddress + postalCode + localityName + stateOrProvinceName + title + organization + specialization + domainID + + + deleteDirectoryEntries + + DelTest2 + + displayName2 + otherName2 + streetAddress2 + postalCode2 + localityName2 + stateOrProvinceName2 + title2 + organization2 + specialization2 + domainID2 + + \ No newline at end of file diff --git a/bspData/Beispiel_Config_Datei.txt b/bspData/Beispiel_Config_Datei.txt new file mode 100644 index 0000000..16d89ba --- /dev/null +++ b/bspData/Beispiel_Config_Datei.txt @@ -0,0 +1,3 @@ +base=https://LocationOfVzd:443 +retryingOAuth=https://LocationOfOAuth2Server:8443/oauth/token +commands=src\main\resources\bspCommands.xml \ No newline at end of file diff --git a/bspData/Beispiel_Credential_Datei.txt b/bspData/Beispiel_Credential_Datei.txt new file mode 100644 index 0000000..0ce6b34 --- /dev/null +++ b/bspData/Beispiel_Credential_Datei.txt @@ -0,0 +1,2 @@ +id=someId +secret=someSecret \ No newline at end of file diff --git a/bspData/startVzdC_Log.bat b/bspData/startVzdC_Log.bat new file mode 100644 index 0000000..ebdab3b --- /dev/null +++ b/bspData/startVzdC_Log.bat @@ -0,0 +1,27 @@ +@echo off +cls + +if not "%OPENJDK11_HOME%" == "" ( + set JAVA=%OPENJDK11_HOME% +) else ( + if not "%OPENJDK_HOME%" == "" ( + set JAVA=%OPENJDK_HOME% + ) else ( + set JAVA=%JAVA_HOME% + ) +) + +set PATH=%JAVA_HOME%\bin;%PATH% + +rem Checks if the user want's to debug the program on the console +for %%z in (%*) do ( + if "%%z"=="-debug" set EXTRA_JVM_ARGUMENTS=-Dl4j.lvl=DEBUG + if "%%z"=="-info" set EXTRA_JVM_ARGUMENTS=-Dl4j.lvl=INFO + if "%%z"=="-trace" set EXTRA_JVM_ARGUMENTS=-Dl4j.lvl=TRACE + if "%%z"=="-error" set EXTRA_JVM_ARGUMENTS=-Dl4j.lvl=ERROR + if "%%z"=="-warn" set EXTRA_JVM_ARGUMENTS=-Dl4j.lvl=WARN +) + +set EXTRA_JVM_ARGUMENTS=%EXTRA_JVM_ARGUMENTS% -Dl4j.logDir=%1 + +call java %EXTRA_JVM_ARGUMENTS% -jar %~dp0.\VZD-Client\VZD-Client.jar %* \ No newline at end of file diff --git a/bspData/startVzdClient.bat b/bspData/startVzdClient.bat new file mode 100644 index 0000000..3703c8a --- /dev/null +++ b/bspData/startVzdClient.bat @@ -0,0 +1,24 @@ +@echo off +cls + +if not "%OPENJDK11_HOME%" == "" ( + set JAVA=%OPENJDK11_HOME% +) else ( + if not "%OPENJDK_HOME%" == "" ( + set JAVA=%OPENJDK_HOME% + ) else ( + set JAVA=%JAVA_HOME% + ) +) +set PATH=%JAVA_HOME%\bin;%PATH% + +rem Checks if the user want's to debug the program on the console +for %%z in (%*) do ( + if "%%z"=="-debug" set EXTRA_JVM_ARGUMENTS=-Dl4j.lvl=DEBUG + if "%%z"=="-info" set EXTRA_JVM_ARGUMENTS=-Dl4j.lvl=INFO + if "%%z"=="-trace" set EXTRA_JVM_ARGUMENTS=-Dl4j.lvl=TRACE + if "%%z"=="-error" set EXTRA_JVM_ARGUMENTS=-Dl4j.lvl=ERROR + if "%%z"=="-warn" set EXTRA_JVM_ARGUMENTS=-Dl4j.lvl=WARN +) + +call java -jar %~dp0.\VZD-Client\VZD-Client.jar %* \ No newline at end of file diff --git a/bspData/vorlage_commands_data.xml b/bspData/vorlage_commands_data.xml new file mode 100644 index 0000000..08b0286 --- /dev/null +++ b/bspData/vorlage_commands_data.xml @@ -0,0 +1,152 @@ + + + + addDirectoryEntries + + + + + + + + + + + + + + + + + + <organization/> + <otherName/> + <specialization/> + <domainID/> + <mail/> + <UserCertificate> + <dn> + <cn/> + <uid/> + <dc/> + <dc/> + </dn> + <telematikID/> + <entryType/> + <professionOID/> + <usage/> + <userCertificate/> + <description/> + </UserCertificate> + <personalEntry/> + <dataFromAuthority/> + </Command> + <Command> + <name>readDirectoryEntries</name> + <dn> + <uid/> + <dc/> + <dc/> + </dn> + <givenName/> + <sn/> + <streetAddress/> + <postalCode/> + <localityName/> + <stateOrProvinceName/> + <cn/> + <displayName/> + <title/> + <organization/> + <otherName/> + <specialization/> + <domainID/> + <mail/> + <UserCertificate> + <dn> + <cn/> + <uid/> + <dc/> + <dc/> + </dn> + <telematikID/> + <entryType/> + <professionOID/> + <usage/> + <userCertificate/> + <description/> + </UserCertificate> + <personalEntry/> + <dataFromAuthority/> + </Command> + <Command> + <name>modifyDirectoryEntries</name> + <dn> + <uid required="true"/> + </dn> + <displayName/> + <otherName/> + <streetAddress/> + <postalCode/> + <localityName/> + <stateOrProvinceName/> + <title/> + <organization/> + <specialization/> + <domainID/> + </Command> + <Command> + <name>deleteDirectoryEntries</name> + <dn> + <uid required="true"/> + </dn> + </Command> + <Command> + <name>addDirectoryEntryCertificate</name> + <dn> + <uid required="true"/> + </dn> + <UserCertificate> + <usage/> + <userCertificate required="true"/> + <description/> + </UserCertificate> + </Command> + <Command> + <name>readDirectoryEntryCertificate</name> + <dn> + <uid/> + </dn> + <UserCertificate> + <dn> + <uid/> + </dn> + <telematikID/> + </UserCertificate> + </Command> + <Command> + <name>modifyDirectoryEntryCertificate</name> + <uid required="true"/> + <UserCertificate> + <dn> + <cn required="true"/> + <uid required="true"/> + </dn> + <usage/> + <userCertificate required="true"/> + <description/> + </UserCertificate> + </Command> + <Command> + <name>deleteDirectoryEntryCertificate</name> + <dn> + <uid required="true"/> + </dn> + <UserCertificate> + <dn> + <cn required="true"/> + </dn> + <description/> + </UserCertificate> + </Command> +</CommandList> \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..8683d80 --- /dev/null +++ b/build.gradle @@ -0,0 +1,149 @@ +buildscript { + dependencies { + classpath 'de.gematik:gematik-parent-plugin:1.2.1' + classpath 'de.gematik:documentation-plugin:1.1.2' + } + repositories { + mavenCentral() + jcenter() + } +} + +apply plugin: 'de.gematik.parent' +apply plugin: 'de.gematik.publish' +apply plugin: 'de.gematik.asciidoctor' +apply plugin: 'distribution' + +group = 'de.gematik.ti.epa' +description = 'Verzeichnisdienst Client' + +sourceSets { + main.java.srcDirs = ['src/main/java', 'build/generated-sources/jaxb'] + test.java.srcDirs = ['src/test/java'] +} + +javadoc { + failOnError = false + options.noTimestamp = true +} + +configurations { + jaxb +} + +dependencies { + implementation 'io.swagger:swagger-annotations:1.5.22' + implementation 'com.google.code.findbugs:jsr305:3.0.2' + implementation 'com.squareup.okhttp3:okhttp:3.14.2' + implementation 'com.squareup.okhttp3:logging-interceptor:3.14.2' + implementation 'com.google.code.gson:gson:2.8.5' + implementation 'io.gsonfire:gson-fire:1.8.3' + implementation 'org.apache.commons:commons-lang3:3.9' + implementation 'org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:1.0.2' + implementation 'org.apache.oltu.oauth2:org.apache.oltu.oauth2.common:1.0.2' + implementation 'org.apache.logging.log4j:log4j-core:2.11.2' + implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.2' + implementation 'org.slf4j:slf4j-api:1.7.9' + implementation 'javax.xml.bind:jaxb-api:2.3.1' + implementation 'com.sun.xml.bind:jaxb-impl:2.3.1' + implementation 'com.sun.xml.bind:jaxb-core:2.3.0.1' + jaxb 'com.sun.xml.bind:jaxb-xjc:2.3.1' + jaxb 'org.glassfish.jaxb:jaxb-runtime:2.3.1' + testImplementation 'junit:junit:4.13' + testImplementation 'org.mockito:mockito-core:3.1.0' +} + +jar { + manifest.attributes 'Main-Class': 'de.gematik.ti.epa.vzd.gemClient.Main' + baseName 'VZD-CLient' + from configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } +} + +task jaxb { + description 'Converts xsds to classes' + doLast { + def jaxbTargetDir = file("build/generated-sources/jaxb/") + jaxbTargetDir.mkdirs() + ant.taskdef( + name: 'xjc', + classname: 'com.sun.tools.xjc.XJCTask', + classpath: configurations.jaxb.asPath + ) + ant.jaxbTargetDir = jaxbTargetDir + ant.xjc( + destdir: '${jaxbTargetDir}', + package: 'generated', + schema: 'src/main/resources/xsd/commands.xsd', + binding: 'src/main/resources/jaxb-binding/binding.xml', + extension: 'true') + } +} + +compileJava.dependsOn jaxb + +task sourceJar(type: Jar) { + classifier "sources" + baseName "VZD-CLient" + from "${project.buildDir}/../src/main/java/" + include "**/*.java" +} + +task testSourceJar(type: Jar) { + classifier "test-sources" + from "${project.buildDir}/../src/test/java/" + include "**/*.java" +} + +task adocJar(type: Jar) { + classifier "adoc" + from "${project.buildDir}/../doc" + into "${project.name}" + exclude "**/generated/**/*.*" +} + +task javadocJar(type: Jar) { + classifier "javadoc" + from "${project.buildDir}/docs/javadoc" + into "${project.name}" +} + +artifacts { + archives sourceJar + archives testSourceJar + archives adocJar + archives javadocJar +} + +//tasks.withType(Tar) { +// enabled = false +//} + +gematikPublish { + name = "VZD-Client" + description = "VZD-Client to add, remove and change data" + gitHubProjectName = "app-VZD-Client" +} + + +distributions { + main { + baseName = project.name + contents { + into(project.name) { + from jar + rename '(.*)-.*.jar', project.name + '.jar' + } + into('') { + from '.' + include 'vorlage_commands_data.xml' + include 'doc/**' + from 'bspData/' + include 'startVzdClient.bat' + include 'startVzdC_Log.bat' + include 'Beispiel_Config_Datei.txt' + include 'Beispiel_Credential_Datei.txt' + include 'Beispiel_Command_Datei.xml' + } + } + } +} \ No newline at end of file diff --git a/doc/javadoc/config.adoc b/doc/javadoc/config.adoc new file mode 100644 index 0000000..0bf3921 --- /dev/null +++ b/doc/javadoc/config.adoc @@ -0,0 +1,24 @@ +// asciidoc settings for EN (English) +// ================================== + +:toc-title: Table of Contents + +// enable table-of-contents +:toc: +:toclevels: 4 + +:javadoc: true + +:classdia-caption: Class diagram + +// where are images located? +:imagesdir: ../doc/images +:imagesoutdir: ../doc/images +:testdir: ../src/test/java/de/gematik/ti +:sourcedir: ../src/main/java/de/gematik/ti +:plantumldir: ../plantuml +:userguide: ../doc/userguide + + + + diff --git a/doc/userguide/ReadmeConfig.adoc b/doc/userguide/ReadmeConfig.adoc new file mode 100644 index 0000000..601b68c --- /dev/null +++ b/doc/userguide/ReadmeConfig.adoc @@ -0,0 +1,29 @@ +ifndef::globalConfig[] +:globalConfig: true +:useCachePlantuml: true +// asciidoc settings for EN (English) +// ================================== + + +:toc-title: Table of Contents + +// enable table-of-contents +:toc: +:toclevels: 4 + +:classdia-caption: Class diagram +:seqdia-caption: Sequence diagram + +:source-highlighter: prettify + +// where are images located? +:imagesdir: de.gematik.ti.openhealthcard.events/doc/images +:imagesoutdir: de.gematik.ti.openhealthcard.events/doc/images/ +:testdir: ../../src/test/java/de/gematik/ti +:sourcedir: /de.gematik.ti.openhealthcard.events/src/main/java/de/gematik/ti +:plantumldir: ../plantuml + + + + +endif::globalConfig[] \ No newline at end of file diff --git a/doc/userguide/VZDCL_API.adoc b/doc/userguide/VZDCL_API.adoc new file mode 100644 index 0000000..a32aed1 --- /dev/null +++ b/doc/userguide/VZDCL_API.adoc @@ -0,0 +1,5 @@ +include::config.adoc[] + +== API Documentation + +Generated API docs are available at https://gematik.github.io/app-VZD-Client. diff --git a/doc/userguide/VZDCL_GettingStarted.adoc b/doc/userguide/VZDCL_GettingStarted.adoc new file mode 100644 index 0000000..5f30b88 --- /dev/null +++ b/doc/userguide/VZDCL_GettingStarted.adoc @@ -0,0 +1,8 @@ +include::config.adoc[] + +== Getting Started + +=== Build setup + +soon + diff --git a/doc/userguide/VZDCL_Introduction.adoc b/doc/userguide/VZDCL_Introduction.adoc new file mode 100644 index 0000000..0e636c1 --- /dev/null +++ b/doc/userguide/VZDCL_Introduction.adoc @@ -0,0 +1,7 @@ +ifndef::javadoc[] +include::config.adoc[] +endif::javadoc[] + +== Introduction + +This part describes the VZD-Client functionalities and structure. \ No newline at end of file diff --git a/doc/userguide/VZDCL_License.adoc b/doc/userguide/VZDCL_License.adoc new file mode 100644 index 0000000..30947bf --- /dev/null +++ b/doc/userguide/VZDCL_License.adoc @@ -0,0 +1,4 @@ +include::config.adoc[] +== License + +Licensed under the https://www.apache.org/licenses/LICENSE-2.0[Apache License, Version 2.0]. diff --git a/doc/userguide/VZDCL_Main.adoc b/doc/userguide/VZDCL_Main.adoc new file mode 100644 index 0000000..b194f60 --- /dev/null +++ b/doc/userguide/VZDCL_Main.adoc @@ -0,0 +1,7 @@ +include::config.adoc[] + +== VZD-Client + +include::VZDCL_Introduction.adoc[] +include::VZDCL_Overview.adoc[] +include::VZDCL_GettingStarted.adoc[] diff --git a/doc/userguide/VZDCL_Overview.adoc b/doc/userguide/VZDCL_Overview.adoc new file mode 100644 index 0000000..b76cdc2 --- /dev/null +++ b/doc/userguide/VZDCL_Overview.adoc @@ -0,0 +1,4 @@ +include::config.adoc[] + +== Overview + diff --git a/doc/userguide/config.adoc b/doc/userguide/config.adoc new file mode 100644 index 0000000..48039a5 --- /dev/null +++ b/doc/userguide/config.adoc @@ -0,0 +1,27 @@ +ifndef::globalConfig[] +// asciidoc settings for EN (English) +// ================================== + + +:toc-title: Table of Contents + +// enable table-of-contents +:toc: +:toclevels: 4 + +:classdia-caption: Class diagram +:seqdia-caption: Sequence diagram + +:source-highlighter: prettify + +// where are images located? +:imagesdir: ../images +:imagesoutdir: ../images +:testdir: ../../src/test/java/de/gematik/ti +:sourcedir: ../../src/main/java/de/gematik/ti +:plantumldir: ../plantuml + + + + +endif::globalConfig[] \ No newline at end of file diff --git a/docs/AdministrationApi.md b/docs/AdministrationApi.md new file mode 100644 index 0000000..546f8b1 --- /dev/null +++ b/docs/AdministrationApi.md @@ -0,0 +1,134 @@ +# AdministrationApi + +All URIs are relative to *https://gematik.de/fdv* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**changeProvider1**](AdministrationApi.md#changeProvider1) | **POST** /changeProvider | Anbieter wechseln +[**putNotificationInformation1**](AdministrationApi.md#putNotificationInformation1) | **POST** /NotificationInformation | Benachrichtigungsadresse fuer Geraeteautorisierung aktualisieren + + +<a name="changeProvider1"></a> +# **changeProvider1** +> ResponseDTO changeProvider1(changeProviderRequestDTO) + +Anbieter wechseln + +Umsetzung Operation I_FdV::changeProvicer (A_18047) + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.AdministrationApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + AdministrationApi apiInstance = new AdministrationApi(defaultClient); + ChangeProviderRequestDTO changeProviderRequestDTO = new ChangeProviderRequestDTO(); // ChangeProviderRequestDTO | + try { + ResponseDTO result = apiInstance.changeProvider1(changeProviderRequestDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling AdministrationApi#changeProvider1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **changeProviderRequestDTO** | [**ChangeProviderRequestDTO**](ChangeProviderRequestDTO.md)| | [optional] + +### Return type + +[**ResponseDTO**](ResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + +<a name="putNotificationInformation1"></a> +# **putNotificationInformation1** +> ResponseDTO putNotificationInformation1(notificationInformationRequestDTO) + +Benachrichtigungsadresse fuer Geraeteautorisierung aktualisieren + +Umsetzung Operation I_FdV::putNotificationInformation (A_18063); Hinterlegt eine Benachrichtigungsadresse fuer das Aktenkonto. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.AdministrationApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + AdministrationApi apiInstance = new AdministrationApi(defaultClient); + NotificationInformationRequestDTO notificationInformationRequestDTO = new NotificationInformationRequestDTO(); // NotificationInformationRequestDTO | + try { + ResponseDTO result = apiInstance.putNotificationInformation1(notificationInformationRequestDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling AdministrationApi#putNotificationInformation1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **notificationInformationRequestDTO** | [**NotificationInformationRequestDTO**](NotificationInformationRequestDTO.md)| | [optional] + +### Return type + +[**ResponseDTO**](ResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + diff --git a/docs/Author.md b/docs/Author.md new file mode 100644 index 0000000..6f38a23 --- /dev/null +++ b/docs/Author.md @@ -0,0 +1,21 @@ + + +# Author + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**identifier** | **String** | authorPerson, fuer Leistungserbringer Lebenslange Identifikationsnummer eines Arztes, fuer Versicherte Versicherten-ID (unveraenderliche Teil der KVNR) | [optional] +**familyName** | **String** | authorPerson, Nachname | [optional] +**givenName** | **String** | authorPerson, Vorname | [optional] +**otherName** | **String** | authorPerson, weiterer Vorname | [optional] +**nameAffix** | **String** | authorPerson, Nameszusatz | [optional] +**title** | **String** | authorPerson, Titel | [optional] +**authorInstitution** | [**List<AuthorInstitution>**](AuthorInstitution.md) | | [optional] +**authorRole** | **List<String>** | | [optional] +**authorSpecialty** | **List<String>** | | [optional] +**authorTelecommunication** | **List<String>** | | [optional] + + + diff --git a/docs/AuthorInstitution.md b/docs/AuthorInstitution.md new file mode 100644 index 0000000..508a80e --- /dev/null +++ b/docs/AuthorInstitution.md @@ -0,0 +1,13 @@ + + +# AuthorInstitution + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**name** | **String** | Name der Leistungserbringerinstitution oder Name des Kostentraegers | [optional] +**identifier** | **String** | Institutionskennzeichen der Leistungserbringerinstitution oder Betriebsnummer des Kostentraegers | [optional] + + + diff --git a/docs/BaseDirectoryEntry.md b/docs/BaseDirectoryEntry.md new file mode 100644 index 0000000..6162c7d --- /dev/null +++ b/docs/BaseDirectoryEntry.md @@ -0,0 +1,27 @@ + + +# BaseDirectoryEntry + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**dn** | [**DistinguishedName**](DistinguishedName.md) | | +**givenName** | **String** | HBA: Vorname, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet | [optional] [readonly] +**sn** | **String** | HBA: Name, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet | [optional] [readonly] +**cn** | **String** | HBA: Vorname und Nachname / SMC-B: Bezeichner: Name Wird vom VZD aus dem Zertifikatsattribut commonName übernommen. | [readonly] +**displayName** | **String** | Anzeigename, kann geändert werden. Dieses Attribut wird genutzt um den Namen der Organisation gegenüber dem Anwender darzustellen (Verwendung als Filter-Attribut um die Suche einzuschränken und bei der Darstellung des Ergebnisses). Der Wert wird von der pflegenden Stelle festgelegt. | [optional] +**streetAddress** | **String** | Straße und Hausnummer Der Wert wird von der pflegenden Stelle festgelegt | [optional] +**postalCode** | **String** | Postleitzahl Der Wert wird von der pflegenden Stelle festgelegt | [optional] +**localityName** | **String** | Ort Der Wert wird von der pflegenden Stelle festgelegt | [optional] +**stateOrProvienceName** | **String** | Bundesland Der Wert wird von der pflegenden Stelle festgelegt | [optional] +**title** | **String** | HBA: Titel, optional / SMC-B: nicht verwendet | [optional] +**organization** | **String** | Organisation Der Wert wird von der pflegenden Stelle festgelegt | [optional] +**otherName** | **String** | Anderer Name. Wird vom VZD aus dem Zertifikatsattribut otherName übernommen. | [optional] +**specialization** | **List<String>** | Fachgebiet Der Wert wird von der pflegenden Stelle festgelegt | [optional] +**domainID** | **List<String>** | Ärzte: Betriebsstättennummer Der Wert wird aus dem Zertifikat übernommen (Attribut organizationName) | [optional] +**personalEntry** | **Boolean** | Wird vom VZD eingetragen / Wert == TRUE, wenn alle Zertifikate den entryType 1 haben (Berufsgruppe), Wert == FALSE sonst | [optional] [readonly] +**dataFromAuthority** | **Boolean** | Wird vom VZD eingetragen / Wert == TRUE, wenn der Verzeichnisdienst_Eintrag von dem Kartenherausgeber geschrieben wurde, Wert == FALSE sonst | [optional] [readonly] + + + diff --git a/docs/CertificateAdministrationApi.md b/docs/CertificateAdministrationApi.md new file mode 100644 index 0000000..35f56ca --- /dev/null +++ b/docs/CertificateAdministrationApi.md @@ -0,0 +1,307 @@ +# CertificateAdministrationApi + +All URIs are relative to *https://to.be.defined* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**addDirectoryEntryCertificate**](CertificateAdministrationApi.md#addDirectoryEntryCertificate) | **POST** /DirectoryEntries/{uid}/Certificates | Der Zertifikatseintrag wird im Verzeichnisdienst hinzugefügt und ist logisch über dn.uid mit dem übergeordneten Verzeichniseintrag verknüpft. +[**deleteDirectoryEntryCertificate**](CertificateAdministrationApi.md#deleteDirectoryEntryCertificate) | **DELETE** /DirectoryEntries/{uid}/Certificates/{certificateEntryID} | Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. +[**modifyDirectoryEntryCertificate**](CertificateAdministrationApi.md#modifyDirectoryEntryCertificate) | **PUT** /DirectoryEntries/{uid}/Certificates/{certificateEntryID} | Der Zertifikatseintrag wird mit den übergebenen Daten aktualisiert. +[**readDirectoryCertificates**](CertificateAdministrationApi.md#readDirectoryCertificates) | **GET** /DirectoryEntries/Certificates | Zertifikat lesen + + +<a name="addDirectoryEntryCertificate"></a> +# **addDirectoryEntryCertificate** +> DistinguishedName addDirectoryEntryCertificate(uid, userCertificate) + +Der Zertifikatseintrag wird im Verzeichnisdienst hinzugefügt und ist logisch über dn.uid mit dem übergeordneten Verzeichniseintrag verknüpft. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.vzd.client.invoker.ApiClient; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.Configuration; +import de.gematik.ti.epa.vzd.client.invoker.auth.*; +import de.gematik.ti.epa.vzd.client.invoker.models.*; +import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://to.be.defined"); + + // Configure OAuth2 access token for authorization: OAuth2 + OAuth OAuth2 = (OAuth) defaultClient.getAuthentication("OAuth2"); + OAuth2.setAccessToken("YOUR ACCESS TOKEN"); + + CertificateAdministrationApi apiInstance = new CertificateAdministrationApi(defaultClient); + String uid = "uid_example"; // String | ID (dn.uid) vom übergeordneten Verzeichniseintrag + UserCertificate userCertificate = new UserCertificate(); // UserCertificate | Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path) telematikID Nicht vorhanden (wird vom Verzeichnisdienst belegt) entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden userCertificate Muss vorhanden sein description Kann optional belegt werden + try { + DistinguishedName result = apiInstance.addDirectoryEntryCertificate(uid, userCertificate); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling CertificateAdministrationApi#addDirectoryEntryCertificate"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **uid** | **String**| ID (dn.uid) vom übergeordneten Verzeichniseintrag | + **userCertificate** | [**UserCertificate**](UserCertificate.md)| Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path) telematikID Nicht vorhanden (wird vom Verzeichnisdienst belegt) entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden userCertificate Muss vorhanden sein description Kann optional belegt werden | + +### Return type + +[**DistinguishedName**](DistinguishedName.md) + +### Authorization + +[OAuth2](../README.md#OAuth2) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**201** | Created Der Zertifikatseintrag wurde hinzugefügt. Zurückgegeben wird der distinguishedName des erzeugten Eintrags. | - | +**401** | Unauthorized Der WWW-Authenticate Header im Response muss auf OAuth gesetzt werden. | - | +**403** | Forbidden | - | +**405** | Invalid input | - | + +<a name="deleteDirectoryEntryCertificate"></a> +# **deleteDirectoryEntryCertificate** +> deleteDirectoryEntryCertificate(uid, certificateEntryID) + +Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.vzd.client.invoker.ApiClient; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.Configuration; +import de.gematik.ti.epa.vzd.client.invoker.auth.*; +import de.gematik.ti.epa.vzd.client.invoker.models.*; +import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://to.be.defined"); + + // Configure OAuth2 access token for authorization: OAuth2 + OAuth OAuth2 = (OAuth) defaultClient.getAuthentication("OAuth2"); + OAuth2.setAccessToken("YOUR ACCESS TOKEN"); + + CertificateAdministrationApi apiInstance = new CertificateAdministrationApi(defaultClient); + String uid = "uid_example"; // String | ID vom übergeordneten Verzeichniseintrag + String certificateEntryID = "certificateEntryID_example"; // String | ID von dem zu löschenden Zertifikatseintrag + try { + apiInstance.deleteDirectoryEntryCertificate(uid, certificateEntryID); + } catch (ApiException e) { + System.err.println("Exception when calling CertificateAdministrationApi#deleteDirectoryEntryCertificate"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **uid** | **String**| ID vom übergeordneten Verzeichniseintrag | + **certificateEntryID** | **String**| ID von dem zu löschenden Zertifikatseintrag | + +### Return type + +null (empty response body) + +### Authorization + +[OAuth2](../README.md#OAuth2) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | Successful operation | - | +**400** | Invalid ID supplied | - | +**401** | Unauthorized Der WWW-Authenticate Header im Response muss auf OAuth gesetzt werden. | - | +**403** | Forbidden | - | +**404** | Certificate not found | - | + +<a name="modifyDirectoryEntryCertificate"></a> +# **modifyDirectoryEntryCertificate** +> UserCertificate modifyDirectoryEntryCertificate(uid, certificateEntryID, userCertificate) + +Der Zertifikatseintrag wird mit den übergebenen Daten aktualisiert. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.vzd.client.invoker.ApiClient; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.Configuration; +import de.gematik.ti.epa.vzd.client.invoker.auth.*; +import de.gematik.ti.epa.vzd.client.invoker.models.*; +import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://to.be.defined"); + + // Configure OAuth2 access token for authorization: OAuth2 + OAuth OAuth2 = (OAuth) defaultClient.getAuthentication("OAuth2"); + OAuth2.setAccessToken("YOUR ACCESS TOKEN"); + + CertificateAdministrationApi apiInstance = new CertificateAdministrationApi(defaultClient); + String uid = "uid_example"; // String | ID vom übergeordneten Verzeichniseintrag + String certificateEntryID = "certificateEntryID_example"; // String | ID von dem Zertifikat + UserCertificate userCertificate = new UserCertificate(); // UserCertificate | Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & certificateEntryID in Path). telematikID Nicht vorhanden (wird vom Verzeichnisdienst belegt) entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate unverändert gegenüber VZD description Kann optional belegt werden. + try { + UserCertificate result = apiInstance.modifyDirectoryEntryCertificate(uid, certificateEntryID, userCertificate); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling CertificateAdministrationApi#modifyDirectoryEntryCertificate"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **uid** | **String**| ID vom übergeordneten Verzeichniseintrag | + **certificateEntryID** | **String**| ID von dem Zertifikat | + **userCertificate** | [**UserCertificate**](UserCertificate.md)| Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & certificateEntryID in Path). telematikID Nicht vorhanden (wird vom Verzeichnisdienst belegt) entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate unverändert gegenüber VZD description Kann optional belegt werden. | + +### Return type + +[**UserCertificate**](UserCertificate.md) + +### Authorization + +[OAuth2](../README.md#OAuth2) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | Der aktualisierte Zertifikatseintrag wird zurückgegeben. | - | +**400** | Invalid ID supplied or userCertificate changed | - | +**401** | Unauthorized Der WWW-Authenticate Header im Response muss auf OAuth gesetzt werden. | - | +**403** | Forbidden | - | +**404** | Certificate not found | - | +**405** | Invalid input | - | + +<a name="readDirectoryCertificates"></a> +# **readDirectoryCertificates** +> List<UserCertificate> readDirectoryCertificates(uid, certificateEntryID, entryType, telematikID, professionOID, usage) + +Zertifikat lesen + +Liefert alle Zertifikate, die dem Filter (siehe 'parameters') entsprechen. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.vzd.client.invoker.ApiClient; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.Configuration; +import de.gematik.ti.epa.vzd.client.invoker.auth.*; +import de.gematik.ti.epa.vzd.client.invoker.models.*; +import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://to.be.defined"); + + // Configure OAuth2 access token for authorization: OAuth2 + OAuth OAuth2 = (OAuth) defaultClient.getAuthentication("OAuth2"); + OAuth2.setAccessToken("YOUR ACCESS TOKEN"); + + CertificateAdministrationApi apiInstance = new CertificateAdministrationApi(defaultClient); + String uid = "uid_example"; // String | ID vom übergeordneten Verzeichniseintrag + String certificateEntryID = "certificateEntryID_example"; // String | ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. + String entryType = "entryType_example"; // String | Erlaubt die Suche mit Hilfe des Attributs entryType. + String telematikID = "telematikID_example"; // String | telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. + String professionOID = "professionOID_example"; // String | Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. + String usage = "usage_example"; // String | Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im Attribut usage (array) des Zertifikatseintrags enthalten ist. + try { + List<UserCertificate> result = apiInstance.readDirectoryCertificates(uid, certificateEntryID, entryType, telematikID, professionOID, usage); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling CertificateAdministrationApi#readDirectoryCertificates"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **uid** | **String**| ID vom übergeordneten Verzeichniseintrag | + **certificateEntryID** | **String**| ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. | [optional] + **entryType** | **String**| Erlaubt die Suche mit Hilfe des Attributs entryType. | [optional] + **telematikID** | **String**| telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. | [optional] + **professionOID** | **String**| Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. | [optional] + **usage** | **String**| Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im Attribut usage (array) des Zertifikatseintrags enthalten ist. | [optional] + +### Return type + +[**List<UserCertificate>**](UserCertificate.md) + +### Authorization + +[OAuth2](../README.md#OAuth2) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | successful operation Es werden alle gefundenen Zertifikatseinträge zurückgegeben. | - | +**400** | Bad Request Wird zurückgegeben wenn mehr als 100 Einträge gefunden wurden. In diesem Fall müssen die Filter Parameter vom Client genauer belegt werden. | - | +**401** | Unauthorized Der WWW-Authenticate Header im Response muss auf OAuth gesetzt werden. | - | +**403** | Forbidden | - | +**404** | Certificate not found | - | + diff --git a/docs/ChangeProviderRequestDTO.md b/docs/ChangeProviderRequestDTO.md new file mode 100644 index 0000000..a60e123 --- /dev/null +++ b/docs/ChangeProviderRequestDTO.md @@ -0,0 +1,15 @@ + + +# ChangeProviderRequestDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**account** | [**Login**](Login.md) | | +**fqdnNewProvider** | **String** | FQDN des neuen Anbieters | +**transferPermission** | **Boolean** | legt fest, ob die Berechtigungen mit uebertragen werden sollen | [optional] +**representativeNotificationInfo** | [**List<NotificationInformationWithId>**](NotificationInformationWithId.md) | | [optional] + + + diff --git a/docs/ConfigurationApi.md b/docs/ConfigurationApi.md new file mode 100644 index 0000000..b500c31 --- /dev/null +++ b/docs/ConfigurationApi.md @@ -0,0 +1,136 @@ +# ConfigurationApi + +All URIs are relative to *https://gematik.de/fdv* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**getConfigurationEntries1**](ConfigurationApi.md#getConfigurationEntries1) | **GET** /configuration | Gesamte Konfiguration lesen +[**updateConfigurationEntries1**](ConfigurationApi.md#updateConfigurationEntries1) | **PUT** /configuration | Konfigurationseintrag aendern + + +<a name="getConfigurationEntries1"></a> +# **getConfigurationEntries1** +> List<ConfigurationEntry> getConfigurationEntries1(uid) + +Gesamte Konfiguration lesen + +Umsetzung Operation I_FdV_Management::getConfiguration (A_18067); Liefert alle Konfigurationseintraege, die dem Filter entsprechen. Als Filter ist configurationEntryId moeglich. Wird kein Filter angegeben, dann werden alle Eintraege aus der Konfiguration zurueckgegeben. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.ConfigurationApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + ConfigurationApi apiInstance = new ConfigurationApi(defaultClient); + String uid = "uid_example"; // String | Bezeichner eines Konfigurationseintrages (configurationEntryId) + try { + List<ConfigurationEntry> result = apiInstance.getConfigurationEntries1(uid); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling ConfigurationApi#getConfigurationEntries1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **uid** | **String**| Bezeichner eines Konfigurationseintrages (configurationEntryId) | [optional] + +### Return type + +[**List<ConfigurationEntry>**](ConfigurationEntry.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | successful operation; Es werden alle Konfigurationseintraege zurueckgegeben.; Wenn nach einer uid gesucht wurde, wird genau dieser Eintrag zurueckgegeben.; Falls der Konfigurationseintrag vorab nicht gesetzt wurde, wird ein Leerstring zurückgegeben. | - | +**400** | Invalid ID supplied | - | +**404** | Entry not found | - | + +<a name="updateConfigurationEntries1"></a> +# **updateConfigurationEntries1** +> ResponseDTO updateConfigurationEntries1(configurationEntry) + +Konfigurationseintrag aendern + +Umsetzung Operation I_FdV_Management::setConfiguration (A_18066); Setzt einen Konfigurationseintrag + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.ConfigurationApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + ConfigurationApi apiInstance = new ConfigurationApi(defaultClient); + ConfigurationEntry configurationEntry = new ConfigurationEntry(); // ConfigurationEntry | + try { + ResponseDTO result = apiInstance.updateConfigurationEntries1(configurationEntry); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling ConfigurationApi#updateConfigurationEntries1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **configurationEntry** | [**ConfigurationEntry**](ConfigurationEntry.md)| | [optional] + +### Return type + +[**ResponseDTO**](ResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + diff --git a/docs/ConfigurationEntry.md b/docs/ConfigurationEntry.md new file mode 100644 index 0000000..ebded38 --- /dev/null +++ b/docs/ConfigurationEntry.md @@ -0,0 +1,33 @@ + + +# ConfigurationEntry + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**configurationEntryId** | [**ConfigurationEntryIdEnum**](#ConfigurationEntryIdEnum) | Schluesselwerte fuer die in A_15292 definierten Parameter, die durch den Nutzer fuer die Konfiguration des FdV eingegeben werden.; OwnerInsurantId - Versicherten-ID des Aktenkontoinhabers, Teil der Akten-ID; OwnerFqdnProvider - FQDN Anbieter ePA-Aktensystem des Aktenkontoinhabers; OwnerDeviceName - Gerätename des GdV; In der Testtreiber-Konfiguration können 2 Vertretungen eingerichtet werden.; Representation1Name - Name des zu Vertretenden; Representation1InsurantId - Versicherten-ID des zu Vertretenden, Teil der Akten-ID; Representation1FqdnProvider - FQDN Anbieter ePA-Aktensystem des zu Vertretenden; Notifcation - Benachrichtigungen aktivieren; NotificationPeriod - Benachrichtigungszeitraum; ShowPermissionOnAddDocuments - Dokumente einstellen Berechtigte anzeigen; UseEGK (boolean) - gibt an, ob fuer die Authentisierung die eGK oder die alternative kryptographische Versichertenidentitaet genutzt wird. | +**configurationEntryValue** | **String** | Wert fuer den Konfigurationsparameter | + + + +## Enum: ConfigurationEntryIdEnum + +Name | Value +---- | ----- +OWNERINSURANTID | "OwnerInsurantId" +OWNERFQDNPROVIDER | "OwnerFqdnProvider" +OWNERDEVICENAME | "OwnerDeviceName" +REPRESENTATION1NAME | "Representation1Name" +REPRESENTATION1INSURANTID | "Representation1InsurantId" +REPRESENTATION1FQDNPROVIDER | "Representation1FqdnProvider" +REPRESENTATION2NAME | "Representation2Name" +REPRESENTATION2INSURANTID | "Representation2InsurantId" +REPRESENTATION2FQDNPROVIDER | "Representation2FqdnProvider" +NOTIFCATION | "Notifcation" +NOTIFICATIONPERIOD | "NotificationPeriod" +SHOWPERMISSIONONADDDOCUMENTS | "ShowPermissionOnAddDocuments" +USEEGK | "UseEGK" + + + diff --git a/docs/CreateDirectoryEntry.md b/docs/CreateDirectoryEntry.md new file mode 100644 index 0000000..2e16fe8 --- /dev/null +++ b/docs/CreateDirectoryEntry.md @@ -0,0 +1,13 @@ + + +# CreateDirectoryEntry + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**directoryEntryBase** | [**BaseDirectoryEntry**](BaseDirectoryEntry.md) | | [optional] +**userCertificates** | [**List<UserCertificate>**](UserCertificate.md) | | [optional] + + + diff --git a/docs/DefaultApi.md b/docs/DefaultApi.md new file mode 100644 index 0000000..24c2cca --- /dev/null +++ b/docs/DefaultApi.md @@ -0,0 +1,126 @@ +# DefaultApi + +All URIs are relative to *https://gematik.de/fdv* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**getProductInformation1**](DefaultApi.md#getProductInformation1) | **POST** /productinformation | Gibt die Informationen zur Produktinformation zurueck +[**ping1**](DefaultApi.md#ping1) | **POST** /ping | Prueft die Erreichbarkeit der Schnittstelle auf Anwendungsebene + + +<a name="getProductInformation1"></a> +# **getProductInformation1** +> ResponseProductInformationDTO getProductInformation1() + +Gibt die Informationen zur Produktinformation zurueck + +Umsetzung Operation I_FdV_Management::getProductInformation (A_18068) + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.DefaultApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + DefaultApi apiInstance = new DefaultApi(defaultClient); + try { + ResponseProductInformationDTO result = apiInstance.getProductInformation1(); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling DefaultApi#getProductInformation1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters +This endpoint does not need any parameter. + +### Return type + +[**ResponseProductInformationDTO**](ResponseProductInformationDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + +<a name="ping1"></a> +# **ping1** +> ResponsePingDTO ping1() + +Prueft die Erreichbarkeit der Schnittstelle auf Anwendungsebene + +Ping prueft die Erreichbarkeit der Schnittstelle auf Anwendungsebene. In der Response wird die Schnittstellenversion zurueckgegeben, was der Pruefung der Interoberabilitaet dient. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.DefaultApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + DefaultApi apiInstance = new DefaultApi(defaultClient); + try { + ResponsePingDTO result = apiInstance.ping1(); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling DefaultApi#ping1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters +This endpoint does not need any parameter. + +### Return type + +[**ResponsePingDTO**](ResponsePingDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + diff --git a/docs/DeletePermissionDTO.md b/docs/DeletePermissionDTO.md new file mode 100644 index 0000000..df1d12c --- /dev/null +++ b/docs/DeletePermissionDTO.md @@ -0,0 +1,13 @@ + + +# DeletePermissionDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**account** | [**Login**](Login.md) | | +**telematikOrInsurantId** | **String** | Versicherten-ID für Vertreter oder Telematik-ID für LEI bzw. KTR | + + + diff --git a/docs/DirectoryApi.md b/docs/DirectoryApi.md new file mode 100644 index 0000000..2f81e82 --- /dev/null +++ b/docs/DirectoryApi.md @@ -0,0 +1,134 @@ +# DirectoryApi + +All URIs are relative to *https://gematik.de/fdv* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**findHcpos1**](DirectoryApi.md#findHcpos1) | **POST** /findHcpos | Suche nach Leistungserbringerinstitutionen im Verzeichnisdienst +[**findInsurances1**](DirectoryApi.md#findInsurances1) | **POST** /findInsurances | Suche nach Kostentraegern im Verzeichnisdienst + + +<a name="findHcpos1"></a> +# **findHcpos1** +> FindDirectoryResponseDTO findHcpos1(findDirectoryRequestDTO) + +Suche nach Leistungserbringerinstitutionen im Verzeichnisdienst + +Umsetzung Operation I_FdV::findHcp (A_18048) + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.DirectoryApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + DirectoryApi apiInstance = new DirectoryApi(defaultClient); + FindDirectoryRequestDTO findDirectoryRequestDTO = new FindDirectoryRequestDTO(); // FindDirectoryRequestDTO | + try { + FindDirectoryResponseDTO result = apiInstance.findHcpos1(findDirectoryRequestDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling DirectoryApi#findHcpos1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **findDirectoryRequestDTO** | [**FindDirectoryRequestDTO**](FindDirectoryRequestDTO.md)| | [optional] + +### Return type + +[**FindDirectoryResponseDTO**](FindDirectoryResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + +<a name="findInsurances1"></a> +# **findInsurances1** +> FindDirectoryResponseDTO findInsurances1(findDirectoryRequestDTO) + +Suche nach Kostentraegern im Verzeichnisdienst + +Umsetzung Operation I_FdV::findInsurance (A_18051) + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.DirectoryApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + DirectoryApi apiInstance = new DirectoryApi(defaultClient); + FindDirectoryRequestDTO findDirectoryRequestDTO = new FindDirectoryRequestDTO(); // FindDirectoryRequestDTO | + try { + FindDirectoryResponseDTO result = apiInstance.findInsurances1(findDirectoryRequestDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling DirectoryApi#findInsurances1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **findDirectoryRequestDTO** | [**FindDirectoryRequestDTO**](FindDirectoryRequestDTO.md)| | [optional] + +### Return type + +[**FindDirectoryResponseDTO**](FindDirectoryResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + diff --git a/docs/DirectoryEntry.md b/docs/DirectoryEntry.md new file mode 100644 index 0000000..c7834b8 --- /dev/null +++ b/docs/DirectoryEntry.md @@ -0,0 +1,14 @@ + + +# DirectoryEntry + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**directoryEntryBase** | [**BaseDirectoryEntry**](BaseDirectoryEntry.md) | | [optional] +**userCertificates** | [**List<UserCertificate>**](UserCertificate.md) | | [optional] +**fachdaten** | [**List<Fachdaten>**](Fachdaten.md) | | [optional] + + + diff --git a/docs/DirectoryEntryAdministrationApi.md b/docs/DirectoryEntryAdministrationApi.md new file mode 100644 index 0000000..e42a9ba --- /dev/null +++ b/docs/DirectoryEntryAdministrationApi.md @@ -0,0 +1,321 @@ +# DirectoryEntryAdministrationApi + +All URIs are relative to *https://to.be.defined* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**addDirectoryEntry**](DirectoryEntryAdministrationApi.md#addDirectoryEntry) | **POST** /DirectoryEntries | Einen Eintrag zum Verzeichnisdienst hinzufügen +[**deleteDirectoryEntry**](DirectoryEntryAdministrationApi.md#deleteDirectoryEntry) | **DELETE** /DirectoryEntries/{uid} | Gesamten Verzeichniseintrag löschen +[**modifyDirectoryEntry**](DirectoryEntryAdministrationApi.md#modifyDirectoryEntry) | **PUT** /DirectoryEntries/{uid}/baseDirectoryEntries | Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. +[**readDirectoryEntry**](DirectoryEntryAdministrationApi.md#readDirectoryEntry) | **GET** /DirectoryEntries | Gesamten Verzeichniseintrag lesen + + +<a name="addDirectoryEntry"></a> +# **addDirectoryEntry** +> DistinguishedName addDirectoryEntry(createDirectoryEntry) + +Einen Eintrag zum Verzeichnisdienst hinzufügen + +### Example +```java +// Import classes: +import de.gematik.ti.epa.vzd.client.invoker.ApiClient; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.Configuration; +import de.gematik.ti.epa.vzd.client.invoker.auth.*; +import de.gematik.ti.epa.vzd.client.invoker.models.*; +import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://to.be.defined"); + + // Configure OAuth2 access token for authorization: OAuth2 + OAuth OAuth2 = (OAuth) defaultClient.getAuthentication("OAuth2"); + OAuth2.setAccessToken("YOUR ACCESS TOKEN"); + + DirectoryEntryAdministrationApi apiInstance = new DirectoryEntryAdministrationApi(defaultClient); + CreateDirectoryEntry createDirectoryEntry = new CreateDirectoryEntry(); // CreateDirectoryEntry | Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn.* Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht vorhanden (wird vom Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) displayName Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. localityName Kann optional belegt werden. stateOrProvienceName Kann optional belegt werden. title Kann optional belegt werden. organization Kann optional belegt werden. otherName Kann optional belegt werden. specialization Kann optional belegt werden. domainID Kann optional belegt werden. personalEntry Nicht vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority Nicht vorhanden (wird vom Verzeichnisdienst belegt) userCertificates Kann optional belegt werden. dn.uid Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) telematikID Nicht vorhanden (wird vom Verzeichnisdienst belegt) entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Muss vorhanden sein (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird ein Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in dieser Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im Verzeichniseintrag gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden kann). + try { + DistinguishedName result = apiInstance.addDirectoryEntry(createDirectoryEntry); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling DirectoryEntryAdministrationApi#addDirectoryEntry"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **createDirectoryEntry** | [**CreateDirectoryEntry**](CreateDirectoryEntry.md)| Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn.* Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht vorhanden (wird vom Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) displayName Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. localityName Kann optional belegt werden. stateOrProvienceName Kann optional belegt werden. title Kann optional belegt werden. organization Kann optional belegt werden. otherName Kann optional belegt werden. specialization Kann optional belegt werden. domainID Kann optional belegt werden. personalEntry Nicht vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority Nicht vorhanden (wird vom Verzeichnisdienst belegt) userCertificates Kann optional belegt werden. dn.uid Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) telematikID Nicht vorhanden (wird vom Verzeichnisdienst belegt) entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Muss vorhanden sein (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird ein Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in dieser Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im Verzeichniseintrag gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden kann). | + +### Return type + +[**DistinguishedName**](DistinguishedName.md) + +### Authorization + +[OAuth2](../README.md#OAuth2) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**201** | Created Der Verzeichniseintrag wurde angelegt. Zurückgegeben wird der distinguishedName des erzeugten Eintrags. | - | +**401** | Unauthorized Der WWW-Authenticate Header im Response muss auf OAuth gesetzt werden. | - | +**403** | Forbidden | - | +**405** | Invalid input | - | + +<a name="deleteDirectoryEntry"></a> +# **deleteDirectoryEntry** +> deleteDirectoryEntry(uid) + +Gesamten Verzeichniseintrag löschen + +### Example +```java +// Import classes: +import de.gematik.ti.epa.vzd.client.invoker.ApiClient; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.Configuration; +import de.gematik.ti.epa.vzd.client.invoker.auth.*; +import de.gematik.ti.epa.vzd.client.invoker.models.*; +import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://to.be.defined"); + + // Configure OAuth2 access token for authorization: OAuth2 + OAuth OAuth2 = (OAuth) defaultClient.getAuthentication("OAuth2"); + OAuth2.setAccessToken("YOUR ACCESS TOKEN"); + + DirectoryEntryAdministrationApi apiInstance = new DirectoryEntryAdministrationApi(defaultClient); + String uid = "uid_example"; // String | ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate und Fachdaten. + try { + apiInstance.deleteDirectoryEntry(uid); + } catch (ApiException e) { + System.err.println("Exception when calling DirectoryEntryAdministrationApi#deleteDirectoryEntry"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **uid** | **String**| ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate und Fachdaten. | + +### Return type + +null (empty response body) + +### Authorization + +[OAuth2](../README.md#OAuth2) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | Successful operation | - | +**400** | Invalid ID supplied | - | +**401** | Unauthorized Der WWW-Authenticate Header im Response muss auf OAuth gesetzt werden. | - | +**403** | Forbidden | - | +**404** | DirectoryEntry not found | - | + +<a name="modifyDirectoryEntry"></a> +# **modifyDirectoryEntry** +> DistinguishedName modifyDirectoryEntry(uid, baseDirectoryEntry) + +Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.vzd.client.invoker.ApiClient; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.Configuration; +import de.gematik.ti.epa.vzd.client.invoker.auth.*; +import de.gematik.ti.epa.vzd.client.invoker.models.*; +import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://to.be.defined"); + + // Configure OAuth2 access token for authorization: OAuth2 + OAuth OAuth2 = (OAuth) defaultClient.getAuthentication("OAuth2"); + OAuth2.setAccessToken("YOUR ACCESS TOKEN"); + + DirectoryEntryAdministrationApi apiInstance = new DirectoryEntryAdministrationApi(defaultClient); + String uid = "uid_example"; // String | ID von dem Verzeichniseintrag + BaseDirectoryEntry baseDirectoryEntry = new BaseDirectoryEntry(); // BaseDirectoryEntry | Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn Nicht vorhanden. displayName Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. localityName Kann optional belegt werden. stateOrProvienceName Kann optional belegt werden. title Kann optional belegt werden. organization Kann optional belegt werden. otherName Nicht vorhanden. specialization Kann optional belegt werden. domainID Kann optional belegt werden. personalEntry Nicht vorhanden. dataFromAuthority Nicht vorhanden + try { + DistinguishedName result = apiInstance.modifyDirectoryEntry(uid, baseDirectoryEntry); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling DirectoryEntryAdministrationApi#modifyDirectoryEntry"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **uid** | **String**| ID von dem Verzeichniseintrag | + **baseDirectoryEntry** | [**BaseDirectoryEntry**](BaseDirectoryEntry.md)| Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn Nicht vorhanden. displayName Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. localityName Kann optional belegt werden. stateOrProvienceName Kann optional belegt werden. title Kann optional belegt werden. organization Kann optional belegt werden. otherName Nicht vorhanden. specialization Kann optional belegt werden. domainID Kann optional belegt werden. personalEntry Nicht vorhanden. dataFromAuthority Nicht vorhanden | + +### Return type + +[**DistinguishedName**](DistinguishedName.md) + +### Authorization + +[OAuth2](../README.md#OAuth2) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | Der Verzeichniseintrag wurde aktualisiert. | - | +**400** | Invalid ID supplied | - | +**401** | Unauthorized Der WWW-Authenticate Header im Response muss auf OAuth gesetzt werden. | - | +**403** | Forbidden | - | +**404** | DirectoryEntry not found | - | +**405** | Invalid input | - | + +<a name="readDirectoryEntry"></a> +# **readDirectoryEntry** +> List<DirectoryEntry> readDirectoryEntry(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, stateOrProvienceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority) + +Gesamten Verzeichniseintrag lesen + +Liefert alle zum Filter passenden Verzeichniseinträge. Die angegebenen Parameter werden mit logischen UND verknüpft. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.vzd.client.invoker.ApiClient; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.Configuration; +import de.gematik.ti.epa.vzd.client.invoker.auth.*; +import de.gematik.ti.epa.vzd.client.invoker.models.*; +import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://to.be.defined"); + + // Configure OAuth2 access token for authorization: OAuth2 + OAuth OAuth2 = (OAuth) defaultClient.getAuthentication("OAuth2"); + OAuth2.setAccessToken("YOUR ACCESS TOKEN"); + + DirectoryEntryAdministrationApi apiInstance = new DirectoryEntryAdministrationApi(defaultClient); + String uid = "uid_example"; // String | ID von dem Verzeichniseintrag (distinguishedName.uid) + String givenName = "givenName_example"; // String | Erlaubt die Suche mit Hilfe des Attributs givenName. + String sn = "sn_example"; // String | Erlaubt die Suche mit Hilfe des Attributs sn. + String cn = "cn_example"; // String | Erlaubt die Suche mit Hilfe des Attributs cn. + String displayName = "displayName_example"; // String | Erlaubt die Suche mit Hilfe des Attributs displayName. + String streetAddress = "streetAddress_example"; // String | Erlaubt die Suche mit Hilfe des Attributs streetAddress. + String postalCode = "postalCode_example"; // String | Erlaubt die Suche mit Hilfe des Attributs postalCode. + String localityName = "localityName_example"; // String | Erlaubt die Suche mit Hilfe des Attributs localityName. + String stateOrProvienceName = "stateOrProvienceName_example"; // String | Erlaubt die Suche mit Hilfe des Attributs stateOrProvienceName. + String title = "title_example"; // String | Erlaubt die Suche mit Hilfe des Attributs title. + String organization = "organization_example"; // String | Erlaubt die Suche mit Hilfe des Attributs organization. + String otherName = "otherName_example"; // String | Erlaubt die Suche mit Hilfe des Attributs otherName. + String specialization = "specialization_example"; // String | Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die angegebene domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. + String domainID = "domainID_example"; // String | Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. + String personalEntry = "personalEntry_example"; // String | Erlaubt die Suche mit Hilfe des Attributs personalEntry. + String dataFromAuthority = "dataFromAuthority_example"; // String | Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. + try { + List<DirectoryEntry> result = apiInstance.readDirectoryEntry(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, stateOrProvienceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling DirectoryEntryAdministrationApi#readDirectoryEntry"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **uid** | **String**| ID von dem Verzeichniseintrag (distinguishedName.uid) | [optional] + **givenName** | **String**| Erlaubt die Suche mit Hilfe des Attributs givenName. | [optional] + **sn** | **String**| Erlaubt die Suche mit Hilfe des Attributs sn. | [optional] + **cn** | **String**| Erlaubt die Suche mit Hilfe des Attributs cn. | [optional] + **displayName** | **String**| Erlaubt die Suche mit Hilfe des Attributs displayName. | [optional] + **streetAddress** | **String**| Erlaubt die Suche mit Hilfe des Attributs streetAddress. | [optional] + **postalCode** | **String**| Erlaubt die Suche mit Hilfe des Attributs postalCode. | [optional] + **localityName** | **String**| Erlaubt die Suche mit Hilfe des Attributs localityName. | [optional] + **stateOrProvienceName** | **String**| Erlaubt die Suche mit Hilfe des Attributs stateOrProvienceName. | [optional] + **title** | **String**| Erlaubt die Suche mit Hilfe des Attributs title. | [optional] + **organization** | **String**| Erlaubt die Suche mit Hilfe des Attributs organization. | [optional] + **otherName** | **String**| Erlaubt die Suche mit Hilfe des Attributs otherName. | [optional] + **specialization** | **String**| Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die angegebene domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. | [optional] + **domainID** | **String**| Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. | [optional] + **personalEntry** | **String**| Erlaubt die Suche mit Hilfe des Attributs personalEntry. | [optional] + **dataFromAuthority** | **String**| Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. | [optional] + +### Return type + +[**List<DirectoryEntry>**](DirectoryEntry.md) + +### Authorization + +[OAuth2](../README.md#OAuth2) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | successful operation Es werden alle zu dem Filter Parametern passenden Verzeichniseinträge - inklusive Zertifikaten und Fachdaten - zurückgegeben. | - | +**400** | Bad Request Wird zurückgegeben wenn mehr als 100 Einträge gefunden wurden. In diesem Fall müssen die Filter Parameter vom Client genauer belegt werden. | - | +**401** | Unauthorized Der WWW-Authenticate Header im Response wird auf OAuth gesetzt. | - | +**403** | Forbidden | - | +**404** | DirectoryEntry not found | - | + diff --git a/docs/DirectoryEntryWithCertificates.md b/docs/DirectoryEntryWithCertificates.md new file mode 100644 index 0000000..e752564 --- /dev/null +++ b/docs/DirectoryEntryWithCertificates.md @@ -0,0 +1,13 @@ + + +# DirectoryEntryWithCertificates + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**directoryEntry** | [**DirectoryEntry**](DirectoryEntry.md) | | +**certificates** | **List<byte[]>** | | + + + diff --git a/docs/DistinguishedName.md b/docs/DistinguishedName.md new file mode 100644 index 0000000..cb79422 --- /dev/null +++ b/docs/DistinguishedName.md @@ -0,0 +1,15 @@ + + +# DistinguishedName + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**uid** | **String** | entryID: Name/ID, den den Eintrag eindeutig identifiziert. Hat für den Verzeichnisdienst_Eintrag, Certificate, KOM-LE_Fachdaten und FAD1 eines Verzeichniseintrags den gleichen Wert. | +**dc** | **List<String>** | | [optional] +**ou** | **List<String>** | | [optional] +**cn** | **String** | Common Name | [optional] + + + diff --git a/docs/Document.md b/docs/Document.md new file mode 100644 index 0000000..cf1c61c --- /dev/null +++ b/docs/Document.md @@ -0,0 +1,12 @@ + + +# Document + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**doc** | **byte[]** | Dokument (Base64 kodiert) | [optional] + + + diff --git a/docs/DocumentWithMetadata.md b/docs/DocumentWithMetadata.md new file mode 100644 index 0000000..269aa49 --- /dev/null +++ b/docs/DocumentWithMetadata.md @@ -0,0 +1,51 @@ + + +# DocumentWithMetadata + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**author** | [**List<Author>**](Author.md) | | [optional] +**classCode** | **String** | Grobe Klassifizierung des Dokuments, einem Code des in [IHE-ITI-VS] definierten Value Sets fuer DocumentEntry.classCode | [optional] +**comments** | **String** | Ergaenzende Hinweise in Freitext | [optional] +**confidentialityCode** | **List<String>** | | [optional] +**creationTime** | [**OffsetDateTime**](OffsetDateTime.md) | Erstellungszeitpunkt des Dokuments; date-time notation as defined by RFC 3339, section 5.6, for example, 2017-07-21T17:32:28Z | [optional] +**eventCodeList** | **List<String>** | Ereignisse, die zur Erstellung des Dokuments geführt haben.; ein Code des in [IHE-ITI-VS] definierten Value Sets fuer DocumentEntry.eventCodeList oder aus der Tabelle in A_17540 | [optional] +**formatCode** | **List<String>** | | [optional] +**hash** | **String** | | [optional] +**healthcareFacilityTypeCode** | **String** | Art der Einrichtung, in der das dokumentierte Ereignis stattgefunden hat.; ein Code des in [IHE-ITI-VS] definierten Value Sets fuer DocumentEntry.healthcareFacilityTypeCode | [optional] +**languageCode** | **String** | Sprache, in der das Dokument abgefasst ist.; ein Code des in [IHE-ITI-VS] definierten Value Sets fuer DocumentEntry.languageCode | [optional] +**legalAuthenticator** | **String** | Rechtlich Verantwortlicher fuer das Dokument | [optional] +**mimeType** | [**MimeTypeEnum**](#MimeTypeEnum) | MIME-Type des Dokuments | [optional] +**practiceSettingCode** | **String** | Art der Fachrichtung der erstellenden Einrichtung, in der das dokumentiere Ereignis stattgefunden hat.; ein Code des in [IHE-ITI-VS] definierten Value Sets fuer DocumentEntry.practiceSettingCode oder aus der Tabelle in A_16944 | [optional] +**referenceIdList** | **List<String>** | Liste von IDs, mit denen das Dokument assoziiert wird | [optional] +**serviceStartTime** | [**OffsetDateTime**](OffsetDateTime.md) | Zeitpunkt, an dem das im Dokument dokumentierte (Behandlungs-)Ereignis begonnen wurde.; date-time notation as defined by RFC 3339, section 5.6, for example, 2017-07-21T17:32:28Z | [optional] +**serviceStopTime** | [**OffsetDateTime**](OffsetDateTime.md) | Zeitpunkt, an dem das im Dokument dokumentierte (Behandlungs-)Ereignis beendet wurde.; date-time notation as defined by RFC 3339, section 5.6, for example, 2017-07-21T17:32:28Z | [optional] +**size** | **Integer** | Groesse des Dokuments in Bytes | [optional] +**title** | **String** | Titel des Dokuments | [optional] +**typeCode** | **String** | Art des Dokumentes; ein Code des in [IHE-ITI-VS] definierten Value Sets fuer DocumentEntry.typeCode | [optional] +**uniqueId** | **String** | Eindeutige, aktenweite Kennung des Dokuments | [optional] +**uri** | **String** | Dateiname | [optional] +**doc** | **byte[]** | Dokument (Base64 kodiert) | [optional] + + + +## Enum: MimeTypeEnum + +Name | Value +---- | ----- +APPLICATION_PDF | "application/pdf" +IMAGE_JPEG | "image/jpeg" +IMAGE_TIFF | "image/tiff" +TEXT_PLAIN | "text/plain" +TEXT_RTF | "text/rtf" +APPLICATION_MSWORD | "application/msword" +APPLICATION_MSEXCEL | "application/msexcel" +APPLICATION_VND_OASIS_OPENDOCUMENT_TEXT | "application/vnd.oasis.opendocument.text" +APPLICATION_VND_OASIS_OPENDOCUMENT_SPREADSHEET | "application/vnd.oasis.opendocument.spreadsheet" +APPLICATION_XML | "application/xml" +APPLICATION_HL7_V3 | "application/hl7-v3" + + + diff --git a/docs/DocumentsApi.md b/docs/DocumentsApi.md new file mode 100644 index 0000000..ab47529 --- /dev/null +++ b/docs/DocumentsApi.md @@ -0,0 +1,260 @@ +# DocumentsApi + +All URIs are relative to *https://gematik.de/fdv* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**deleteDocuments1**](DocumentsApi.md#deleteDocuments1) | **POST** /deleteDocuments | Dokumente aus dem Aktenkonto loeschen +[**findDocuments1**](DocumentsApi.md#findDocuments1) | **POST** /findDocuments | Dokumente und Submission Sets in einem Aktenkonto finden +[**retrieveDocuments1**](DocumentsApi.md#retrieveDocuments1) | **POST** /retrieveDocuments | Dokumente aus Aktenkonto herunterladen +[**storeDocuments1**](DocumentsApi.md#storeDocuments1) | **POST** /storeDocuments | Dokumente in ein Aktenkonto laden + + +<a name="deleteDocuments1"></a> +# **deleteDocuments1** +> ResponseDTO deleteDocuments1(documentsRequestDTO) + +Dokumente aus dem Aktenkonto loeschen + +Umsetzung Operation I_FdV::deleteDocuments (A_18061); Loescht die Dokumente mit dem im Request angegebenen Ids aus dem Aktenkonto. Die Ids werden mittels findDocuments ermittelt. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.DocumentsApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + DocumentsApi apiInstance = new DocumentsApi(defaultClient); + DocumentsRequestDTO documentsRequestDTO = new DocumentsRequestDTO(); // DocumentsRequestDTO | + try { + ResponseDTO result = apiInstance.deleteDocuments1(documentsRequestDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling DocumentsApi#deleteDocuments1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **documentsRequestDTO** | [**DocumentsRequestDTO**](DocumentsRequestDTO.md)| | [optional] + +### Return type + +[**ResponseDTO**](ResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + +<a name="findDocuments1"></a> +# **findDocuments1** +> FindDocumentsResponseDTO findDocuments1(findDocumentsRequestDTO) + +Dokumente und Submission Sets in einem Aktenkonto finden + +Umsetzung Operation I_FdV::findDocuments (A_18059); Die fuer die Suchoperation zu verwendende Stored Query wird durch den Parameter vorgegeben. Falls dieser nicht angegeben ist, muss eine geeignete Stored Query gewaehlt werden.; Wenn die Suchparameter ein SubmissionSet adressieren, dann soll der Response die Metadaten der im SubmissionSet enthaltenen Dokumente (unter Beachtung ggf. zusätzlich angegebenener Suchkriterien zu Dokumenten) beinhalten.; Der Response enthält Metadaten zu Dokumenten aber nicht die Dokumente selbst. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.DocumentsApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + DocumentsApi apiInstance = new DocumentsApi(defaultClient); + FindDocumentsRequestDTO findDocumentsRequestDTO = new FindDocumentsRequestDTO(); // FindDocumentsRequestDTO | + try { + FindDocumentsResponseDTO result = apiInstance.findDocuments1(findDocumentsRequestDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling DocumentsApi#findDocuments1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **findDocumentsRequestDTO** | [**FindDocumentsRequestDTO**](FindDocumentsRequestDTO.md)| | [optional] + +### Return type + +[**FindDocumentsResponseDTO**](FindDocumentsResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + +<a name="retrieveDocuments1"></a> +# **retrieveDocuments1** +> RetrieveDocumentsResponseDTO retrieveDocuments1(documentsRequestDTO) + +Dokumente aus Aktenkonto herunterladen + +Umsetzung Operation I_FdV::getDocuments (A_18060); Laedt die Dokumente mit den im Request angegebenen Ids aus dem Aktenkonto. Die Ids werden mittels findDocuments ermittelt. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.DocumentsApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + DocumentsApi apiInstance = new DocumentsApi(defaultClient); + DocumentsRequestDTO documentsRequestDTO = new DocumentsRequestDTO(); // DocumentsRequestDTO | + try { + RetrieveDocumentsResponseDTO result = apiInstance.retrieveDocuments1(documentsRequestDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling DocumentsApi#retrieveDocuments1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **documentsRequestDTO** | [**DocumentsRequestDTO**](DocumentsRequestDTO.md)| | [optional] + +### Return type + +[**RetrieveDocumentsResponseDTO**](RetrieveDocumentsResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + +<a name="storeDocuments1"></a> +# **storeDocuments1** +> ResponseDTO storeDocuments1(storeDocumentRequestDTO) + +Dokumente in ein Aktenkonto laden + +Umsetzung Operation I_FdV::putDocuments (A_18058) + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.DocumentsApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + DocumentsApi apiInstance = new DocumentsApi(defaultClient); + StoreDocumentRequestDTO storeDocumentRequestDTO = new StoreDocumentRequestDTO(); // StoreDocumentRequestDTO | + try { + ResponseDTO result = apiInstance.storeDocuments1(storeDocumentRequestDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling DocumentsApi#storeDocuments1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **storeDocumentRequestDTO** | [**StoreDocumentRequestDTO**](StoreDocumentRequestDTO.md)| | [optional] + +### Return type + +[**ResponseDTO**](ResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + diff --git a/docs/DocumentsRequestDTO.md b/docs/DocumentsRequestDTO.md new file mode 100644 index 0000000..5d2f5d3 --- /dev/null +++ b/docs/DocumentsRequestDTO.md @@ -0,0 +1,13 @@ + + +# DocumentsRequestDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**account** | [**Login**](Login.md) | | +**documentIds** | **List<String>** | | + + + diff --git a/docs/Error.md b/docs/Error.md new file mode 100644 index 0000000..a188609 --- /dev/null +++ b/docs/Error.md @@ -0,0 +1,13 @@ + + +# Error + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**attributeName** | **String** | Name des Attributs, in dem ein Fehler erkannt wurde | [optional] +**attributeError** | **String** | Beschreibung des erkannten Fehlers | [optional] + + + diff --git a/docs/FAD1.md b/docs/FAD1.md new file mode 100644 index 0000000..12f686a --- /dev/null +++ b/docs/FAD1.md @@ -0,0 +1,13 @@ + + +# FAD1 + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**dn** | [**DistinguishedName**](DistinguishedName.md) | | +**mail** | **List<String>** | | [optional] [readonly] + + + diff --git a/docs/Fachdaten.md b/docs/Fachdaten.md new file mode 100644 index 0000000..fcec795 --- /dev/null +++ b/docs/Fachdaten.md @@ -0,0 +1,13 @@ + + +# Fachdaten + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**dn** | [**DistinguishedName**](DistinguishedName.md) | | +**FAD1** | [**List<FAD1>**](FAD1.md) | | [optional] + + + diff --git a/docs/FindDirectoryRequestDTO.md b/docs/FindDirectoryRequestDTO.md new file mode 100644 index 0000000..95e7b71 --- /dev/null +++ b/docs/FindDirectoryRequestDTO.md @@ -0,0 +1,13 @@ + + +# FindDirectoryRequestDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**account** | [**Login**](Login.md) | | +**directoryEntry** | [**DirectoryEntry**](DirectoryEntry.md) | | + + + diff --git a/docs/FindDirectoryResponseDTO.md b/docs/FindDirectoryResponseDTO.md new file mode 100644 index 0000000..f5ab8c6 --- /dev/null +++ b/docs/FindDirectoryResponseDTO.md @@ -0,0 +1,14 @@ + + +# FindDirectoryResponseDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**success** | **Boolean** | | +**error** | **String** | | [optional] +**directoryEntries** | [**List<DirectoryEntryWithCertificates>**](DirectoryEntryWithCertificates.md) | | [optional] + + + diff --git a/docs/FindDocumentMetaData.md b/docs/FindDocumentMetaData.md new file mode 100644 index 0000000..8477d13 --- /dev/null +++ b/docs/FindDocumentMetaData.md @@ -0,0 +1,33 @@ + + +# FindDocumentMetaData + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**xdSSubmissionSetEntryUUID** | **List<String>** | | [optional] +**xdSSubmissionSetUniqueId** | **List<String>** | | [optional] +**xdSSubmissionSetSubmissionTimeFrom** | [**OffsetDateTime**](OffsetDateTime.md) | Beginn Zeitraum fuer Suche; date-time notation as defined by RFC 3339, section 5.6 | [optional] +**xdSSubmissionSetSubmissionTimeTo** | [**OffsetDateTime**](OffsetDateTime.md) | XDSDocumentEntryCreationTimeTo, Ende Zeitraum fuer Suche; date-time notation as defined by RFC 3339, section 5.6 | [optional] +**xdSDocumentEntryEntryUUID** | **List<String>** | | [optional] +**xdSDocumentEntryUniqueId** | **List<String>** | | [optional] +**xdSDocumentEntryClassCode** | **List<String>** | | [optional] +**xdSDocumentEntryPracticeSettingCode** | **List<String>** | | [optional] +**xdSDocumentEntryCreationTimeFrom** | [**OffsetDateTime**](OffsetDateTime.md) | Beginn Zeitraum fuer Suche; date-time notation as defined by RFC 3339, section 5.6 | [optional] +**xdSDocumentEntryCreationTimeTo** | [**OffsetDateTime**](OffsetDateTime.md) | Ende Zeitraum fuer Suche; date-time notation as defined by RFC 3339, section 5.6 | [optional] +**xdSDocumentEntryServiceStartTimeFrom** | [**OffsetDateTime**](OffsetDateTime.md) | Zeitpunkt, an dem das im Dokument dokumentierte (Behandlungs-)Ereignis begonnen wurde. Beginn Zeitraum fuer Suche; date-time notation as defined by RFC 3339, section 5.6 | [optional] +**xdSDocumentEntryServiceStartTimeTo** | [**OffsetDateTime**](OffsetDateTime.md) | Zeitpunkt, an dem das im Dokument dokumentierte (Behandlungs-)Ereignis begonnen wurde. Ende Zeitraum fuer Suche; date-time notation as defined by RFC 3339, section 5.6 | [optional] +**xdSDocumentEntryServiceStopTimeFrom** | [**OffsetDateTime**](OffsetDateTime.md) | Zeitpunkt, an dem das im Dokument dokumentierte (Behandlungs-)Ereignis beendet wurde. Beginn Zeitraum fuer Suche; date-time notation as defined by RFC 3339, section 5.6 | [optional] +**xdSDocumentEntryServiceStopTimeTo** | [**OffsetDateTime**](OffsetDateTime.md) | Zeitpunkt, an dem das im Dokument dokumentierte (Behandlungs-)Ereignis beendet wurde. Ende Zeitraum fuer Suche; date-time notation as defined by RFC 3339, section 5.6 | [optional] +**xdSDocumentEntryHealthcareFacilityTypeCode** | **List<String>** | | [optional] +**xdSDocumentEntryEventCodeList** | **List<String>** | | [optional] +**xdSDocumentEntryConfidentialityCode** | **List<String>** | | [optional] +**xdSDocumentEntryAuthorPerson** | **List<String>** | | [optional] +**xdSDocumentEntryFormatCode** | **List<String>** | | [optional] +**xdSDocumentEntryTypeCode** | **List<String>** | | [optional] +**xdSDocumentEntryTitle** | **List<String>** | | [optional] +**xdSDocumentAuthorInstitution** | **List<String>** | | [optional] + + + diff --git a/docs/FindDocumentsRequestDTO.md b/docs/FindDocumentsRequestDTO.md new file mode 100644 index 0000000..cb8faf6 --- /dev/null +++ b/docs/FindDocumentsRequestDTO.md @@ -0,0 +1,29 @@ + + +# FindDocumentsRequestDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**account** | [**Login**](Login.md) | | +**doc** | [**FindDocumentMetaData**](FindDocumentMetaData.md) | | [optional] +**query** | [**QueryEnum**](#QueryEnum) | optional; falls angegeben, ist die entsprechende Stored Query anzuwenden; falls nicht angegeben, ist eine entsprechend der Suchparameter geeignete Stored Query anzuwenden | [optional] + + + +## Enum: QueryEnum + +Name | Value +---- | ----- +FINDDOCUMENTS | "FindDocuments" +FINDSUBMISSIONSETS | "FindSubmissionSets" +FINDDOCUMENTSBYTITLE | "FindDocumentsByTitle" +GETALL | "GetAll" +GETDOCUMENTS | "GetDocuments" +GETSUBMISSIONSETS | "GetSubmissionSets" +GETSUBMISSIONSETANDCONTENTS | "GetSubmissionSetAndContents" +FINDDOCUMENTSBYREFERENCEID | "FindDocumentsByReferenceId" + + + diff --git a/docs/FindDocumentsResponseDTO.md b/docs/FindDocumentsResponseDTO.md new file mode 100644 index 0000000..89ac6a3 --- /dev/null +++ b/docs/FindDocumentsResponseDTO.md @@ -0,0 +1,14 @@ + + +# FindDocumentsResponseDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**success** | **Boolean** | | +**error** | **String** | | [optional] +**docs** | [**List<SubmissionSetWithDocumentWithMetadata>**](SubmissionSetWithDocumentWithMetadata.md) | | [optional] + + + diff --git a/docs/Login.md b/docs/Login.md new file mode 100644 index 0000000..66b004a --- /dev/null +++ b/docs/Login.md @@ -0,0 +1,16 @@ + + +# Login + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**account** | **String** | referenziert eine Aktensession, in der die Operation ausgefuehrt werden soll; Wenn keine Aktensession besteht, dann muss fuer dieses Aktenkonto (entspricht OwnerInsurantId oder RepresentationXInsurantId in der Konfiguration) eine Aktensession eroeffnet werden (implizites Login). | +**insurantId** | **String** | Versicherten-ID des Nutzers (Aktenkontoinhaber oder Vertreter) des FdV; Wenn dieser Parameter gesetzt ist, dann wird für die Authentisierung entsprechend dem Konfigurationsparameter useEGK eine angebundene eGK oder der Signaturdienst genutzt.; Wenn dieser Parameter gesetzt ist, dann werden die Parameter pkcs12, passwordPrivateKey und passwordKeyStore ignoriert. | [optional] +**pkcs12** | **byte[]** | C.CH.AUT-Zertifikat des Nutzers mit private Key im pkcs12 Format; Aus dem C.CH.AUT-Zertifikat wird die Versicherten-ID des Nutzers (Aktenkontoinhaber oder Vertreter) bestimmt.; Mit dem private Key werden die Signaturen bei der Authentisierung und der Schluesselerzeugung (SGD) erstellt | [optional] +**passwordPrivateKey** | **String** | Passwort für private Key in pkcs12 | [optional] +**passwordKeyStore** | **String** | | [optional] + + + diff --git a/docs/NotificationInformationRequestDTO.md b/docs/NotificationInformationRequestDTO.md new file mode 100644 index 0000000..ed6b47f --- /dev/null +++ b/docs/NotificationInformationRequestDTO.md @@ -0,0 +1,13 @@ + + +# NotificationInformationRequestDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**account** | [**Login**](Login.md) | | +**notificationInformation** | **String** | Benachrichtigungsadresse (E-Mail) fuer die Geraeteautorisierung | + + + diff --git a/docs/NotificationInformationWithId.md b/docs/NotificationInformationWithId.md new file mode 100644 index 0000000..5674ae8 --- /dev/null +++ b/docs/NotificationInformationWithId.md @@ -0,0 +1,13 @@ + + +# NotificationInformationWithId + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**insurantId** | **String** | Versicherten-ID (unveraenderliche Teil der KVNR) | +**notificationInformation** | **String** | Benachrichtigungsadresse (E-Mail) fuer die Geraeteautorisierung | + + + diff --git a/docs/PermissionEntry.md b/docs/PermissionEntry.md new file mode 100644 index 0000000..0c8f7e2 --- /dev/null +++ b/docs/PermissionEntry.md @@ -0,0 +1,14 @@ + + +# PermissionEntry + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**telematikOrInsurantId** | **String** | Telematik-ID bzw. Versicherten-ID des Berechtigten | +**name** | **String** | Name des Berechtigten | +**permissionLeiProperties** | [**PermissionLeiProp**](PermissionLeiProp.md) | | [optional] + + + diff --git a/docs/PermissionHcpoDTO.md b/docs/PermissionHcpoDTO.md new file mode 100644 index 0000000..f378532 --- /dev/null +++ b/docs/PermissionHcpoDTO.md @@ -0,0 +1,16 @@ + + +# PermissionHcpoDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**account** | [**Login**](Login.md) | | +**hcpoTelematikId** | **String** | Telematik-ID der Leistungserbringerinstitution (LEI) | +**hcpoName** | **String** | Name der LEI | +**certificate** | **byte[]** | aus dem Verzeichnisdienst ermittelte Zertifikat der LEI (Format DER, Base64 kodiert) | +**permissionLeiProperties** | [**PermissionLeiProp**](PermissionLeiProp.md) | | + + + diff --git a/docs/PermissionInsuranceDTO.md b/docs/PermissionInsuranceDTO.md new file mode 100644 index 0000000..32afb6f --- /dev/null +++ b/docs/PermissionInsuranceDTO.md @@ -0,0 +1,15 @@ + + +# PermissionInsuranceDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**account** | [**Login**](Login.md) | | +**insuranceTelematikId** | **String** | Telematik-ID des Kostentraegers (KTR) | +**insuranceName** | **String** | Name des KTR | +**certificate** | **byte[]** | aus dem Verzeichnisdienst ermittelte Zertifikat des KTR (Format DER, Base64 kodiert) | + + + diff --git a/docs/PermissionLeiProp.md b/docs/PermissionLeiProp.md new file mode 100644 index 0000000..cad4a10 --- /dev/null +++ b/docs/PermissionLeiProp.md @@ -0,0 +1,15 @@ + + +# PermissionLeiProp + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**permissionAccessHcpoDocuments** | **Boolean** | Option Zugriff auf durch LEI eingestellte Dokumente erlaubt | [optional] +**permissionAccessInsuranceDocuments** | **Boolean** | Option Zugriff auf durch Kostentraeger eingestellte Dokumente erlaubt | [optional] +**permissionAccessInsurantDocuments** | **Boolean** | Option Zugriff auf durch Versicherte eingestellte Dokumente erlaubt | [optional] +**validity** | [**LocalDate**](LocalDate.md) | Zugriff gueltig bis; full-date notation as defined by RFC 3339, section 5.6, for example, 2017-07-21 | + + + diff --git a/docs/PermissionRepresentativeDTO.md b/docs/PermissionRepresentativeDTO.md new file mode 100644 index 0000000..ab5844c --- /dev/null +++ b/docs/PermissionRepresentativeDTO.md @@ -0,0 +1,15 @@ + + +# PermissionRepresentativeDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**account** | [**Login**](Login.md) | | +**represantativeInsurantId** | **String** | Versicherten-ID des Vertreters (unveraenderliche Teil der KVNR) | +**represantativeName** | **String** | Name des Vertreters | +**representativeNotificationInfo** | **String** | Benachrichtigungsadresse (E-Mail) fuer die Geraeteautorisierung | + + + diff --git a/docs/PermissionsApi.md b/docs/PermissionsApi.md new file mode 100644 index 0000000..05d5599 --- /dev/null +++ b/docs/PermissionsApi.md @@ -0,0 +1,515 @@ +# PermissionsApi + +All URIs are relative to *https://gematik.de/fdv* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**addPermissionHcpo1**](PermissionsApi.md#addPermissionHcpo1) | **POST** /permissionHcpo | Berechtigung fuer LEI erteilen +[**addPermissionInsurance1**](PermissionsApi.md#addPermissionInsurance1) | **POST** /permissionInsurance | Berechtigung fuer einen Kostentraeger erteilen +[**addPermissionRepresentative1**](PermissionsApi.md#addPermissionRepresentative1) | **POST** /permissionRepresentative | Berechtigung fuer einen Vertreter erteilen +[**changePermissionHcpo1**](PermissionsApi.md#changePermissionHcpo1) | **PUT** /permissionHcpo | Berechtigung fuer eine LEI aendern +[**deletePermissionHcpo1**](PermissionsApi.md#deletePermissionHcpo1) | **DELETE** /permissionHcpo | Berechtigung fuer eine LEI loeschen +[**deletePermissionInsurance1**](PermissionsApi.md#deletePermissionInsurance1) | **DELETE** /permissionInsurance | Berechtigung fuer einen Kostentraeger loeschen +[**deletePermissionRepresentative1**](PermissionsApi.md#deletePermissionRepresentative1) | **DELETE** /permissionRepresentative | Berechtigung fuer einen Vertreter loeschen +[**getPermissions1**](PermissionsApi.md#getPermissions1) | **POST** /permissions | Alle Berechtigungen lesen + + +<a name="addPermissionHcpo1"></a> +# **addPermissionHcpo1** +> ResponseDTO addPermissionHcpo1(permissionHcpoDTO) + +Berechtigung fuer LEI erteilen + +Umsetzung Operation I_FdV::grantPermissionHcp (A_18049) + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.PermissionsApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + PermissionsApi apiInstance = new PermissionsApi(defaultClient); + PermissionHcpoDTO permissionHcpoDTO = new PermissionHcpoDTO(); // PermissionHcpoDTO | + try { + ResponseDTO result = apiInstance.addPermissionHcpo1(permissionHcpoDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling PermissionsApi#addPermissionHcpo1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **permissionHcpoDTO** | [**PermissionHcpoDTO**](PermissionHcpoDTO.md)| | [optional] + +### Return type + +[**ResponseDTO**](ResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + +<a name="addPermissionInsurance1"></a> +# **addPermissionInsurance1** +> ResponseDTO addPermissionInsurance1(permissionInsuranceDTO) + +Berechtigung fuer einen Kostentraeger erteilen + +Umsetzung Operation I_FdV::grantPermissionInsurance (A_18052) + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.PermissionsApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + PermissionsApi apiInstance = new PermissionsApi(defaultClient); + PermissionInsuranceDTO permissionInsuranceDTO = new PermissionInsuranceDTO(); // PermissionInsuranceDTO | + try { + ResponseDTO result = apiInstance.addPermissionInsurance1(permissionInsuranceDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling PermissionsApi#addPermissionInsurance1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **permissionInsuranceDTO** | [**PermissionInsuranceDTO**](PermissionInsuranceDTO.md)| | [optional] + +### Return type + +[**ResponseDTO**](ResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + +<a name="addPermissionRepresentative1"></a> +# **addPermissionRepresentative1** +> ResponseDTO addPermissionRepresentative1(permissionRepresentativeDTO) + +Berechtigung fuer einen Vertreter erteilen + +Umsetzung Operation I_FdV::grantPermissionRepresentative (A_18050) + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.PermissionsApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + PermissionsApi apiInstance = new PermissionsApi(defaultClient); + PermissionRepresentativeDTO permissionRepresentativeDTO = new PermissionRepresentativeDTO(); // PermissionRepresentativeDTO | + try { + ResponseDTO result = apiInstance.addPermissionRepresentative1(permissionRepresentativeDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling PermissionsApi#addPermissionRepresentative1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **permissionRepresentativeDTO** | [**PermissionRepresentativeDTO**](PermissionRepresentativeDTO.md)| | [optional] + +### Return type + +[**ResponseDTO**](ResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + +<a name="changePermissionHcpo1"></a> +# **changePermissionHcpo1** +> ResponseDTO changePermissionHcpo1(permissionHcpoDTO) + +Berechtigung fuer eine LEI aendern + +Umsetzung Operation I_FdV::changePermissionHcp (A_18054) + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.PermissionsApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + PermissionsApi apiInstance = new PermissionsApi(defaultClient); + PermissionHcpoDTO permissionHcpoDTO = new PermissionHcpoDTO(); // PermissionHcpoDTO | + try { + ResponseDTO result = apiInstance.changePermissionHcpo1(permissionHcpoDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling PermissionsApi#changePermissionHcpo1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **permissionHcpoDTO** | [**PermissionHcpoDTO**](PermissionHcpoDTO.md)| | [optional] + +### Return type + +[**ResponseDTO**](ResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + +<a name="deletePermissionHcpo1"></a> +# **deletePermissionHcpo1** +> deletePermissionHcpo1(body) + +Berechtigung fuer eine LEI loeschen + +Umsetzung Operation I_FdV::deletePermissionHcp (A_18055) + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.PermissionsApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + PermissionsApi apiInstance = new PermissionsApi(defaultClient); + DeletePermissionDTO body = new DeletePermissionDTO(); // DeletePermissionDTO | + try { + apiInstance.deletePermissionHcpo1(body); + } catch (ApiException e) { + System.err.println("Exception when calling PermissionsApi#deletePermissionHcpo1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **body** | [**DeletePermissionDTO**](.md)| | + +### Return type + +null (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | successful operation | - | +**400** | Invalid ID supplied | - | +**404** | Entry not found | - | + +<a name="deletePermissionInsurance1"></a> +# **deletePermissionInsurance1** +> deletePermissionInsurance1(body) + +Berechtigung fuer einen Kostentraeger loeschen + +Umsetzung Operation I_FdV::deletePermissionInsurance (A_18057) + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.PermissionsApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + PermissionsApi apiInstance = new PermissionsApi(defaultClient); + DeletePermissionDTO body = new DeletePermissionDTO(); // DeletePermissionDTO | + try { + apiInstance.deletePermissionInsurance1(body); + } catch (ApiException e) { + System.err.println("Exception when calling PermissionsApi#deletePermissionInsurance1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **body** | [**DeletePermissionDTO**](.md)| | + +### Return type + +null (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | successful operation | - | +**400** | Invalid ID supplied | - | +**404** | Entry not found | - | + +<a name="deletePermissionRepresentative1"></a> +# **deletePermissionRepresentative1** +> deletePermissionRepresentative1(body) + +Berechtigung fuer einen Vertreter loeschen + +Umsetzung Operation I_FdV::deletePermissionRepresentative (A_18056) + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.PermissionsApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + PermissionsApi apiInstance = new PermissionsApi(defaultClient); + DeletePermissionDTO body = new DeletePermissionDTO(); // DeletePermissionDTO | + try { + apiInstance.deletePermissionRepresentative1(body); + } catch (ApiException e) { + System.err.println("Exception when calling PermissionsApi#deletePermissionRepresentative1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **body** | [**DeletePermissionDTO**](.md)| | + +### Return type + +null (empty response body) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | successful operation | - | +**400** | Invalid ID supplied | - | +**404** | Entry not found | - | + +<a name="getPermissions1"></a> +# **getPermissions1** +> PermissionsResponseDTO getPermissions1(requestDTO) + +Alle Berechtigungen lesen + +Umsetzung Operation I_FdV::getPermissions (A_18053) + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.PermissionsApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + PermissionsApi apiInstance = new PermissionsApi(defaultClient); + RequestDTO requestDTO = new RequestDTO(); // RequestDTO | + try { + PermissionsResponseDTO result = apiInstance.getPermissions1(requestDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling PermissionsApi#getPermissions1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **requestDTO** | [**RequestDTO**](RequestDTO.md)| | [optional] + +### Return type + +[**PermissionsResponseDTO**](PermissionsResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + diff --git a/docs/PermissionsResponseDTO.md b/docs/PermissionsResponseDTO.md new file mode 100644 index 0000000..ed7baed --- /dev/null +++ b/docs/PermissionsResponseDTO.md @@ -0,0 +1,14 @@ + + +# PermissionsResponseDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**success** | **Boolean** | | +**error** | **String** | | [optional] +**permissions** | [**List<PermissionEntry>**](PermissionEntry.md) | | [optional] + + + diff --git a/docs/ProtocolApi.md b/docs/ProtocolApi.md new file mode 100644 index 0000000..27f670c --- /dev/null +++ b/docs/ProtocolApi.md @@ -0,0 +1,71 @@ +# ProtocolApi + +All URIs are relative to *https://gematik.de/fdv* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**getProtocol1**](ProtocolApi.md#getProtocol1) | **POST** /protocol | Zugriffsprotokoll lesen + + +<a name="getProtocol1"></a> +# **getProtocol1** +> ProtocolResponseDTO getProtocol1(requestDTO) + +Zugriffsprotokoll lesen + +Umsetzung Operation I_FdV::getProtocol (A_18062); Liefert alle Eintraege aus dem §291a und Verwaltungsprotokoll fuer das Aktenkonto. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.ProtocolApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + ProtocolApi apiInstance = new ProtocolApi(defaultClient); + RequestDTO requestDTO = new RequestDTO(); // RequestDTO | + try { + ProtocolResponseDTO result = apiInstance.getProtocol1(requestDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling ProtocolApi#getProtocol1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **requestDTO** | [**RequestDTO**](RequestDTO.md)| | [optional] + +### Return type + +[**ProtocolResponseDTO**](ProtocolResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + diff --git a/docs/ProtocolEntry.md b/docs/ProtocolEntry.md new file mode 100644 index 0000000..827d855 --- /dev/null +++ b/docs/ProtocolEntry.md @@ -0,0 +1,22 @@ + + +# ProtocolEntry + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**eventId** | **String** | Aufgerufene Operation gemass A_14505 | +**eventDisplayName** | **String** | Anzeigename der aufgerufenen Operation | +**eventDateTime** | [**OffsetDateTime**](OffsetDateTime.md) | Datum und Uhrzeit des Zugriffs; date-time notation as defined by RFC 3339, section 5.6, for example, 2017-07-21T17:32:28Z | +**eventResult** | **String** | Ergebnis der aufgerufenen Operation | +**userId** | **String** | | +**userName** | **String** | | +**objectId** | **String** | | [optional] +**objectName** | **String** | | [optional] +**deviceId** | **String** | Device-ID bei Zugriff durch Versicherte | [optional] +**providerId** | **String** | Home-Community-ID des ePA-Aktensystems | +**providerName** | **String** | Name des Anbieters ePA-Aktensystem | + + + diff --git a/docs/ProtocolResponseDTO.md b/docs/ProtocolResponseDTO.md new file mode 100644 index 0000000..bdb7998 --- /dev/null +++ b/docs/ProtocolResponseDTO.md @@ -0,0 +1,14 @@ + + +# ProtocolResponseDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**success** | **Boolean** | | +**error** | **String** | | [optional] +**protocolEntries** | [**List<ProtocolEntry>**](ProtocolEntry.md) | | [optional] + + + diff --git a/docs/RequestDTO.md b/docs/RequestDTO.md new file mode 100644 index 0000000..339ab6e --- /dev/null +++ b/docs/RequestDTO.md @@ -0,0 +1,12 @@ + + +# RequestDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**account** | [**Login**](Login.md) | | + + + diff --git a/docs/ResponseDTO.md b/docs/ResponseDTO.md new file mode 100644 index 0000000..269e829 --- /dev/null +++ b/docs/ResponseDTO.md @@ -0,0 +1,13 @@ + + +# ResponseDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**success** | **Boolean** | | +**error** | **String** | | [optional] + + + diff --git a/docs/ResponsePingDTO.md b/docs/ResponsePingDTO.md new file mode 100644 index 0000000..f1517a2 --- /dev/null +++ b/docs/ResponsePingDTO.md @@ -0,0 +1,14 @@ + + +# ResponsePingDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**success** | **Boolean** | | +**error** | **String** | | [optional] +**version** | **String** | | + + + diff --git a/docs/ResponseProductInformationDTO.md b/docs/ResponseProductInformationDTO.md new file mode 100644 index 0000000..5215f11 --- /dev/null +++ b/docs/ResponseProductInformationDTO.md @@ -0,0 +1,14 @@ + + +# ResponseProductInformationDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**producerId** | **String** | Hersteller-ID | +**code** | **String** | Produktkuerzel | +**version** | **String** | Produktversion | + + + diff --git a/docs/RetrieveDocumentsResponseDTO.md b/docs/RetrieveDocumentsResponseDTO.md new file mode 100644 index 0000000..1e72859 --- /dev/null +++ b/docs/RetrieveDocumentsResponseDTO.md @@ -0,0 +1,14 @@ + + +# RetrieveDocumentsResponseDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**success** | **Boolean** | | +**error** | **String** | | [optional] +**docs** | [**List<Document>**](Document.md) | | [optional] + + + diff --git a/docs/StoreDocumentRequestDTO.md b/docs/StoreDocumentRequestDTO.md new file mode 100644 index 0000000..746fdf8 --- /dev/null +++ b/docs/StoreDocumentRequestDTO.md @@ -0,0 +1,14 @@ + + +# StoreDocumentRequestDTO + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**account** | [**Login**](Login.md) | | +**title** | **String** | Titel des Submission Sets | [optional] +**docs** | [**List<DocumentWithMetadata>**](DocumentWithMetadata.md) | | + + + diff --git a/docs/SubmissionSetMetaData.md b/docs/SubmissionSetMetaData.md new file mode 100644 index 0000000..6be1280 --- /dev/null +++ b/docs/SubmissionSetMetaData.md @@ -0,0 +1,18 @@ + + +# SubmissionSetMetaData + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**author** | [**Author**](Author.md) | | [optional] +**comments** | **String** | Ergaenzende Hinweise zum Submission Set in Freitext | [optional] +**contentTypeCode** | **String** | | [optional] +**intendedRecipient** | **List<String>** | | [optional] +**submissionTime** | [**OffsetDateTime**](OffsetDateTime.md) | date-time notation as defined by RFC 3339, section 5.6, for example, 2017-07-21T17:32:28Z | [optional] +**title** | **String** | Titel des Submission Sets | [optional] +**uniqueId** | **String** | Eindeutige Kennung des Submission Sets | [optional] + + + diff --git a/docs/SubmissionSetWithDocumentWithMetadata.md b/docs/SubmissionSetWithDocumentWithMetadata.md new file mode 100644 index 0000000..35484e1 --- /dev/null +++ b/docs/SubmissionSetWithDocumentWithMetadata.md @@ -0,0 +1,13 @@ + + +# SubmissionSetWithDocumentWithMetadata + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**submissionSet** | [**SubmissionSetMetaData**](SubmissionSetMetaData.md) | | [optional] +**documentMetadata** | [**List<DocumentWithMetadata>**](DocumentWithMetadata.md) | | [optional] + + + diff --git a/docs/UserApi.md b/docs/UserApi.md new file mode 100644 index 0000000..bb58991 --- /dev/null +++ b/docs/UserApi.md @@ -0,0 +1,134 @@ +# UserApi + +All URIs are relative to *https://gematik.de/fdv* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**login1**](UserApi.md#login1) | **POST** /login | Login Aktensession +[**logout1**](UserApi.md#logout1) | **POST** /logout | Logout Aktensession + + +<a name="login1"></a> +# **login1** +> ResponseDTO login1(requestDTO) + +Login Aktensession + +Umsetzung Operation I_FdV::login A_18045; Login in zwei Varianten; Falls die insurantID uebergeben wird, dann referenziert die insurantID die AUT-Identitaet des Nutzers, welche ueber eine eGK oder einen Signaturdienst (Konfigurationsparameter UseEGK) verfuegbar ist. Falls keine insurantID übergeben wird, dann wird eine PKCS12-Datei uebergeben. Das C.CH.AUT Zertifikat und der private Schluessel aus der PKCS12-Datei werden im Testtreiber genutzt (bspw. Signatur bei der Authentisierung und der Schluesselerzeugung mit SGD). + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.UserApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + UserApi apiInstance = new UserApi(defaultClient); + RequestDTO requestDTO = new RequestDTO(); // RequestDTO | + try { + ResponseDTO result = apiInstance.login1(requestDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling UserApi#login1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **requestDTO** | [**RequestDTO**](RequestDTO.md)| | [optional] + +### Return type + +[**ResponseDTO**](ResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + +<a name="logout1"></a> +# **logout1** +> ResponseDTO logout1(requestDTO) + +Logout Aktensession + +Umsetzung Operation I_FdV::logout A_18046; Logout wird fuer eine per InsurantID (KVNR) referenzierte Identitaet ausgeloest. + +### Example +```java +// Import classes: +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiClient; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.ApiException; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.Configuration; +import de.gematik.ti.epa.fdv.testtreiber.client.invoker.models.*; +import de.gematik.ti.epa.fdv.testtreiber.client.api.UserApi; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("https://gematik.de/fdv"); + + UserApi apiInstance = new UserApi(defaultClient); + RequestDTO requestDTO = new RequestDTO(); // RequestDTO | + try { + ResponseDTO result = apiInstance.logout1(requestDTO); + System.out.println(result); + } catch (ApiException e) { + System.err.println("Exception when calling UserApi#logout1"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **requestDTO** | [**RequestDTO**](RequestDTO.md)| | [optional] + +### Return type + +[**ResponseDTO**](ResponseDTO.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**0** | default response | - | + diff --git a/docs/UserCertificate.md b/docs/UserCertificate.md new file mode 100644 index 0000000..9b7d76c --- /dev/null +++ b/docs/UserCertificate.md @@ -0,0 +1,28 @@ + + +# UserCertificate + +Jeder Verzeichniseintrag muss mindestens ein Zertifikat enthalten. +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**dn** | [**DistinguishedName**](DistinguishedName.md) | | +**entryType** | **String** | Eintragstyp Wird vom VZD anhand der in dem Zertifikat enthaltenen OID (Extension Admission, Attribut ProfessionOID) und der Spalte Eintragstyp in Tab_VZD_Mapping_Eintragstyp_und_ProfessionOID automatisch eingetragen. Siehe auch [gemSpecOID]# Tab_PKI_402 und Tab_PKI_403 | [optional] [readonly] +**telematikID** | **String** | TelematikID Wird beim Anlegen des Eintrags vom VZD aus dem Zertifikat übernommen (Feld registrationNumber der Extension Admission). | [optional] [readonly] +**professionOID** | **List<String>** | | [optional] [readonly] +**usage** | [**List<UsageEnum>**](#List<UsageEnum>) | Nutzungskennzeichnung kann pro Zertifikat mehrfach vergeben werden. Vorgegebener Wertebereich [KOM-LE, ePA]. Obligatorisch für LEI und KTR mit vorgegebenem Wert usage=ePA | [optional] +**userCertificate** | **String** | Zertifikat im DER Format. Base64 kodiert. Die pflegende Stelle erhält das Zertifikat vom TSP oder falls das nicht möglich ist wird ein Ersatzverfahren abgestimmt. | [optional] +**description** | **String** | Dieses Attribut ermöglicht das Zertifikat zu beschreiben, um die Administration des VZD Eintrags zu vereinfachen. | [optional] + + + +## Enum: List<UsageEnum> + +Name | Value +---- | ----- +KOM_LE | "KOM-LE" +EPA | "ePA" + + + diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..05644f0 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +# Uncomment to build for Android +#target = android \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..78e52e2 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "VZD-Client" \ No newline at end of file diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApi.java b/src/main/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApi.java new file mode 100644 index 0000000..7d298f2 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApi.java @@ -0,0 +1,736 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.api; + +import com.google.gson.reflect.TypeToken; +import de.gematik.ti.epa.vzd.client.invoker.ApiCallback; +import de.gematik.ti.epa.vzd.client.invoker.ApiClient; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.invoker.Configuration; +import de.gematik.ti.epa.vzd.client.invoker.Pair; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CertificateAdministrationApi { + + private ApiClient localVarApiClient; + + public CertificateAdministrationApi() { + this(Configuration.getDefaultApiClient()); + } + + public CertificateAdministrationApi(ApiClient apiClient) { + this.localVarApiClient = apiClient; + } + + public ApiClient getApiClient() { + return localVarApiClient; + } + + public void setApiClient(ApiClient apiClient) { + this.localVarApiClient = apiClient; + } + + /** + * Build call for addDirectoryEntryCertificate + * + * @param uid ID (dn.uid) vom übergeordneten Verzeichniseintrag (required) + * @param userCertificate Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn. Nicht vorhanden (Adressierung erfolgt über uid in Path) telematikID Kann + * optional belegt werden. Wird telematikID angegeben, dann muss diese telematikID mit der telematikID im userCertificate + * übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode 422 abgelehnt. entryType Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann + * optional belegt werden userCertificate Muss vorhanden sein description Kann optional belegt werden (required) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call addDirectoryEntryCertificateCall(String uid, UserCertificate userCertificate, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = userCertificate; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/Certificates" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + @SuppressWarnings("rawtypes") + private okhttp3.Call addDirectoryEntryCertificateValidateBeforeCall(String uid, UserCertificate userCertificate, final ApiCallback _callback) + throws ApiException { + + // verify the required parameter 'uid' is set + if (uid == null) { + throw new ApiException("Missing the required parameter 'uid' when calling addDirectoryEntryCertificate(Async)"); + } + + // verify the required parameter 'userCertificate' is set + if (userCertificate == null) { + throw new ApiException("Missing the required parameter 'userCertificate' when calling addDirectoryEntryCertificate(Async)"); + } + + okhttp3.Call localVarCall = addDirectoryEntryCertificateCall(uid, userCertificate, _callback); + return localVarCall; + + } + + /** + * Der Zertifikatseintrag wird im Verzeichnisdienst hinzugefügt und ist logisch über dn.uid mit dem übergeordneten Verzeichniseintrag verknüpft. + * + * @param uid ID (dn.uid) vom übergeordneten Verzeichniseintrag (required) + * @param userCertificate Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path) telematikID Kann + * optional belegt werden. Wird telematikID angegeben, dann muss diese telematikID mit der telematikID im userCertificate + * übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode 422 abgelehnt. entryType Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann + * optional belegt werden userCertificate Muss vorhanden sein description Kann optional belegt werden (required) + * @return DistinguishedName + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public DistinguishedName addDirectoryEntryCertificate(String uid, UserCertificate userCertificate) throws ApiException { + ApiResponse<DistinguishedName> localVarResp = addDirectoryEntryCertificateWithHttpInfo(uid, userCertificate); + return localVarResp.getData(); + } + + /** + * Der Zertifikatseintrag wird im Verzeichnisdienst hinzugefügt und ist logisch über dn.uid mit dem übergeordneten Verzeichniseintrag verknüpft. + * + * @param uid ID (dn.uid) vom übergeordneten Verzeichniseintrag (required) + * @param userCertificate Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path) telematikID Kann + * optional belegt werden. Wird telematikID angegeben, dann muss diese telematikID mit der telematikID im userCertificate + * übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode 422 abgelehnt. entryType Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann + * optional belegt werden userCertificate Muss vorhanden sein description Kann optional belegt werden (required) + * @return ApiResponse<DistinguishedName> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public ApiResponse<DistinguishedName> addDirectoryEntryCertificateWithHttpInfo(String uid, UserCertificate userCertificate) throws ApiException { + okhttp3.Call localVarCall = addDirectoryEntryCertificateValidateBeforeCall(uid, userCertificate, null); + Type localVarReturnType = new TypeToken<DistinguishedName>() { + }.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); + } + + /** + * Der Zertifikatseintrag wird im Verzeichnisdienst hinzugefügt und ist logisch über dn.uid mit dem übergeordneten Verzeichniseintrag verknüpft. + * (asynchronously) + * + * @param uid ID (dn.uid) vom übergeordneten Verzeichniseintrag (required) + * @param userCertificate Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path) telematikID Kann + * optional belegt werden. Wird telematikID angegeben, dann muss diese telematikID mit der telematikID im userCertificate + * übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode 422 abgelehnt. entryType Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann + * optional belegt werden userCertificate Muss vorhanden sein description Kann optional belegt werden (required) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call addDirectoryEntryCertificateAsync(String uid, UserCertificate userCertificate, final ApiCallback<DistinguishedName> _callback) + throws ApiException { + + okhttp3.Call localVarCall = addDirectoryEntryCertificateValidateBeforeCall(uid, userCertificate, _callback); + Type localVarReturnType = new TypeToken<DistinguishedName>() { + }.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; + } + + /** + * Build call for deleteDirectoryEntryCertificate + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem zu löschenden Zertifikatseintrag (required) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call deleteDirectoryEntryCertificateCall(String uid, String certificateEntryID, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/Certificates/{certificateEntryID}" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())) + .replaceAll("\\{" + "certificateEntryID" + "\\}", localVarApiClient.escapeString(certificateEntryID.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "DELETE", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + @SuppressWarnings("rawtypes") + private okhttp3.Call deleteDirectoryEntryCertificateValidateBeforeCall(String uid, String certificateEntryID, final ApiCallback _callback) + throws ApiException { + + // verify the required parameter 'uid' is set + if (uid == null) { + throw new ApiException("Missing the required parameter 'uid' when calling deleteDirectoryEntryCertificate(Async)"); + } + + // verify the required parameter 'certificateEntryID' is set + if (certificateEntryID == null) { + throw new ApiException("Missing the required parameter 'certificateEntryID' when calling deleteDirectoryEntryCertificate(Async)"); + } + + okhttp3.Call localVarCall = deleteDirectoryEntryCertificateCall(uid, certificateEntryID, _callback); + return localVarCall; + + } + + /** + * Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat + * mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem zu löschenden Zertifikatseintrag (required) + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public void deleteDirectoryEntryCertificate(String uid, String certificateEntryID) throws ApiException { + deleteDirectoryEntryCertificateWithHttpInfo(uid, certificateEntryID); + } + + /** + * Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat + * mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem zu löschenden Zertifikatseintrag (required) + * @return ApiResponse<Void> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public ApiResponse<Void> deleteDirectoryEntryCertificateWithHttpInfo(String uid, String certificateEntryID) throws ApiException { + okhttp3.Call localVarCall = deleteDirectoryEntryCertificateValidateBeforeCall(uid, certificateEntryID, null); + return localVarApiClient.execute(localVarCall); + } + + /** + * Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat + * mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. (asynchronously) + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem zu löschenden Zertifikatseintrag (required) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call deleteDirectoryEntryCertificateAsync(String uid, String certificateEntryID, final ApiCallback<Void> _callback) + throws ApiException { + + okhttp3.Call localVarCall = deleteDirectoryEntryCertificateValidateBeforeCall(uid, certificateEntryID, _callback); + localVarApiClient.executeAsync(localVarCall, _callback); + return localVarCall; + } + + /** + * Build call for modifyDirectoryEntryCertificate + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem Zertifikat (required) + * @param userCertificate Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & + * certificateEntryID in Path). telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss diese + * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation + * mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate unverändert gegenüber + * VZD description Kann optional belegt werden. (required) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call modifyDirectoryEntryCertificateCall(String uid, String certificateEntryID, UserCertificate userCertificate, + final ApiCallback _callback) throws ApiException { + Object localVarPostBody = userCertificate; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/Certificates/{certificateEntryID}" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())) + .replaceAll("\\{" + "certificateEntryID" + "\\}", localVarApiClient.escapeString(certificateEntryID.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "PUT", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + @SuppressWarnings("rawtypes") + private okhttp3.Call modifyDirectoryEntryCertificateValidateBeforeCall(String uid, String certificateEntryID, UserCertificate userCertificate, + final ApiCallback _callback) throws ApiException { + + // verify the required parameter 'uid' is set + if (uid == null) { + throw new ApiException("Missing the required parameter 'uid' when calling modifyDirectoryEntryCertificate(Async)"); + } + + // verify the required parameter 'certificateEntryID' is set + if (certificateEntryID == null) { + throw new ApiException("Missing the required parameter 'certificateEntryID' when calling modifyDirectoryEntryCertificate(Async)"); + } + + // verify the required parameter 'userCertificate' is set + if (userCertificate == null) { + throw new ApiException("Missing the required parameter 'userCertificate' when calling modifyDirectoryEntryCertificate(Async)"); + } + + okhttp3.Call localVarCall = modifyDirectoryEntryCertificateCall(uid, certificateEntryID, userCertificate, _callback); + return localVarCall; + + } + + /** + * Der Zertifikatseintrag wird mit den übergebenen Daten aktualisiert. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem Zertifikat (required) + * @param userCertificate Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & + * certificateEntryID in Path). telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss diese + * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation + * mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate unverändert gegenüber + * VZD description Kann optional belegt werden. (required) + * @return UserCertificate + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public UserCertificate modifyDirectoryEntryCertificate(String uid, String certificateEntryID, UserCertificate userCertificate) throws ApiException { + ApiResponse<UserCertificate> localVarResp = modifyDirectoryEntryCertificateWithHttpInfo(uid, certificateEntryID, userCertificate); + return localVarResp.getData(); + } + + /** + * Der Zertifikatseintrag wird mit den übergebenen Daten aktualisiert. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem Zertifikat (required) + * @param userCertificate Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & + * certificateEntryID in Path). telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss diese + * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation + * mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate unverändert gegenüber + * VZD description Kann optional belegt werden. (required) + * @return ApiResponse<UserCertificate> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public ApiResponse<UserCertificate> modifyDirectoryEntryCertificateWithHttpInfo(String uid, String certificateEntryID, + UserCertificate userCertificate) throws ApiException { + okhttp3.Call localVarCall = modifyDirectoryEntryCertificateValidateBeforeCall(uid, certificateEntryID, userCertificate, null); + Type localVarReturnType = new TypeToken<UserCertificate>() { + }.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); + } + + /** + * Der Zertifikatseintrag wird mit den übergebenen Daten aktualisiert. (asynchronously) + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem Zertifikat (required) + * @param userCertificate Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & + * certificateEntryID in Path). telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss diese + * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation + * mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate unverändert gegenüber + * VZD description Kann optional belegt werden. (required) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call modifyDirectoryEntryCertificateAsync(String uid, String certificateEntryID, UserCertificate userCertificate, + final ApiCallback<UserCertificate> _callback) throws ApiException { + + okhttp3.Call localVarCall = modifyDirectoryEntryCertificateValidateBeforeCall(uid, certificateEntryID, userCertificate, _callback); + Type localVarReturnType = new TypeToken<UserCertificate>() { + }.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; + } + + /** + * Build call for readDirectoryCertificates + * + * @param uid ID vom übergeordneten Verzeichniseintrag (optional) + * @param certificateEntryID ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) + * Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. + * (optional) + * @param entryType Erlaubt die Suche mit Hilfe des Attributs entryType. (optional) + * @param telematikID telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. (optional) + * @param professionOID Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. (optional) + * @param usage Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im + * Attribut usage (array) des Zertifikatseintrags enthalten ist. (optional) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call readDirectoryCertificatesCall(String uid, String certificateEntryID, String entryType, String telematikID, String professionOID, + String usage, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries/Certificates"; + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + if (uid != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("uid", uid)); + } + + if (certificateEntryID != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("certificateEntryID", certificateEntryID)); + } + + if (entryType != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("entryType", entryType)); + } + + if (telematikID != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("telematikID", telematikID)); + } + + if (professionOID != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("professionOID", professionOID)); + } + + if (usage != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("usage", usage)); + } + + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + @SuppressWarnings("rawtypes") + private okhttp3.Call readDirectoryCertificatesValidateBeforeCall(String uid, String certificateEntryID, String entryType, String telematikID, + String professionOID, String usage, final ApiCallback _callback) throws ApiException { + + okhttp3.Call localVarCall = readDirectoryCertificatesCall(uid, certificateEntryID, entryType, telematikID, professionOID, usage, _callback); + return localVarCall; + + } + + /** + * Zertifikat lesen Liefert alle Zertifikate, die dem Filter (siehe 'parameters') entsprechen. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (optional) + * @param certificateEntryID ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) + * Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. + * (optional) + * @param entryType Erlaubt die Suche mit Hilfe des Attributs entryType. (optional) + * @param telematikID telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. (optional) + * @param professionOID Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. (optional) + * @param usage Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im + * Attribut usage (array) des Zertifikatseintrags enthalten ist. (optional) + * @return List<UserCertificate> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public List<UserCertificate> readDirectoryCertificates(String uid, String certificateEntryID, String entryType, String telematikID, + String professionOID, String usage) throws ApiException { + ApiResponse<List<UserCertificate>> localVarResp = readDirectoryCertificatesWithHttpInfo(uid, certificateEntryID, entryType, telematikID, + professionOID, usage); + return localVarResp.getData(); + } + + /** + * Zertifikat lesen Liefert alle Zertifikate, die dem Filter (siehe 'parameters') entsprechen. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (optional) + * @param certificateEntryID ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) + * Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. + * (optional) + * @param entryType Erlaubt die Suche mit Hilfe des Attributs entryType. (optional) + * @param telematikID telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. (optional) + * @param professionOID Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. (optional) + * @param usage Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im + * Attribut usage (array) des Zertifikatseintrags enthalten ist. (optional) + * @return ApiResponse<List<UserCertificate>> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public ApiResponse<List<UserCertificate>> readDirectoryCertificatesWithHttpInfo(String uid, String certificateEntryID, String entryType, + String telematikID, String professionOID, String usage) throws ApiException { + okhttp3.Call localVarCall = readDirectoryCertificatesValidateBeforeCall(uid, certificateEntryID, entryType, telematikID, professionOID, usage, + null); + Type localVarReturnType = new TypeToken<List<UserCertificate>>() { + }.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); + } + + /** + * Zertifikat lesen (asynchronously) Liefert alle Zertifikate, die dem Filter (siehe 'parameters') entsprechen. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (optional) + * @param certificateEntryID ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) + * Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. + * (optional) + * @param entryType Erlaubt die Suche mit Hilfe des Attributs entryType. (optional) + * @param telematikID telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. (optional) + * @param professionOID Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. (optional) + * @param usage Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im + * Attribut usage (array) des Zertifikatseintrags enthalten ist. (optional) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call readDirectoryCertificatesAsync(String uid, String certificateEntryID, String entryType, String telematikID, + String professionOID, String usage, final ApiCallback<List<UserCertificate>> _callback) throws ApiException { + + okhttp3.Call localVarCall = readDirectoryCertificatesValidateBeforeCall(uid, certificateEntryID, entryType, telematikID, professionOID, usage, + _callback); + Type localVarReturnType = new TypeToken<List<UserCertificate>>() { + }.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApi.java b/src/main/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApi.java new file mode 100644 index 0000000..45e81ad --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApi.java @@ -0,0 +1,829 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.api; + +import com.google.gson.reflect.TypeToken; +import de.gematik.ti.epa.vzd.client.invoker.ApiCallback; +import de.gematik.ti.epa.vzd.client.invoker.ApiClient; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.invoker.Configuration; +import de.gematik.ti.epa.vzd.client.invoker.Pair; +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class DirectoryEntryAdministrationApi { + + private ApiClient localVarApiClient; + + public DirectoryEntryAdministrationApi() { + this(Configuration.getDefaultApiClient()); + } + + public DirectoryEntryAdministrationApi(ApiClient apiClient) { + this.localVarApiClient = apiClient; + } + + public ApiClient getApiClient() { + return localVarApiClient; + } + + public void setApiClient(ApiClient apiClient) { + this.localVarApiClient = apiClient; + } + + /** + * Build call for addDirectoryEntry + * + * @param createDirectoryEntry Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn. Leer/nicht vorhanden (wird vom + * Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht vorhanden (wird vom + * Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) displayName Kann optional belegt + * werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. localityName Kann optional + * belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt werden. organization Kann + * optional belegt werden. otherName Kann optional belegt werden. specialization Kann optional belegt werden. domainID + * Kann optional belegt werden. personalEntry Nicht vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority + * Nicht vorhanden (wird vom Verzeichnisdienst belegt) userCertificates Kann optional belegt werden. dn.uid Leer/nicht + * vorhanden (wird vom Verzeichnisdienst belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou + * Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst + * belegt) telematikID Kann optional belegt werden. Wird telematikID und userCertificate angegeben, dann muss diese + * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die + * Operation mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID + * Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Kann optional + * belegt werden (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird ein + * Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in dieser + * Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im Verzeichniseintrag + * gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden kann). (required) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call addDirectoryEntryCall(CreateDirectoryEntry createDirectoryEntry, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = createDirectoryEntry; + + // create path and map variables + String localVarPath = "/DirectoryEntries"; + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + @SuppressWarnings("rawtypes") + private okhttp3.Call addDirectoryEntryValidateBeforeCall(CreateDirectoryEntry createDirectoryEntry, final ApiCallback _callback) + throws ApiException { + + // verify the required parameter 'createDirectoryEntry' is set + if (createDirectoryEntry == null) { + throw new ApiException("Missing the required parameter 'createDirectoryEntry' when calling addDirectoryEntry(Async)"); + } + + okhttp3.Call localVarCall = addDirectoryEntryCall(createDirectoryEntry, _callback); + return localVarCall; + + } + + /** + * Einen Eintrag zum Verzeichnisdienst hinzufügen + * + * @param createDirectoryEntry Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn. Leer/nicht vorhanden (wird vom + * Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht vorhanden (wird vom + * Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) displayName Kann optional belegt + * werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. localityName Kann optional + * belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt werden. organization Kann + * optional belegt werden. otherName Kann optional belegt werden. specialization Kann optional belegt werden. domainID + * Kann optional belegt werden. personalEntry Nicht vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority + * Nicht vorhanden (wird vom Verzeichnisdienst belegt) userCertificates Kann optional belegt werden. dn.uid Leer/nicht + * vorhanden (wird vom Verzeichnisdienst belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou + * Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst + * belegt) telematikID Kann optional belegt werden. Wird telematikID und userCertificate angegeben, dann muss diese + * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die + * Operation mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID + * Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Kann optional + * belegt werden (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird ein + * Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in dieser + * Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im Verzeichniseintrag + * gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden kann). (required) + * @return DistinguishedName + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public DistinguishedName addDirectoryEntry(CreateDirectoryEntry createDirectoryEntry) throws ApiException { + ApiResponse<DistinguishedName> localVarResp = addDirectoryEntryWithHttpInfo(createDirectoryEntry); + return localVarResp.getData(); + } + + /** + * Einen Eintrag zum Verzeichnisdienst hinzufügen + * + * @param createDirectoryEntry Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn. Leer/nicht vorhanden (wird vom + * Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht vorhanden (wird vom + * Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) displayName Kann optional belegt + * werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. localityName Kann optional + * belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt werden. organization Kann + * optional belegt werden. otherName Kann optional belegt werden. specialization Kann optional belegt werden. domainID + * Kann optional belegt werden. personalEntry Nicht vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority + * Nicht vorhanden (wird vom Verzeichnisdienst belegt) userCertificates Kann optional belegt werden. dn.uid Leer/nicht + * vorhanden (wird vom Verzeichnisdienst belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou + * Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst + * belegt) telematikID Kann optional belegt werden. Wird telematikID und userCertificate angegeben, dann muss diese + * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die + * Operation mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID + * Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Kann optional + * belegt werden (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird ein + * Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in dieser + * Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im Verzeichniseintrag + * gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden kann). (required) + * @return ApiResponse<DistinguishedName> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public ApiResponse<DistinguishedName> addDirectoryEntryWithHttpInfo(CreateDirectoryEntry createDirectoryEntry) throws ApiException { + okhttp3.Call localVarCall = addDirectoryEntryValidateBeforeCall(createDirectoryEntry, null); + Type localVarReturnType = new TypeToken<DistinguishedName>() { + }.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); + } + + /** + * Einen Eintrag zum Verzeichnisdienst hinzufügen (asynchronously) + * + * @param createDirectoryEntry Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn. Leer/nicht vorhanden (wird vom + * Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht vorhanden (wird vom + * Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) displayName Kann optional belegt + * werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. localityName Kann optional + * belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt werden. organization Kann + * optional belegt werden. otherName Kann optional belegt werden. specialization Kann optional belegt werden. domainID + * Kann optional belegt werden. personalEntry Nicht vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority + * Nicht vorhanden (wird vom Verzeichnisdienst belegt) userCertificates Kann optional belegt werden. dn.uid Leer/nicht + * vorhanden (wird vom Verzeichnisdienst belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou + * Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst + * belegt) telematikID Kann optional belegt werden. Wird telematikID und userCertificate angegeben, dann muss diese + * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die + * Operation mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID + * Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Kann optional + * belegt werden (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird ein + * Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in dieser + * Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im Verzeichniseintrag + * gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden kann). (required) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call addDirectoryEntryAsync(CreateDirectoryEntry createDirectoryEntry, final ApiCallback<DistinguishedName> _callback) + throws ApiException { + + okhttp3.Call localVarCall = addDirectoryEntryValidateBeforeCall(createDirectoryEntry, _callback); + Type localVarReturnType = new TypeToken<DistinguishedName>() { + }.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; + } + + /** + * Build call for deleteDirectoryEntry + * + * @param uid ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate + * und Fachdaten. (required) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call deleteDirectoryEntryCall(String uid, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "DELETE", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + @SuppressWarnings("rawtypes") + private okhttp3.Call deleteDirectoryEntryValidateBeforeCall(String uid, final ApiCallback _callback) throws ApiException { + + // verify the required parameter 'uid' is set + if (uid == null) { + throw new ApiException("Missing the required parameter 'uid' when calling deleteDirectoryEntry(Async)"); + } + + okhttp3.Call localVarCall = deleteDirectoryEntryCall(uid, _callback); + return localVarCall; + + } + + /** + * Gesamten Verzeichniseintrag löschen + * + * @param uid ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate und + * Fachdaten. (required) + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public void deleteDirectoryEntry(String uid) throws ApiException { + deleteDirectoryEntryWithHttpInfo(uid); + } + + /** + * Gesamten Verzeichniseintrag löschen + * + * @param uid ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate und + * Fachdaten. (required) + * @return ApiResponse<Void> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public ApiResponse<Void> deleteDirectoryEntryWithHttpInfo(String uid) throws ApiException { + okhttp3.Call localVarCall = deleteDirectoryEntryValidateBeforeCall(uid, null); + return localVarApiClient.execute(localVarCall); + } + + /** + * Gesamten Verzeichniseintrag löschen (asynchronously) + * + * @param uid ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate + * und Fachdaten. (required) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call deleteDirectoryEntryAsync(String uid, final ApiCallback<Void> _callback) throws ApiException { + + okhttp3.Call localVarCall = deleteDirectoryEntryValidateBeforeCall(uid, _callback); + localVarApiClient.executeAsync(localVarCall, _callback); + return localVarCall; + } + + /** + * Build call for modifyDirectoryEntry + * + * @param uid ID von dem Verzeichniseintrag (required) + * @param baseDirectoryEntry Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn. Nicht vorhanden + * (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn Nicht vorhanden. displayName + * Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. + * localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt + * werden. organization Kann optional belegt werden. otherName Nicht vorhanden. specialization Kann optional belegt + * werden. domainID Kann optional belegt werden. personalEntry Nicht vorhanden. dataFromAuthority Nicht vorhanden + * (required) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call modifyDirectoryEntryCall(String uid, BaseDirectoryEntry baseDirectoryEntry, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = baseDirectoryEntry; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/baseDirectoryEntries" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "PUT", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + @SuppressWarnings("rawtypes") + private okhttp3.Call modifyDirectoryEntryValidateBeforeCall(String uid, BaseDirectoryEntry baseDirectoryEntry, final ApiCallback _callback) + throws ApiException { + + // verify the required parameter 'uid' is set + if (uid == null) { + throw new ApiException("Missing the required parameter 'uid' when calling modifyDirectoryEntry(Async)"); + } + + // verify the required parameter 'baseDirectoryEntry' is set + if (baseDirectoryEntry == null) { + throw new ApiException("Missing the required parameter 'baseDirectoryEntry' when calling modifyDirectoryEntry(Async)"); + } + + okhttp3.Call localVarCall = modifyDirectoryEntryCall(uid, baseDirectoryEntry, _callback); + return localVarCall; + + } + + /** + * Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. + * + * @param uid ID von dem Verzeichniseintrag (required) + * @param baseDirectoryEntry Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn. Nicht vorhanden + * (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn Nicht vorhanden. displayName + * Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. + * localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt + * werden. organization Kann optional belegt werden. otherName Nicht vorhanden. specialization Kann optional belegt + * werden. domainID Kann optional belegt werden. personalEntry Nicht vorhanden. dataFromAuthority Nicht vorhanden + * (required) + * @return DistinguishedName + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * </table> + */ + public DistinguishedName modifyDirectoryEntry(String uid, BaseDirectoryEntry baseDirectoryEntry) throws ApiException { + ApiResponse<DistinguishedName> localVarResp = modifyDirectoryEntryWithHttpInfo(uid, baseDirectoryEntry); + return localVarResp.getData(); + } + + /** + * Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. + * + * @param uid ID von dem Verzeichniseintrag (required) + * @param baseDirectoryEntry Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn. Nicht vorhanden + * (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn Nicht vorhanden. displayName + * Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. + * localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt + * werden. organization Kann optional belegt werden. otherName Nicht vorhanden. specialization Kann optional belegt + * werden. domainID Kann optional belegt werden. personalEntry Nicht vorhanden. dataFromAuthority Nicht vorhanden + * (required) + * @return ApiResponse<DistinguishedName> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * </table> + */ + public ApiResponse<DistinguishedName> modifyDirectoryEntryWithHttpInfo(String uid, BaseDirectoryEntry baseDirectoryEntry) throws ApiException { + okhttp3.Call localVarCall = modifyDirectoryEntryValidateBeforeCall(uid, baseDirectoryEntry, null); + Type localVarReturnType = new TypeToken<DistinguishedName>() { + }.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); + } + + /** + * Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. (asynchronously) + * + * @param uid ID von dem Verzeichniseintrag (required) + * @param baseDirectoryEntry Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn. Nicht vorhanden + * (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn Nicht vorhanden. displayName + * Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. + * localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt + * werden. organization Kann optional belegt werden. otherName Nicht vorhanden. specialization Kann optional belegt + * werden. domainID Kann optional belegt werden. personalEntry Nicht vorhanden. dataFromAuthority Nicht vorhanden + * (required) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call modifyDirectoryEntryAsync(String uid, BaseDirectoryEntry baseDirectoryEntry, final ApiCallback<DistinguishedName> _callback) + throws ApiException { + + okhttp3.Call localVarCall = modifyDirectoryEntryValidateBeforeCall(uid, baseDirectoryEntry, _callback); + Type localVarReturnType = new TypeToken<DistinguishedName>() { + }.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; + } + + /** + * Build call for readDirectoryEntry + * + * @param uid ID von dem Verzeichniseintrag (distinguishedName.uid) (optional) + * @param givenName Erlaubt die Suche mit Hilfe des Attributs givenName. (optional) + * @param sn Erlaubt die Suche mit Hilfe des Attributs sn. (optional) + * @param cn Erlaubt die Suche mit Hilfe des Attributs cn. (optional) + * @param displayName Erlaubt die Suche mit Hilfe des Attributs displayName. (optional) + * @param streetAddress Erlaubt die Suche mit Hilfe des Attributs streetAddress. (optional) + * @param postalCode Erlaubt die Suche mit Hilfe des Attributs postalCode. (optional) + * @param localityName Erlaubt die Suche mit Hilfe des Attributs localityName. (optional) + * @param stateOrProvinceName Erlaubt die Suche mit Hilfe des Attributs stateOrProvinceName. (optional) + * @param title Erlaubt die Suche mit Hilfe des Attributs title. (optional) + * @param organization Erlaubt die Suche mit Hilfe des Attributs organization. (optional) + * @param otherName Erlaubt die Suche mit Hilfe des Attributs otherName. (optional) + * @param specialization Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param domainID Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param personalEntry Erlaubt die Suche mit Hilfe des Attributs personalEntry. (optional) + * @param dataFromAuthority Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. (optional) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call readDirectoryEntryCall(String uid, String givenName, String sn, String cn, String displayName, String streetAddress, + String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, String specialization, + String domainID, String personalEntry, String dataFromAuthority, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries"; + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + if (uid != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("uid", uid)); + } + + if (givenName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("givenName", givenName)); + } + + if (sn != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("sn", sn)); + } + + if (cn != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("cn", cn)); + } + + if (displayName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("displayName", displayName)); + } + + if (streetAddress != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("streetAddress", streetAddress)); + } + + if (postalCode != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("postalCode", postalCode)); + } + + if (localityName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("localityName", localityName)); + } + + if (stateOrProvinceName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("stateOrProvinceName", stateOrProvinceName)); + } + + if (title != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("title", title)); + } + + if (organization != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("organization", organization)); + } + + if (otherName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("otherName", otherName)); + } + + if (specialization != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("specialization", specialization)); + } + + if (domainID != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("domainID", domainID)); + } + + if (personalEntry != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("personalEntry", personalEntry)); + } + + if (dataFromAuthority != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("dataFromAuthority", dataFromAuthority)); + } + + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + @SuppressWarnings("rawtypes") + private okhttp3.Call readDirectoryEntryValidateBeforeCall(String uid, String givenName, String sn, String cn, String displayName, + String streetAddress, String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, + String specialization, String domainID, String personalEntry, String dataFromAuthority, final ApiCallback _callback) throws ApiException { + + okhttp3.Call localVarCall = readDirectoryEntryCall(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, + stateOrProvinceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority, _callback); + return localVarCall; + + } + + /** + * Gesamten Verzeichniseintrag lesen Liefert alle zum Filter passenden Verzeichniseinträge. Die angegebenen Parameter werden mit logischen UND + * verknüpft. + * + * @param uid ID von dem Verzeichniseintrag (distinguishedName.uid) (optional) + * @param givenName Erlaubt die Suche mit Hilfe des Attributs givenName. (optional) + * @param sn Erlaubt die Suche mit Hilfe des Attributs sn. (optional) + * @param cn Erlaubt die Suche mit Hilfe des Attributs cn. (optional) + * @param displayName Erlaubt die Suche mit Hilfe des Attributs displayName. (optional) + * @param streetAddress Erlaubt die Suche mit Hilfe des Attributs streetAddress. (optional) + * @param postalCode Erlaubt die Suche mit Hilfe des Attributs postalCode. (optional) + * @param localityName Erlaubt die Suche mit Hilfe des Attributs localityName. (optional) + * @param stateOrProvinceName Erlaubt die Suche mit Hilfe des Attributs stateOrProvinceName. (optional) + * @param title Erlaubt die Suche mit Hilfe des Attributs title. (optional) + * @param organization Erlaubt die Suche mit Hilfe des Attributs organization. (optional) + * @param otherName Erlaubt die Suche mit Hilfe des Attributs otherName. (optional) + * @param specialization Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param domainID Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param personalEntry Erlaubt die Suche mit Hilfe des Attributs personalEntry. (optional) + * @param dataFromAuthority Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. (optional) + * @return List<DirectoryEntry> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public List<DirectoryEntry> readDirectoryEntry(String uid, String givenName, String sn, String cn, String displayName, String streetAddress, + String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, String specialization, + String domainID, String personalEntry, String dataFromAuthority) throws ApiException { + ApiResponse<List<DirectoryEntry>> localVarResp = readDirectoryEntryWithHttpInfo(uid, givenName, sn, cn, displayName, streetAddress, postalCode, + localityName, stateOrProvinceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority); + return localVarResp.getData(); + } + + /** + * Gesamten Verzeichniseintrag lesen Liefert alle zum Filter passenden Verzeichniseinträge. Die angegebenen Parameter werden mit logischen UND + * verknüpft. + * + * @param uid ID von dem Verzeichniseintrag (distinguishedName.uid) (optional) + * @param givenName Erlaubt die Suche mit Hilfe des Attributs givenName. (optional) + * @param sn Erlaubt die Suche mit Hilfe des Attributs sn. (optional) + * @param cn Erlaubt die Suche mit Hilfe des Attributs cn. (optional) + * @param displayName Erlaubt die Suche mit Hilfe des Attributs displayName. (optional) + * @param streetAddress Erlaubt die Suche mit Hilfe des Attributs streetAddress. (optional) + * @param postalCode Erlaubt die Suche mit Hilfe des Attributs postalCode. (optional) + * @param localityName Erlaubt die Suche mit Hilfe des Attributs localityName. (optional) + * @param stateOrProvinceName Erlaubt die Suche mit Hilfe des Attributs stateOrProvinceName. (optional) + * @param title Erlaubt die Suche mit Hilfe des Attributs title. (optional) + * @param organization Erlaubt die Suche mit Hilfe des Attributs organization. (optional) + * @param otherName Erlaubt die Suche mit Hilfe des Attributs otherName. (optional) + * @param specialization Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param domainID Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param personalEntry Erlaubt die Suche mit Hilfe des Attributs personalEntry. (optional) + * @param dataFromAuthority Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. (optional) + * @return ApiResponse<List<DirectoryEntry>> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public ApiResponse<List<DirectoryEntry>> readDirectoryEntryWithHttpInfo(String uid, String givenName, String sn, String cn, String displayName, + String streetAddress, String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, + String specialization, String domainID, String personalEntry, String dataFromAuthority) throws ApiException { + okhttp3.Call localVarCall = readDirectoryEntryValidateBeforeCall(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, + stateOrProvinceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority, null); + Type localVarReturnType = new TypeToken<List<DirectoryEntry>>() { + }.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); + } + + /** + * Gesamten Verzeichniseintrag lesen (asynchronously) Liefert alle zum Filter passenden Verzeichniseinträge. Die angegebenen Parameter werden mit + * logischen UND verknüpft. + * + * @param uid ID von dem Verzeichniseintrag (distinguishedName.uid) (optional) + * @param givenName Erlaubt die Suche mit Hilfe des Attributs givenName. (optional) + * @param sn Erlaubt die Suche mit Hilfe des Attributs sn. (optional) + * @param cn Erlaubt die Suche mit Hilfe des Attributs cn. (optional) + * @param displayName Erlaubt die Suche mit Hilfe des Attributs displayName. (optional) + * @param streetAddress Erlaubt die Suche mit Hilfe des Attributs streetAddress. (optional) + * @param postalCode Erlaubt die Suche mit Hilfe des Attributs postalCode. (optional) + * @param localityName Erlaubt die Suche mit Hilfe des Attributs localityName. (optional) + * @param stateOrProvinceName Erlaubt die Suche mit Hilfe des Attributs stateOrProvinceName. (optional) + * @param title Erlaubt die Suche mit Hilfe des Attributs title. (optional) + * @param organization Erlaubt die Suche mit Hilfe des Attributs organization. (optional) + * @param otherName Erlaubt die Suche mit Hilfe des Attributs otherName. (optional) + * @param specialization Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param domainID Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param personalEntry Erlaubt die Suche mit Hilfe des Attributs personalEntry. (optional) + * @param dataFromAuthority Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. (optional) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call readDirectoryEntryAsync(String uid, String givenName, String sn, String cn, String displayName, String streetAddress, + String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, String specialization, + String domainID, String personalEntry, String dataFromAuthority, final ApiCallback<List<DirectoryEntry>> _callback) throws ApiException { + + okhttp3.Call localVarCall = readDirectoryEntryValidateBeforeCall(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, + stateOrProvinceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority, _callback); + Type localVarReturnType = new TypeToken<List<DirectoryEntry>>() { + }.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiCallback.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiCallback.java new file mode 100644 index 0000000..b513c74 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiCallback.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker; + +import java.util.List; +import java.util.Map; + +/** + * Callback for asynchronous API call. + * + * @param <T> The return type + */ +public interface ApiCallback<T> { + + /** + * This is called when the API call fails. + * + * @param e The exception causing the failure + * @param statusCode Status code of the response if available, otherwise it would be 0 + * @param responseHeaders Headers of the response if available, otherwise it would be null + */ + void onFailure(ApiException e, int statusCode, Map<String, List<String>> responseHeaders); + + /** + * This is called when the API call succeeded. + * + * @param result The result deserialized from response + * @param statusCode Status code of the response + * @param responseHeaders Headers of the response + */ + void onSuccess(T result, int statusCode, Map<String, List<String>> responseHeaders); + + /** + * This is called when the API upload processing. + * + * @param bytesWritten bytes Written + * @param contentLength content length of request body + * @param done write end + */ + void onUploadProgress(long bytesWritten, long contentLength, boolean done); + + /** + * This is called when the API downlond processing. + * + * @param bytesRead bytes Read + * @param contentLength content lenngth of the response + * @param done Read end + */ + void onDownloadProgress(long bytesRead, long contentLength, boolean done); +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiClient.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiClient.java new file mode 100644 index 0000000..65363ce --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiClient.java @@ -0,0 +1,1384 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker; + +import de.gematik.ti.epa.vzd.client.invoker.auth.ApiKeyAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.Authentication; +import de.gematik.ti.epa.vzd.client.invoker.auth.HttpBasicAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.OAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.OAuthFlow; +import de.gematik.ti.epa.vzd.client.invoker.auth.RetryingOAuth; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Type; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.text.DateFormat; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Headers; +import okhttp3.Interceptor; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.internal.http.HttpMethod; +import okhttp3.internal.tls.OkHostnameVerifier; +import okhttp3.logging.HttpLoggingInterceptor; +import okhttp3.logging.HttpLoggingInterceptor.Level; +import okio.BufferedSink; +import okio.Okio; +import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder; + +public class ApiClient { + + private String basePath = "https://to.be.defined"; + private boolean debugging = false; + private final Map<String, String> defaultHeaderMap = new HashMap<String, String>(); + private final Map<String, String> defaultCookieMap = new HashMap<String, String>(); + private String tempFolderPath = null; + + private Map<String, Authentication> authentications; + + private DateFormat dateFormat; + private DateFormat datetimeFormat; + private boolean lenientDatetimeFormat; + private int dateLength; + + private InputStream sslCaCert; + private boolean verifyingSsl; + private KeyManager[] keyManagers; + + private OkHttpClient httpClient; + private JSON json; + + private HttpLoggingInterceptor loggingInterceptor; + + /* + * Basic constructor for ApiClient + */ + public ApiClient() { + init(); + + // Setup authentications (key: authentication name, value: authentication). + authentications.put("OAuth2", new OAuth()); + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID + */ + public ApiClient(String clientId) { + this(clientId, null, null); + } + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID and additional parameters + */ + public ApiClient(String clientId, Map<String, String> parameters) { + this(clientId, null, parameters); + } + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID, secret, and additional parameters + */ + public ApiClient(String clientId, String clientSecret, Map<String, String> parameters) { + init(); + + RetryingOAuth retryingOAuth = new RetryingOAuth("https://to.be.defined/oauth/token", clientId, OAuthFlow.application, clientSecret, parameters); + authentications.put( + "OAuth2", + retryingOAuth + ); + httpClient.interceptors().add(retryingOAuth); + + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + private void init() { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.addNetworkInterceptor(getProgressInterceptor()); + httpClient = builder.build(); + + verifyingSsl = true; + + json = new JSON(); + + // Set default User-Agent. + setUserAgent("OpenAPI-Generator/1.1.1/java"); + + authentications = new HashMap<String, Authentication>(); + } + + /** + * Get base path + * + * @return Base path + */ + public String getBasePath() { + return basePath; + } + + /** + * Set base path + * + * @param basePath Base path of the URL (e.g https://to.be.defined + * @return An instance of OkHttpClient + */ + public ApiClient setBasePath(String basePath) { + this.basePath = basePath; + return this; + } + + /** + * Get HTTP client + * + * @return An instance of OkHttpClient + */ + public OkHttpClient getHttpClient() { + return httpClient; + } + + /** + * Set HTTP client, which must never be null. + * + * @param newHttpClient An instance of OkHttpClient + * @return Api Client + * @throws NullPointerException when newHttpClient is null + */ + public ApiClient setHttpClient(OkHttpClient newHttpClient) { + this.httpClient = Objects.requireNonNull(newHttpClient, "HttpClient must not be null!"); + return this; + } + + /** + * Get JSON + * + * @return JSON object + */ + public JSON getJSON() { + return json; + } + + /** + * Set JSON + * + * @param json JSON object + * @return Api client + */ + public ApiClient setJSON(JSON json) { + this.json = json; + return this; + } + + /** + * True if isVerifyingSsl flag is on + * + * @return True if isVerifySsl flag is on + */ + public boolean isVerifyingSsl() { + return verifyingSsl; + } + + /** + * Configure whether to verify certificate and hostname when making https requests. Default to true. NOTE: Do NOT set to false in production code, + * otherwise you would face multiple types of cryptographic attacks. + * + * @param verifyingSsl True to verify TLS/SSL connection + * @return ApiClient + */ + public ApiClient setVerifyingSsl(boolean verifyingSsl) { + this.verifyingSsl = verifyingSsl; + applySslSettings(); + return this; + } + + /** + * Get SSL CA cert. + * + * @return Input stream to the SSL CA cert + */ + public InputStream getSslCaCert() { + return sslCaCert; + } + + /** + * Configure the CA certificate to be trusted when making https requests. Use null to reset to default. + * + * @param sslCaCert input stream for SSL CA cert + * @return ApiClient + */ + public ApiClient setSslCaCert(InputStream sslCaCert) { + this.sslCaCert = sslCaCert; + applySslSettings(); + return this; + } + + public KeyManager[] getKeyManagers() { + return keyManagers; + } + + /** + * Configure client keys to use for authorization in an SSL session. Use null to reset to default. + * + * @param managers The KeyManagers to use + * @return ApiClient + */ + public ApiClient setKeyManagers(KeyManager[] managers) { + this.keyManagers = managers; + applySslSettings(); + return this; + } + + public DateFormat getDateFormat() { + return dateFormat; + } + + public ApiClient setDateFormat(DateFormat dateFormat) { + this.json.setDateFormat(dateFormat); + return this; + } + + public ApiClient setSqlDateFormat(DateFormat dateFormat) { + this.json.setSqlDateFormat(dateFormat); + return this; + } + + public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + this.json.setOffsetDateTimeFormat(dateFormat); + return this; + } + + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + this.json.setLocalDateFormat(dateFormat); + return this; + } + + public ApiClient setLenientOnJson(boolean lenientOnJson) { + this.json.setLenientOnJson(lenientOnJson); + return this; + } + + /** + * Get authentications (key: authentication name, value: authentication). + * + * @return Map of authentication objects + */ + public Map<String, Authentication> getAuthentications() { + return authentications; + } + + /** + * Get authentication for the given name. + * + * @param authName The authentication name + * @return The authentication, null if not found + */ + public Authentication getAuthentication(String authName) { + return authentications.get(authName); + } + + /** + * Helper method to set username for the first HTTP basic authentication. + * + * @param username Username + */ + public void setUsername(String username) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setUsername(username); + return; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set password for the first HTTP basic authentication. + * + * @param password Password + */ + public void setPassword(String password) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setPassword(password); + return; + } + } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set API key value for the first API key authentication. + * + * @param apiKey API key + */ + public void setApiKey(String apiKey) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKey(apiKey); + return; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set API key prefix for the first API key authentication. + * + * @param apiKeyPrefix API key prefix + */ + public void setApiKeyPrefix(String apiKeyPrefix) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); + return; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set access token for the first OAuth2 authentication. + * + * @param accessToken Access token + */ + public void setAccessToken(String accessToken) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setAccessToken(accessToken); + return; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Set the User-Agent header's value (by adding to the default header map). + * + * @param userAgent HTTP request's user agent + * @return ApiClient + */ + public ApiClient setUserAgent(String userAgent) { + addDefaultHeader("User-Agent", userAgent); + return this; + } + + /** + * Add a default header. + * + * @param key The header's key + * @param value The header's value + * @return ApiClient + */ + public ApiClient addDefaultHeader(String key, String value) { + defaultHeaderMap.put(key, value); + return this; + } + + /** + * Add a default cookie. + * + * @param key The cookie's key + * @param value The cookie's value + * @return ApiClient + */ + public ApiClient addDefaultCookie(String key, String value) { + defaultCookieMap.put(key, value); + return this; + } + + /** + * Check that whether debugging is enabled for this API client. + * + * @return True if debugging is enabled, false otherwise. + */ + public boolean isDebugging() { + return debugging; + } + + /** + * Enable/disable debugging for this API client. + * + * @param debugging To enable (true) or disable (false) debugging + * @return ApiClient + */ + public ApiClient setDebugging(boolean debugging) { + if (debugging != this.debugging) { + if (debugging) { + loggingInterceptor = new HttpLoggingInterceptor(); + loggingInterceptor.setLevel(Level.BODY); + httpClient = httpClient.newBuilder().addInterceptor(loggingInterceptor).build(); + } else { + httpClient.interceptors().remove(loggingInterceptor); + loggingInterceptor = null; + } + } + this.debugging = debugging; + return this; + } + + /** + * The path of temporary folder used to store downloaded files from endpoints with file response. The default value is <code>null</code>, i.e. using + * the system's default tempopary folder. + * + * @return Temporary folder path + * @see <a href="https://docs.oracle.com/javase/7/docs/api/java/io/File.html#createTempFile">createTempFile</a> + */ + public String getTempFolderPath() { + return tempFolderPath; + } + + /** + * Set the temporary folder path (for downloading files) + * + * @param tempFolderPath Temporary folder path + * @return ApiClient + */ + public ApiClient setTempFolderPath(String tempFolderPath) { + this.tempFolderPath = tempFolderPath; + return this; + } + + /** + * Get connection timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getConnectTimeout() { + return httpClient.connectTimeoutMillis(); + } + + /** + * Sets the connect timeout (in milliseconds). A value of 0 means no timeout, otherwise values must be between 1 and {@link Integer#MAX_VALUE}. + * + * @param connectionTimeout connection timeout in milliseconds + * @return Api client + */ + public ApiClient setConnectTimeout(int connectionTimeout) { + httpClient = httpClient.newBuilder().connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS).build(); + return this; + } + + /** + * Get read timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getReadTimeout() { + return httpClient.readTimeoutMillis(); + } + + /** + * Sets the read timeout (in milliseconds). A value of 0 means no timeout, otherwise values must be between 1 and {@link Integer#MAX_VALUE}. + * + * @param readTimeout read timeout in milliseconds + * @return Api client + */ + public ApiClient setReadTimeout(int readTimeout) { + httpClient = httpClient.newBuilder().readTimeout(readTimeout, TimeUnit.MILLISECONDS).build(); + return this; + } + + /** + * Get write timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getWriteTimeout() { + return httpClient.writeTimeoutMillis(); + } + + /** + * Sets the write timeout (in milliseconds). A value of 0 means no timeout, otherwise values must be between 1 and {@link Integer#MAX_VALUE}. + * + * @param writeTimeout connection timeout in milliseconds + * @return Api client + */ + public ApiClient setWriteTimeout(int writeTimeout) { + httpClient = httpClient.newBuilder().writeTimeout(writeTimeout, TimeUnit.MILLISECONDS).build(); + return this; + } + + /** + * Helper method to configure the token endpoint of the first oauth found in the apiAuthorizations (there should be only one) + * + * @return Token request builder + */ + public TokenRequestBuilder getTokenEndPoint() { + for (Authentication apiAuth : authentications.values()) { + if (apiAuth instanceof RetryingOAuth) { + RetryingOAuth retryingOAuth = (RetryingOAuth) apiAuth; + return retryingOAuth.getTokenRequestBuilder(); + } + } + return null; + } + + /** + * Format the given parameter object into string. + * + * @param param Parameter + * @return String representation of the parameter + */ + public String parameterToString(Object param) { + if (param == null) { + return ""; + } else if (param instanceof Date || param instanceof OffsetDateTime || param instanceof LocalDate) { + //Serialize to json string and remove the " enclosing characters + String jsonStr = json.serialize(param); + return jsonStr.substring(1, jsonStr.length() - 1); + } else if (param instanceof Collection) { + StringBuilder b = new StringBuilder(); + for (Object o : (Collection) param) { + if (b.length() > 0) { + b.append(","); + } + b.append(o); + } + return b.toString(); + } else { + return String.valueOf(param); + } + } + + /** + * Formats the specified query parameter to a list containing a single {@code Pair} object. + * <p> + * Note that {@code value} must not be a collection. + * + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list containing a single {@code Pair} object. + */ + public List<Pair> parameterToPair(String name, Object value) { + List<Pair> params = new ArrayList<Pair>(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value instanceof Collection) { + return params; + } + + params.add(new Pair(name, parameterToString(value))); + return params; + } + + /** + * Formats the specified collection query parameters to a list of {@code Pair} objects. + * <p> + * Note that the values of each of the returned Pair objects are percent-encoded. + * + * @param collectionFormat The collection format of the parameter. + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list of {@code Pair} objects. + */ + public List<Pair> parameterToPairs(String collectionFormat, String name, Collection value) { + List<Pair> params = new ArrayList<Pair>(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value.isEmpty()) { + return params; + } + + // create the params based on the collection format + if ("multi".equals(collectionFormat)) { + for (Object item : value) { + params.add(new Pair(name, escapeString(parameterToString(item)))); + } + return params; + } + + // collectionFormat is assumed to be "csv" by default + String delimiter = ","; + + // escape all delimiters except commas, which are URI reserved + // characters + if ("ssv".equals(collectionFormat)) { + delimiter = escapeString(" "); + } else if ("tsv".equals(collectionFormat)) { + delimiter = escapeString("\t"); + } else if ("pipes".equals(collectionFormat)) { + delimiter = escapeString("|"); + } + + StringBuilder sb = new StringBuilder(); + for (Object item : value) { + sb.append(delimiter); + sb.append(escapeString(parameterToString(item))); + } + + params.add(new Pair(name, sb.substring(delimiter.length()))); + + return params; + } + + /** + * Formats the specified collection path parameter to a string value. + * + * @param collectionFormat The collection format of the parameter. + * @param value The value of the parameter. + * @return String representation of the parameter + */ + public String collectionPathParameterToString(String collectionFormat, Collection value) { + // create the value based on the collection format + if ("multi".equals(collectionFormat)) { + // not valid for path params + return parameterToString(value); + } + + // collectionFormat is assumed to be "csv" by default + String delimiter = ","; + + if ("ssv".equals(collectionFormat)) { + delimiter = " "; + } else if ("tsv".equals(collectionFormat)) { + delimiter = "\t"; + } else if ("pipes".equals(collectionFormat)) { + delimiter = "|"; + } + + StringBuilder sb = new StringBuilder(); + for (Object item : value) { + sb.append(delimiter); + sb.append(parameterToString(item)); + } + + return sb.substring(delimiter.length()); + } + + /** + * Sanitize filename by removing path. e.g. ../../sun.gif becomes sun.gif + * + * @param filename The filename to be sanitized + * @return The sanitized filename + */ + public String sanitizeFilename(String filename) { + return filename.replaceAll(".*[/\\\\]", ""); + } + + /** + * Check if the given MIME is a JSON MIME. JSON MIME examples: application/json application/json; charset=UTF8 APPLICATION/JSON + * application/vnd.company+json "* / *" is also default to JSON + * + * @param mime MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public boolean isJsonMime(String mime) { + String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; + return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); + } + + /** + * Select the Accept header's value from the given accepts array: if JSON exists in the given array, use it; otherwise use all of them (joining into + * a string) + * + * @param accepts The accepts array to select from + * @return The Accept header to use. If the given array is empty, null will be returned (not to set the Accept header explicitly). + */ + public String selectHeaderAccept(String[] accepts) { + if (accepts.length == 0) { + return null; + } + for (String accept : accepts) { + if (isJsonMime(accept)) { + return accept; + } + } + return StringUtil.join(accepts, ","); + } + + /** + * Select the Content-Type header's value from the given array: if JSON exists in the given array, use it; otherwise use the first one of the + * array. + * + * @param contentTypes The Content-Type array to select from + * @return The Content-Type header to use. If the given array is empty, or matches "any", JSON will be used. + */ + public String selectHeaderContentType(String[] contentTypes) { + if (contentTypes.length == 0 || contentTypes[0].equals("*/*")) { + return "application/json"; + } + for (String contentType : contentTypes) { + if (isJsonMime(contentType)) { + return contentType; + } + } + return contentTypes[0]; + } + + /** + * Escape the given string to be used as URL query value. + * + * @param str String to be escaped + * @return Escaped string + */ + public String escapeString(String str) { + return URLEncoder.encode(str, StandardCharsets.UTF_8).replaceAll("\\+", "%20"); + } + + /** + * Deserialize response body to Java object, according to the return type and the Content-Type response header. + * + * @param <T> Type + * @param response HTTP response + * @param returnType The type of the Java object + * @return The deserialized Java object + * @throws ApiException If fail to deserialize response body, i.e. cannot read response body or the Content-Type of the response is not supported. + */ + @SuppressWarnings("unchecked") + public <T> T deserialize(Response response, Type returnType) throws ApiException { + if (response == null || returnType == null) { + return null; + } + + if ("byte[]".equals(returnType.toString())) { + // Handle binary response (byte array). + try { + return (T) response.body().bytes(); + } catch (IOException e) { + throw new ApiException(e); + } + } else if (returnType.equals(File.class)) { + // Handle file downloading. + return (T) downloadFileFromResponse(response); + } + + String respBody; + try { + if (response.body() != null) { + respBody = response.body().string(); + } else { + respBody = null; + } + } catch (IOException e) { + throw new ApiException(e); + } + + if (respBody == null || "".equals(respBody)) { + return null; + } + + String contentType = response.headers().get("Content-Type"); + if (contentType == null) { + // ensuring a default content type + contentType = "application/json"; + } + if (isJsonMime(contentType)) { + return json.deserialize(respBody, returnType); + } else if (returnType.equals(String.class)) { + // Expecting string, return the raw response body. + return (T) respBody; + } else { + throw new ApiException( + "Content type \"" + contentType + "\" is not supported for type: " + returnType, + response.code(), + response.headers().toMultimap(), + respBody); + } + } + + /** + * Serialize the given Java object into request body according to the object's class and the request Content-Type. + * + * @param obj The Java object + * @param contentType The request Content-Type + * @return The serialized request body + * @throws ApiException If fail to serialize the given object + */ + public RequestBody serialize(Object obj, String contentType) throws ApiException { + if (obj instanceof byte[]) { + // Binary (byte array) body parameter support. + return RequestBody.create(MediaType.parse(contentType), (byte[]) obj); + } else if (obj instanceof File) { + // File body parameter support. + return RequestBody.create(MediaType.parse(contentType), (File) obj); + } else if (isJsonMime(contentType)) { + String content; + if (obj != null) { + content = json.serialize(obj); + } else { + content = null; + } + return RequestBody.create(MediaType.parse(contentType), content); + } else { + throw new ApiException("Content type \"" + contentType + "\" is not supported"); + } + } + + /** + * Download file from the given response. + * + * @param response An instance of the Response object + * @return Downloaded file + * @throws ApiException If fail to read file content from response and write to disk + */ + public File downloadFileFromResponse(Response response) throws ApiException { + try { + File file = prepareDownloadFile(response); + BufferedSink sink = Okio.buffer(Okio.sink(file)); + sink.writeAll(response.body().source()); + sink.close(); + return file; + } catch (IOException e) { + throw new ApiException(e); + } + } + + /** + * Prepare file for download + * + * @param response An instance of the Response object + * @return Prepared file for the download + * @throws IOException If fail to prepare file for download + */ + public File prepareDownloadFile(Response response) throws IOException { + String filename = null; + String contentDisposition = response.header("Content-Disposition"); + if (contentDisposition != null && !"".equals(contentDisposition)) { + // Get filename from the Content-Disposition header. + Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?"); + Matcher matcher = pattern.matcher(contentDisposition); + if (matcher.find()) { + filename = sanitizeFilename(matcher.group(1)); + } + } + + String prefix = null; + String suffix = null; + if (filename == null) { + prefix = "download-"; + suffix = ""; + } else { + int pos = filename.lastIndexOf("."); + if (pos == -1) { + prefix = filename + "-"; + } else { + prefix = filename.substring(0, pos) + "-"; + suffix = filename.substring(pos); + } + // File.createTempFile requires the prefix to be at least three characters long + if (prefix.length() < 3) { + prefix = "download-"; + } + } + + if (tempFolderPath == null) { + return File.createTempFile(prefix, suffix); + } else { + return File.createTempFile(prefix, suffix, new File(tempFolderPath)); + } + } + + /** + * {@link #execute(Call, Type)} + * + * @param <T> Type + * @param call An instance of the Call object + * @return ApiResponse<T> + * @throws ApiException If fail to execute the call + */ + public <T> ApiResponse<T> execute(Call call) throws ApiException { + return execute(call, null); + } + + /** + * Execute HTTP call and deserialize the HTTP response body into the given return type. + * + * @param returnType The return type used to deserialize HTTP response body + * @param <T> The return type corresponding to (same with) returnType + * @param call Call + * @return ApiResponse object containing response status, headers and data, which is a Java object deserialized from response body and would be null + * when returnType is null. + * @throws ApiException If fail to execute the call + */ + public <T> ApiResponse<T> execute(Call call, Type returnType) throws ApiException { + try { + Response response = call.execute(); + T data = handleResponse(response, returnType); + return new ApiResponse<T>(response.code(), response.headers().toMultimap(), data); + } catch (IOException e) { + throw new ApiException(e); + } + } + + /** + * {@link #executeAsync(Call, Type, ApiCallback)} + * + * @param <T> Type + * @param call An instance of the Call object + * @param callback ApiCallback<T> + */ + public <T> void executeAsync(Call call, ApiCallback<T> callback) { + executeAsync(call, null, callback); + } + + /** + * Execute HTTP call asynchronously. + * + * @param <T> Type + * @param call The callback to be executed when the API call finishes + * @param returnType Return type + * @param callback ApiCallback + * @see #execute(Call, Type) + */ + @SuppressWarnings("unchecked") + public <T> void executeAsync(Call call, final Type returnType, final ApiCallback<T> callback) { + call.enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + callback.onFailure(new ApiException(e), 0, null); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + T result; + try { + result = handleResponse(response, returnType); + } catch (ApiException e) { + callback.onFailure(e, response.code(), response.headers().toMultimap()); + return; + } + callback.onSuccess(result, response.code(), response.headers().toMultimap()); + } + }); + } + + /** + * Handle the given response, return the deserialized object when the response is successful. + * + * @param <T> Type + * @param response Response + * @param returnType Return type + * @return Type + * @throws ApiException If the response has an unsuccessful status code or fail to deserialize the response body + */ + public <T> T handleResponse(Response response, Type returnType) throws ApiException { + if (response.isSuccessful()) { + if (returnType == null || response.code() == 204) { + // returning null if the returnType is not defined, + // or the status code is 204 (No Content) + if (response.body() != null) { + try { + response.body().close(); + } catch (Exception e) { + throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + } + } + return null; + } else { + return deserialize(response, returnType); + } + } else { + String respBody = null; + if (response.body() != null) { + try { + respBody = response.body().string(); + } catch (IOException e) { + throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + } + } + throw new ApiException(response.message(), response.code(), response.headers().toMultimap(), respBody); + } + } + + /** + * Build HTTP call with the given options. + * + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param cookieParams The cookie parameters + * @param formParams The form parameters + * @param authNames The authentications to apply + * @param callback Callback for upload/download progress + * @return The HTTP call + * @throws ApiException If fail to serialize the request body object + */ + public Call buildCall(String path, String method, List<Pair> queryParams, List<Pair> collectionQueryParams, Object body, + Map<String, String> headerParams, Map<String, String> cookieParams, Map<String, Object> formParams, String[] authNames, ApiCallback callback) + throws ApiException { + Request request = buildRequest(path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, authNames, + callback); + + return httpClient.newCall(request); + } + + /** + * Build an HTTP request with the given options. + * + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param cookieParams The cookie parameters + * @param formParams The form parameters + * @param authNames The authentications to apply + * @param callback Callback for upload/download progress + * @return The HTTP request + * @throws ApiException If fail to serialize the request body object + */ + public Request buildRequest(String path, String method, List<Pair> queryParams, List<Pair> collectionQueryParams, Object body, + Map<String, String> headerParams, Map<String, String> cookieParams, Map<String, Object> formParams, String[] authNames, ApiCallback callback) + throws ApiException { + updateParamsForAuth(authNames, queryParams, headerParams, cookieParams); + + final String url = buildUrl(path, queryParams, collectionQueryParams); + final Request.Builder reqBuilder = new Request.Builder().url(url); + processHeaderParams(headerParams, reqBuilder); + processCookieParams(cookieParams, reqBuilder); + + String contentType = headerParams.get("Content-Type"); + // ensuring a default content type + if (contentType == null) { + contentType = "application/json"; + } + + RequestBody reqBody; + if (!HttpMethod.permitsRequestBody(method)) { + reqBody = null; + } else if ("application/x-www-form-urlencoded".equals(contentType)) { + reqBody = buildRequestBodyFormEncoding(formParams); + } else if ("multipart/form-data".equals(contentType)) { + reqBody = buildRequestBodyMultipart(formParams); + } else if (body == null) { + if ("DELETE".equals(method)) { + // allow calling DELETE without sending a request body + reqBody = null; + } else { + // use an empty request body (for POST, PUT and PATCH) + reqBody = RequestBody.create(MediaType.parse(contentType), ""); + } + } else { + reqBody = serialize(body, contentType); + } + + // Associate callback with request (if not null) so interceptor can + // access it when creating ProgressResponseBody + reqBuilder.tag(callback); + + Request request = null; + + if (callback != null && reqBody != null) { + ProgressRequestBody progressRequestBody = new ProgressRequestBody(reqBody, callback); + request = reqBuilder.method(method, progressRequestBody).build(); + } else { + request = reqBuilder.method(method, reqBody).build(); + } + + return request; + } + + /** + * Build full URL by concatenating base path, the given sub path and query parameters. + * + * @param path The sub path + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @return The full URL + */ + public String buildUrl(String path, List<Pair> queryParams, List<Pair> collectionQueryParams) { + final StringBuilder url = new StringBuilder(); + url.append(basePath).append(path); + + if (queryParams != null && !queryParams.isEmpty()) { + // support (constant) query string in `path`, e.g. "/posts?draft=1" + String prefix = path.contains("?") ? "&" : "?"; + for (Pair param : queryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + url.append(escapeString(param.getName())).append("=").append(escapeString(value)); + } + } + } + + if (collectionQueryParams != null && !collectionQueryParams.isEmpty()) { + String prefix = url.toString().contains("?") ? "&" : "?"; + for (Pair param : collectionQueryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + // collection query parameter value already escaped as part of parameterToPairs + url.append(escapeString(param.getName())).append("=").append(value); + } + } + } + + return url.toString(); + } + + /** + * Set header parameters to the request builder, including default headers. + * + * @param headerParams Header parameters in the form of Map + * @param reqBuilder Request.Builder + */ + public void processHeaderParams(Map<String, String> headerParams, Request.Builder reqBuilder) { + for (Entry<String, String> param : headerParams.entrySet()) { + reqBuilder.header(param.getKey(), parameterToString(param.getValue())); + } + for (Entry<String, String> header : defaultHeaderMap.entrySet()) { + if (!headerParams.containsKey(header.getKey())) { + reqBuilder.header(header.getKey(), parameterToString(header.getValue())); + } + } + } + + /** + * Set cookie parameters to the request builder, including default cookies. + * + * @param cookieParams Cookie parameters in the form of Map + * @param reqBuilder Request.Builder + */ + public void processCookieParams(Map<String, String> cookieParams, Request.Builder reqBuilder) { + for (Entry<String, String> param : cookieParams.entrySet()) { + reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue())); + } + for (Entry<String, String> param : defaultCookieMap.entrySet()) { + if (!cookieParams.containsKey(param.getKey())) { + reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue())); + } + } + } + + /** + * Update query and header parameters based on authentication settings. + * + * @param authNames The authentications to apply + * @param queryParams List of query parameters + * @param headerParams Map of header parameters + * @param cookieParams Map of cookie parameters + */ + public void updateParamsForAuth(String[] authNames, List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { + for (String authName : authNames) { + Authentication auth = authentications.get(authName); + if (auth == null) { + throw new RuntimeException("Authentication undefined: " + authName); + } + auth.applyToParams(queryParams, headerParams, cookieParams); + } + } + + /** + * Build a form-encoding request body with the given form parameters. + * + * @param formParams Form parameters in the form of Map + * @return RequestBody + */ + public RequestBody buildRequestBodyFormEncoding(Map<String, Object> formParams) { + okhttp3.FormBody.Builder formBuilder = new okhttp3.FormBody.Builder(); + for (Entry<String, Object> param : formParams.entrySet()) { + formBuilder.add(param.getKey(), parameterToString(param.getValue())); + } + return formBuilder.build(); + } + + /** + * Build a multipart (file uploading) request body with the given form parameters, which could contain text fields and file fields. + * + * @param formParams Form parameters in the form of Map + * @return RequestBody + */ + public RequestBody buildRequestBodyMultipart(Map<String, Object> formParams) { + MultipartBody.Builder mpBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); + for (Entry<String, Object> param : formParams.entrySet()) { + if (param.getValue() instanceof File) { + File file = (File) param.getValue(); + Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\"; filename=\"" + file.getName() + "\""); + MediaType mediaType = MediaType.parse(guessContentTypeFromFile(file)); + mpBuilder.addPart(partHeaders, RequestBody.create(mediaType, file)); + } else { + Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\""); + mpBuilder.addPart(partHeaders, RequestBody.create(null, parameterToString(param.getValue()))); + } + } + return mpBuilder.build(); + } + + /** + * Guess Content-Type header from the given file (defaults to "application/octet-stream"). + * + * @param file The given file + * @return The guessed Content-Type + */ + public String guessContentTypeFromFile(File file) { + String contentType = URLConnection.guessContentTypeFromName(file.getName()); + if (contentType == null) { + return "application/octet-stream"; + } else { + return contentType; + } + } + + /** + * Get network interceptor to add it to the httpClient to track download progress for async requests. + */ + public Interceptor getProgressInterceptor() { + return new Interceptor() { + @Override + public Response intercept(Interceptor.Chain chain) throws IOException { + final Request request = chain.request(); + final Response originalResponse = chain.proceed(request); + if (request.tag() instanceof ApiCallback) { + final ApiCallback callback = (ApiCallback) request.tag(); + return originalResponse.newBuilder() + .body(new ProgressResponseBody(originalResponse.body(), callback)) + .build(); + } + return originalResponse; + } + }; + } + + /** + * Apply SSL related settings to httpClient according to the current values of verifyingSsl and sslCaCert. + */ + private void applySslSettings() { + try { + TrustManager[] trustManagers; + HostnameVerifier hostnameVerifier; + if (!verifyingSsl) { + trustManagers = new TrustManager[]{ + new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[]{}; + } + } + }; + hostnameVerifier = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + } else { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + + if (sslCaCert == null) { + trustManagerFactory.init((KeyStore) null); + } else { + char[] password = null; // Any password will work. + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(sslCaCert); + if (certificates.isEmpty()) { + throw new IllegalArgumentException("expected non-empty set of trusted certificates"); + } + KeyStore caKeyStore = newEmptyKeyStore(password); + int index = 0; + for (Certificate certificate : certificates) { + String certificateAlias = "ca" + index++; + caKeyStore.setCertificateEntry(certificateAlias, certificate); + } + trustManagerFactory.init(caKeyStore); + } + trustManagers = trustManagerFactory.getTrustManagers(); + hostnameVerifier = OkHostnameVerifier.INSTANCE; + } + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagers, trustManagers, new SecureRandom()); + httpClient = httpClient.newBuilder() + .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0]) + .hostnameVerifier(hostnameVerifier) + .build(); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { + try { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, password); + return keyStore; + } catch (IOException e) { + throw new AssertionError(e); + } + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiException.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiException.java new file mode 100644 index 0000000..bbb8b68 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiException.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker; + +import java.util.List; +import java.util.Map; + + +public class ApiException extends Exception { + + private int code = 0; + private Map<String, List<String>> responseHeaders = null; + private String responseBody = null; + + public ApiException() { + } + + public ApiException(Throwable throwable) { + super(throwable); + } + + public ApiException(String message) { + super(message); + } + + public ApiException(String message, Throwable throwable, int code, Map<String, List<String>> responseHeaders, String responseBody) { + super(message, throwable); + this.code = code; + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + public ApiException(String message, int code, Map<String, List<String>> responseHeaders, String responseBody) { + this(message, null, code, responseHeaders, responseBody); + } + + public ApiException(String message, Throwable throwable, int code, Map<String, List<String>> responseHeaders) { + this(message, throwable, code, responseHeaders, null); + } + + public ApiException(int code, Map<String, List<String>> responseHeaders, String responseBody) { + this(null, null, code, responseHeaders, responseBody); + } + + public ApiException(int code, String message) { + super(message); + this.code = code; + } + + public ApiException(int code, String message, Map<String, List<String>> responseHeaders, String responseBody) { + this(code, message); + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + /** + * Get the HTTP status code. + * + * @return HTTP status code + */ + public int getCode() { + return code; + } + + /** + * Get the HTTP response headers. + * + * @return A map of list of string + */ + public Map<String, List<String>> getResponseHeaders() { + return responseHeaders; + } + + /** + * Get the HTTP response body. + * + * @return Response body in the form of string + */ + public String getResponseBody() { + return responseBody; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiResponse.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiResponse.java new file mode 100644 index 0000000..da68d2a --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiResponse.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker; + +import java.util.List; +import java.util.Map; + +/** + * API response returned by API call. + * + * @param <T> The type of data that is deserialized from response body + */ +public class ApiResponse<T> { + + final private int statusCode; + final private Map<String, List<String>> headers; + final private T data; + + /** + * @param statusCode The status code of HTTP response + * @param headers The headers of HTTP response + */ + public ApiResponse(int statusCode, Map<String, List<String>> headers) { + this(statusCode, headers, null); + } + + /** + * @param statusCode The status code of HTTP response + * @param headers The headers of HTTP response + * @param data The object deserialized from response bod + */ + public ApiResponse(int statusCode, Map<String, List<String>> headers, T data) { + this.statusCode = statusCode; + this.headers = headers; + this.data = data; + } + + public int getStatusCode() { + return statusCode; + } + + public Map<String, List<String>> getHeaders() { + return headers; + } + + public T getData() { + return data; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Configuration.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Configuration.java new file mode 100644 index 0000000..94e41bc --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Configuration.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker; + + +public class Configuration { + + private static ApiClient defaultApiClient = new ApiClient(); + + /** + * Get the default API client, which would be used when creating API instances without providing an API client. + * + * @return Default API client + */ + public static ApiClient getDefaultApiClient() { + return defaultApiClient; + } + + /** + * Set the default API client, which would be used when creating API instances without providing an API client. + * + * @param apiClient API client + */ + public static void setDefaultApiClient(ApiClient apiClient) { + defaultApiClient = apiClient; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/GzipRequestInterceptor.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/GzipRequestInterceptor.java new file mode 100644 index 0000000..beebb21 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/GzipRequestInterceptor.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker; + +import java.io.IOException; +import okhttp3.Interceptor; +import okhttp3.MediaType; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okio.Buffer; +import okio.BufferedSink; +import okio.GzipSink; +import okio.Okio; + +/** + * Encodes request bodies using gzip. + * <p> + * Taken from https://github.com/square/okhttp/issues/350 + */ +class GzipRequestInterceptor implements Interceptor { + + @Override + public Response intercept(Chain chain) throws IOException { + Request originalRequest = chain.request(); + if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) { + return chain.proceed(originalRequest); + } + + Request compressedRequest = originalRequest.newBuilder() + .header("Content-Encoding", "gzip") + .method(originalRequest.method(), forceContentLength(gzip(originalRequest.body()))) + .build(); + return chain.proceed(compressedRequest); + } + + private RequestBody forceContentLength(final RequestBody requestBody) throws IOException { + final Buffer buffer = new Buffer(); + requestBody.writeTo(buffer); + return new RequestBody() { + @Override + public MediaType contentType() { + return requestBody.contentType(); + } + + @Override + public long contentLength() { + return buffer.size(); + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + sink.write(buffer.snapshot()); + } + }; + } + + private RequestBody gzip(final RequestBody body) { + return new RequestBody() { + @Override + public MediaType contentType() { + return body.contentType(); + } + + @Override + public long contentLength() { + return -1; // We don't know the compressed length in advance! + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + BufferedSink gzipSink = Okio.buffer(new GzipSink(sink)); + body.writeTo(gzipSink); + gzipSink.close(); + } + }; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/JSON.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/JSON.java new file mode 100644 index 0000000..97a813c --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/JSON.java @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.internal.bind.util.ISO8601Utils; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.gsonfire.GsonFireBuilder; +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.ParsePosition; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.Locale; +import java.util.Map; +import okio.ByteString; + +public class JSON { + + private Gson gson; + private boolean isLenientOnJson = false; + private final DateTypeAdapter dateTypeAdapter = new DateTypeAdapter(); + private final SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter(); + private final OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(); + private final LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); + private final ByteArrayAdapter byteArrayAdapter = new ByteArrayAdapter(); + + public static GsonBuilder createGson() { + GsonFireBuilder fireBuilder = new GsonFireBuilder(); + GsonBuilder builder = fireBuilder.createGsonBuilder(); + return builder; + } + + private static String getDiscriminatorValue(JsonElement readElement, String discriminatorField) { + JsonElement element = readElement.getAsJsonObject().get(discriminatorField); + if (null == element) { + throw new IllegalArgumentException("missing discriminator field: <" + discriminatorField + ">"); + } + return element.getAsString(); + } + + private static Class getClassByDiscriminator(Map classByDiscriminatorValue, String discriminatorValue) { + Class clazz = (Class) classByDiscriminatorValue.get(discriminatorValue.toUpperCase(Locale.ROOT)); + if (null == clazz) { + throw new IllegalArgumentException("cannot determine model class of name: <" + discriminatorValue + ">"); + } + return clazz; + } + + public JSON() { + gson = createGson() + .registerTypeAdapter(Date.class, dateTypeAdapter) + .registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter) + .registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter) + .registerTypeAdapter(LocalDate.class, localDateTypeAdapter) + .registerTypeAdapter(byte[].class, byteArrayAdapter) + .create(); + } + + /** + * Get Gson. + * + * @return Gson + */ + public Gson getGson() { + return gson; + } + + /** + * Set Gson. + * + * @param gson Gson + * @return JSON + */ + public JSON setGson(Gson gson) { + this.gson = gson; + return this; + } + + public JSON setLenientOnJson(boolean lenientOnJson) { + isLenientOnJson = lenientOnJson; + return this; + } + + /** + * Serialize the given Java object into JSON string. + * + * @param obj Object + * @return String representation of the JSON + */ + public String serialize(Object obj) { + return gson.toJson(obj); + } + + /** + * Deserialize the given JSON string to Java object. + * + * @param <T> Type + * @param body The JSON string + * @param returnType The type to deserialize into + * @return The deserialized Java object + */ + @SuppressWarnings("unchecked") + public <T> T deserialize(String body, Type returnType) { + try { + if (isLenientOnJson) { + JsonReader jsonReader = new JsonReader(new StringReader(body)); + // see https://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/stream/JsonReader.html#setLenient(boolean) + jsonReader.setLenient(true); + return gson.fromJson(jsonReader, returnType); + } else { + return gson.fromJson(body, returnType); + } + } catch (JsonParseException e) { + // Fallback processing when failed to parse JSON form response body: + // return the response body string directly for the String return type; + if (returnType.equals(String.class)) { + return (T) body; + } else { + throw (e); + } + } + } + + /** + * Gson TypeAdapter for Byte Array type + */ + public class ByteArrayAdapter extends TypeAdapter<byte[]> { + + @Override + public void write(JsonWriter out, byte[] value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(ByteString.of(value).base64()); + } + } + + @Override + public byte[] read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String bytesAsBase64 = in.nextString(); + ByteString byteString = ByteString.decodeBase64(bytesAsBase64); + return byteString.toByteArray(); + } + } + } + + /** + * Gson TypeAdapter for JSR310 OffsetDateTime type + */ + public static class OffsetDateTimeTypeAdapter extends TypeAdapter<OffsetDateTime> { + + private DateTimeFormatter formatter; + + public OffsetDateTimeTypeAdapter() { + this(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } + + public OffsetDateTimeTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, OffsetDateTime date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } + + @Override + public OffsetDateTime read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + if (date.endsWith("+0000")) { + date = date.substring(0, date.length() - 5) + "Z"; + } + return OffsetDateTime.parse(date, formatter); + } + } + } + + /** + * Gson TypeAdapter for JSR310 LocalDate type + */ + public class LocalDateTypeAdapter extends TypeAdapter<LocalDate> { + + private DateTimeFormatter formatter; + + public LocalDateTypeAdapter() { + this(DateTimeFormatter.ISO_LOCAL_DATE); + } + + public LocalDateTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, LocalDate date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } + + @Override + public LocalDate read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return LocalDate.parse(date, formatter); + } + } + } + + public JSON setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + offsetDateTimeTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setLocalDateFormat(DateTimeFormatter dateFormat) { + localDateTypeAdapter.setFormat(dateFormat); + return this; + } + + /** + * Gson TypeAdapter for java.sql.Date type If the dateFormat is null, a simple "yyyy-MM-dd" format will be used (more efficient than + * SimpleDateFormat). + */ + public static class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> { + + private DateFormat dateFormat; + + public SqlDateTypeAdapter() { + } + + public SqlDateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, java.sql.Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = date.toString(); + } + out.value(value); + } + } + + @Override + public java.sql.Date read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return new java.sql.Date(dateFormat.parse(date).getTime()); + } + return new java.sql.Date(ISO8601Utils.parse(date, new ParsePosition(0)).getTime()); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } + } + + /** + * Gson TypeAdapter for java.util.Date type If the dateFormat is null, ISO8601Utils will be used. + */ + public static class DateTypeAdapter extends TypeAdapter<Date> { + + private DateFormat dateFormat; + + public DateTypeAdapter() { + } + + public DateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = ISO8601Utils.format(date, true); + } + out.value(value); + } + } + + @Override + public Date read(JsonReader in) throws IOException { + try { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return dateFormat.parse(date); + } + return ISO8601Utils.parse(date, new ParsePosition(0)); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } catch (IllegalArgumentException e) { + throw new JsonParseException(e); + } + } + } + + public JSON setDateFormat(DateFormat dateFormat) { + dateTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setSqlDateFormat(DateFormat dateFormat) { + sqlDateTypeAdapter.setFormat(dateFormat); + return this; + } + +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Pair.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Pair.java new file mode 100644 index 0000000..1eda4e8 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Pair.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker; + + +public class Pair { + + private String name = ""; + private String value = ""; + + public Pair(String name, String value) { + setName(name); + setValue(value); + } + + private void setName(String name) { + if (!isValidString(name)) { + return; + } + + this.name = name; + } + + private void setValue(String value) { + if (!isValidString(value)) { + return; + } + + this.value = value; + } + + public String getName() { + return this.name; + } + + public String getValue() { + return this.value; + } + + private boolean isValidString(String arg) { + if (arg == null) { + return false; + } + + return !arg.trim().isEmpty(); + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressRequestBody.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressRequestBody.java new file mode 100644 index 0000000..3b481b7 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressRequestBody.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker; + +import java.io.IOException; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okio.Buffer; +import okio.BufferedSink; +import okio.ForwardingSink; +import okio.Okio; +import okio.Sink; + +public class ProgressRequestBody extends RequestBody { + + private final RequestBody requestBody; + + private final ApiCallback callback; + + public ProgressRequestBody(RequestBody requestBody, ApiCallback callback) { + this.requestBody = requestBody; + this.callback = callback; + } + + @Override + public MediaType contentType() { + return requestBody.contentType(); + } + + @Override + public long contentLength() throws IOException { + return requestBody.contentLength(); + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + BufferedSink bufferedSink = Okio.buffer(sink(sink)); + requestBody.writeTo(bufferedSink); + bufferedSink.flush(); + } + + private Sink sink(Sink sink) { + return new ForwardingSink(sink) { + + long bytesWritten = 0L; + long contentLength = 0L; + + @Override + public void write(Buffer source, long byteCount) throws IOException { + super.write(source, byteCount); + if (contentLength == 0) { + contentLength = contentLength(); + } + + bytesWritten += byteCount; + callback.onUploadProgress(bytesWritten, contentLength, bytesWritten == contentLength); + } + }; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressResponseBody.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressResponseBody.java new file mode 100644 index 0000000..fa2f950 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressResponseBody.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker; + +import java.io.IOException; +import okhttp3.MediaType; +import okhttp3.ResponseBody; +import okio.Buffer; +import okio.BufferedSource; +import okio.ForwardingSource; +import okio.Okio; +import okio.Source; + +public class ProgressResponseBody extends ResponseBody { + + private final ResponseBody responseBody; + private final ApiCallback callback; + private BufferedSource bufferedSource; + + public ProgressResponseBody(ResponseBody responseBody, ApiCallback callback) { + this.responseBody = responseBody; + this.callback = callback; + } + + @Override + public MediaType contentType() { + return responseBody.contentType(); + } + + @Override + public long contentLength() { + return responseBody.contentLength(); + } + + @Override + public BufferedSource source() { + if (bufferedSource == null) { + bufferedSource = Okio.buffer(source(responseBody.source())); + } + return bufferedSource; + } + + private Source source(Source source) { + return new ForwardingSource(source) { + long totalBytesRead = 0L; + + @Override + public long read(Buffer sink, long byteCount) throws IOException { + long bytesRead = super.read(sink, byteCount); + // read() returns the number of bytes read, or -1 if this source is exhausted. + totalBytesRead += bytesRead != -1 ? bytesRead : 0; + callback.onDownloadProgress(totalBytesRead, responseBody.contentLength(), bytesRead == -1); + return bytesRead; + } + }; + } +} + + diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/StringUtil.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/StringUtil.java new file mode 100644 index 0000000..d17a928 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/StringUtil.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker; + + +public class StringUtil { + + /** + * Check if the given array contains the given value (with case-insensitive comparison). + * + * @param array The array + * @param value The value to search + * @return true if the array contains the value + */ + public static boolean containsIgnoreCase(String[] array, String value) { + for (String str : array) { + if (value == null && str == null) { + return true; + } + if (value != null && value.equalsIgnoreCase(str)) { + return true; + } + } + return false; + } + + /** + * Join an array of strings with the given separator. + * <p> + * Note: This might be replaced by utility method from commons-lang or guava someday if one of those libraries is added as dependency. + * </p> + * + * @param array The array of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(String[] array, String separator) { + int len = array.length; + if (len == 0) { + return ""; + } + + StringBuilder out = new StringBuilder(); + out.append(array[0]); + for (int i = 1; i < len; i++) { + out.append(separator).append(array[i]); + } + return out.toString(); + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/ApiKeyAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/ApiKeyAuth.java new file mode 100644 index 0000000..6b11d3f --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/ApiKeyAuth.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker.auth; + +import de.gematik.ti.epa.vzd.client.invoker.Pair; +import java.util.List; +import java.util.Map; + + +public class ApiKeyAuth implements Authentication { + + private final String location; + private final String paramName; + + private String apiKey; + private String apiKeyPrefix; + + public ApiKeyAuth(String location, String paramName) { + this.location = location; + this.paramName = paramName; + } + + public String getLocation() { + return location; + } + + public String getParamName() { + return paramName; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public String getApiKeyPrefix() { + return apiKeyPrefix; + } + + public void setApiKeyPrefix(String apiKeyPrefix) { + this.apiKeyPrefix = apiKeyPrefix; + } + + @Override + public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { + if (apiKey == null) { + return; + } + String value; + if (apiKeyPrefix != null) { + value = apiKeyPrefix + " " + apiKey; + } else { + value = apiKey; + } + if ("query".equals(location)) { + queryParams.add(new Pair(paramName, value)); + } else if ("header".equals(location)) { + headerParams.put(paramName, value); + } else if ("cookie".equals(location)) { + cookieParams.put(paramName, value); + } + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/Authentication.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/Authentication.java new file mode 100644 index 0000000..0c3dfa7 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/Authentication.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker.auth; + +import de.gematik.ti.epa.vzd.client.invoker.Pair; +import java.util.List; +import java.util.Map; + +public interface Authentication { + + /** + * Apply authentication settings to header and query params. + * + * @param queryParams List of query parameters + * @param headerParams Map of header parameters + * @param cookieParams Map of cookie parameters + */ + void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams); +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBasicAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBasicAuth.java new file mode 100644 index 0000000..2904481 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBasicAuth.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker.auth; + +import de.gematik.ti.epa.vzd.client.invoker.Pair; +import java.util.List; +import java.util.Map; +import okhttp3.Credentials; + +public class HttpBasicAuth implements Authentication { + + private String username; + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { + if (username == null && password == null) { + return; + } + headerParams.put("Authorization", Credentials.basic( + username == null ? "" : username, + password == null ? "" : password)); + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBearerAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBearerAuth.java new file mode 100644 index 0000000..6cccd8c --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBearerAuth.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker.auth; + +import de.gematik.ti.epa.vzd.client.invoker.Pair; +import java.util.List; +import java.util.Map; + + +public class HttpBearerAuth implements Authentication { + + private final String scheme; + private String bearerToken; + + public HttpBearerAuth(String scheme) { + this.scheme = scheme; + } + + /** + * Gets the token, which together with the scheme, will be sent as the value of the Authorization header. + * + * @return The bearer token + */ + public String getBearerToken() { + return bearerToken; + } + + /** + * Sets the token, which together with the scheme, will be sent as the value of the Authorization header. + * + * @param bearerToken The bearer token to send in the Authorization header + */ + public void setBearerToken(String bearerToken) { + this.bearerToken = bearerToken; + } + + @Override + public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { + if (bearerToken == null) { + return; + } + + headerParams.put("Authorization", (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken); + } + + private static String upperCaseBearer(String scheme) { + return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuth.java new file mode 100644 index 0000000..95d30ee --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuth.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker.auth; + +import de.gematik.ti.epa.vzd.client.invoker.Pair; +import java.util.List; +import java.util.Map; + + +public class OAuth implements Authentication { + + private String accessToken; + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + @Override + public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { + if (accessToken != null) { + headerParams.put("Authorization", "Bearer " + accessToken); + } + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthFlow.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthFlow.java new file mode 100644 index 0000000..1db3f44 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthFlow.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.invoker.auth; + +public enum OAuthFlow { + accessCode, implicit, password, application +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthOkHttpClient.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthOkHttpClient.java new file mode 100644 index 0000000..fb165f8 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthOkHttpClient.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.client.invoker.auth; + +import java.io.IOException; +import java.util.Map; +import java.util.Map.Entry; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.apache.oltu.oauth2.client.HttpClient; +import org.apache.oltu.oauth2.client.request.OAuthClientRequest; +import org.apache.oltu.oauth2.client.response.OAuthClientResponse; +import org.apache.oltu.oauth2.client.response.OAuthClientResponseFactory; +import org.apache.oltu.oauth2.common.exception.OAuthProblemException; +import org.apache.oltu.oauth2.common.exception.OAuthSystemException; + +public class OAuthOkHttpClient implements HttpClient { + + private final OkHttpClient client; + + public OAuthOkHttpClient() { + this.client = new OkHttpClient(); + } + + public OAuthOkHttpClient(OkHttpClient client) { + this.client = client; + } + + @Override + public <T extends OAuthClientResponse> T execute(OAuthClientRequest request, Map<String, String> headers, + String requestMethod, Class<T> responseClass) + throws OAuthSystemException, OAuthProblemException { + + MediaType mediaType = MediaType.parse("application/json"); + Request.Builder requestBuilder = new Request.Builder().url(request.getLocationUri()); + + if (headers != null) { + for (Entry<String, String> entry : headers.entrySet()) { + if (entry.getKey().equalsIgnoreCase("Content-Type")) { + mediaType = MediaType.parse(entry.getValue()); + } else { + requestBuilder.addHeader(entry.getKey(), entry.getValue()); + } + } + } + + RequestBody body = request.getBody() != null ? RequestBody.create(mediaType, request.getBody()) : null; + requestBuilder.method(requestMethod, body); + + try { + Response response = client.newCall(requestBuilder.build()).execute(); + return OAuthClientResponseFactory.createCustomResponse( + response.body().string(), + response.body().contentType().toString(), + response.code(), + response.headers().toMultimap(), + responseClass); + } catch (IOException e) { + throw new OAuthSystemException(e); + } + } + + @Override + public void shutdown() { + // Nothing to do here + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/RetryingOAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/RetryingOAuth.java new file mode 100644 index 0000000..14b6f55 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/RetryingOAuth.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.client.invoker.auth; + +import de.gematik.ti.epa.vzd.client.invoker.Pair; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.List; +import java.util.Map; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.apache.oltu.oauth2.client.OAuthClient; +import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest; +import org.apache.oltu.oauth2.client.request.OAuthClientRequest; +import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder; +import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse; +import org.apache.oltu.oauth2.common.exception.OAuthProblemException; +import org.apache.oltu.oauth2.common.exception.OAuthSystemException; +import org.apache.oltu.oauth2.common.message.types.GrantType; + +public class RetryingOAuth extends OAuth implements Interceptor { + + private final OAuthClient oAuthClient; + + private TokenRequestBuilder tokenRequestBuilder; + + public RetryingOAuth(OkHttpClient client, TokenRequestBuilder tokenRequestBuilder) { + this.oAuthClient = new OAuthClient(new OAuthOkHttpClient(client)); + this.tokenRequestBuilder = tokenRequestBuilder; + } + + public RetryingOAuth(TokenRequestBuilder tokenRequestBuilder) { + this(new OkHttpClient(), tokenRequestBuilder); + } + + public RetryingOAuth( + String tokenUrl, + String clientId, + OAuthFlow flow, + String clientSecret, + Map<String, String> parameters + ) { + this(OAuthClientRequest.tokenLocation(tokenUrl) + .setClientId(clientId) + .setClientSecret(clientSecret)); + setFlow(flow); + if (parameters != null) { + for (String paramName : parameters.keySet()) { + tokenRequestBuilder.setParameter(paramName, parameters.get(paramName)); + } + } + } + + public void setFlow(OAuthFlow flow) { + switch (flow) { + case accessCode: + tokenRequestBuilder.setGrantType(GrantType.AUTHORIZATION_CODE); + break; + case implicit: + tokenRequestBuilder.setGrantType(GrantType.IMPLICIT); + break; + case password: + tokenRequestBuilder.setGrantType(GrantType.PASSWORD); + break; + case application: + tokenRequestBuilder.setGrantType(GrantType.CLIENT_CREDENTIALS); + break; + default: + break; + } + } + + @Override + public Response intercept(Chain chain) throws IOException { + return retryingIntercept(chain, true); + } + + private Response retryingIntercept(Chain chain, boolean updateTokenAndRetryOnAuthorizationFailure) throws IOException { + Request request = chain.request(); + + // If the request already has an authorization (e.g. Basic auth), proceed with the request as is + if (request.header("Authorization") != null) { + return chain.proceed(request); + } + + // Get the token if it has not yet been acquired + if (getAccessToken() == null) { + updateAccessToken(null); + } + + OAuthClientRequest oAuthRequest; + if (getAccessToken() != null) { + // Build the request + Request.Builder requestBuilder = request.newBuilder(); + + String requestAccessToken = getAccessToken(); + try { + oAuthRequest = + new OAuthBearerClientRequest(request.url().toString()). + setAccessToken(requestAccessToken). + buildHeaderMessage(); + } catch (OAuthSystemException e) { + throw new IOException(e); + } + + Map<String, String> headers = oAuthRequest.getHeaders(); + for (String headerName : headers.keySet()) { + requestBuilder.addHeader(headerName, headers.get(headerName)); + } + requestBuilder.url(oAuthRequest.getLocationUri()); + + // Execute the request + Response response = chain.proceed(requestBuilder.build()); + + // 401/403 response codes most likely indicate an expired access token, unless it happens two times in a row + if ( + response != null && + (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED || + response.code() == HttpURLConnection.HTTP_FORBIDDEN) && + updateTokenAndRetryOnAuthorizationFailure + ) { + try { + if (updateAccessToken(requestAccessToken)) { + response.body().close(); + return retryingIntercept(chain, false); + } + } catch (Exception e) { + response.body().close(); + throw e; + } + } + return response; + } else { + return chain.proceed(chain.request()); + } + } + + /* + * Returns true if the access token has been updated + */ + public synchronized boolean updateAccessToken(String requestAccessToken) throws IOException { + if (getAccessToken() == null || getAccessToken().equals(requestAccessToken)) { + try { + OAuthJSONAccessTokenResponse accessTokenResponse = + oAuthClient.accessToken(tokenRequestBuilder.buildBodyMessage()); + if (accessTokenResponse != null && accessTokenResponse.getAccessToken() != null) { + setAccessToken(accessTokenResponse.getAccessToken()); + return !getAccessToken().equals(requestAccessToken); + } + } catch (OAuthSystemException | OAuthProblemException e) { + throw new IOException(e); + } + } + + return false; + } + + public TokenRequestBuilder getTokenRequestBuilder() { + return tokenRequestBuilder; + } + + public void setTokenRequestBuilder(TokenRequestBuilder tokenRequestBuilder) { + this.tokenRequestBuilder = tokenRequestBuilder; + } + + // Applying authorization to parameters is performed in the retryingIntercept method + @Override + public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { + // No implementation necessary + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntry.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntry.java new file mode 100644 index 0000000..77908a5 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntry.java @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.annotations.SerializedName; +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * BaseDirectoryEntry + */ + +public class BaseDirectoryEntry { + + public static final String SERIALIZED_NAME_DN = "dn"; + @SerializedName(SERIALIZED_NAME_DN) + private DistinguishedName dn; + + public static final String SERIALIZED_NAME_GIVEN_NAME = "givenName"; + @SerializedName(SERIALIZED_NAME_GIVEN_NAME) + private String givenName; + + public static final String SERIALIZED_NAME_SN = "sn"; + @SerializedName(SERIALIZED_NAME_SN) + private String sn; + + public static final String SERIALIZED_NAME_CN = "cn"; + @SerializedName(SERIALIZED_NAME_CN) + private String cn; + + public static final String SERIALIZED_NAME_DISPLAY_NAME = "displayName"; + @SerializedName(SERIALIZED_NAME_DISPLAY_NAME) + private String displayName; + + public static final String SERIALIZED_NAME_STREET_ADDRESS = "streetAddress"; + @SerializedName(SERIALIZED_NAME_STREET_ADDRESS) + private String streetAddress; + + public static final String SERIALIZED_NAME_POSTAL_CODE = "postalCode"; + @SerializedName(SERIALIZED_NAME_POSTAL_CODE) + private String postalCode; + + public static final String SERIALIZED_NAME_LOCALITY_NAME = "localityName"; + @SerializedName(SERIALIZED_NAME_LOCALITY_NAME) + private String localityName; + + public static final String SERIALIZED_NAME_STATE_OR_PROVINCE_NAME = "stateOrProvinceName"; + @SerializedName(SERIALIZED_NAME_STATE_OR_PROVINCE_NAME) + private String stateOrProvinceName; + + public static final String SERIALIZED_NAME_TITLE = "title"; + @SerializedName(SERIALIZED_NAME_TITLE) + private String title; + + public static final String SERIALIZED_NAME_ORGANIZATION = "organization"; + @SerializedName(SERIALIZED_NAME_ORGANIZATION) + private String organization; + + public static final String SERIALIZED_NAME_OTHER_NAME = "otherName"; + @SerializedName(SERIALIZED_NAME_OTHER_NAME) + private String otherName; + + public static final String SERIALIZED_NAME_SPECIALIZATION = "specialization"; + @SerializedName(SERIALIZED_NAME_SPECIALIZATION) + private List<String> specialization = null; + + public static final String SERIALIZED_NAME_DOMAIN_I_D = "domainID"; + @SerializedName(SERIALIZED_NAME_DOMAIN_I_D) + private List<String> domainID = null; + + public static final String SERIALIZED_NAME_PERSONAL_ENTRY = "personalEntry"; + @SerializedName(SERIALIZED_NAME_PERSONAL_ENTRY) + private Boolean personalEntry; + + public static final String SERIALIZED_NAME_DATA_FROM_AUTHORITY = "dataFromAuthority"; + @SerializedName(SERIALIZED_NAME_DATA_FROM_AUTHORITY) + private Boolean dataFromAuthority; + + + public BaseDirectoryEntry dn(DistinguishedName dn) { + + this.dn = dn; + return this; + } + + /** + * Get dn + * + * @return dn + **/ + @ApiModelProperty(required = true, value = "") + + public DistinguishedName getDn() { + return dn; + } + + + public void setDn(DistinguishedName dn) { + this.dn = dn; + } + + + /** + * HBA: Vorname, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet + * + * @return givenName + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "Vorname", value = "HBA: Vorname, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet") + + public String getGivenName() { + return givenName; + } + + + /** + * HBA: Name, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet + * + * @return sn + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "Nachname", value = "HBA: Name, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet") + + public String getSn() { + return sn; + } + + + /** + * HBA: Vorname und Nachname / SMC-B: Bezeichner: Name Wird vom VZD aus dem Zertifikatsattribut commonName übernommen. + * + * @return cn + **/ + @ApiModelProperty(example = "Vorname Nachname", required = true, value = "HBA: Vorname und Nachname / SMC-B: Bezeichner: Name Wird vom VZD aus dem Zertifikatsattribut commonName übernommen.") + + public String getCn() { + return cn; + } + + public void setCn(String cn) { + this.cn = cn; + } + + + public BaseDirectoryEntry displayName(String displayName) { + + this.displayName = displayName; + return this; + } + + /** + * Anzeigename, kann geändert werden. Dieses Attribut wird genutzt um den Namen der Organisation gegenüber dem Anwender darzustellen (Verwendung als + * Filter-Attribut um die Suche einzuschränken und bei der Darstellung des Ergebnisses). Der Wert wird von der pflegenden Stelle festgelegt. + * + * @return displayName + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "Vorname Nachname", value = "Anzeigename, kann geändert werden. Dieses Attribut wird genutzt um den Namen der Organisation gegenüber dem Anwender darzustellen (Verwendung als Filter-Attribut um die Suche einzuschränken und bei der Darstellung des Ergebnisses). Der Wert wird von der pflegenden Stelle festgelegt.") + + public String getDisplayName() { + return displayName; + } + + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + + public BaseDirectoryEntry streetAddress(String streetAddress) { + + this.streetAddress = streetAddress; + return this; + } + + /** + * Straße und Hausnummer Der Wert wird von der pflegenden Stelle festgelegt + * + * @return streetAddress + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "Friedrichstraße 136", value = "Straße und Hausnummer Der Wert wird von der pflegenden Stelle festgelegt") + + public String getStreetAddress() { + return streetAddress; + } + + + public void setStreetAddress(String streetAddress) { + this.streetAddress = streetAddress; + } + + + public BaseDirectoryEntry postalCode(String postalCode) { + + this.postalCode = postalCode; + return this; + } + + /** + * Postleitzahl Der Wert wird von der pflegenden Stelle festgelegt + * + * @return postalCode + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "10117", value = "Postleitzahl Der Wert wird von der pflegenden Stelle festgelegt") + + public String getPostalCode() { + return postalCode; + } + + + public void setPostalCode(String postalCode) { + this.postalCode = postalCode; + } + + + public BaseDirectoryEntry localityName(String localityName) { + + this.localityName = localityName; + return this; + } + + /** + * Ort Der Wert wird von der pflegenden Stelle festgelegt + * + * @return localityName + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "Berlin", value = "Ort Der Wert wird von der pflegenden Stelle festgelegt") + + public String getLocalityName() { + return localityName; + } + + + public void setLocalityName(String localityName) { + this.localityName = localityName; + } + + + public BaseDirectoryEntry stateOrProvinceName(String stateOrProvinceName) { + + this.stateOrProvinceName = stateOrProvinceName; + return this; + } + + /** + * Bundesland Der Wert wird von der pflegenden Stelle festgelegt + * + * @return stateOrProvinceName + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "Berlin", value = "Bundesland Der Wert wird von der pflegenden Stelle festgelegt") + + public String getStateOrProvinceName() { + return stateOrProvinceName; + } + + + public void setStateOrProvinceName(String stateOrProvinceName) { + this.stateOrProvinceName = stateOrProvinceName; + } + + + public BaseDirectoryEntry title(String title) { + + this.title = title; + return this; + } + + /** + * HBA: Titel, optional / SMC-B: nicht verwendet + * + * @return title + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "HBA: Titel, optional / SMC-B: nicht verwendet") + + public String getTitle() { + return title; + } + + + public void setTitle(String title) { + this.title = title; + } + + + public BaseDirectoryEntry organization(String organization) { + + this.organization = organization; + return this; + } + + /** + * Organisation Der Wert wird von der pflegenden Stelle festgelegt + * + * @return organization + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "12345670", value = "Organisation Der Wert wird von der pflegenden Stelle festgelegt") + + public String getOrganization() { + return organization; + } + + + public void setOrganization(String organization) { + this.organization = organization; + } + + + public BaseDirectoryEntry otherName(String otherName) { + + this.otherName = otherName; + return this; + } + + /** + * Anderer Name. Wird vom VZD aus dem Zertifikatsattribut otherName übernommen. + * + * @return otherName + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Anderer Name. Wird vom VZD aus dem Zertifikatsattribut otherName übernommen.") + + public String getOtherName() { + return otherName; + } + + + public void setOtherName(String otherName) { + this.otherName = otherName; + } + + + public BaseDirectoryEntry specialization(List<String> specialization) { + + this.specialization = specialization; + return this; + } + + public BaseDirectoryEntry addSpecializationItem(String specializationItem) { + if (this.specialization == null) { + this.specialization = new ArrayList<>(); + } + this.specialization.add(specializationItem); + return this; + } + + /** + * Fachgebiet Der Wert wird von der pflegenden Stelle festgelegt + * + * @return specialization + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Fachgebiet Der Wert wird von der pflegenden Stelle festgelegt") + + public List<String> getSpecialization() { + return specialization; + } + + + public void setSpecialization(List<String> specialization) { + this.specialization = specialization; + } + + + public BaseDirectoryEntry domainID(List<String> domainID) { + + this.domainID = domainID; + return this; + } + + public BaseDirectoryEntry addDomainIDItem(String domainIDItem) { + if (this.domainID == null) { + this.domainID = new ArrayList<>(); + } + this.domainID.add(domainIDItem); + return this; + } + + /** + * Ärzte: Betriebsstättennummer Der Wert wird aus dem Zertifikat übernommen (Attribut organizationName) + * + * @return domainID + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Ärzte: Betriebsstättennummer Der Wert wird aus dem Zertifikat übernommen (Attribut organizationName)") + + public List<String> getDomainID() { + return domainID; + } + + + public void setDomainID(List<String> domainID) { + this.domainID = domainID; + } + + + /** + * Wird vom VZD eingetragen / Wert == TRUE, wenn alle Zertifikate den entryType 1 haben (Berufsgruppe), Wert == FALSE sonst + * + * @return personalEntry + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "true", value = "Wird vom VZD eingetragen / Wert == TRUE, wenn alle Zertifikate den entryType 1 haben (Berufsgruppe), Wert == FALSE sonst") + + public Boolean getPersonalEntry() { + return personalEntry; + } + + + /** + * Wird vom VZD eingetragen / Wert == TRUE, wenn der Verzeichnisdienst_Eintrag von dem Kartenherausgeber geschrieben wurde, Wert + * == FALSE sonst + * + * @return dataFromAuthority + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "true", value = "Wird vom VZD eingetragen / Wert == TRUE, wenn der Verzeichnisdienst_Eintrag von dem Kartenherausgeber geschrieben wurde, Wert == FALSE sonst") + + public Boolean getDataFromAuthority() { + return dataFromAuthority; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BaseDirectoryEntry baseDirectoryEntry = (BaseDirectoryEntry) o; + return Objects.equals(this.dn, baseDirectoryEntry.dn) && + Objects.equals(this.givenName, baseDirectoryEntry.givenName) && + Objects.equals(this.sn, baseDirectoryEntry.sn) && + Objects.equals(this.cn, baseDirectoryEntry.cn) && + Objects.equals(this.displayName, baseDirectoryEntry.displayName) && + Objects.equals(this.streetAddress, baseDirectoryEntry.streetAddress) && + Objects.equals(this.postalCode, baseDirectoryEntry.postalCode) && + Objects.equals(this.localityName, baseDirectoryEntry.localityName) && + Objects.equals(this.stateOrProvinceName, baseDirectoryEntry.stateOrProvinceName) && + Objects.equals(this.title, baseDirectoryEntry.title) && + Objects.equals(this.organization, baseDirectoryEntry.organization) && + Objects.equals(this.otherName, baseDirectoryEntry.otherName) && + Objects.equals(this.specialization, baseDirectoryEntry.specialization) && + Objects.equals(this.domainID, baseDirectoryEntry.domainID) && + Objects.equals(this.personalEntry, baseDirectoryEntry.personalEntry) && + Objects.equals(this.dataFromAuthority, baseDirectoryEntry.dataFromAuthority); + } + + @Override + public int hashCode() { + return Objects + .hash(dn, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, + stateOrProvinceName, title, organization, otherName, specialization, domainID, + personalEntry, dataFromAuthority); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class BaseDirectoryEntry {\n"); + sb.append(" dn: ").append(toIndentedString(dn)).append("\n"); + sb.append(" givenName: ").append(toIndentedString(givenName)).append("\n"); + sb.append(" sn: ").append(toIndentedString(sn)).append("\n"); + sb.append(" cn: ").append(toIndentedString(cn)).append("\n"); + sb.append(" displayName: ").append(toIndentedString(displayName)).append("\n"); + sb.append(" streetAddress: ").append(toIndentedString(streetAddress)).append("\n"); + sb.append(" postalCode: ").append(toIndentedString(postalCode)).append("\n"); + sb.append(" localityName: ").append(toIndentedString(localityName)).append("\n"); + sb.append(" stateOrProvinceName: ").append(toIndentedString(stateOrProvinceName)) + .append("\n"); + sb.append(" title: ").append(toIndentedString(title)).append("\n"); + sb.append(" organization: ").append(toIndentedString(organization)).append("\n"); + sb.append(" otherName: ").append(toIndentedString(otherName)).append("\n"); + sb.append(" specialization: ").append(toIndentedString(specialization)).append("\n"); + sb.append(" domainID: ").append(toIndentedString(domainID)).append("\n"); + sb.append(" personalEntry: ").append(toIndentedString(personalEntry)).append("\n"); + sb.append(" dataFromAuthority: ").append(toIndentedString(dataFromAuthority)) + .append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntry.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntry.java new file mode 100644 index 0000000..5a42fa2 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntry.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.annotations.SerializedName; +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * CreateDirectoryEntry + */ + +public class CreateDirectoryEntry { + + public static final String SERIALIZED_NAME_DIRECTORY_ENTRY_BASE = "DirectoryEntryBase"; + @SerializedName(SERIALIZED_NAME_DIRECTORY_ENTRY_BASE) + private BaseDirectoryEntry directoryEntryBase; + + public static final String SERIALIZED_NAME_USER_CERTIFICATES = "userCertificates"; + @SerializedName(SERIALIZED_NAME_USER_CERTIFICATES) + private List<UserCertificate> userCertificates = null; + + + public CreateDirectoryEntry directoryEntryBase(BaseDirectoryEntry directoryEntryBase) { + + this.directoryEntryBase = directoryEntryBase; + return this; + } + + /** + * Get directoryEntryBase + * + * @return directoryEntryBase + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public BaseDirectoryEntry getDirectoryEntryBase() { + return directoryEntryBase; + } + + + public void setDirectoryEntryBase(BaseDirectoryEntry directoryEntryBase) { + this.directoryEntryBase = directoryEntryBase; + } + + + public CreateDirectoryEntry userCertificates(List<UserCertificate> userCertificates) { + + this.userCertificates = userCertificates; + return this; + } + + public CreateDirectoryEntry addUserCertificatesItem(UserCertificate userCertificatesItem) { + if (this.userCertificates == null) { + this.userCertificates = new ArrayList<>(); + } + this.userCertificates.add(userCertificatesItem); + return this; + } + + /** + * Get userCertificates + * + * @return userCertificates + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public List<UserCertificate> getUserCertificates() { + return userCertificates; + } + + + public void setUserCertificates(List<UserCertificate> userCertificates) { + this.userCertificates = userCertificates; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CreateDirectoryEntry createDirectoryEntry = (CreateDirectoryEntry) o; + return Objects.equals(this.directoryEntryBase, createDirectoryEntry.directoryEntryBase) && + Objects.equals(this.userCertificates, createDirectoryEntry.userCertificates); + } + + @Override + public int hashCode() { + return Objects.hash(directoryEntryBase, userCertificates); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CreateDirectoryEntry {\n"); + sb.append(" directoryEntryBase: ").append(toIndentedString(directoryEntryBase)).append("\n"); + sb.append(" userCertificates: ").append(toIndentedString(userCertificates)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntry.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntry.java new file mode 100644 index 0000000..1e64fb5 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntry.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.annotations.SerializedName; +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * DirectoryEntry + */ + +public class DirectoryEntry { + + public static final String SERIALIZED_NAME_DIRECTORY_ENTRY_BASE = "DirectoryEntryBase"; + @SerializedName(SERIALIZED_NAME_DIRECTORY_ENTRY_BASE) + private BaseDirectoryEntry directoryEntryBase; + + public static final String SERIALIZED_NAME_USER_CERTIFICATES = "userCertificates"; + @SerializedName(SERIALIZED_NAME_USER_CERTIFICATES) + private List<UserCertificate> userCertificates = null; + + public static final String SERIALIZED_NAME_FACHDATEN = "Fachdaten"; + @SerializedName(SERIALIZED_NAME_FACHDATEN) + private List<Fachdaten> fachdaten = null; + + + public DirectoryEntry directoryEntryBase(BaseDirectoryEntry directoryEntryBase) { + + this.directoryEntryBase = directoryEntryBase; + return this; + } + + /** + * Get directoryEntryBase + * + * @return directoryEntryBase + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public BaseDirectoryEntry getDirectoryEntryBase() { + return directoryEntryBase; + } + + + public void setDirectoryEntryBase(BaseDirectoryEntry directoryEntryBase) { + this.directoryEntryBase = directoryEntryBase; + } + + + public DirectoryEntry userCertificates(List<UserCertificate> userCertificates) { + + this.userCertificates = userCertificates; + return this; + } + + public DirectoryEntry addUserCertificatesItem(UserCertificate userCertificatesItem) { + if (this.userCertificates == null) { + this.userCertificates = new ArrayList<>(); + } + this.userCertificates.add(userCertificatesItem); + return this; + } + + /** + * Get userCertificates + * + * @return userCertificates + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public List<UserCertificate> getUserCertificates() { + return userCertificates; + } + + + public void setUserCertificates(List<UserCertificate> userCertificates) { + this.userCertificates = userCertificates; + } + + + public DirectoryEntry fachdaten(List<Fachdaten> fachdaten) { + + this.fachdaten = fachdaten; + return this; + } + + public DirectoryEntry addFachdatenItem(Fachdaten fachdatenItem) { + if (this.fachdaten == null) { + this.fachdaten = new ArrayList<>(); + } + this.fachdaten.add(fachdatenItem); + return this; + } + + /** + * Get fachdaten + * + * @return fachdaten + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public List<Fachdaten> getFachdaten() { + return fachdaten; + } + + + public void setFachdaten(List<Fachdaten> fachdaten) { + this.fachdaten = fachdaten; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DirectoryEntry directoryEntry = (DirectoryEntry) o; + return Objects.equals(this.directoryEntryBase, directoryEntry.directoryEntryBase) && + Objects.equals(this.userCertificates, directoryEntry.userCertificates) && + Objects.equals(this.fachdaten, directoryEntry.fachdaten); + } + + @Override + public int hashCode() { + return Objects.hash(directoryEntryBase, userCertificates, fachdaten); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class DirectoryEntry {\n"); + sb.append(" directoryEntryBase: ").append(toIndentedString(directoryEntryBase)).append("\n"); + sb.append(" userCertificates: ").append(toIndentedString(userCertificates)).append("\n"); + sb.append(" fachdaten: ").append(toIndentedString(fachdaten)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/DistinguishedName.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/DistinguishedName.java new file mode 100644 index 0000000..9106b91 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/DistinguishedName.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.annotations.SerializedName; +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * DistinguishedName + */ + +public class DistinguishedName { + + public static final String SERIALIZED_NAME_UID = "uid"; + @SerializedName(SERIALIZED_NAME_UID) + private String uid; + + public static final String SERIALIZED_NAME_DC = "dc"; + @SerializedName(SERIALIZED_NAME_DC) + private List<String> dc = null; + + public static final String SERIALIZED_NAME_OU = "ou"; + @SerializedName(SERIALIZED_NAME_OU) + private List<String> ou = null; + + public static final String SERIALIZED_NAME_CN = "cn"; + @SerializedName(SERIALIZED_NAME_CN) + private String cn; + + + public DistinguishedName uid(String uid) { + + this.uid = uid; + return this; + } + + /** + * entryID: Name/ID, den den Eintrag eindeutig identifiziert. Hat für den Verzeichnisdienst_Eintrag, Certificate, KOM-LE_Fachdaten und FAD1 eines + * Verzeichniseintrags den gleichen Wert. + * + * @return uid + **/ + @ApiModelProperty(required = true, value = "entryID: Name/ID, den den Eintrag eindeutig identifiziert. Hat für den Verzeichnisdienst_Eintrag, Certificate, KOM-LE_Fachdaten und FAD1 eines Verzeichniseintrags den gleichen Wert.") + + public String getUid() { + return uid; + } + + + public void setUid(String uid) { + this.uid = uid; + } + + + public DistinguishedName dc(List<String> dc) { + + this.dc = dc; + return this; + } + + public DistinguishedName addDcItem(String dcItem) { + if (this.dc == null) { + this.dc = new ArrayList<>(); + } + this.dc.add(dcItem); + return this; + } + + /** + * Get dc + * + * @return dc + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public List<String> getDc() { + return dc; + } + + + public void setDc(List<String> dc) { + this.dc = dc; + } + + + public DistinguishedName ou(List<String> ou) { + + this.ou = ou; + return this; + } + + public DistinguishedName addOuItem(String ouItem) { + if (this.ou == null) { + this.ou = new ArrayList<>(); + } + this.ou.add(ouItem); + return this; + } + + /** + * Get ou + * + * @return ou + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public List<String> getOu() { + return ou; + } + + + public void setOu(List<String> ou) { + this.ou = ou; + } + + + public DistinguishedName cn(String cn) { + + this.cn = cn; + return this; + } + + /** + * Common Name + * + * @return cn + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Common Name") + + public String getCn() { + return cn; + } + + + public void setCn(String cn) { + this.cn = cn; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DistinguishedName distinguishedName = (DistinguishedName) o; + return Objects.equals(this.uid, distinguishedName.uid) && + Objects.equals(this.dc, distinguishedName.dc) && + Objects.equals(this.ou, distinguishedName.ou) && + Objects.equals(this.cn, distinguishedName.cn); + } + + @Override + public int hashCode() { + return Objects.hash(uid, dc, ou, cn); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class DistinguishedName {\n"); + sb.append(" uid: ").append(toIndentedString(uid)).append("\n"); + sb.append(" dc: ").append(toIndentedString(dc)).append("\n"); + sb.append(" ou: ").append(toIndentedString(ou)).append("\n"); + sb.append(" cn: ").append(toIndentedString(cn)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/Error.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/Error.java new file mode 100644 index 0000000..33d7ed8 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/Error.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.annotations.SerializedName; +import io.swagger.annotations.ApiModelProperty; +import java.util.Objects; + +/** + * Error + */ + +public class Error { + + public static final String SERIALIZED_NAME_ATTRIBUTE_NAME = "attributeName"; + @SerializedName(SERIALIZED_NAME_ATTRIBUTE_NAME) + private String attributeName; + + public static final String SERIALIZED_NAME_ATTRIBUTE_ERROR = "attributeError"; + @SerializedName(SERIALIZED_NAME_ATTRIBUTE_ERROR) + private String attributeError; + + + public Error attributeName(String attributeName) { + + this.attributeName = attributeName; + return this; + } + + /** + * Name des Attributs, in dem ein Fehler erkannt wurde + * + * @return attributeName + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Name des Attributs, in dem ein Fehler erkannt wurde") + + public String getAttributeName() { + return attributeName; + } + + + public void setAttributeName(String attributeName) { + this.attributeName = attributeName; + } + + + public Error attributeError(String attributeError) { + + this.attributeError = attributeError; + return this; + } + + /** + * Beschreibung des erkannten Fehlers + * + * @return attributeError + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Beschreibung des erkannten Fehlers") + + public String getAttributeError() { + return attributeError; + } + + + public void setAttributeError(String attributeError) { + this.attributeError = attributeError; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Error error = (Error) o; + return Objects.equals(this.attributeName, error.attributeName) && + Objects.equals(this.attributeError, error.attributeError); + } + + @Override + public int hashCode() { + return Objects.hash(attributeName, attributeError); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Error {\n"); + sb.append(" attributeName: ").append(toIndentedString(attributeName)).append("\n"); + sb.append(" attributeError: ").append(toIndentedString(attributeError)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/FAD1.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/FAD1.java new file mode 100644 index 0000000..bc63e56 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/FAD1.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.annotations.SerializedName; +import io.swagger.annotations.ApiModelProperty; +import java.util.List; +import java.util.Objects; + +/** + * FAD1 + */ + +public class FAD1 { + + public static final String SERIALIZED_NAME_DN = "dn"; + @SerializedName(SERIALIZED_NAME_DN) + private DistinguishedName dn; + + public static final String SERIALIZED_NAME_MAIL = "mail"; + @SerializedName(SERIALIZED_NAME_MAIL) + private List<String> mail = null; + + + public FAD1 dn(DistinguishedName dn) { + + this.dn = dn; + return this; + } + + /** + * Get dn + * + * @return dn + **/ + @ApiModelProperty(required = true, value = "") + + public DistinguishedName getDn() { + return dn; + } + + + public void setDn(DistinguishedName dn) { + this.dn = dn; + } + + + /** + * Get mail + * + * @return mail + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public List<String> getMail() { + return mail; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FAD1 FAD1 = (FAD1) o; + return Objects.equals(this.dn, FAD1.dn) && + Objects.equals(this.mail, FAD1.mail); + } + + @Override + public int hashCode() { + return Objects.hash(dn, mail); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FAD1 {\n"); + sb.append(" dn: ").append(toIndentedString(dn)).append("\n"); + sb.append(" mail: ").append(toIndentedString(mail)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/Fachdaten.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/Fachdaten.java new file mode 100644 index 0000000..0c1836c --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/Fachdaten.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.annotations.SerializedName; +import io.swagger.annotations.ApiModelProperty; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Fachdaten + */ + +public class Fachdaten { + + public static final String SERIALIZED_NAME_DN = "dn"; + @SerializedName(SERIALIZED_NAME_DN) + private DistinguishedName dn; + + public static final String SERIALIZED_NAME_F_A_D1 = "FAD1"; + @SerializedName(SERIALIZED_NAME_F_A_D1) + private List<FAD1> FAD1 = null; + + + public Fachdaten dn(DistinguishedName dn) { + + this.dn = dn; + return this; + } + + /** + * Get dn + * + * @return dn + **/ + @ApiModelProperty(required = true, value = "") + + public DistinguishedName getDn() { + return dn; + } + + + public void setDn(DistinguishedName dn) { + this.dn = dn; + } + + + public Fachdaten FAD1(List<FAD1> FAD1) { + + this.FAD1 = FAD1; + return this; + } + + public Fachdaten addFAD1Item(FAD1 FAD1Item) { + if (this.FAD1 == null) { + this.FAD1 = new ArrayList<>(); + } + this.FAD1.add(FAD1Item); + return this; + } + + /** + * Get FAD1 + * + * @return FAD1 + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public List<FAD1> getFAD1() { + return FAD1; + } + + + public void setFAD1(List<FAD1> FAD1) { + this.FAD1 = FAD1; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Fachdaten fachdaten = (Fachdaten) o; + return Objects.equals(this.dn, fachdaten.dn) && + Objects.equals(this.FAD1, fachdaten.FAD1); + } + + @Override + public int hashCode() { + return Objects.hash(dn, FAD1); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Fachdaten {\n"); + sb.append(" dn: ").append(toIndentedString(dn)).append("\n"); + sb.append(" FAD1: ").append(toIndentedString(FAD1)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/UserCertificate.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/UserCertificate.java new file mode 100644 index 0000000..e46d9bb --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/UserCertificate.java @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.1.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Jeder Verzeichniseintrag muss mindestens ein Zertifikat enthalten. + */ +@ApiModel(description = "Jeder Verzeichniseintrag muss mindestens ein Zertifikat enthalten.") + +public class UserCertificate { + + public static final String SERIALIZED_NAME_DN = "dn"; + @SerializedName(SERIALIZED_NAME_DN) + private DistinguishedName dn; + + public static final String SERIALIZED_NAME_ENTRY_TYPE = "entryType"; + @SerializedName(SERIALIZED_NAME_ENTRY_TYPE) + private String entryType; + + public static final String SERIALIZED_NAME_TELEMATIK_I_D = "telematikID"; + @SerializedName(SERIALIZED_NAME_TELEMATIK_I_D) + private String telematikID; + + public static final String SERIALIZED_NAME_PROFESSION_O_I_D = "professionOID"; + @SerializedName(SERIALIZED_NAME_PROFESSION_O_I_D) + private List<String> professionOID = null; + + /** + * Gets or Sets usage + */ + @JsonAdapter(UsageEnum.Adapter.class) + public enum UsageEnum { + KOM_LE("KOM-LE"), + + EPA("ePA"); + + private String value; + + UsageEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + public static UsageEnum fromValue(String value) { + for (UsageEnum b : UsageEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + public static class Adapter extends TypeAdapter<UsageEnum> { + + @Override + public void write(final JsonWriter jsonWriter, final UsageEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } + + @Override + public UsageEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return UsageEnum.fromValue(value); + } + } + } + + public static final String SERIALIZED_NAME_USAGE = "usage"; + @SerializedName(SERIALIZED_NAME_USAGE) + private List<UsageEnum> usage = null; + + public static final String SERIALIZED_NAME_USER_CERTIFICATE = "userCertificate"; + @SerializedName(SERIALIZED_NAME_USER_CERTIFICATE) + private String userCertificate; + + public static final String SERIALIZED_NAME_DESCRIPTION = "description"; + @SerializedName(SERIALIZED_NAME_DESCRIPTION) + private String description; + + + public UserCertificate dn(DistinguishedName dn) { + + this.dn = dn; + return this; + } + + /** + * Get dn + * + * @return dn + **/ + @ApiModelProperty(required = true, value = "") + + public DistinguishedName getDn() { + return dn; + } + + + public void setDn(DistinguishedName dn) { + this.dn = dn; + } + + + /** + * Eintragstyp Wird vom VZD anhand der in dem Zertifikat enthaltenen OID (Extension Admission, Attribut ProfessionOID) und der Spalte Eintragstyp in + * Tab_VZD_Mapping_Eintragstyp_und_ProfessionOID automatisch eingetragen. Siehe auch [gemSpecOID]# Tab_PKI_402 und Tab_PKI_403 + * + * @return entryType + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Eintragstyp Wird vom VZD anhand der in dem Zertifikat enthaltenen OID (Extension Admission, Attribut ProfessionOID) und der Spalte Eintragstyp in Tab_VZD_Mapping_Eintragstyp_und_ProfessionOID automatisch eingetragen. Siehe auch [gemSpecOID]# Tab_PKI_402 und Tab_PKI_403") + + public String getEntryType() { + return entryType; + } + + + public UserCertificate telematikID(String telematikID) { + + this.telematikID = telematikID; + return this; + } + + /** + * Wird beim Anlegen des Eintrags vom VZD aus dem Zertifikat übernommen (Feld registrationNumber der Extension Admission). Falls der Basiseintrag + * (baseDirectoryEntry) ohne Zertifikat angelegt wird, kann in Operation add_Directory_Entry die telematikID angegeben werden. Damit ist der + * Verzeichniseintrag bereits über die telematikID auffindbar. + * + * @return telematikID + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Wird beim Anlegen des Eintrags vom VZD aus dem Zertifikat übernommen (Feld registrationNumber der Extension Admission). Falls der Basiseintrag (baseDirectoryEntry) ohne Zertifikat angelegt wird, kann in Operation add_Directory_Entry die telematikID angegeben werden. Damit ist der Verzeichniseintrag bereits über die telematikID auffindbar.") + + public String getTelematikID() { + return telematikID; + } + + + public void setTelematikID(String telematikID) { + this.telematikID = telematikID; + } + + + /** + * Get professionOID + * + * @return professionOID + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public List<String> getProfessionOID() { + return professionOID; + } + + + public UserCertificate usage(List<UsageEnum> usage) { + + this.usage = usage; + return this; + } + + public UserCertificate addUsageItem(UsageEnum usageItem) { + if (this.usage == null) { + this.usage = new ArrayList<>(); + } + this.usage.add(usageItem); + return this; + } + + /** + * Nutzungskennzeichnung kann pro Zertifikat mehrfach vergeben werden. Vorgegebener Wertebereich [KOM-LE, ePA]. Obligatorisch für LEI und KTR mit + * vorgegebenem Wert usage=ePA + * + * @return usage + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Nutzungskennzeichnung kann pro Zertifikat mehrfach vergeben werden. Vorgegebener Wertebereich [KOM-LE, ePA]. Obligatorisch für LEI und KTR mit vorgegebenem Wert usage=ePA") + + public List<UsageEnum> getUsage() { + return usage; + } + + + public void setUsage(List<UsageEnum> usage) { + this.usage = usage; + } + + + public UserCertificate userCertificate(String userCertificate) { + + this.userCertificate = userCertificate; + return this; + } + + /** + * Zertifikat im DER Format. Base64 kodiert. Die pflegende Stelle erhält das Zertifikat vom TSP oder falls das nicht möglich ist wird ein + * Ersatzverfahren abgestimmt. + * + * @return userCertificate + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Zertifikat im DER Format. Base64 kodiert. Die pflegende Stelle erhält das Zertifikat vom TSP oder falls das nicht möglich ist wird ein Ersatzverfahren abgestimmt.") + + public String getUserCertificate() { + return userCertificate; + } + + + public void setUserCertificate(String userCertificate) { + this.userCertificate = userCertificate; + } + + + public UserCertificate description(String description) { + + this.description = description; + return this; + } + + /** + * Dieses Attribut ermöglicht das Zertifikat zu beschreiben, um die Administration des VZD Eintrags zu vereinfachen. + * + * @return description + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Dieses Attribut ermöglicht das Zertifikat zu beschreiben, um die Administration des VZD Eintrags zu vereinfachen.") + + public String getDescription() { + return description; + } + + + public void setDescription(String description) { + this.description = description; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UserCertificate userCertificate = (UserCertificate) o; + return Objects.equals(this.dn, userCertificate.dn) && + Objects.equals(this.entryType, userCertificate.entryType) && + Objects.equals(this.telematikID, userCertificate.telematikID) && + Objects.equals(this.professionOID, userCertificate.professionOID) && + Objects.equals(this.usage, userCertificate.usage) && + Objects.equals(this.userCertificate, userCertificate.userCertificate) && + Objects.equals(this.description, userCertificate.description); + } + + @Override + public int hashCode() { + return Objects.hash(dn, entryType, telematikID, professionOID, usage, userCertificate, description); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class UserCertificate {\n"); + sb.append(" dn: ").append(toIndentedString(dn)).append("\n"); + sb.append(" entryType: ").append(toIndentedString(entryType)).append("\n"); + sb.append(" telematikID: ").append(toIndentedString(telematikID)).append("\n"); + sb.append(" professionOID: ").append(toIndentedString(professionOID)).append("\n"); + sb.append(" usage: ").append(toIndentedString(usage)).append("\n"); + sb.append(" userCertificate: ").append(toIndentedString(userCertificate)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/CommandNamesEnum.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/CommandNamesEnum.java new file mode 100644 index 0000000..cadcfba --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/CommandNamesEnum.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient; + +public enum CommandNamesEnum { + + ADD_DIR_ENTRY("addDirectoryEntries"), + READ_DIR_ENTRY("readDirectoryEntries"), + MOD_DIR_ENTRY("modifyDirectoryEntries"), + DEL_DIR_ENTRY("deleteDirectoryEntries"), + ADD_DIR_CERT("addDirectoryEntryCertificate"), + READ_DIR_CERT("readDirectoryEntryCertificate"), + MOD_DIR_CERT("modifyDirectoryEntryCertificate"), + DEL_DIR_CERT("deleteDirectoryEntryCertificate"); + + private final String name; + + CommandNamesEnum(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + public static CommandNamesEnum getEntry(String name) { + for (CommandNamesEnum cn : CommandNamesEnum.values()) { + if (name.equals(cn.getName())) { + return cn; + } + } + return null; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/GemStringUtils.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/GemStringUtils.java new file mode 100644 index 0000000..387b214 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/GemStringUtils.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +public class GemStringUtils { + + private static final String gothic = "____ ______________________ _________ .__ .__ __ \n\\ \\ / /\\____ /\\______ \\ \\_ ___ \\| | |__| ____ _____/ |_ \n \\ Y / / / | | \\ ______ / \\ \\/| | | |/ __ \\ / \\ __\\ \n \\ / / /_ | ` \\ /_____/ \\ \\___| |_| \\ ___/| | \\ |\n \\___/ /_______ \\/_______ / \\______ /____/__|\\___ >___| /__|\n \\/ \\/ \\/ \\/ \\/ \n"; + private static final String goofy = " __ __ __ ___________ __ _____ __ __ ___ __ __\n| | | | (___ ) | \\ / __) \\ | (_ _) \\ ___) | \\ | | (__ __) \n| | | | / / | | ___ | / | | | | | (__ | | \\ | | | \n| | | | / / | | (___) | | | | | | | __) | | \\ \\| | | | \n \\ \\/ / / /__ | | | \\__ | |__ _| |_ | (___ | | \\ | | | \n__\\ /___( )_| /_________\\ )_/ )_( )_/ )_ | |___\\ |____| |____\n"; + private static final String fuzzy = ".-..-..----..---. .--. .-. _ .-. \n: :: :`--. :: . : : .--': : :_; .' `.\n: :: : ,',': :: : _____ : : : : .-. .--. ,-.,-.`. .'\n: `' ;.'.'_ : :; ::_____:: :__ : :_ : :' '_.': ,. : : : \n `.,' :____;:___.' `.__.'`.__;:_;`.__.':_;:_; :_;\n"; + private static final String fourtops = "| |~~/|~~\\ /~~|' | \n \\ / / | |---| ||/~/|/~\\~|~\n \\/ /__|__/ \\__||\\/_| || \n"; + private static final String fender = "\\\\ // |'''''/ '||'''|. .|'''', '||` || \n \\\\ // // || || || || '' || \n \\\\ // // || || --- || || || .|''|, `||''|, ''||'' \n \\\\// // || || || || || ||..|| || || || \n \\/ /.....| .||...|' `|....' .||. .||. `|... .|| ||. `|..' \n"; + private static final String univers = "8b d8 888888888888 88888888ba, ,ad8888ba, 88 88 \n`8b d8' ,88 88 `\"8b d8\"' `\"8b 88 \"\" ,d \n `8b d8' ,88\" 88 `8b d8' 88 88 \n `8b d8' ,88\" 88 88 88 88 88 ,adPPYba, 8b,dPPYba, MM88MMM \n `8b d8' ,88\" 88 88 aaaaaaaa 88 88 88 a8P_____88 88P' `\"8a 88 \n `8b d8' ,88\" 88 8P \"\"\"\"\"\"\"\" Y8, 88 88 8PP\"\"\"\"\"\"\" 88 88 88 \n `888' 88\" 88 .a8P Y8a. .a8P 88 88 \"8b, ,aa 88 88 88, \n `8' 888888888888 88888888Y\"' `\"Y8888Y\"' 88 88 `\\\"Ybbd8\\\"'88 88 \\\"Y888 \"\n"; + public static final List<String> pics = Arrays + .asList(gothic, goofy, fuzzy, fourtops, fender, univers); + + public static String listToString(List<String> list) { + StringBuilder sb = new StringBuilder(); + for (String s : list) { + sb.append(s + ","); + } + if (sb.length() > 0) { + sb.setLength(sb.length() - 1); + } + return sb.toString(); + } + + public static String getPic() { + StringBuffer asciPic = new StringBuffer(); + asciPic.append("\n=====================================================================\n"); + asciPic.append("\t\t\t=========================================================\n"); + asciPic.append("=====================================================================\n"); + asciPic.append(pics.get(new Random().nextInt(GemStringUtils.pics.size()))); + asciPic.append("=====================================================================\n"); + asciPic.append("\t\t\t=========================================================\n"); + asciPic.append("=====================================================================\n"); + return asciPic.toString(); + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/Main.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/Main.java new file mode 100644 index 0000000..c10de94 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/Main.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient; + +import de.gematik.ti.epa.vzd.gemClient.command.CommandsBuilder; +import de.gematik.ti.epa.vzd.gemClient.command.ExecutionController; +import de.gematik.ti.epa.vzd.gemClient.command.commandExecutions.ExecutionCollection; +import de.gematik.ti.epa.vzd.gemClient.invoker.ConfigHandler; +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class Main { + + private static final Logger LOG = LoggerFactory.getLogger(Main.class); + + public static void main(final String[] args) { + LOG.info("VZD-Client started"); + LOG.info(GemStringUtils.getPic()); + ConfigHandler.init(args); + start(); + } + + private static void start() { + ExecutionCollection.init(new GemApiClient()); + List<CommandType> commands = new CommandsBuilder().buildCommands(); + ConfigHandler configHandler = ConfigHandler.getInstance(); + LOG.debug("============ Execution parameter ============"); + LOG.debug("Server: " + configHandler.getBasePath()); + LOG.debug("OAuth Server: " + configHandler.getRetryingOAuthPath()); + LOG.debug("Command data: " + configHandler.getCommandsPath()); + LOG.debug("Commands in progress: " + commands.size()); + LOG.debug("============================================="); + new ExecutionController().execute(commands); + } + + // <editor-fold desc="Private Constructor"> + private Main() { + super(); + } + // </editor-fold> +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemCertificateAdministrationApi.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemCertificateAdministrationApi.java new file mode 100644 index 0000000..ba9db31 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemCertificateAdministrationApi.java @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.api; + +import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; +import de.gematik.ti.epa.vzd.client.invoker.ApiCallback; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.Pair; +import de.gematik.ti.epa.vzd.client.invoker.auth.OAuth; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Overrides all functions of CertificateAdministration api that build calls for the different commands to add the OAuth2 Token to the header + */ +public class GemCertificateAdministrationApi extends CertificateAdministrationApi { + + private GemApiClient localVarApiClient; + + public GemCertificateAdministrationApi(GemApiClient apiClient) { + this.localVarApiClient = apiClient; + } + + @Override + public okhttp3.Call readDirectoryCertificatesCall(String uid, String certificateEntryID, + String entryType, String telematikID, String professionOID, String usage, + final ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries/Certificates"; + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + if (uid != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("uid", uid)); + } + + if (certificateEntryID != null) { + localVarQueryParams.addAll( + localVarApiClient.parameterToPair("certificateEntryID", certificateEntryID)); + } + + if (entryType != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("entryType", entryType)); + } + + if (telematikID != null) { + localVarQueryParams + .addAll(localVarApiClient.parameterToPair("telematikID", telematikID)); + } + + if (professionOID != null) { + localVarQueryParams + .addAll(localVarApiClient.parameterToPair("professionOID", professionOID)); + } + + if (usage != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("usage", usage)); + } + + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = {"application/json"}; + + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + //Setze Auth token + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient + .selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, + localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, + localVarAuthNames, _callback); + } + + @Override + public okhttp3.Call addDirectoryEntryCertificateCall(String uid, + UserCertificate userCertificate, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = userCertificate; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/Certificates" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + //Setze Auth token + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient + .selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, + localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, + localVarAuthNames, _callback); + } + + @Override + public okhttp3.Call deleteDirectoryEntryCertificateCall(String uid, String certificateEntryID, + final ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/Certificates/{certificateEntryID}" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())) + .replaceAll("\\{" + "certificateEntryID" + "\\}", + localVarApiClient.escapeString(certificateEntryID.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient + .selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + //Set OAuth2 token + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "DELETE", localVarQueryParams, localVarCollectionQueryParams, + localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, + localVarAuthNames, _callback); + } + + @Override + public okhttp3.Call modifyDirectoryEntryCertificateCall(String uid, String certificateEntryID, + UserCertificate userCertificate, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = userCertificate; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/Certificates/{certificateEntryID}" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())) + .replaceAll("\\{" + "certificateEntryID" + "\\}", + localVarApiClient.escapeString(certificateEntryID.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient + .selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + //Set OAuth2 token + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "PUT", localVarQueryParams, localVarCollectionQueryParams, + localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, + localVarAuthNames, _callback); + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemDirectoryEntryAdministrationApi.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemDirectoryEntryAdministrationApi.java new file mode 100644 index 0000000..ec3bef2 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemDirectoryEntryAdministrationApi.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.api; + +import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.client.invoker.ApiCallback; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.Pair; +import de.gematik.ti.epa.vzd.client.invoker.auth.OAuth; +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import okhttp3.Call; + +/** + * Overrides all functions of DirectoryEntryAdministration api that build calls for the different commands to add the OAuth2 Token to the header + */ +public class GemDirectoryEntryAdministrationApi extends DirectoryEntryAdministrationApi { + + private GemApiClient localVarApiClient; + + public GemDirectoryEntryAdministrationApi(GemApiClient apiClient) { + this.localVarApiClient = apiClient; + } + + @Override + public Call addDirectoryEntryCall(CreateDirectoryEntry createDirectoryEntry, + ApiCallback _callback) throws ApiException { + Object localVarPostBody = createDirectoryEntry; + + // create path and map variables + String localVarPath = "/DirectoryEntries"; + + List<Pair> localVarQueryParams = new ArrayList<>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<>(); + Map<String, String> localVarHeaderParams = new HashMap<>(); + Map<String, String> localVarCookieParams = new HashMap<>(); + Map<String, Object> localVarFormParams = new HashMap<>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient + .selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + // add OAuth2 token for authorization + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, + localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, + localVarAuthNames, _callback); + } + + @Override + public Call deleteDirectoryEntryCall(String uid, ApiCallback _callback) + throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json;charset=UTF-8" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + // add OAuth2 token for authorization + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient + .selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "DELETE", localVarQueryParams, localVarCollectionQueryParams, + localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, + localVarAuthNames, _callback); + } + + @Override + public Call modifyDirectoryEntryCall(String uid, BaseDirectoryEntry baseDirectoryEntry, + ApiCallback _callback) throws ApiException { + Object localVarPostBody = baseDirectoryEntry; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/baseDirectoryEntries" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + // add OAuth2 token for authorization + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "PUT", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + @Override + public Call readDirectoryEntryCall(String uid, String givenName, String sn, String cn, + String displayName, String streetAddress, String postalCode, String localityName, + String stateOrProvienceName, String title, String organization, String otherName, + String specialization, String domainID, String personalEntry, String dataFromAuthority, + ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries"; + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + if (uid != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("uid", uid)); + } + + if (givenName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("givenName", givenName)); + } + + if (sn != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("sn", sn)); + } + + if (cn != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("cn", cn)); + } + + if (displayName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("displayName", displayName)); + } + + if (streetAddress != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("streetAddress", streetAddress)); + } + + if (postalCode != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("postalCode", postalCode)); + } + + if (localityName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("localityName", localityName)); + } + + if (stateOrProvienceName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("stateOrProvienceName", stateOrProvienceName)); + } + + if (title != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("title", title)); + } + + if (organization != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("organization", organization)); + } + + if (otherName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("otherName", otherName)); + } + + if (specialization != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("specialization", specialization)); + } + + if (domainID != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("domainID", domainID)); + } + + if (personalEntry != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("personalEntry", personalEntry)); + } + + if (dataFromAuthority != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("dataFromAuthority", dataFromAuthority)); + } + + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + // add OAuth2 token for authorization + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilder.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilder.java new file mode 100644 index 0000000..92d78ab --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilder.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command; + +import de.gematik.ti.epa.vzd.gemClient.exceptions.ReadException; +import de.gematik.ti.epa.vzd.gemClient.invoker.ConfigHandler; +import generated.CommandListType; +import generated.CommandType; +import generated.ObjectFactory; +import java.io.File; +import java.io.IOException; +import java.util.List; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +/** + * This class is responsible for reading the commands out of the .xml File. Before the commands can be build the ConfigHandler have to be initialized + */ +public class CommandsBuilder { + + private JAXBContext jaxbContext; + private Logger LOGGER = LoggerFactory.getLogger(CommandsBuilder.class); + + private static DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + + /** + * Standard constructor initialize an jaxbContext + */ + public CommandsBuilder() { + try { + jaxbContext = JAXBContext.newInstance(ObjectFactory.class); + } catch (JAXBException e) { + throw new ReadException("Error occurred by creating JAXBContext"); + } + } + + /** + * Reads the given command file and creates a list of commands to execute + * + * @return CommandListType - List of all commands to execute + */ + public List<CommandType> buildCommands() { + CommandListType commandList; + ConfigHandler configHandler = ConfigHandler.getInstance(); + try { + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + Document doc = builderFactory.newDocumentBuilder() + .parse(new File(configHandler.getCommandsPath())); + Object obj = unmarshaller.unmarshal(doc); + Object commands = ((JAXBElement) obj).getValue(); + if (commands instanceof CommandListType) { + commandList = (CommandListType) commands; + LOGGER.debug("Commands have been build"); + return commandList.getCommand(); + } + } catch (ParserConfigurationException | SAXException | JAXBException e) { + throw new ReadException( + "An error have been occurred while reading your command file. Please check if this file is a valid .xml file"); + } catch (IOException e) { + throw new ReadException( + "A problem with your named file have occurred. Please if check " + configHandler.getCommandsPath() + " exist"); + } + return null; + } + +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/ExecutionController.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/ExecutionController.java new file mode 100644 index 0000000..a69e454 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/ExecutionController.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command; + +import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gemClient.command.commandExecutions.ExecutionBase; +import de.gematik.ti.epa.vzd.gemClient.command.commandExecutions.ExecutionCollection; +import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; +import generated.CommandType; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * This class handles the execution process. It orders the commands to the specific executors where they belong and calls the + */ +public class ExecutionController { + + private Logger LOG = LoggerFactory.getLogger(ExecutionController.class); + + + /** + * Order the commands to the specific executions where they belong and than call the execution functions of the specific executors + * + * @param commandList + */ + public void execute(List<CommandType> commandList) { + Map<String, Boolean> report = new HashMap<>(); + loadCommands(commandList); + LOG.debug("Execution -> Run executions"); + boolean correctExecution = true; + for (ExecutionBase executor : ExecutionCollection.getInstance().getExecutors()) { + String executorName = extractExecutorName(executor); + if (!executor.executeCommands()) { + correctExecution = false; + report.put(executorName, false); + LOG.error("Error while execute commands in " + executorName); + } else { + report.put(executorName, true); + LOG.debug("All commands of " + executorName + " operated correctly"); + } + if (!executor.postCheck()) { + throw new CommandException("Command executed, but postcheck failed!"); + } + } + logReport(report, correctExecution); + return; + } + + /** + * Gets the name of the specific executor for logging + * + * @param executor + * @return + */ + private String extractExecutorName(ExecutionBase executor) { + String[] splitClass = executor.getClass().getName().split("\\."); + return splitClass[splitClass.length - 1]; + } + + private void logReport(Map<String, Boolean> report, boolean correctExecution) { + Iterator<String> keys = report.keySet().iterator(); + while (keys.hasNext()) { + String key = keys.next(); + LOG.info(report.get(key) + " <-- All executions for " + key + " run correctly"); + } + String path = System.getProperties().getProperty("l4j.logDir") == null ? + System.getProperties().getProperty("java.io.tmpdir") + "logs" + : System.getProperties().getProperty("l4j.logDir"); + LOG.info("Execution -> All executions done" + (correctExecution ? " correctly" + : ". Some commands failed. Please look at the logfile at " + path)); + } + + private void loadCommands(List<CommandType> commandList) { + LOG.debug("Execution -> Precheck started"); + boolean commandError = false; + for (CommandType command : commandList) { + boolean unknownCommand = true; + for (ExecutionBase specificExecutor : ExecutionCollection.getInstance() + .getExecutors()) { + if (specificExecutor + .canHandleCommand(CommandNamesEnum.getEntry(command.getName()))) { + unknownCommand = false; + if (!specificExecutor.preCheck(command)) { + commandError = true; + } + } + } + if (unknownCommand) { + LOG.error("Unknown command " + command.getName() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + commandError = true; + } + } + if (commandError) { + throw new CommandException("Commands not executed, preCheck failed!"); + } + LOG.debug("Execution -> Precheck successful"); + } + + /** + * If a command was identified with the wrong name, the name can be changed and reordered by this function. Only call this in pre-check phase! + * + * @param command with another name + * @return + */ + public boolean reorder(CommandType command) { + for (ExecutionBase specificExecutor : ExecutionCollection.getInstance().getExecutors()) { + if (specificExecutor.canHandleCommand(CommandNamesEnum.getEntry(command.getName()))) { + return specificExecutor.preCheck(command); + } + } + return false; + } +} + diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/Transformer.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/Transformer.java new file mode 100644 index 0000000..8a0be01 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/Transformer.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command; + +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import generated.CommandType; +import generated.DistinguishedNameType; +import generated.UserCertificateType; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.StringUtils; + +/** + * This helper class helps to transform the input data (CommandType) to objects the API needs + */ +public class Transformer { + + /** + * Transforms CommandType to BaseDirectorEntry + * + * @param command <type>CommandType</type> + * @return baseDirectoryEntry <type>BaseDirectoryEntry</type> + */ + public static BaseDirectoryEntry getBaseDirectoryEntryFromCommandType(CommandType command) { + BaseDirectoryEntry baseDirectoryEntry = new BaseDirectoryEntry(); + + if (command.getDn() != null) { + baseDirectoryEntry.setDn(getDnFromDnType(command.getDn())); + } + baseDirectoryEntry.setDisplayName(command.getDisplayName()); + baseDirectoryEntry.setStreetAddress(command.getStreetAddress()); + baseDirectoryEntry.setPostalCode(command.getPostalCode()); + baseDirectoryEntry.setLocalityName(command.getLocalityName()); + baseDirectoryEntry.setStateOrProvinceName(command.getStateOrProvinceName()); + // This setter is manually added to the generated class BaseDirectoryEntry + baseDirectoryEntry.setCn(command.getCn()); + baseDirectoryEntry.setTitle(command.getTitle()); + baseDirectoryEntry.setOrganization(command.getOrganization()); + baseDirectoryEntry.setOtherName(command.getOtherName()); + if (command.getSpecialization().size() != 0) { + baseDirectoryEntry.setSpecialization(command.getSpecialization()); + } + if (command.getDomainID().size() != 0) { + baseDirectoryEntry.setDomainID(command.getDomainID()); + } + return baseDirectoryEntry; + } + + private static DistinguishedName getDnFromDnType(DistinguishedNameType dn) { + DistinguishedName distinguishedName = new DistinguishedName(); + distinguishedName.setUid(dn.getUid()); + distinguishedName.setCn(dn.getCn()); + if (dn.getDc().size() != 0) { + distinguishedName.setDc(dn.getDc()); + } + if (dn.getOu().size() != 0) { + distinguishedName.setOu(dn.getOu()); + } + return distinguishedName; + } + + /** + * Transforms CommandType to CreateDirectoryEntry + * + * @param command <type>CommandType</type> + * @return baseDirectoryEntry <type>BaseDirectoryEntry</type> + */ + public static CreateDirectoryEntry getCreateDirectoryEntry(CommandType command) { + CreateDirectoryEntry createDirectoryEntry = new CreateDirectoryEntry(); + createDirectoryEntry.setDirectoryEntryBase(getBaseDirectoryEntryFromCommandType(command)); + if (command.getUserCertificate() != null) { + createDirectoryEntry + .setUserCertificates(getUserCertificates(command.getUserCertificate())); + } + return createDirectoryEntry; + } + + private static List<UserCertificate> getUserCertificates( + UserCertificateType userCertificateType) { + List<UserCertificate> userCertificateList = new ArrayList<>(); + UserCertificate userCertificate = new UserCertificate(); + + if (userCertificateType.getDn() != null) { + userCertificate.setDn(getDnFromDnType(userCertificateType.getDn())); + } + userCertificate.setTelematikID(userCertificateType.getTelematikID()); + if (userCertificateType.getUsage().size() != 0) { + for (String usage : userCertificateType.getUsage()) { + userCertificate.addUsageItem(UserCertificate.UsageEnum.fromValue(usage)); + } + } + userCertificate.setDescription(userCertificateType.getDescription()); + if (StringUtils.isNoneBlank(userCertificateType.getUserCertificate())) { + userCertificate + .setUserCertificate(userCertificateType.getUserCertificate().replaceAll("\\n", "")); + } + + userCertificateList.add(userCertificate); + return userCertificateList; + } + +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryCertExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryCertExecution.java new file mode 100644 index 0000000..e190c77 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryCertExecution.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gemClient.api.GemCertificateAdministrationApi; +import de.gematik.ti.epa.vzd.gemClient.command.Transformer; +import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import generated.DistinguishedNameType; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific execution for Command "AddDirectoryEntryCertificate" + */ +public class AddDirEntryCertExecution extends ExecutionBase { + + private CertificateAdministrationApi certificateAdministrationApi; + private Logger LOG = LoggerFactory.getLogger(AddDirEntryExecution.class); + + public AddDirEntryCertExecution(GemApiClient api) { + super(api, CommandNamesEnum.ADD_DIR_CERT); + certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); + } + + @Override + public boolean checkValidation(CommandType command) { + if (command.getUserCertificate() == null) { + return false; + } + if (StringUtils.isBlank(command.getUserCertificate().getUserCertificate())) { + return false; + } + String uid = null; + if (command.getDn() != null) { + uid = command.getDn().getUid(); + } + if (StringUtils.isBlank(uid)) { + DistinguishedNameType certDn = command.getUserCertificate().getDn(); + if (certDn != null) { + uid = certDn.getUid(); + } + } + if (StringUtils.isBlank(uid)) { + return false; + } + return true; + } + + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + if (commands.size() == 0) { + return true; + } + for (CommandType command : commands) { + if (isEntryPresent(command)) { + try { + executeCommand(command); + } catch (Exception ex) { + LOG.error("An error have occured: " + ex.getMessage()); + runSuccessful = false; + } + } else { + runSuccessful = doModify(command); + } + } + return runSuccessful; + } + + private ApiResponse<DistinguishedName> executeCommand(CommandType command) { + apiClient.validateToken(); + boolean runSucsessfull = true; + CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); + + ApiResponse<DistinguishedName> response = null; + for (UserCertificate userCertificate : createDirectoryEntry.getUserCertificates()) { + try { + String uid = getUid(createDirectoryEntry.getDirectoryEntryBase(), userCertificate); + response = addSingleCertificate(uid, userCertificate); + if (response.getStatusCode() == 201) { + LOG.debug( + "Certificate successful added: \n" + userCertificate); + } + } catch (ApiException e) { + runSucsessfull = false; + LOG.error( + "Something went wrong will adding certificate. Responsecode: " + e.getCode() + + " certificate: " + userCertificate + .getUserCertificate()); + } + } + if (!runSucsessfull) { + throw new CommandException( + "At least one certificate could not be added in:" + "\n" + Transformer + .getCreateDirectoryEntry(command)); + } + + return response; + } + + private String getUid(BaseDirectoryEntry directoryEntryBase, UserCertificate userCertificate) { + // uid present because checked in validation + String uidCert = null; + String uidEntry = null; + + if (userCertificate.getDn() != null) { + uidCert = userCertificate.getDn().getUid(); + } + + if (directoryEntryBase != null) { + DistinguishedName dn = directoryEntryBase.getDn(); + if (dn != null) { + uidEntry = dn.getUid(); + } + } + return uidCert == null ? uidEntry : uidCert; + } + + private ApiResponse<DistinguishedName> addSingleCertificate(String uid, + UserCertificate userCertificate) throws ApiException { + return certificateAdministrationApi + .addDirectoryEntryCertificateWithHttpInfo(uid, userCertificate); + } + + private boolean doModify(CommandType command) { + LOG.debug( + "Certificate is already present in VZD. Will Proceed with modify certificate entry command"); + try { + ExecutionCollection.getInstance().getModifyDirEntryCertExecution() + .executeCommand(command); + return true; + } catch (Exception ex) { + LOG.error( + "Modify certificate entry execution failed. " + ex.getMessage() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + return false; + } + } + + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + return false; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecution.java new file mode 100644 index 0000000..69dafa7 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecution.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gemClient.api.GemDirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.gemClient.command.Transformer; +import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import generated.UserCertificateType; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific execution for Command "AddDirectoryEntry" + */ +public class AddDirEntryExecution extends ExecutionBase { + + private final DirectoryEntryAdministrationApi directoryEntryAdministrationApi; + private Logger LOG = LoggerFactory.getLogger(AddDirEntryExecution.class); + + public AddDirEntryExecution(GemApiClient api) { + super(api, CommandNamesEnum.ADD_DIR_ENTRY); + directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); + } + + @Override + public boolean checkValidation(CommandType command) { + boolean check = true; + UserCertificateType userCertificateType = command.getUserCertificate(); + if (userCertificateType == null) { + LOG.error( + "Missing element \"UserCertificate\" " + command.getName() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + check = false; + } else { + String telematikId = userCertificateType.getTelematikID(); + String userCertificate = userCertificateType.getUserCertificate(); + if ((StringUtils.isBlank(telematikId) && StringUtils + .isBlank(userCertificate))) { + check = false; + } + if (check == false) { + LOG.error( + "Missing argument -> telematikId or userCertificate for command " + command + .getName() + + "\n" + Transformer.getBaseDirectoryEntryFromCommandType(command)); + } + } + return check; + } + + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + if (commands.size() == 0) { + return true; + } + for (CommandType command : commands) { + if (!isEntryPresent(command)) { + try { + executeCommand(command); + } catch (Exception ex) { + LOG.error("An error have occured: " + ex.getMessage() + "\n" + Transformer + .getCreateDirectoryEntry(command)); + runSuccessful = false; + } + } else { + runSuccessful = doModify(command); + } + } + return runSuccessful; + } + + /** + * Function that execute one command and logs the result + * + * @param command + * @return + * @throws ApiException + */ + protected ApiResponse<DistinguishedName> executeCommand(CommandType command) throws + ApiException { + apiClient.validateToken(); + CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); + ApiResponse<DistinguishedName> response = directoryEntryAdministrationApi + .addDirectoryEntryWithHttpInfo(createDirectoryEntry); + if (response.getStatusCode() == 201) { + LOG.debug("Add directory entry execution successful operated\n" + response.getData()); + } else { + throw new CommandException( + "Add directory entry execution failed. Response-Status was: " + response + .getStatusCode() + "\n" + Transformer.getCreateDirectoryEntry(command)); + } + return response; + } + + private boolean doModify(CommandType command) { + LOG.debug( + "Entry is already present in VZD. Will Proceed with modify directory entry command"); + try { + ExecutionCollection.getInstance().getModifyDirEntry().executeCommand(command); + return true; + } catch (Exception ex) { + LOG.error( + "Modify directory entry execution failed. " + ex.getMessage() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + return false; + } + } + + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + return false; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecution.java new file mode 100644 index 0000000..5e0790c --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecution.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gemClient.api.GemCertificateAdministrationApi; +import de.gematik.ti.epa.vzd.gemClient.command.Transformer; +import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific execution for Command "DeleteDirectoryEntryCertificate" + */ +public class DeleteDirEntryCertExecution extends ExecutionBase { + + private CertificateAdministrationApi certificateAdministrationApi; + private Logger LOG = LoggerFactory.getLogger(AddDirEntryExecution.class); + + + public DeleteDirEntryCertExecution(GemApiClient api) { + super(api, CommandNamesEnum.DEL_DIR_CERT); + certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); + } + + @Override + public boolean checkValidation(CommandType command) { + String uid = null; + String cn = null; + if (command.getUserCertificate() != null) { + if (command.getUserCertificate().getDn() != null) { + uid = command.getUserCertificate().getDn().getUid(); + cn = command.getUserCertificate().getDn().getCn(); + } + } + if (StringUtils.isBlank(uid)) { + if (command.getDomainID() != null) { + uid = command.getDn().getUid(); + } + } + if (StringUtils.isBlank(uid) || StringUtils.isBlank(cn)) { + return false; + } + return true; + } + + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + if (commands.size() == 0) { + return true; + } + for (CommandType command : commands) { + System.out.println( + "Warning isEntryPresent abgeschlatet <- executeCommands " + this.getClass()); + try { + executeCommand(command); + } catch (Exception ex) { + LOG.error("An error have occured: " + ex.getMessage()); + runSuccessful = false; + } + } + return runSuccessful; + } + + private void executeCommand(CommandType command) { + apiClient.validateToken(); + boolean runSucsessfull = true; + CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); + + ApiResponse<Void> response; + for (UserCertificate userCertificate : createDirectoryEntry.getUserCertificates()) { + //todo check if present mit einfügen + try { + String uid = getUid(createDirectoryEntry.getDirectoryEntryBase(), userCertificate); + String cn = command.getUserCertificate().getDn().getCn(); + response = deleteSingleCertificate(uid, cn); + if (response.getStatusCode() == 200) { + LOG.debug( + "Certificate successful deleted: \n" + userCertificate); + } + } catch (ApiException e) { + runSucsessfull = false; + LOG.error( + "Something went wrong will adding certificate. Responsecode: " + e.getCode() + + " certificate: " + userCertificate + .getUserCertificate()); + } + } + if (!runSucsessfull) { + throw new CommandException( + "At least one certificate could not be added in:" + "\n" + Transformer + .getCreateDirectoryEntry(command)); + } + } + + private String getUid(BaseDirectoryEntry directoryEntryBase, UserCertificate userCertificate) { + String uidCert = userCertificate.getDn().getUid(); + String uidEntry = null; + + if (directoryEntryBase != null) { + DistinguishedName dn = directoryEntryBase.getDn(); + if (dn != null) { + uidEntry = dn.getUid(); + } + } + return uidCert == null ? uidEntry : uidCert; + } + + private ApiResponse<Void> deleteSingleCertificate(String uid, String certificateEntryId) + throws ApiException { + return certificateAdministrationApi + .deleteDirectoryEntryCertificateWithHttpInfo(uid, certificateEntryId); + } + + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + return false; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecution.java new file mode 100644 index 0000000..c30a289 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecution.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gemClient.api.GemDirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.gemClient.command.Transformer; +import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific execution for Command "DeleteDirectoryEntry" + */ +public class DeleteDirEntryExecution extends ExecutionBase { + + private Logger LOG = LoggerFactory.getLogger(DeleteDirEntryExecution.class); + + private final DirectoryEntryAdministrationApi directoryEntryAdministrationApi; + + public DeleteDirEntryExecution(GemApiClient api) { + super(api, CommandNamesEnum.DEL_DIR_ENTRY); + directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); + } + + @Override + public boolean checkValidation(CommandType command) { + boolean check = true; + if (command.getDn() != null) { + if (command.getDn().getUid() == null || command.getDn().getUid().equals("")) { + LOG.error( + "Missing argument -> uid for command " + command.getName() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + check = false; + } + } else { + LOG.error("Missing element \"dn\" " + command.getName() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + check = false; + } + return check; + } + + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + if (commands.size() == 0) { + return true; + } + for (CommandType command : commands) { + if (isEntryPresent(command)) { + try { + executeCommand(command); + } catch (Exception ex) { + LOG.error("An error have occured: " + ex.getMessage()); + runSuccessful = false; + } + } else { + LOG.debug(command.getDn().getUid() + " could not be found"); + runSuccessful = false; + } + } + return runSuccessful; + } + + private void executeCommand(CommandType command) throws ApiException { + apiClient.validateToken(); + ApiResponse<Void> response = directoryEntryAdministrationApi + .deleteDirectoryEntryWithHttpInfo(command.getDn().getUid()); + if (response.getStatusCode() == 200) { + LOG.debug("Delete directory entry execution successful operated for " + command.getDn() + .getUid()); + } else if (response.getStatusCode() == 404) { + LOG.debug(command.getDn().getUid() + " could not be found"); + throw new CommandException(command.getDn().getUid() + " could not be found"); + } else { + throw new CommandException( + "Delete directory entry execution failed. Response-Status was: " + response + .getStatusCode() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + } + } + + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + return false; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBase.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBase.java new file mode 100644 index 0000000..3748617 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBase.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gemClient.command.Transformer; +import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gemClient.exceptions.GemClientException; +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import generated.DistinguishedNameType; +import generated.UserCertificateType; +import java.util.ArrayList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Represents the base for every specific execution + */ +public abstract class ExecutionBase { + + private Logger LOG = LoggerFactory.getLogger(ExecutionBase.class); + + protected GemApiClient apiClient; + protected CommandNamesEnum execCommand; + protected List<CommandType> commands; + + public ExecutionBase(GemApiClient api, CommandNamesEnum cmd) { + this.apiClient = api; + this.execCommand = cmd; + this.commands = new ArrayList<>(); + } + + public boolean canHandleCommand(CommandNamesEnum cmd) { + return this.execCommand.equals(cmd); + } + + /** + * Checks the given command for validation and adds it to the queue of commands to execute + * + * @param command + * @return + */ + public boolean preCheck(CommandType command) { + try { + if (!checkValidation(command)) { + throw new CommandException( + "Command invalid. Please check " + command.getName() + " " + command + .getUserCertificate()); + } + commands.add(command); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + return false; + } + } + + /** + * Every single executor validates their commands and log the missing or wrong values + * + * @param command + * @return + */ + public abstract boolean checkValidation(CommandType command); + + /** + * Executes all commands in the queue + * + * @return + */ + public abstract boolean executeCommands(); + + /** + * Checks if the execution was successful and logs the result + * + * @return + */ + public boolean postCheck() { + try { + return true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + return false; + } + + /** + * This function proceed a read command, to check if a entry without cert is already present + * + * @param command + * @return + */ + protected boolean isEntryPresent(CommandType command) { + if (command.getDn() != null) { + CommandType searchCommand = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid(command.getDn().getUid()); + searchCommand.setDn(dn); + try { + ApiResponse<List<DirectoryEntry>> response = ExecutionCollection + .getInstance().getReadDirEntryExecution().executeCommand(searchCommand); + return response.getStatusCode() == 200 ? true : false; + } catch (ApiException ex) { + if (ex.getCode() == 0) { + throw new GemClientException( + "The server you address is probably not reachable at the moment"); + } + return false; + } + } else { + return serachByTelematikId(command); + } + } + + /** + * This function proceed a read command, to check if a entry without cert is already present + * + * @param command + * @return + */ + private boolean serachByTelematikId(CommandType command) { + UserCertificateType userCertificate = command.getUserCertificate(); + if (userCertificate != null) { + try { + CommandType searchCommand = new CommandType(); + searchCommand.setUserCertificate(new UserCertificateType()); + searchCommand.getUserCertificate().setTelematikID(userCertificate.getTelematikID()); + ApiResponse<List<UserCertificate>> response = ExecutionCollection + .getInstance().getReadDirEntryCertExecution().executeCommand(searchCommand); + return response.getStatusCode() == 200 ? true : false; + } catch (ApiException ex) { + LOG.error(ex.getMessage()); + return false; + } + } + throw new GemClientException("No valid parameter found for present check" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + } + +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionCollection.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionCollection.java new file mode 100644 index 0000000..e674b44 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionCollection.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import java.util.ArrayList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class that maintains all the specific Executions, have to be initialisized once with a GemApiClient + */ +public class ExecutionCollection { + + private final ReadDirEntryExecution readDirEntryExecution; + private final ReadDirEntryCertExecution readDirEntryCertExecution; + private final AddDirEntryExecution addDirEntryExecution; + private final AddDirEntryCertExecution addDirEntryCertExecution; + private final ModifyDirEntryExecution modifyDirEntryExecution; + private final ModifyDirEntryCertExecution modifyDirEntryCertExecution; + + private static Logger LOG = LoggerFactory.getLogger(ExecutionCollection.class); + + private ArrayList<ExecutionBase> specificExecutors = new ArrayList<>(); + + private static ExecutionCollection executions; + + /** + * Gives the instance as long as it exists. + * + * @return + */ + public static ExecutionCollection getInstance() { + if (executions == null) { + throw new InstantiationError("Please instance a executor first. It needs an ApiClient"); + } + return executions; + } + + /** + * Instances the executions + * + * @param apiClient + * @return + */ + public static ExecutionCollection init(GemApiClient apiClient) { + if (executions != null) { + LOG.error("Error occurred while initializing executions. Executor is already instanced"); + throw new InstantiationError("Executor is already instanced"); + } + executions = new ExecutionCollection(apiClient); + LOG.debug("Executions have been initialized correctly"); + + return executions; + } + + private ExecutionCollection(GemApiClient apiClient) { + this.readDirEntryExecution = new ReadDirEntryExecution(apiClient); + this.readDirEntryCertExecution = new ReadDirEntryCertExecution(apiClient); + this.addDirEntryExecution = new AddDirEntryExecution(apiClient); + this.addDirEntryCertExecution = new AddDirEntryCertExecution(apiClient); + this.modifyDirEntryExecution = new ModifyDirEntryExecution(apiClient); + this.modifyDirEntryCertExecution = new ModifyDirEntryCertExecution(apiClient); + + specificExecutors.add(readDirEntryExecution); + specificExecutors.add(addDirEntryExecution); + specificExecutors.add(modifyDirEntryExecution); + specificExecutors.add(new DeleteDirEntryExecution(apiClient)); + specificExecutors.add(readDirEntryCertExecution); + specificExecutors.add(addDirEntryCertExecution); + specificExecutors.add(modifyDirEntryCertExecution); + specificExecutors.add(new DeleteDirEntryCertExecution(apiClient)); + } + + + //<editor-fold desc="Getter"> + public List<ExecutionBase> getExecutors() { + return this.specificExecutors; + } + + public ReadDirEntryExecution getReadDirEntryExecution() { + return this.readDirEntryExecution; + } + + public ReadDirEntryCertExecution getReadDirEntryCertExecution() { + return this.readDirEntryCertExecution; + } + + public AddDirEntryExecution getAddDirEntryExecution() { + return this.addDirEntryExecution; + } + + public AddDirEntryCertExecution getAddDirEntryCertExecution() { + return this.addDirEntryCertExecution; + } + + public ModifyDirEntryExecution getModifyDirEntry() { + return this.modifyDirEntryExecution; + } + + public ModifyDirEntryCertExecution getModifyDirEntryCertExecution() { + return this.modifyDirEntryCertExecution; + } + //</editor-fold> +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryCertExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryCertExecution.java new file mode 100644 index 0000000..8ffe6b5 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryCertExecution.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gemClient.api.GemCertificateAdministrationApi; +import de.gematik.ti.epa.vzd.gemClient.command.Transformer; +import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import generated.DistinguishedNameType; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific execution for Command "ModifyDirectoryEntryCertificate" + */ +public class ModifyDirEntryCertExecution extends ExecutionBase { + + private Logger LOG = LoggerFactory.getLogger(ModifyDirEntryExecution.class); + private CertificateAdministrationApi certificateAdministrationApi; + + public ModifyDirEntryCertExecution(GemApiClient api) { + super(api, CommandNamesEnum.MOD_DIR_CERT); + certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); + } + + @Override + public boolean checkValidation(CommandType command) { + if (command.getUserCertificate() != null) { + if (command.getUserCertificate().getDn() != null) { + DistinguishedNameType dn = command.getUserCertificate().getDn(); + if (StringUtils.isBlank(dn.getUid()) || StringUtils.isBlank(dn.getCn())) { + return false; + } + } + } + return true; + } + + @Override + public boolean executeCommands() { + LOG.info("The execution for ModifyCertificate is exposed"); + return true; +// boolean runSuccessful = true; +// if (commands.size() == 0) { +// return true; +// } +// for (CommandType command : commands) { +// try { +// executeCommand(command); +// } catch (Exception ex) { +// runSuccessful = false; +// ex.printStackTrace(); +// } +// } +// return runSuccessful; + } + + protected ApiResponse<UserCertificate> executeCommand(CommandType command) { + apiClient.validateToken(); + CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); + ApiResponse<UserCertificate> response = null; + + for (UserCertificate userCertificate : createDirectoryEntry.getUserCertificates()) { + try { + response = certificateAdministrationApi + .modifyDirectoryEntryCertificateWithHttpInfo(userCertificate.getDn().getUid(), + userCertificate.getDn().getCn(), userCertificate); + } catch (ApiException e) { + e.printStackTrace(); + } + if (response.getStatusCode() == 200) { + LOG.debug( + "Modify directory entry execution successful operated\n" + response.getData()); + } else { + throw new CommandException( + "Modify directory entry execution failed. Response-Status was: " + response + .getStatusCode() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + } + } + return response; + } + + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + return false; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecution.java new file mode 100644 index 0000000..93fcf77 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecution.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gemClient.api.GemDirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.gemClient.command.Transformer; +import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific execution for Command "ModifyDirectoryEntry" + */ +public class ModifyDirEntryExecution extends ExecutionBase { + + private Logger LOG = LoggerFactory.getLogger(ModifyDirEntryExecution.class); + private final DirectoryEntryAdministrationApi directoryEntryAdministrationApi; + + public ModifyDirEntryExecution(GemApiClient api) { + super(api, CommandNamesEnum.MOD_DIR_ENTRY); + directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); + } + + @Override + public boolean checkValidation(CommandType command) { + boolean check = true; + if (command.getDn() != null) { + if (StringUtils.isBlank(command.getDn().getUid())) { + LOG.error( + "Missing argument -> uid for command " + command.getName() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + check = false; + } + } else { + LOG.error("Missing element \"dn\" " + command.getName() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + check = false; + } + return check; + } + + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + if (commands.size() == 0) { + return true; + } + for (CommandType command : commands) { + if (isEntryPresent(command)) { + try { + executeCommand(command); + } catch (Exception ex) { + LOG.error("An error have occured: " + ex.getMessage()); + runSuccessful = false; + } + } else { + runSuccessful = doAdd(command); + } + } + return runSuccessful; + } + + /** + * Function that execute one command and logs the result + * + * @param command + * @return + * @throws ApiException + */ + protected ApiResponse<DistinguishedName> executeCommand(CommandType command) + throws ApiException { + apiClient.validateToken(); + BaseDirectoryEntry baseDirectoryEntry = Transformer + .getBaseDirectoryEntryFromCommandType(command); + ApiResponse<DistinguishedName> response = directoryEntryAdministrationApi + .modifyDirectoryEntryWithHttpInfo(command.getDn().getUid(), baseDirectoryEntry); + if (response.getStatusCode() == 200) { + LOG.debug( + "Modify directory entry execution successful operated\n" + response.getData()); + } else { + throw new CommandException( + "Modify directory entry execution failed. Response-Status was: " + response + .getStatusCode() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + } + return response; + } + + private boolean doAdd(CommandType command) { + LOG.info("Ended here!"); + return true; +// LOG.debug("Entry not present at VZD. Will proceed with add directory entry command"); +// try { +// ExecutionCollection.getInstance().getAddDirEntryExecution().executeCommand(command); +// return true; +// } catch (ApiException ex) { +// LOG.error("Add directory entry execution failed\n" + Transformer +// .getCreateDirectoryEntry(command)); +// return false; +// } + + } + + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + return false; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryCertExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryCertExecution.java new file mode 100644 index 0000000..5698308 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryCertExecution.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gemClient.GemStringUtils; +import de.gematik.ti.epa.vzd.gemClient.api.GemCertificateAdministrationApi; +import de.gematik.ti.epa.vzd.gemClient.command.Transformer; +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import generated.UserCertificateType; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific execution for Command "ReadDirectoryEntryCertificate" + */ +public class ReadDirEntryCertExecution extends ExecutionBase { + + private CertificateAdministrationApi certificateAdministrationApi; + private Logger LOG = LoggerFactory.getLogger(AddDirEntryExecution.class); + + public ReadDirEntryCertExecution(GemApiClient api) { + super(api, CommandNamesEnum.READ_DIR_CERT); + certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); + } + + @Override + public boolean checkValidation(CommandType command) { + UserCertificateType cert = command.getUserCertificate(); + if (cert != null) { + List<String> params = new ArrayList<>(); + if (command.getDn() != null) { + params.add(command.getDn().getUid()); + } + params.add(cert.getEntryType()); + params.add(cert.getTelematikID()); + params.add(cert.getProfessionOID()); + if (cert.getUsage().size() != 0) { + params.add("usage Vorhanden"); + } + for (String param : params) { + if (!StringUtils.isBlank(param)) { + return true; + } + } + } + return false; + } + + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + if (commands.size() == 0) { + return true; + } + for (CommandType command : commands) { + try { + for (UserCertificate userCertificate : executeCommand(command).getData()) { + LOG.debug("Entry found: " + userCertificate); + } + } catch (Exception ex) { + LOG.error("Read directory entry execution failed\n" + Transformer + .getCreateDirectoryEntry(command)); + runSuccessful = false; + } + } + return runSuccessful; + } + + protected ApiResponse<List<UserCertificate>> executeCommand(CommandType command) + throws ApiException { + UserCertificateType cert = command.getUserCertificate(); + String uid = null; + String cn = null; + if (cert.getDn() != null) { + uid = cert.getDn().getUid(); + cn = cert.getDn().getCn(); + } + String entryType = cert.getEntryType(); + String telematikID = cert.getTelematikID(); + String professionOID = cert.getProfessionOID(); + String usage = null; + if (cert.getUsage().size() != 0) { + usage = GemStringUtils.listToString(cert.getUsage()); + } + + ApiResponse<List<UserCertificate>> response = certificateAdministrationApi + .readDirectoryCertificatesWithHttpInfo(uid, cn, entryType, + telematikID, professionOID, usage); + + return response; + } + + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + ex.printStackTrace(); + } + return false; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecution.java new file mode 100644 index 0000000..d0e0ffd --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecution.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; +import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gemClient.GemStringUtils; +import de.gematik.ti.epa.vzd.gemClient.api.GemDirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.gemClient.command.Transformer; +import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific execution for Command "ReadDirectoryEntry" + */ +public class ReadDirEntryExecution extends ExecutionBase { + + private Logger LOG = LoggerFactory.getLogger(ReadDirEntryExecution.class); + private DirectoryEntryAdministrationApi directoryEntryAdministrationApi; + + public ReadDirEntryExecution(GemApiClient api) { + super(api, CommandNamesEnum.READ_DIR_ENTRY); + directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); + } + + @Override + public boolean checkValidation(CommandType command) { + List<String> params = new ArrayList<>(); + if (command.getDn() != null) { + params.add(command.getDn().getUid()); + } + params.add(command.getGivenName()); + params.add(command.getSn()); + params.add(command.getCn()); + params.add(command.getDisplayName()); + params.add(command.getStreetAddress()); + params.add(command.getPostalCode()); + params.add(command.getLocalityName()); + params.add(command.getStateOrProvinceName()); + params.add(command.getTitle()); + params.add(command.getOrganization()); + params.add(command.getOtherName()); + params.add(GemStringUtils.listToString(command.getSpecialization())); + params.add(GemStringUtils.listToString(command.getDomainID())); + params.add(command.getPersonalEntry()); + params.add(command.getDataFromAuthority()); + for (String parameter : params) { + if (!StringUtils.isBlank(parameter)) { + return true; + } + } + LOG.error("Missing argument -> The given command have no argument to search for " + command + .getName() + + "\n" + Transformer.getBaseDirectoryEntryFromCommandType(command)); + return false; + } + + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + if (commands.size() == 0) { + return true; + } + for (CommandType command : commands) { + try { + for (DirectoryEntry directoryEntry : executeCommand(command).getData()) { + LOG.debug("Entry found: " + directoryEntry); + } + } catch (Exception ex) { + LOG.error("Read directory entry execution failed\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + runSuccessful = false; + } + } + return runSuccessful; + } + + public ApiResponse<List<DirectoryEntry>> executeCommand(CommandType command) + throws ApiException { + apiClient.validateToken(); + String uid = command.getDn().getUid(); + String givenName = command.getGivenName(); + String sn = command.getSn(); + String cn = command.getCn(); + String displayName = command.getDisplayName(); + String streetAddress = command.getStreetAddress(); + String postalCode = command.getPostalCode(); + String localityName = command.getLocalityName(); + String stateOrProvinceName = command.getStateOrProvinceName(); + String title = command.getTitle(); + String organization = command.getOrganization(); + String otherName = command.getOtherName(); + String specialization = GemStringUtils.listToString(command.getSpecialization()); + String domainID = GemStringUtils.listToString(command.getDomainID()); + String personalEntry = command.getPersonalEntry(); + String dataFromAuthority = command.getDataFromAuthority(); + + ApiResponse<List<DirectoryEntry>> response = directoryEntryAdministrationApi + .readDirectoryEntryWithHttpInfo(uid, givenName, sn, cn, displayName, streetAddress, + postalCode, localityName, stateOrProvinceName, title, organization, otherName, + specialization, domainID, personalEntry, dataFromAuthority); + if (response.getStatusCode() == 200) { + return response; + } else { + throw new CommandException( + "Modify directory entry execution failed. Response-Status was: " + response + .getStatusCode() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + } + } + + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + ex.printStackTrace(); + } + + return false; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/CommandException.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/CommandException.java new file mode 100644 index 0000000..f364421 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/CommandException.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.exceptions; + +/** + * Exception that should be thrown if something went wrong during executing commands + */ +public class CommandException extends RuntimeException { + + public CommandException() { + } + + public CommandException(String message) { + super(message); + } + + public CommandException(String message, Throwable cause) { + super(message, cause); + } + + public CommandException(Throwable cause) { + super(cause); + } + + protected CommandException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/GemClientException.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/GemClientException.java new file mode 100644 index 0000000..86b837b --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/GemClientException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.exceptions; + +public class GemClientException extends RuntimeException { + + public GemClientException() { + } + + public GemClientException(String message) { + super(message); + } + + public GemClientException(String message, Throwable cause) { + super(message, cause); + } + + public GemClientException(Throwable cause) { + super(cause); + } + + protected GemClientException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/ReadException.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/ReadException.java new file mode 100644 index 0000000..031fe11 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/ReadException.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.exceptions; + +/** + * Exception that should be thrown when something while reading files went wrong + */ +public class ReadException extends RuntimeException { + + public ReadException() { + } + + public ReadException(String message) { + super(message); + } + + public ReadException(String message, Throwable cause) { + super(message, cause); + } + + public ReadException(Throwable cause) { + super(cause); + } + + protected ReadException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandler.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandler.java new file mode 100644 index 0000000..538b797 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandler.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.invoker; + +import de.gematik.ti.epa.vzd.gemClient.exceptions.GemClientException; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class reads the user input and store all the locations and configurations the user did + * <p> + * Implemented as singelton + */ +public final class ConfigHandler { + + private static final Logger LOG = LoggerFactory.getLogger(ConfigHandler.class); + private static final String BASE_PATH = "base"; + private static final String RETRY_OAUTH = "retryingOAuth"; + private static final String COMMANDS = "commands"; + + private static ConfigHandler configHandler = null; + private String configPath; + private String basePath; + private String credentialPath; + private String commandsPath; + private String retryingOAuthPath; + + private ConfigHandler() { + } + + /** + * This function returns the instance of the ConfigHandler as long as it is initialized + * + * @return + */ + public static ConfigHandler getInstance() { + if (configHandler == null) { + throw new GemClientException("A ConfigHandler have to be initialized first"); + } + return configHandler; + } + + /** + * Create an instance of a ConfigHandler while reading the commandline + * + * @param args input parameter from commandline + */ + public static ConfigHandler init(String[] args) { + if (configHandler == null) { + configHandler = new ConfigHandler(); + for (int iIndex = 0; iIndex < args.length; iIndex++) { + switch (args[iIndex]) { + case "-p": + configHandler.configPath = new File((args[iIndex + 1])).getAbsolutePath(); + configHandler.setParams(configHandler.configPath); + break; + case "-c": + configHandler.credentialPath = new File((args[iIndex + 1])).getAbsolutePath(); + break; + case "-b": + configHandler.commandsPath = new File((args[iIndex + 1])).getAbsolutePath(); + break; + default: + break; + } + } + } else { + throw new GemClientException("Configurations are only allowed to set once"); + } + if (StringUtils.isBlank(configHandler.credentialPath) || StringUtils + .isBlank(configHandler.configPath)) { + LOG.error("At least CredentialPath or ConfigPath is missing and have to be set"); + throw new GemClientException( + "At least CredentialPath or ConfigPath is missing and have to be set"); + } + LOG.debug("Configurations have been set"); + return configHandler; + } + + + private void setParams(String arg) { + File file = new File(arg); + try { + BufferedReader br = new BufferedReader(new FileReader(file)); + String line = br.readLine(); + while (line != null) { + if (StringUtils.isNotBlank(line) && line.contains("=")) { + String[] param = line.split("="); + String name = param[0]; + String value = param[1]; + switch (name) { + case BASE_PATH: + configHandler.basePath = value; + break; + case RETRY_OAUTH: + configHandler.retryingOAuthPath = value; + break; + case COMMANDS: + if (StringUtils.isBlank(configHandler.commandsPath)) { + configHandler.commandsPath = new File(value).getAbsolutePath(); + } + break; + default: + break; + } + } + line = br.readLine(); + } + } catch (IOException e) { + LOG.error("File not found at " + file.getAbsolutePath()); + throw new IllegalArgumentException("No access to given file " + file.getAbsolutePath()); + } + if (StringUtils.isBlank(configHandler.retryingOAuthPath)) { + LOG.error("No authorization server named"); + throw new GemClientException("No authorization server named"); + } + if (StringUtils.isBlank(configHandler.basePath)) { + LOG.error("No vzd server named"); + throw new GemClientException("No server named"); + } + } + + // <editor-fold desc="Getter & Setter"> + public static void setConfigHandler(ConfigHandler setConfigHandler) { + configHandler = setConfigHandler; + } + + public String getRetryingOAuthPath() { + return retryingOAuthPath; + } + + public String getConfigPath() { + return configPath; + } + + public String getCredentialPath() { + return credentialPath; + } + + public String getBasePath() { + return basePath; + } + + public String getCommandsPath() { + return commandsPath; + } + // </editor-fold> +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/GemApiClient.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/GemApiClient.java new file mode 100644 index 0000000..6d4eefb --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/GemApiClient.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.invoker; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import de.gematik.ti.epa.vzd.client.invoker.ApiClient; +import de.gematik.ti.epa.vzd.client.invoker.JSON; +import de.gematik.ti.epa.vzd.client.invoker.auth.Authentication; +import de.gematik.ti.epa.vzd.client.invoker.auth.HttpBasicAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.OAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.OAuthFlow; +import de.gematik.ti.epa.vzd.client.invoker.auth.RetryingOAuth; +import de.gematik.ti.epa.vzd.gemClient.exceptions.GemClientException; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import okhttp3.OkHttpClient; +import org.apache.oltu.oauth2.client.OAuthClient; +import org.apache.oltu.oauth2.client.URLConnectionClient; +import org.apache.oltu.oauth2.client.request.OAuthClientRequest; +import org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse; +import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse; +import org.apache.oltu.oauth2.common.exception.OAuthProblemException; +import org.apache.oltu.oauth2.common.exception.OAuthSystemException; +import org.apache.oltu.oauth2.common.message.types.GrantType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GemApiClient extends ApiClient { + + private static final Logger LOG = LoggerFactory.getLogger(GemApiClient.class); + + private String retryingOAuthPath; + private Map<String, Authentication> authentications; + private LocalDateTime tokenvalidationDate; + + public GemApiClient() { + init(); + authentications = Collections.unmodifiableMap(authentications); + } + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID + */ + public GemApiClient(final String clientId) { + this(clientId, null, null); + } + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID and additional parameters + */ + public GemApiClient(final String clientId, final Map<String, String> parameters) { + this(clientId, null, parameters); + } + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID, secret, and additional parameters + */ + public GemApiClient(final String clientId, final String clientSecret, + final Map<String, String> parameters) { + init(); + + final RetryingOAuth retryingOAuth = new RetryingOAuth(retryingOAuthPath, clientId, + OAuthFlow.application, clientSecret, parameters); + authentications.put("OAuth2", retryingOAuth); + getHttpClient().interceptors().add(retryingOAuth); + + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + /** + * The function <code> getProgressInterceptor </code> comes from the generated class ApiClient and maybe have to be set on public again. + * <p> + * The OAuth2 token is stored in authentications.get("OAuth") + */ + private void init() { + ConfigHandler configHandler = ConfigHandler.getInstance(); + setBasePath(configHandler.getBasePath()); + retryingOAuthPath = configHandler.getRetryingOAuthPath(); + + final OkHttpClient.Builder builder = new OkHttpClient.Builder(); + // Function have to be set public when client is regenerated + builder.addNetworkInterceptor(getProgressInterceptor()); + setHttpClient(builder.build()); + + setVerifyingSsl(true); + + setJSON(new JSON()); + + // Set default User-Agent. + setUserAgent("OpenAPI-Generator/1.0.0/java"); + + authentications = new HashMap<>(); + authentications + .put("HttpBasicAuth", getHttpBasicAuthFromFile(configHandler.getCredentialPath())); + try { + authentications.put("OAuth", getNewOAuth2Token()); + } catch (OAuthSystemException | OAuthProblemException e) { + throw new ExceptionInInitializerError("Error while getting Token"); + } + } + + /** + * Requests an OAuth2 Token with Username and Password + * + * @return + * @throws OAuthSystemException + * @throws OAuthProblemException + */ + private OAuth getNewOAuth2Token() throws OAuthProblemException, OAuthSystemException { + LOG.debug("Trying to get new access token"); + HttpBasicAuth baseAuth = (HttpBasicAuth) getAuthentications().get("HttpBasicAuth"); + + OAuthClientRequest request = OAuthClientRequest + .tokenLocation(retryingOAuthPath) + .setClientId(baseAuth.getUsername()) + .setClientSecret(baseAuth.getPassword()) + .setGrantType(GrantType.CLIENT_CREDENTIALS) + .buildBodyMessage(); + + request.setHeader("Accept", "application/json"); + + OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient()); + OAuthAccessTokenResponse oAuthResponse = oAuthClient + .accessToken(request, OAuthJSONAccessTokenResponse.class); + + JsonObject jObj = new JsonParser().parse(oAuthResponse.getBody()).getAsJsonObject(); + OAuth oAuth = new OAuth(); + oAuth.setAccessToken(jObj.get("access_token").toString().replaceAll("\"", "")); + setTokenValidation(jObj.get("expires_in").toString()); + LOG.debug("Requesting new OAuth2 token successful"); + return oAuth; + } + + /** + * Sets the time - 10% until the token expires. This ensures that the token is every time valid + * + * @param expires_in is an String with numbers. For example 3600 equals 1 hour. + */ + private void setTokenValidation(String expires_in) { + int seconds = Integer.parseInt(expires_in); + int secureSeconds = (int) (seconds * 0.90); + tokenvalidationDate = LocalDateTime.now().plusSeconds(secureSeconds); + } + + /** + * Checks if the token is still valid. If not request a new one + * + * @return + */ + public boolean validateToken() { + if (LocalDateTime.now().isBefore(tokenvalidationDate)) { + return true; + } + try { + getNewOAuth2Token(); + } catch (OAuthProblemException | OAuthSystemException e) { + throw new GemClientException("Requesting a new OAuth2 token failed.", e); + } + return LocalDateTime.now().isBefore(tokenvalidationDate); + } + + /** + * Reads the credentialFile and stores the client_id and client_secret + * + * @param arg + * @return + */ + private HttpBasicAuth getHttpBasicAuthFromFile(String arg) { + String client_id = ""; + String client_secret = ""; + File file = new File(arg); + + try { + BufferedReader br = new BufferedReader(new FileReader(file)); + String line = br.readLine(); + while (line != null) { + String[] param = line.split("="); + switch (param[0]) { + case "id": + client_id = param[1]; + break; + case "secret": + client_secret = param[1]; + break; + default: + break; + } + line = br.readLine(); + } + HttpBasicAuth basicAuth = new HttpBasicAuth(); + basicAuth.setPassword(client_secret); + basicAuth.setUsername(client_id); + return basicAuth; + } catch (IOException e) { + LOG.error( + "The named file on path " + file.getAbsolutePath() + " could not be accessed"); + throw new IllegalArgumentException( + "The named file on path " + file.getAbsolutePath() + " could not be accessed"); + } + } + + // <editor-fold desc="Getter & Setter"> + + /** + * Get authentications (key: authentication name, value: authentication). + * + * @return Map of authentication objects + */ + @Override + public Map<String, Authentication> getAuthentications() { + return authentications; + } + + /** + * Get authentication for the given name. + * + * @param authName The authentication name + * @return The authentication, null if not found + */ + @Override + public Authentication getAuthentication(final String authName) { + return authentications.get(authName); + } + + // </editor-fold> +} diff --git a/src/main/resources/jaxb-binding/binding.xml b/src/main/resources/jaxb-binding/binding.xml new file mode 100644 index 0000000..f6c7f3e --- /dev/null +++ b/src/main/resources/jaxb-binding/binding.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" + version="2.0" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + + <jaxb:bindings schemaLocation="..\xsd\commands.xsd" node="/xs:schema"> + <jaxb:globalBindings fixedAttributeAsConstantProperty="true"/> + </jaxb:bindings> + +</jaxb:bindings> \ No newline at end of file diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000..d94bd51 --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration status="INFO"> + <Properties> + <Property name="toolname">VZD-Client</Property> + <Property name="l4j.lvl">${sys:l4j.lvl:-INFO}</Property> + <Property name="l4j.logDir">${sys:l4j.logDir:-${sys:java.io.tmpdir}/logs}</Property> + </Properties> + <Appenders> + <File name="file-log" fileName="${l4j.logDir}/VZD-Client_${date:yyyy-MM-dd_HH-mm-ss.SSSS}.log" append="false"> + <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1}: %L - %m%n"/> + </File> + <Console name="console-log"> + <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1}: %L - %m%n"/> + </Console> + </Appenders> + + <Loggers> + <Logger name="de.gematik.ti.epa" level="DEBUG" additivity="false"> + <appender-ref ref="file-log" level="DEBUG"/> + <appender-ref ref="console-log" level="${l4j.lvl}"/> + </Logger> + <Root level="DEBUG" additivity="false"> + <AppenderRef ref="file-log" level="DEBUG"/> + <AppenderRef ref="console-log" level="${l4j.lvl}"/> + </Root> + </Loggers> +</Configuration> \ No newline at end of file diff --git a/src/main/resources/xsd/commands.xsd b/src/main/resources/xsd/commands.xsd new file mode 100644 index 0000000..c45a6aa --- /dev/null +++ b/src/main/resources/xsd/commands.xsd @@ -0,0 +1,58 @@ +<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + + <xs:element name="CommandList" type="CommandListType"/> + + <xs:complexType name="CommandListType"> + <xs:sequence> + <xs:element name="Command" type="CommandType" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="CommandType"> + <xs:sequence> + <xs:element name="name" type="xs:string" minOccurs="1" maxOccurs="1"/> + <xs:element name="dn" type="DistinguishedNameType" minOccurs="0" maxOccurs="1"/> + <xs:element name="givenName" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="sn" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="streetAddress" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="postalCode" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="localityName" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="stateOrProvinceName" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="cn" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="displayName" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="title" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="organization" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="otherName" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="specialization" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="domainID" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="mail" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="UserCertificate" type="UserCertificateType" minOccurs="0" maxOccurs="1"/> + <xs:element name="personalEntry" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="dataFromAuthority" type="xs:string" minOccurs="0" maxOccurs="1"/> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="DistinguishedNameType"> + <xs:sequence> + <xs:element name="cn" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="uid" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="dc" type="xs:string" minOccurs="0" maxOccurs="2"/> + <xs:element name="ou" type="xs:string" minOccurs="0" maxOccurs="2"/> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="UserCertificateType"> + <xs:sequence> + <xs:element name="dn" type="DistinguishedNameType" minOccurs="0" maxOccurs="1"/> + <xs:element name="entryType" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="telematikID" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="professionOID" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="usage" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="userCertificate" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="description" type="xs:string" minOccurs="0" maxOccurs="1"/> + + </xs:sequence> + </xs:complexType> + +</xs:schema> \ No newline at end of file diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApiTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApiTest.java new file mode 100644 index 0000000..72e78e7 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApiTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.api; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.client.model.Error; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import org.junit.Test; +import org.junit.Ignore; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * API tests for CertificateAdministrationApi + */ +@Ignore +public class CertificateAdministrationApiTest { + + private final CertificateAdministrationApi api = new CertificateAdministrationApi(); + + + /** + * Der Zertifikatseintrag wird im Verzeichnisdienst hinzugefügt und ist logisch über dn.uid mit dem übergeordneten Verzeichniseintrag verknüpft. + * + * + * + * @throws ApiException + * if the Api call fails + */ + @Test + public void addDirectoryEntryCertificateTest() throws ApiException { + String uid = null; + UserCertificate userCertificate = null; + DistinguishedName response = api.addDirectoryEntryCertificate(uid, userCertificate); + + // TODO: test validations + } + + /** + * Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. + * + * + * + * @throws ApiException + * if the Api call fails + */ + @Test + public void deleteDirectoryEntryCertificateTest() throws ApiException { + String uid = null; + String certificateEntryID = null; + api.deleteDirectoryEntryCertificate(uid, certificateEntryID); + + // TODO: test validations + } + + /** + * Der Zertifikatseintrag wird mit den übergebenen Daten aktualisiert. + * + * + * + * @throws ApiException + * if the Api call fails + */ + @Test + public void modifyDirectoryEntryCertificateTest() throws ApiException { + String uid = null; + String certificateEntryID = null; + UserCertificate userCertificate = null; + UserCertificate response = api.modifyDirectoryEntryCertificate(uid, certificateEntryID, userCertificate); + + // TODO: test validations + } + + /** + * Zertifikat lesen + * + * Liefert alle Zertifikate, die dem Filter (siehe 'parameters') entsprechen. + * + * @throws ApiException + * if the Api call fails + */ + @Test + public void readDirectoryCertificatesTest() throws ApiException { + String uid = null; + String certificateEntryID = null; + String entryType = null; + String telematikID = null; + String professionOID = null; + String usage = null; + List<UserCertificate> response = api.readDirectoryCertificates(uid, certificateEntryID, entryType, telematikID, professionOID, usage); + + // TODO: test validations + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApiTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApiTest.java new file mode 100644 index 0000000..65d65d4 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApiTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.api; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.client.model.Error; +import org.junit.Test; +import org.junit.Ignore; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * API tests for DirectoryEntryAdministrationApi + */ +@Ignore +public class DirectoryEntryAdministrationApiTest { + + private final DirectoryEntryAdministrationApi api = new DirectoryEntryAdministrationApi(); + + + /** + * Einen Eintrag zum Verzeichnisdienst hinzufügen + * + * + * + * @throws ApiException + * if the Api call fails + */ + @Test + public void addDirectoryEntryTest() throws ApiException { + CreateDirectoryEntry createDirectoryEntry = null; + DistinguishedName response = api.addDirectoryEntry(createDirectoryEntry); + + // TODO: test validations + } + + /** + * Gesamten Verzeichniseintrag löschen + * + * + * + * @throws ApiException + * if the Api call fails + */ + @Test + public void deleteDirectoryEntryTest() throws ApiException { + String uid = null; + api.deleteDirectoryEntry(uid); + + // TODO: test validations + } + + /** + * Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. + * + * + * + * @throws ApiException + * if the Api call fails + */ + @Test + public void modifyDirectoryEntryTest() throws ApiException { + String uid = null; + BaseDirectoryEntry baseDirectoryEntry = null; + DistinguishedName response = api.modifyDirectoryEntry(uid, baseDirectoryEntry); + + // TODO: test validations + } + + /** + * Gesamten Verzeichniseintrag lesen + * + * Liefert alle zum Filter passenden Verzeichniseinträge. Die angegebenen Parameter werden mit logischen UND verknüpft. + * + * @throws ApiException + * if the Api call fails + */ + @Test + public void readDirectoryEntryTest() throws ApiException { + String uid = null; + String givenName = null; + String sn = null; + String cn = null; + String displayName = null; + String streetAddress = null; + String postalCode = null; + String localityName = null; + String stateOrProvienceName = null; + String title = null; + String organization = null; + String otherName = null; + String specialization = null; + String domainID = null; + String personalEntry = null; + String dataFromAuthority = null; + List<DirectoryEntry> response = api.readDirectoryEntry(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, stateOrProvienceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority); + + // TODO: test validations + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntryTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntryTest.java new file mode 100644 index 0000000..3b79082 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntryTest.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + + +/** + * Model tests for BaseDirectoryEntry + */ +public class BaseDirectoryEntryTest { + private final BaseDirectoryEntry model = new BaseDirectoryEntry(); + + /** + * Model tests for BaseDirectoryEntry + */ + @Test + public void testBaseDirectoryEntry() { + // TODO: test BaseDirectoryEntry + } + + /** + * Test the property 'dn' + */ + @Test + public void dnTest() { + // TODO: test dn + } + + /** + * Test the property 'givenName' + */ + @Test + public void givenNameTest() { + // TODO: test givenName + } + + /** + * Test the property 'sn' + */ + @Test + public void snTest() { + // TODO: test sn + } + + /** + * Test the property 'cn' + */ + @Test + public void cnTest() { + // TODO: test cn + } + + /** + * Test the property 'displayName' + */ + @Test + public void displayNameTest() { + // TODO: test displayName + } + + /** + * Test the property 'streetAddress' + */ + @Test + public void streetAddressTest() { + // TODO: test streetAddress + } + + /** + * Test the property 'postalCode' + */ + @Test + public void postalCodeTest() { + // TODO: test postalCode + } + + /** + * Test the property 'localityName' + */ + @Test + public void localityNameTest() { + // TODO: test localityName + } + + /** + * Test the property 'stateOrProvienceName' + */ + @Test + public void stateOrProvienceNameTest() { + // TODO: test stateOrProvienceName + } + + /** + * Test the property 'title' + */ + @Test + public void titleTest() { + // TODO: test title + } + + /** + * Test the property 'organization' + */ + @Test + public void organizationTest() { + // TODO: test organization + } + + /** + * Test the property 'otherName' + */ + @Test + public void otherNameTest() { + // TODO: test otherName + } + + /** + * Test the property 'specialization' + */ + @Test + public void specializationTest() { + // TODO: test specialization + } + + /** + * Test the property 'domainID' + */ + @Test + public void domainIDTest() { + // TODO: test domainID + } + + /** + * Test the property 'personalEntry' + */ + @Test + public void personalEntryTest() { + // TODO: test personalEntry + } + + /** + * Test the property 'dataFromAuthority' + */ + @Test + public void dataFromAuthorityTest() { + // TODO: test dataFromAuthority + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntryTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntryTest.java new file mode 100644 index 0000000..90efd3b --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntryTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + + +/** + * Model tests for CreateDirectoryEntry + */ +public class CreateDirectoryEntryTest { + private final CreateDirectoryEntry model = new CreateDirectoryEntry(); + + /** + * Model tests for CreateDirectoryEntry + */ + @Test + public void testCreateDirectoryEntry() { + // TODO: test CreateDirectoryEntry + } + + /** + * Test the property 'directoryEntryBase' + */ + @Test + public void directoryEntryBaseTest() { + // TODO: test directoryEntryBase + } + + /** + * Test the property 'userCertificates' + */ + @Test + public void userCertificatesTest() { + // TODO: test userCertificates + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntryTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntryTest.java new file mode 100644 index 0000000..b11bc3b --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntryTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.Fachdaten; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + + +/** + * Model tests for DirectoryEntry + */ +public class DirectoryEntryTest { + private final DirectoryEntry model = new DirectoryEntry(); + + /** + * Model tests for DirectoryEntry + */ + @Test + public void testDirectoryEntry() { + // TODO: test DirectoryEntry + } + + /** + * Test the property 'directoryEntryBase' + */ + @Test + public void directoryEntryBaseTest() { + // TODO: test directoryEntryBase + } + + /** + * Test the property 'userCertificates' + */ + @Test + public void userCertificatesTest() { + // TODO: test userCertificates + } + + /** + * Test the property 'fachdaten' + */ + @Test + public void fachdatenTest() { + // TODO: test fachdaten + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/DistinguishedNameTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/DistinguishedNameTest.java new file mode 100644 index 0000000..ee9d68a --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/DistinguishedNameTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + + +/** + * Model tests for DistinguishedName + */ +public class DistinguishedNameTest { + private final DistinguishedName model = new DistinguishedName(); + + /** + * Model tests for DistinguishedName + */ + @Test + public void testDistinguishedName() { + // TODO: test DistinguishedName + } + + /** + * Test the property 'uid' + */ + @Test + public void uidTest() { + // TODO: test uid + } + + /** + * Test the property 'dc' + */ + @Test + public void dcTest() { + // TODO: test dc + } + + /** + * Test the property 'ou' + */ + @Test + public void ouTest() { + // TODO: test ou + } + + /** + * Test the property 'cn' + */ + @Test + public void cnTest() { + // TODO: test cn + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/ErrorTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/ErrorTest.java new file mode 100644 index 0000000..91d95f2 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/ErrorTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + + +/** + * Model tests for Error + */ +public class ErrorTest { + private final Error model = new Error(); + + /** + * Model tests for Error + */ + @Test + public void testError() { + // TODO: test Error + } + + /** + * Test the property 'attributeName' + */ + @Test + public void attributeNameTest() { + // TODO: test attributeName + } + + /** + * Test the property 'attributeError' + */ + @Test + public void attributeErrorTest() { + // TODO: test attributeError + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/FAD1Test.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/FAD1Test.java new file mode 100644 index 0000000..1aae66e --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/FAD1Test.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + + +/** + * Model tests for FAD1 + */ +public class FAD1Test { + private final FAD1 model = new FAD1(); + + /** + * Model tests for FAD1 + */ + @Test + public void testFAD1() { + // TODO: test FAD1 + } + + /** + * Test the property 'dn' + */ + @Test + public void dnTest() { + // TODO: test dn + } + + /** + * Test the property 'mail' + */ + @Test + public void mailTest() { + // TODO: test mail + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/FachdatenTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/FachdatenTest.java new file mode 100644 index 0000000..1aae0e9 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/FachdatenTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.client.model.FAD1; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + + +/** + * Model tests for Fachdaten + */ +public class FachdatenTest { + private final Fachdaten model = new Fachdaten(); + + /** + * Model tests for Fachdaten + */ + @Test + public void testFachdaten() { + // TODO: test Fachdaten + } + + /** + * Test the property 'dn' + */ + @Test + public void dnTest() { + // TODO: test dn + } + + /** + * Test the property 'FAD1' + */ + @Test + public void FAD1Test() { + // TODO: test FAD1 + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/UserCertificateTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/UserCertificateTest.java new file mode 100644 index 0000000..147059f --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/UserCertificateTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * I_Directory_Administration + * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package de.gematik.ti.epa.vzd.client.model; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + + +/** + * Model tests for UserCertificate + */ +public class UserCertificateTest { + private final UserCertificate model = new UserCertificate(); + + /** + * Model tests for UserCertificate + */ + @Test + public void testUserCertificate() { + // TODO: test UserCertificate + } + + /** + * Test the property 'dn' + */ + @Test + public void dnTest() { + // TODO: test dn + } + + /** + * Test the property 'entryType' + */ + @Test + public void entryTypeTest() { + // TODO: test entryType + } + + /** + * Test the property 'telematikID' + */ + @Test + public void telematikIDTest() { + // TODO: test telematikID + } + + /** + * Test the property 'professionOID' + */ + @Test + public void professionOIDTest() { + // TODO: test professionOID + } + + /** + * Test the property 'usage' + */ + @Test + public void usageTest() { + // TODO: test usage + } + + /** + * Test the property 'userCertificate' + */ + @Test + public void userCertificateTest() { + // TODO: test userCertificate + } + + /** + * Test the property 'description' + */ + @Test + public void descriptionTest() { + // TODO: test description + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilderTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilderTest.java new file mode 100644 index 0000000..2df2a07 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilderTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import de.gematik.ti.epa.vzd.gemClient.exceptions.ReadException; +import de.gematik.ti.epa.vzd.gemClient.invoker.ConfigHandler; +import java.io.File; +import org.junit.Before; +import org.junit.Test; + +public class CommandsBuilderTest { + + private static final String[] ARGS_Wrong_Formatted = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "ConfigWrongFormattedCommands.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "bspCredentials.txt"}; + private static final String[] ARGS_MISSING_COMMANDS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "ConfigMissingCommands.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "bspCredentials.txt"}; + + @Before + public void createConfigHandler() { + ConfigHandler.setConfigHandler(null); + } + + @Test + public void checkParsingError() { + ConfigHandler.init(ARGS_Wrong_Formatted); + ReadException exception = assertThrows(ReadException.class, + () -> new CommandsBuilder().buildCommands()); + assertEquals( + "An error have been occurred while reading your command file. Please check if this file is a valid .xml file", + exception.getMessage()); + } + + @Test + public void checkFileNotFoundError() { + ConfigHandler.init(ARGS_MISSING_COMMANDS); + ReadException exception = assertThrows(ReadException.class, + () -> new CommandsBuilder().buildCommands()); + assertEquals( + "A problem with your named file have occurred. Please if check "+ new File("doesNotExist.xml").getAbsolutePath() + " exist", + exception.getMessage()); + } +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecutionTest.java new file mode 100644 index 0000000..cf1d687 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecutionTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import generated.UserCertificateType; +import org.junit.Test; + +public class AddDirEntryExecutionTest { + + private static GemApiClient gemApiClient = mock(GemApiClient.class); + + + @Test + public void checkValidationMissingCertificateObjectTest() { + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + assertFalse(addDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationMissingTelematikIdAndCertificateTest() { + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + UserCertificateType certificate = new UserCertificateType(); + command.setUserCertificate(certificate); + assertFalse(addDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationTelematikIdTest() { + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + UserCertificateType certificate = new UserCertificateType(); + certificate.setUserCertificate("Certificate"); + command.setUserCertificate(certificate); + assertTrue(addDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationCertificateTest() { + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + UserCertificateType certificate = new UserCertificateType(); + certificate.setTelematikID("TelematikId"); + command.setUserCertificate(certificate); + assertTrue(addDirEntryExecution.checkValidation(command)); + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecutionTest.java new file mode 100644 index 0000000..412b043 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecutionTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import generated.DistinguishedNameType; +import org.junit.Test; + +public class DeleteDirEntryExecutionTest { + + private static GemApiClient gemApiClient = mock(GemApiClient.class); + private static DeleteDirEntryExecution deleteDirEntryExecution = new DeleteDirEntryExecution(gemApiClient); + + @Test + public void checkValidationMissingArgumentTest() { + CommandType command = new CommandType(); + assertFalse(deleteDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationHaveUID() { + CommandType command = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid("cbca60fe-8ca7-4960-990d-ec526a200582"); + command.setDn(dn); + assertTrue(deleteDirEntryExecution.checkValidation(command)); + } +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecutionTest.java new file mode 100644 index 0000000..02a2f0d --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecutionTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import generated.DistinguishedNameType; +import org.junit.Test; + +public class ModifyDirEntryExecutionTest { + + private static GemApiClient gemApiClient = mock(GemApiClient.class); + + @Test + public void preCheckFalseWhenUidIsEmptyString() { + ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid(""); + command.setDn(dn); + + assertTrue(!modDirEnt.preCheck(command)); + } + + @Test + public void preCheckIsTrue() { + ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid("Test"); + command.setDn(dn); + + assertTrue(modDirEnt.preCheck(command)); + } + + @Test + public void preCheckFalseWhenUidIsMissing() { + ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setCn("Test"); + command.setDn(dn); + + assertTrue(!modDirEnt.preCheck(command)); + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecutionTest.java new file mode 100644 index 0000000..cef3fa2 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecutionTest.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import generated.DistinguishedNameType; +import org.junit.Test; + +public class ReadDirEntryExecutionTest { + + private static GemApiClient gemApiClient = mock(GemApiClient.class); + + @Test + public void checkValidationMissingArgumentTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + assertFalse(readDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationHaveDnTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid("cbca60fe-8ca7-4960-990d-ec526a200582"); + command.setDn(dn); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationHaveGivenNameTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.setGivenName("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationHaveSnTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.setSn("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationHaveCnTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.setCn("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationHaveDisplayNameTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.setDisplayName("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationHaveStreetAddressTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.setStreetAddress("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationHavePostalCodeTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.setPostalCode("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationHaveLocalityNameTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.setLocalityName("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationHaveStateOrProvinceNameTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.setGivenName("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationHaveTitleTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.setTitle("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationHaveOrganizationTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.setOrganization("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + + @Test + public void checkValidationHaveOtherNameTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.setOtherName("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + @Test + public void checkValidationHaveSpecializationTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.getSpecialization().add("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + @Test + public void checkValidationHaveDomainIDTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.getDomainID().add("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + @Test + public void checkValidationHavePersonalEntryTest(){ + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.setPersonalEntry("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } + @Test + public void checkValidationHaveDataFromAuthorityTest(){ + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + command.setDataFromAuthority("TestString"); + assertTrue(readDirEntryExecution.checkValidation(command)); + } +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandlerTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandlerTest.java new file mode 100644 index 0000000..190c7bf --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandlerTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.invoker; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import de.gematik.ti.epa.vzd.gemClient.exceptions.GemClientException; +import de.gematik.ti.epa.vzd.gemClient.invoker.ConfigHandler; +import java.io.File; +import org.junit.Before; +import org.junit.Test; + +public class ConfigHandlerTest { + + private static final String[] TEST_ARGS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Config.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt", "-b", "rightPath"}; + private static final String[] TEST_ARGS_WITH_COMMANDPATH_IN_FILE = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Config.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt"}; + + @Before + public void resetConfigHandler() { + ConfigHandler.setConfigHandler(null); + } + + @Test + public void testGetInstanceBeforeInit() { + GemClientException exception = assertThrows(GemClientException.class, + ConfigHandler::getInstance); + assertEquals("A ConfigHandler have to be initialized first", exception.getMessage()); + } + + @Test + public void doubleInitConfigHandlerTest() { + ConfigHandler.init(TEST_ARGS); + GemClientException exception = assertThrows(GemClientException.class, () -> + ConfigHandler.init(TEST_ARGS)); + assertEquals("Configurations are only allowed to set once", exception.getMessage()); + } + + @Test + public void checkRightCommandsWithCliPath() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS); + + assertEquals(new File("rightPath").getAbsolutePath(), configHandler.getCommandsPath()); + } + + @Test + public void checkRightCommandsWithFilePath() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS_WITH_COMMANDPATH_IN_FILE); + + assertEquals(new File("src\\test\\resources\\config\\Commands.xml").getAbsolutePath(), configHandler.getCommandsPath()); + } + + @Test + public void checkRightRetryOAuthPath() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS); + + assertEquals("https://to.be.defined/oauth/token", configHandler.getRetryingOAuthPath()); + } + + @Test + public void checkRightBasePath() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS); + + assertEquals("http://[::1]:8080/OAuth2Token", configHandler.getBasePath()); + } +} diff --git a/src/test/resources/config/Commands.xml b/src/test/resources/config/Commands.xml new file mode 100644 index 0000000..45e23f7 --- /dev/null +++ b/src/test/resources/config/Commands.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<CommandList> + <Command> + <name>modifyDirectoryEntries</name> + <dn> + <uid>Blub</uid> + </dn> + <displayName>displayName</displayName> + <otherName>otherName</otherName> + <streetAddress>streetAddress</streetAddress> + <postalCode>postalCode</postalCode> + <localityName>localityName</localityName> + <stateOrProvinceName>stateOrProvinceName</stateOrProvinceName> + <title>title + organization + specialization + domainID + + + modifyDirectoryEntries + + Blub2 + + displayName2 + otherName2 + streetAddress2 + postalCode2 + localityName2 + stateOrProvinceName2 + title2 + organization2 + specialization2 + domainID2 + + \ No newline at end of file diff --git a/src/test/resources/config/CommandsMissingOperationName.xml b/src/test/resources/config/CommandsMissingOperationName.xml new file mode 100644 index 0000000..c27c835 --- /dev/null +++ b/src/test/resources/config/CommandsMissingOperationName.xml @@ -0,0 +1,34 @@ + + + + + Blub + + displayName + otherName + streetAddress + postalCode + localityName + stateOrProvinceName + title + organization + specialization + domainID + + + modifyDirectoryEntries + + Blub2 + + displayName2 + otherName2 + streetAddress2 + postalCode2 + localityName2 + stateOrProvinceName2 + title2 + organization2 + specialization2 + domainID2 + + \ No newline at end of file diff --git a/src/test/resources/config/CommandsWrongFormated.xml b/src/test/resources/config/CommandsWrongFormated.xml new file mode 100644 index 0000000..2694a87 --- /dev/null +++ b/src/test/resources/config/CommandsWrongFormated.xml @@ -0,0 +1,35 @@ + + + + modifyDirectoryEntries + + Blub + + displayName + otherName + streetAddress + postalCode + localityName + stateOrProvinceNametitle + organization + specialization + domainID + + + modifyDirectoryEntries + + Blub2 + + displayName2 + otherName2 + streetAddress2 + postalCode2 + localityName2 + stateOrProvinceName2 + title2 + organization2 + specialization2 + domainID2 + + \ No newline at end of file diff --git a/src/test/resources/config/Config.txt b/src/test/resources/config/Config.txt new file mode 100644 index 0000000..6254beb --- /dev/null +++ b/src/test/resources/config/Config.txt @@ -0,0 +1,3 @@ +base=http://[::1]:8080/OAuth2Token +retryingOAuth=https://to.be.defined/oauth/token +commands=src\test\resources\config\Commands.xml \ No newline at end of file diff --git a/src/test/resources/config/ConfigMissingCommands.txt b/src/test/resources/config/ConfigMissingCommands.txt new file mode 100644 index 0000000..7bfcf81 --- /dev/null +++ b/src/test/resources/config/ConfigMissingCommands.txt @@ -0,0 +1,3 @@ +base=http://[::1]:8080/OAuth2Token +retryingOAuth=https://to.be.defined/oauth/token +commands=doesNotExist.xml \ No newline at end of file diff --git a/src/test/resources/config/ConfigMissingOperationname.txt b/src/test/resources/config/ConfigMissingOperationname.txt new file mode 100644 index 0000000..71eecf8 --- /dev/null +++ b/src/test/resources/config/ConfigMissingOperationname.txt @@ -0,0 +1,3 @@ +base=http://[::1]:8080/OAuth2Token +retryingOAuth=https://to.be.defined/oauth/token +commands=src\test\resources\config\CommandsMissingOperationName.xml \ No newline at end of file diff --git a/src/test/resources/config/ConfigWrongFormattedCommands.txt b/src/test/resources/config/ConfigWrongFormattedCommands.txt new file mode 100644 index 0000000..e6900b1 --- /dev/null +++ b/src/test/resources/config/ConfigWrongFormattedCommands.txt @@ -0,0 +1,3 @@ +base=http://[::1]:8080/OAuth2Token +retryingOAuth=https://to.be.defined/oauth/token +commands=src\test\resources\config\CommandsWrongFormated.xml \ No newline at end of file diff --git a/src/test/resources/config/Credentials.txt b/src/test/resources/config/Credentials.txt new file mode 100644 index 0000000..0ce6b34 --- /dev/null +++ b/src/test/resources/config/Credentials.txt @@ -0,0 +1,2 @@ +id=someId +secret=someSecret \ No newline at end of file diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..0affce7 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +0.5.0 From 5fd6f37797738438e8bd8526b06309f9fd9e2c1e Mon Sep 17 00:00:00 2001 From: Gematik Date: Tue, 14 Jul 2020 15:27:22 +0200 Subject: [PATCH 2/5] - Funktionality for modify directory entries\n - Funktionality for add certificates entries\n - Funktionality for delete certificates entries\n - Funktionality for read certificates entries\n --- ReleaseNotes.md | 7 + bspData/Beispiel_Command_Datei.xml | 153 +- bspData/vorlage_commands_data.xml | 298 +- build.gradle | 6 +- .../api/CertificateAdministrationApi.java | 1315 +++++---- .../api/DirectoryEntryAdministrationApi.java | 1479 +++++----- .../epa/vzd/client/invoker/ApiCallback.java | 68 +- .../ti/epa/vzd/client/invoker/ApiClient.java | 2620 ++++++++--------- .../epa/vzd/client/invoker/ApiException.java | 144 +- .../epa/vzd/client/invoker/ApiResponse.java | 58 +- .../epa/vzd/client/invoker/Configuration.java | 38 +- .../invoker/GzipRequestInterceptor.java | 105 +- .../ti/epa/vzd/client/invoker/JSON.java | 597 ++-- .../ti/epa/vzd/client/invoker/Pair.java | 60 +- .../client/invoker/ProgressRequestBody.java | 92 +- .../client/invoker/ProgressResponseBody.java | 84 +- .../ti/epa/vzd/client/invoker/StringUtil.java | 76 +- .../vzd/client/invoker/auth/ApiKeyAuth.java | 91 +- .../client/invoker/auth/Authentication.java | 19 +- .../client/invoker/auth/HttpBasicAuth.java | 52 +- .../client/invoker/auth/HttpBearerAuth.java | 65 +- .../ti/epa/vzd/client/invoker/auth/OAuth.java | 27 +- .../vzd/client/invoker/auth/OAuthFlow.java | 2 +- .../invoker/auth/OAuthOkHttpClient.java | 86 +- .../client/invoker/auth/RetryingOAuth.java | 277 +- .../vzd/client/model/BaseDirectoryEntry.java | 786 ++--- .../client/model/CreateDirectoryEntry.java | 173 +- .../epa/vzd/client/model/DirectoryEntry.java | 230 +- .../vzd/client/model/DistinguishedName.java | 265 +- .../ti/epa/vzd/client/model/Error.java | 159 +- .../gematik/ti/epa/vzd/client/model/FAD1.java | 145 +- .../ti/epa/vzd/client/model/Fachdaten.java | 171 +- .../epa/vzd/client/model/UserCertificate.java | 440 +-- .../epa/vzd/gemClient/CommandNamesEnum.java | 44 +- .../ti/epa/vzd/gemClient/GemStringUtils.java | 56 +- .../de/gematik/ti/epa/vzd/gemClient/Main.java | 48 +- .../api/GemCertificateAdministrationApi.java | 368 +-- .../GemDirectoryEntryAdministrationApi.java | 425 +-- .../gemClient/command/CommandsBuilder.java | 120 +- .../command/ExecutionController.java | 171 +- .../vzd/gemClient/command/Transformer.java | 150 +- .../AddDirEntryCertExecution.java | 243 +- .../AddDirEntryExecution.java | 183 +- .../DeleteDirEntryCertExecution.java | 203 +- .../DeleteDirEntryExecution.java | 137 +- .../commandExecutions/ExecutionBase.java | 268 +- .../ExecutionCollection.java | 176 +- .../ModifyDirEntryCertExecution.java | 136 +- .../ModifyDirEntryExecution.java | 171 +- .../ReadDirEntryCertExecution.java | 163 +- .../ReadDirEntryExecution.java | 224 +- .../exceptions/CommandException.java | 28 +- .../exceptions/GemClientException.java | 28 +- .../gemClient/exceptions/ReadException.java | 28 +- .../vzd/gemClient/invoker/ConfigHandler.java | 239 +- .../vzd/gemClient/invoker/GemApiClient.java | 398 +-- src/main/resources/jaxb-binding/binding.xml | 4 +- src/main/resources/xsd/commands.xsd | 99 +- .../api/CertificateAdministrationApiTest.java | 51 +- .../DirectoryEntryAdministrationApiTest.java | 52 +- .../client/model/BaseDirectoryEntryTest.java | 19 +- .../model/CreateDirectoryEntryTest.java | 19 +- .../vzd/client/model/DirectoryEntryTest.java | 19 +- .../client/model/DistinguishedNameTest.java | 19 +- .../ti/epa/vzd/client/model/ErrorTest.java | 19 +- .../ti/epa/vzd/client/model/FAD1Test.java | 19 +- .../epa/vzd/client/model/FachdatenTest.java | 19 +- .../vzd/client/model/UserCertificateTest.java | 19 +- .../command/CommandsBuilderTest.java | 100 +- .../AddDirEntryExecutionTest.java | 35 +- .../DeleteDirEntryCertExecutionTest.java | 50 + .../DeleteDirEntryExecutionTest.java | 18 +- .../commandExecutions/ExecutionBaseTest.java | 20 + .../ModifyDirEntryExecutionTest.java | 84 +- .../ReadDirEntryExecutionTest.java | 28 +- .../gemClient/invoker/ConfigHandlerTest.java | 16 - src/test/resources/config/Commands.xml | 64 +- .../config/CommandsMissingOperationName.xml | 62 +- .../config/CommandsWrongFormated.xml | 64 +- .../resources/config/DoubleIdCommands.xml | 37 + .../resources/config/DoubledCommandId.txt | 3 + version.txt | 2 +- 82 files changed, 7615 insertions(+), 7521 deletions(-) create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecutionTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBaseTest.java create mode 100644 src/test/resources/config/DoubleIdCommands.xml create mode 100644 src/test/resources/config/DoubledCommandId.txt diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 227c5a9..66b0990 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,3 +1,10 @@ +# Release 1.0.1 +- Funktionality for modify directory entries + - Funktionality for add certificates entries + - Funktionality for delete certificates entries + - Funktionality for read certificates entries + + # Release 0.5.0 - Funktionality for add directory entries - Funktionality for delete directory entries diff --git a/bspData/Beispiel_Command_Datei.xml b/bspData/Beispiel_Command_Datei.xml index c1482dc..54d7f64 100644 --- a/bspData/Beispiel_Command_Datei.xml +++ b/bspData/Beispiel_Command_Datei.xml @@ -1,15 +1,13 @@ - modifyDirectoryEntries + readDirectoryEntries - ModTest - cn - ou - ou2 - dc - dc2 + uid + cn + givenName + sn displayName otherName streetAddress @@ -20,138 +18,81 @@ organization specialization domainID + + telematikId + - modifyDirectoryEntries - - ModTest2 - - displayName2 - otherName2 - streetAddress2 - postalCode2 - localityName2 - stateOrProvinceName2 - title2 - organization2 - specialization2 - domainID2 - - - readDirectoryEntries - - ReadTest - cn - ou - ou2 - dc - dc2 - + addDirectoryEntries displayName - otherName streetAddress postalCode localityName stateOrProvinceName title organization + otherName specialization domainID + + telematikId + usage + userCertificate + description + - readDirectoryEntries - - ReadTest - - displayName2 - otherName2 - streetAddress2 - postalCode2 - localityName2 - stateOrProvinceName2 - title2 - organization2 - specialization2 - domainID2 - - - addDirectoryEntries + modifyDirectoryEntries - AddTest - cn - ou - ou2 - dc - dc2 + uid + cn displayName - otherName streetAddress postalCode localityName stateOrProvinceName title organization + otherName specialization domainID - - SomeTelematikid - - addDirectoryEntries + deleteDirectoryEntries - AddTest2 + uid - displayName2 - otherName2 - streetAddress2 - postalCode2 - localityName2 - stateOrProvinceName2 - title2 - organization2 - specialization2 - domainID2 + + + addDirectoryEntryCertificate - SomeUserCertificate + 1-HBA-Testkarte-883110000218572 + + uid + + usage + userCertificate + description - deleteDirectoryEntries - - DelTest - cn - ou - ou2 - dc - dc2 - - displayName - otherName - streetAddress - postalCode - localityName - stateOrProvinceName - title - organization - specialization - domainID + readDirectoryEntryCertificate + + telematikId + entryType + usage + professionOID + - deleteDirectoryEntries - - DelTest2 - - displayName2 - otherName2 - streetAddress2 - postalCode2 - localityName2 - stateOrProvinceName2 - title2 - organization2 - specialization2 - domainID2 + deleteDirectoryEntryCertificate + + telematikId + + uid + cn + + \ No newline at end of file diff --git a/bspData/vorlage_commands_data.xml b/bspData/vorlage_commands_data.xml index 08b0286..78b668e 100644 --- a/bspData/vorlage_commands_data.xml +++ b/bspData/vorlage_commands_data.xml @@ -1,152 +1,152 @@ - - addDirectoryEntries - - - - - - - - - - - - - - - - - - <organization/> - <otherName/> - <specialization/> - <domainID/> - <mail/> - <UserCertificate> - <dn> - <cn/> - <uid/> - <dc/> - <dc/> - </dn> - <telematikID/> - <entryType/> - <professionOID/> - <usage/> - <userCertificate/> - <description/> - </UserCertificate> - <personalEntry/> - <dataFromAuthority/> - </Command> - <Command> - <name>readDirectoryEntries</name> - <dn> - <uid/> - <dc/> - <dc/> - </dn> - <givenName/> - <sn/> - <streetAddress/> - <postalCode/> - <localityName/> - <stateOrProvinceName/> - <cn/> - <displayName/> - <title/> - <organization/> - <otherName/> - <specialization/> - <domainID/> - <mail/> - <UserCertificate> - <dn> - <cn/> - <uid/> - <dc/> - <dc/> - </dn> - <telematikID/> - <entryType/> - <professionOID/> - <usage/> - <userCertificate/> - <description/> - </UserCertificate> - <personalEntry/> - <dataFromAuthority/> - </Command> - <Command> - <name>modifyDirectoryEntries</name> - <dn> - <uid required="true"/> - </dn> - <displayName/> - <otherName/> - <streetAddress/> - <postalCode/> - <localityName/> - <stateOrProvinceName/> - <title/> - <organization/> - <specialization/> - <domainID/> - </Command> - <Command> - <name>deleteDirectoryEntries</name> - <dn> - <uid required="true"/> - </dn> - </Command> - <Command> - <name>addDirectoryEntryCertificate</name> - <dn> - <uid required="true"/> - </dn> - <UserCertificate> - <usage/> - <userCertificate required="true"/> - <description/> - </UserCertificate> - </Command> - <Command> - <name>readDirectoryEntryCertificate</name> - <dn> - <uid/> - </dn> - <UserCertificate> - <dn> - <uid/> - </dn> - <telematikID/> - </UserCertificate> - </Command> - <Command> - <name>modifyDirectoryEntryCertificate</name> - <uid required="true"/> - <UserCertificate> - <dn> - <cn required="true"/> - <uid required="true"/> - </dn> - <usage/> - <userCertificate required="true"/> - <description/> - </UserCertificate> - </Command> - <Command> - <name>deleteDirectoryEntryCertificate</name> - <dn> - <uid required="true"/> - </dn> - <UserCertificate> - <dn> - <cn required="true"/> - </dn> - <description/> - </UserCertificate> - </Command> + <Command> + <name>addDirectoryEntries</name> + <dn> + <uid/> + <dc/> + <dc/> + <ou/> + <ou/> + <cn/> + </dn> + <givenName/> + <sn/> + <streetAddress/> + <postalCode/> + <localityName/> + <stateOrProvinceName/> + <cn/> + <displayName/> + <title/> + <organization/> + <otherName/> + <specialization/> + <domainID/> + <mail/> + <UserCertificate> + <dn> + <cn/> + <uid/> + <dc/> + <dc/> + </dn> + <telematikID/> + <entryType/> + <professionOID/> + <usage/> + <userCertificate/> + <description/> + </UserCertificate> + <personalEntry/> + <dataFromAuthority/> + </Command> + <Command> + <name>readDirectoryEntries</name> + <dn> + <uid/> + <dc/> + <dc/> + </dn> + <givenName/> + <sn/> + <streetAddress/> + <postalCode/> + <localityName/> + <stateOrProvinceName/> + <cn/> + <displayName/> + <title/> + <organization/> + <otherName/> + <specialization/> + <domainID/> + <mail/> + <UserCertificate> + <dn> + <cn/> + <uid/> + <dc/> + <dc/> + </dn> + <telematikID/> + <entryType/> + <professionOID/> + <usage/> + <userCertificate/> + <description/> + </UserCertificate> + <personalEntry/> + <dataFromAuthority/> + </Command> + <Command> + <name>modifyDirectoryEntries</name> + <dn> + <uid required="true"/> + </dn> + <displayName/> + <otherName/> + <streetAddress/> + <postalCode/> + <localityName/> + <stateOrProvinceName/> + <title/> + <organization/> + <specialization/> + <domainID/> + </Command> + <Command> + <name>deleteDirectoryEntries</name> + <dn> + <uid required="true"/> + </dn> + </Command> + <Command> + <name>addDirectoryEntryCertificate</name> + <dn> + <uid required="true"/> + </dn> + <UserCertificate> + <usage/> + <userCertificate required="true"/> + <description/> + </UserCertificate> + </Command> + <Command> + <name>readDirectoryEntryCertificate</name> + <dn> + <uid/> + </dn> + <UserCertificate> + <dn> + <uid/> + </dn> + <telematikID/> + </UserCertificate> + </Command> + <Command> + <name>modifyDirectoryEntryCertificate</name> + <uid required="true"/> + <UserCertificate> + <dn> + <cn required="true"/> + <uid required="true"/> + </dn> + <usage/> + <userCertificate required="true"/> + <description/> + </UserCertificate> + </Command> + <Command> + <name>deleteDirectoryEntryCertificate</name> + <dn> + <uid required="true"/> + </dn> + <UserCertificate> + <dn> + <cn required="true"/> + </dn> + <description/> + </UserCertificate> + </Command> </CommandList> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 8683d80..67d5e88 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,7 @@ dependencies { implementation 'org.apache.oltu.oauth2:org.apache.oltu.oauth2.common:1.0.2' implementation 'org.apache.logging.log4j:log4j-core:2.11.2' implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.2' + implementation 'org.apache.httpcomponents:httpclient:4.5' implementation 'org.slf4j:slf4j-api:1.7.9' implementation 'javax.xml.bind:jaxb-api:2.3.1' implementation 'com.sun.xml.bind:jaxb-impl:2.3.1' @@ -114,17 +115,12 @@ artifacts { archives javadocJar } -//tasks.withType(Tar) { -// enabled = false -//} - gematikPublish { name = "VZD-Client" description = "VZD-Client to add, remove and change data" gitHubProjectName = "app-VZD-Client" } - distributions { main { baseName = project.name diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApi.java b/src/main/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApi.java index 7d298f2..4533147 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApi.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApi.java @@ -29,15 +29,24 @@ package de.gematik.ti.epa.vzd.client.api; -import com.google.gson.reflect.TypeToken; import de.gematik.ti.epa.vzd.client.invoker.ApiCallback; import de.gematik.ti.epa.vzd.client.invoker.ApiClient; import de.gematik.ti.epa.vzd.client.invoker.ApiException; import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; import de.gematik.ti.epa.vzd.client.invoker.Configuration; import de.gematik.ti.epa.vzd.client.invoker.Pair; +import de.gematik.ti.epa.vzd.client.invoker.ProgressRequestBody; +import de.gematik.ti.epa.vzd.client.invoker.ProgressResponseBody; + +import com.google.gson.reflect.TypeToken; + +import java.io.IOException; + + import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.client.model.Error; import de.gematik.ti.epa.vzd.client.model.UserCertificate; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; @@ -46,691 +55,697 @@ public class CertificateAdministrationApi { - private ApiClient localVarApiClient; - - public CertificateAdministrationApi() { - this(Configuration.getDefaultApiClient()); - } - - public CertificateAdministrationApi(ApiClient apiClient) { - this.localVarApiClient = apiClient; - } - - public ApiClient getApiClient() { - return localVarApiClient; - } - - public void setApiClient(ApiClient apiClient) { - this.localVarApiClient = apiClient; - } - - /** - * Build call for addDirectoryEntryCertificate - * - * @param uid ID (dn.uid) vom übergeordneten Verzeichniseintrag (required) - * @param userCertificate Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert - * ------------------------------------------- dn. Nicht vorhanden (Adressierung erfolgt über uid in Path) telematikID Kann - * optional belegt werden. Wird telematikID angegeben, dann muss diese telematikID mit der telematikID im userCertificate - * übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode 422 abgelehnt. entryType Nicht - * vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann - * optional belegt werden userCertificate Muss vorhanden sein description Kann optional belegt werden (required) - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 201 </td><td> Created </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call addDirectoryEntryCertificateCall(String uid, UserCertificate userCertificate, final ApiCallback _callback) throws ApiException { - Object localVarPostBody = userCertificate; - - // create path and map variables - String localVarPath = "/DirectoryEntries/{uid}/Certificates" - .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - "application/json" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); + private ApiClient localVarApiClient; + + public CertificateAdministrationApi() { + this(Configuration.getDefaultApiClient()); + } + + public CertificateAdministrationApi(ApiClient apiClient) { + this.localVarApiClient = apiClient; + } + + public ApiClient getApiClient() { + return localVarApiClient; + } + + public void setApiClient(ApiClient apiClient) { + this.localVarApiClient = apiClient; + } + + /** + * Build call for addDirectoryEntryCertificate + * + * @param uid ID (dn.uid) vom übergeordneten Verzeichniseintrag (required) + * @param userCertificate Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path) + * telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss diese telematikID mit der + * telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode 422 + * abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird + * vom Verzeichnisdienst belegt) usage Kann optional belegt werden userCertificate Muss vorhanden sein + * description Kann optional belegt werden (required) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call addDirectoryEntryCertificateCall(String uid, UserCertificate userCertificate, final ApiCallback _callback) + throws ApiException { + Object localVarPostBody = userCertificate; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/Certificates" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - final String[] localVarContentTypes = { - "application/json" - }; - final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, - localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); - } - - @SuppressWarnings("rawtypes") - private okhttp3.Call addDirectoryEntryCertificateValidateBeforeCall(String uid, UserCertificate userCertificate, final ApiCallback _callback) - throws ApiException { - - // verify the required parameter 'uid' is set - if (uid == null) { - throw new ApiException("Missing the required parameter 'uid' when calling addDirectoryEntryCertificate(Async)"); + @SuppressWarnings("rawtypes") + private okhttp3.Call addDirectoryEntryCertificateValidateBeforeCall(String uid, UserCertificate userCertificate, final ApiCallback _callback) + throws ApiException { + + // verify the required parameter 'uid' is set + if (uid == null) { + throw new ApiException("Missing the required parameter 'uid' when calling addDirectoryEntryCertificate(Async)"); + } + + // verify the required parameter 'userCertificate' is set + if (userCertificate == null) { + throw new ApiException("Missing the required parameter 'userCertificate' when calling addDirectoryEntryCertificate(Async)"); + } + + okhttp3.Call localVarCall = addDirectoryEntryCertificateCall(uid, userCertificate, _callback); + return localVarCall; + + } + + /** + * Der Zertifikatseintrag wird im Verzeichnisdienst hinzugefügt und ist logisch über dn.uid mit dem übergeordneten Verzeichniseintrag verknüpft. + * + * @param uid ID (dn.uid) vom übergeordneten Verzeichniseintrag (required) + * @param userCertificate Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path) + * telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss diese telematikID mit der + * telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode 422 + * abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird + * vom Verzeichnisdienst belegt) usage Kann optional belegt werden userCertificate Muss vorhanden sein + * description Kann optional belegt werden (required) + * @return DistinguishedName + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public DistinguishedName addDirectoryEntryCertificate(String uid, UserCertificate userCertificate) throws ApiException { + ApiResponse<DistinguishedName> localVarResp = addDirectoryEntryCertificateWithHttpInfo(uid, userCertificate); + return localVarResp.getData(); + } + + /** + * Der Zertifikatseintrag wird im Verzeichnisdienst hinzugefügt und ist logisch über dn.uid mit dem übergeordneten Verzeichniseintrag verknüpft. + * + * @param uid ID (dn.uid) vom übergeordneten Verzeichniseintrag (required) + * @param userCertificate Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path) + * telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss diese telematikID mit der + * telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode 422 + * abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird + * vom Verzeichnisdienst belegt) usage Kann optional belegt werden userCertificate Muss vorhanden sein + * description Kann optional belegt werden (required) + * @return ApiResponse<DistinguishedName> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public ApiResponse<DistinguishedName> addDirectoryEntryCertificateWithHttpInfo(String uid, UserCertificate userCertificate) throws ApiException { + okhttp3.Call localVarCall = addDirectoryEntryCertificateValidateBeforeCall(uid, userCertificate, null); + Type localVarReturnType = new TypeToken<DistinguishedName>() { + }.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); } - // verify the required parameter 'userCertificate' is set - if (userCertificate == null) { - throw new ApiException("Missing the required parameter 'userCertificate' when calling addDirectoryEntryCertificate(Async)"); + /** + * Der Zertifikatseintrag wird im Verzeichnisdienst hinzugefügt und ist logisch über dn.uid mit dem übergeordneten Verzeichniseintrag verknüpft. + * (asynchronously) + * + * @param uid ID (dn.uid) vom übergeordneten Verzeichniseintrag (required) + * @param userCertificate Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path) + * telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss diese telematikID mit der + * telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode 422 + * abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird + * vom Verzeichnisdienst belegt) usage Kann optional belegt werden userCertificate Muss vorhanden sein + * description Kann optional belegt werden (required) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call addDirectoryEntryCertificateAsync(String uid, UserCertificate userCertificate, final ApiCallback<DistinguishedName> _callback) + throws ApiException { + + okhttp3.Call localVarCall = addDirectoryEntryCertificateValidateBeforeCall(uid, userCertificate, _callback); + Type localVarReturnType = new TypeToken<DistinguishedName>() { + }.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; } - okhttp3.Call localVarCall = addDirectoryEntryCertificateCall(uid, userCertificate, _callback); - return localVarCall; - - } - - /** - * Der Zertifikatseintrag wird im Verzeichnisdienst hinzugefügt und ist logisch über dn.uid mit dem übergeordneten Verzeichniseintrag verknüpft. - * - * @param uid ID (dn.uid) vom übergeordneten Verzeichniseintrag (required) - * @param userCertificate Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert - * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path) telematikID Kann - * optional belegt werden. Wird telematikID angegeben, dann muss diese telematikID mit der telematikID im userCertificate - * übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode 422 abgelehnt. entryType Nicht - * vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann - * optional belegt werden userCertificate Muss vorhanden sein description Kann optional belegt werden (required) - * @return DistinguishedName - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 201 </td><td> Created </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> - * </table> - */ - public DistinguishedName addDirectoryEntryCertificate(String uid, UserCertificate userCertificate) throws ApiException { - ApiResponse<DistinguishedName> localVarResp = addDirectoryEntryCertificateWithHttpInfo(uid, userCertificate); - return localVarResp.getData(); - } - - /** - * Der Zertifikatseintrag wird im Verzeichnisdienst hinzugefügt und ist logisch über dn.uid mit dem übergeordneten Verzeichniseintrag verknüpft. - * - * @param uid ID (dn.uid) vom übergeordneten Verzeichniseintrag (required) - * @param userCertificate Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert - * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path) telematikID Kann - * optional belegt werden. Wird telematikID angegeben, dann muss diese telematikID mit der telematikID im userCertificate - * übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode 422 abgelehnt. entryType Nicht - * vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann - * optional belegt werden userCertificate Muss vorhanden sein description Kann optional belegt werden (required) - * @return ApiResponse<DistinguishedName> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 201 </td><td> Created </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> - * </table> - */ - public ApiResponse<DistinguishedName> addDirectoryEntryCertificateWithHttpInfo(String uid, UserCertificate userCertificate) throws ApiException { - okhttp3.Call localVarCall = addDirectoryEntryCertificateValidateBeforeCall(uid, userCertificate, null); - Type localVarReturnType = new TypeToken<DistinguishedName>() { - }.getType(); - return localVarApiClient.execute(localVarCall, localVarReturnType); - } - - /** - * Der Zertifikatseintrag wird im Verzeichnisdienst hinzugefügt und ist logisch über dn.uid mit dem übergeordneten Verzeichniseintrag verknüpft. - * (asynchronously) - * - * @param uid ID (dn.uid) vom übergeordneten Verzeichniseintrag (required) - * @param userCertificate Datensatz für die Erzeugung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert - * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid in Path) telematikID Kann - * optional belegt werden. Wird telematikID angegeben, dann muss diese telematikID mit der telematikID im userCertificate - * übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode 422 abgelehnt. entryType Nicht - * vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann - * optional belegt werden userCertificate Muss vorhanden sein description Kann optional belegt werden (required) - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 201 </td><td> Created </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call addDirectoryEntryCertificateAsync(String uid, UserCertificate userCertificate, final ApiCallback<DistinguishedName> _callback) - throws ApiException { - - okhttp3.Call localVarCall = addDirectoryEntryCertificateValidateBeforeCall(uid, userCertificate, _callback); - Type localVarReturnType = new TypeToken<DistinguishedName>() { - }.getType(); - localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); - return localVarCall; - } - - /** - * Build call for deleteDirectoryEntryCertificate - * - * @param uid ID vom übergeordneten Verzeichniseintrag (required) - * @param certificateEntryID ID von dem zu löschenden Zertifikatseintrag (required) - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call deleteDirectoryEntryCertificateCall(String uid, String certificateEntryID, final ApiCallback _callback) throws ApiException { - Object localVarPostBody = null; - - // create path and map variables - String localVarPath = "/DirectoryEntries/{uid}/Certificates/{certificateEntryID}" - .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())) - .replaceAll("\\{" + "certificateEntryID" + "\\}", localVarApiClient.escapeString(certificateEntryID.toString())); - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); + /** + * Build call for deleteDirectoryEntryCertificate + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem zu löschenden Zertifikatseintrag (required) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call deleteDirectoryEntryCertificateCall(String uid, String certificateEntryID, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/Certificates/{certificateEntryID}" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())) + .replaceAll("\\{" + "certificateEntryID" + "\\}", localVarApiClient.escapeString(certificateEntryID.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "DELETE", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - final String[] localVarContentTypes = { + @SuppressWarnings("rawtypes") + private okhttp3.Call deleteDirectoryEntryCertificateValidateBeforeCall(String uid, String certificateEntryID, final ApiCallback _callback) + throws ApiException { - }; - final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); + // verify the required parameter 'uid' is set + if (uid == null) { + throw new ApiException("Missing the required parameter 'uid' when calling deleteDirectoryEntryCertificate(Async)"); + } - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "DELETE", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, - localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); - } + // verify the required parameter 'certificateEntryID' is set + if (certificateEntryID == null) { + throw new ApiException("Missing the required parameter 'certificateEntryID' when calling deleteDirectoryEntryCertificate(Async)"); + } - @SuppressWarnings("rawtypes") - private okhttp3.Call deleteDirectoryEntryCertificateValidateBeforeCall(String uid, String certificateEntryID, final ApiCallback _callback) - throws ApiException { + okhttp3.Call localVarCall = deleteDirectoryEntryCertificateCall(uid, certificateEntryID, _callback); + return localVarCall; - // verify the required parameter 'uid' is set - if (uid == null) { - throw new ApiException("Missing the required parameter 'uid' when calling deleteDirectoryEntryCertificate(Async)"); } - // verify the required parameter 'certificateEntryID' is set - if (certificateEntryID == null) { - throw new ApiException("Missing the required parameter 'certificateEntryID' when calling deleteDirectoryEntryCertificate(Async)"); + /** + * Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat + * mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem zu löschenden Zertifikatseintrag (required) + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public void deleteDirectoryEntryCertificate(String uid, String certificateEntryID) throws ApiException { + deleteDirectoryEntryCertificateWithHttpInfo(uid, certificateEntryID); } - okhttp3.Call localVarCall = deleteDirectoryEntryCertificateCall(uid, certificateEntryID, _callback); - return localVarCall; - - } - - /** - * Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat - * mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. - * - * @param uid ID vom übergeordneten Verzeichniseintrag (required) - * @param certificateEntryID ID von dem zu löschenden Zertifikatseintrag (required) - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public void deleteDirectoryEntryCertificate(String uid, String certificateEntryID) throws ApiException { - deleteDirectoryEntryCertificateWithHttpInfo(uid, certificateEntryID); - } - - /** - * Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat - * mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. - * - * @param uid ID vom übergeordneten Verzeichniseintrag (required) - * @param certificateEntryID ID von dem zu löschenden Zertifikatseintrag (required) - * @return ApiResponse<Void> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public ApiResponse<Void> deleteDirectoryEntryCertificateWithHttpInfo(String uid, String certificateEntryID) throws ApiException { - okhttp3.Call localVarCall = deleteDirectoryEntryCertificateValidateBeforeCall(uid, certificateEntryID, null); - return localVarApiClient.execute(localVarCall); - } - - /** - * Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat - * mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. (asynchronously) - * - * @param uid ID vom übergeordneten Verzeichniseintrag (required) - * @param certificateEntryID ID von dem zu löschenden Zertifikatseintrag (required) - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call deleteDirectoryEntryCertificateAsync(String uid, String certificateEntryID, final ApiCallback<Void> _callback) - throws ApiException { - - okhttp3.Call localVarCall = deleteDirectoryEntryCertificateValidateBeforeCall(uid, certificateEntryID, _callback); - localVarApiClient.executeAsync(localVarCall, _callback); - return localVarCall; - } - - /** - * Build call for modifyDirectoryEntryCertificate - * - * @param uid ID vom übergeordneten Verzeichniseintrag (required) - * @param certificateEntryID ID von dem Zertifikat (required) - * @param userCertificate Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert - * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & - * certificateEntryID in Path). telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss diese - * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation - * mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht - * vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate unverändert gegenüber - * VZD description Kann optional belegt werden. (required) - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call modifyDirectoryEntryCertificateCall(String uid, String certificateEntryID, UserCertificate userCertificate, - final ApiCallback _callback) throws ApiException { - Object localVarPostBody = userCertificate; - - // create path and map variables - String localVarPath = "/DirectoryEntries/{uid}/Certificates/{certificateEntryID}" - .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())) - .replaceAll("\\{" + "certificateEntryID" + "\\}", localVarApiClient.escapeString(certificateEntryID.toString())); - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - "application/json" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); + /** + * Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat + * mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem zu löschenden Zertifikatseintrag (required) + * @return ApiResponse<Void> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public ApiResponse<Void> deleteDirectoryEntryCertificateWithHttpInfo(String uid, String certificateEntryID) throws ApiException { + okhttp3.Call localVarCall = deleteDirectoryEntryCertificateValidateBeforeCall(uid, certificateEntryID, null); + return localVarApiClient.execute(localVarCall); } - final String[] localVarContentTypes = { - "application/json" - }; - final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "PUT", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, - localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); - } - - @SuppressWarnings("rawtypes") - private okhttp3.Call modifyDirectoryEntryCertificateValidateBeforeCall(String uid, String certificateEntryID, UserCertificate userCertificate, - final ApiCallback _callback) throws ApiException { - - // verify the required parameter 'uid' is set - if (uid == null) { - throw new ApiException("Missing the required parameter 'uid' when calling modifyDirectoryEntryCertificate(Async)"); + /** + * Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat + * mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. (asynchronously) + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem zu löschenden Zertifikatseintrag (required) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call deleteDirectoryEntryCertificateAsync(String uid, String certificateEntryID, final ApiCallback<Void> _callback) + throws ApiException { + + okhttp3.Call localVarCall = deleteDirectoryEntryCertificateValidateBeforeCall(uid, certificateEntryID, _callback); + localVarApiClient.executeAsync(localVarCall, _callback); + return localVarCall; } - // verify the required parameter 'certificateEntryID' is set - if (certificateEntryID == null) { - throw new ApiException("Missing the required parameter 'certificateEntryID' when calling modifyDirectoryEntryCertificate(Async)"); + /** + * Build call for modifyDirectoryEntryCertificate + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem Zertifikat (required) + * @param userCertificate Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & + * certificateEntryID in Path). telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss + * diese telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die + * Operation mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) + * professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. + * userCertificate unverändert gegenüber VZD description Kann optional belegt werden. (required) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call modifyDirectoryEntryCertificateCall(String uid, String certificateEntryID, UserCertificate userCertificate, + final ApiCallback _callback) throws ApiException { + Object localVarPostBody = userCertificate; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/Certificates/{certificateEntryID}" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())) + .replaceAll("\\{" + "certificateEntryID" + "\\}", localVarApiClient.escapeString(certificateEntryID.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "PUT", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - // verify the required parameter 'userCertificate' is set - if (userCertificate == null) { - throw new ApiException("Missing the required parameter 'userCertificate' when calling modifyDirectoryEntryCertificate(Async)"); + @SuppressWarnings("rawtypes") + private okhttp3.Call modifyDirectoryEntryCertificateValidateBeforeCall(String uid, String certificateEntryID, UserCertificate userCertificate, + final ApiCallback _callback) throws ApiException { + + // verify the required parameter 'uid' is set + if (uid == null) { + throw new ApiException("Missing the required parameter 'uid' when calling modifyDirectoryEntryCertificate(Async)"); + } + + // verify the required parameter 'certificateEntryID' is set + if (certificateEntryID == null) { + throw new ApiException("Missing the required parameter 'certificateEntryID' when calling modifyDirectoryEntryCertificate(Async)"); + } + + // verify the required parameter 'userCertificate' is set + if (userCertificate == null) { + throw new ApiException("Missing the required parameter 'userCertificate' when calling modifyDirectoryEntryCertificate(Async)"); + } + + okhttp3.Call localVarCall = modifyDirectoryEntryCertificateCall(uid, certificateEntryID, userCertificate, _callback); + return localVarCall; + } - okhttp3.Call localVarCall = modifyDirectoryEntryCertificateCall(uid, certificateEntryID, userCertificate, _callback); - return localVarCall; - - } - - /** - * Der Zertifikatseintrag wird mit den übergebenen Daten aktualisiert. - * - * @param uid ID vom übergeordneten Verzeichniseintrag (required) - * @param certificateEntryID ID von dem Zertifikat (required) - * @param userCertificate Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert - * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & - * certificateEntryID in Path). telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss diese - * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation - * mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht - * vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate unverändert gegenüber - * VZD description Kann optional belegt werden. (required) - * @return UserCertificate - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> - * </table> - */ - public UserCertificate modifyDirectoryEntryCertificate(String uid, String certificateEntryID, UserCertificate userCertificate) throws ApiException { - ApiResponse<UserCertificate> localVarResp = modifyDirectoryEntryCertificateWithHttpInfo(uid, certificateEntryID, userCertificate); - return localVarResp.getData(); - } - - /** - * Der Zertifikatseintrag wird mit den übergebenen Daten aktualisiert. - * - * @param uid ID vom übergeordneten Verzeichniseintrag (required) - * @param certificateEntryID ID von dem Zertifikat (required) - * @param userCertificate Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert - * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & - * certificateEntryID in Path). telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss diese - * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation - * mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht - * vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate unverändert gegenüber - * VZD description Kann optional belegt werden. (required) - * @return ApiResponse<UserCertificate> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> - * </table> - */ - public ApiResponse<UserCertificate> modifyDirectoryEntryCertificateWithHttpInfo(String uid, String certificateEntryID, - UserCertificate userCertificate) throws ApiException { - okhttp3.Call localVarCall = modifyDirectoryEntryCertificateValidateBeforeCall(uid, certificateEntryID, userCertificate, null); - Type localVarReturnType = new TypeToken<UserCertificate>() { - }.getType(); - return localVarApiClient.execute(localVarCall, localVarReturnType); - } - - /** - * Der Zertifikatseintrag wird mit den übergebenen Daten aktualisiert. (asynchronously) - * - * @param uid ID vom übergeordneten Verzeichniseintrag (required) - * @param certificateEntryID ID von dem Zertifikat (required) - * @param userCertificate Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert - * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & - * certificateEntryID in Path). telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss diese - * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation - * mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht - * vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate unverändert gegenüber - * VZD description Kann optional belegt werden. (required) - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call modifyDirectoryEntryCertificateAsync(String uid, String certificateEntryID, UserCertificate userCertificate, - final ApiCallback<UserCertificate> _callback) throws ApiException { - - okhttp3.Call localVarCall = modifyDirectoryEntryCertificateValidateBeforeCall(uid, certificateEntryID, userCertificate, _callback); - Type localVarReturnType = new TypeToken<UserCertificate>() { - }.getType(); - localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); - return localVarCall; - } - - /** - * Build call for readDirectoryCertificates - * - * @param uid ID vom übergeordneten Verzeichniseintrag (optional) - * @param certificateEntryID ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) - * Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. - * (optional) - * @param entryType Erlaubt die Suche mit Hilfe des Attributs entryType. (optional) - * @param telematikID telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. (optional) - * @param professionOID Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene - * professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. (optional) - * @param usage Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im - * Attribut usage (array) des Zertifikatseintrags enthalten ist. (optional) - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call readDirectoryCertificatesCall(String uid, String certificateEntryID, String entryType, String telematikID, String professionOID, - String usage, final ApiCallback _callback) throws ApiException { - Object localVarPostBody = null; - - // create path and map variables - String localVarPath = "/DirectoryEntries/Certificates"; - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - if (uid != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("uid", uid)); + /** + * Der Zertifikatseintrag wird mit den übergebenen Daten aktualisiert. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem Zertifikat (required) + * @param userCertificate Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & + * certificateEntryID in Path). telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss + * diese telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die + * Operation mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) + * professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. + * userCertificate unverändert gegenüber VZD description Kann optional belegt werden. (required) + * @return UserCertificate + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public UserCertificate modifyDirectoryEntryCertificate(String uid, String certificateEntryID, UserCertificate userCertificate) + throws ApiException { + ApiResponse<UserCertificate> localVarResp = modifyDirectoryEntryCertificateWithHttpInfo(uid, certificateEntryID, userCertificate); + return localVarResp.getData(); } - if (certificateEntryID != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("certificateEntryID", certificateEntryID)); + /** + * Der Zertifikatseintrag wird mit den übergebenen Daten aktualisiert. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem Zertifikat (required) + * @param userCertificate Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & + * certificateEntryID in Path). telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss + * diese telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die + * Operation mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) + * professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. + * userCertificate unverändert gegenüber VZD description Kann optional belegt werden. (required) + * @return ApiResponse<UserCertificate> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public ApiResponse<UserCertificate> modifyDirectoryEntryCertificateWithHttpInfo(String uid, String certificateEntryID, + UserCertificate userCertificate) throws ApiException { + okhttp3.Call localVarCall = modifyDirectoryEntryCertificateValidateBeforeCall(uid, certificateEntryID, userCertificate, null); + Type localVarReturnType = new TypeToken<UserCertificate>() { + }.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); } - if (entryType != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("entryType", entryType)); + /** + * Der Zertifikatseintrag wird mit den übergebenen Daten aktualisiert. (asynchronously) + * + * @param uid ID vom übergeordneten Verzeichniseintrag (required) + * @param certificateEntryID ID von dem Zertifikat (required) + * @param userCertificate Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein Attribut Wert + * ------------------------------------------- dn.* Nicht vorhanden (Adressierung erfolgt über uid & + * certificateEntryID in Path). telematikID Kann optional belegt werden. Wird telematikID angegeben, dann muss + * diese telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die + * Operation mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) + * professionOID Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. + * userCertificate unverändert gegenüber VZD description Kann optional belegt werden. (required) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call modifyDirectoryEntryCertificateAsync(String uid, String certificateEntryID, UserCertificate userCertificate, + final ApiCallback<UserCertificate> _callback) throws ApiException { + + okhttp3.Call localVarCall = modifyDirectoryEntryCertificateValidateBeforeCall(uid, certificateEntryID, userCertificate, _callback); + Type localVarReturnType = new TypeToken<UserCertificate>() { + }.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; } - if (telematikID != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("telematikID", telematikID)); + /** + * Build call for readDirectoryCertificates + * + * @param uid ID vom übergeordneten Verzeichniseintrag (optional) + * @param certificateEntryID ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) + * Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. + * (optional) + * @param entryType Erlaubt die Suche mit Hilfe des Attributs entryType. (optional) + * @param telematikID telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. (optional) + * @param professionOID Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. (optional) + * @param usage Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im + * Attribut usage (array) des Zertifikatseintrags enthalten ist. (optional) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call readDirectoryCertificatesCall(String uid, String certificateEntryID, String entryType, String telematikID, + String professionOID, String usage, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries/Certificates"; + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + if (uid != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("uid", uid)); + } + + if (certificateEntryID != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("certificateEntryID", certificateEntryID)); + } + + if (entryType != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("entryType", entryType)); + } + + if (telematikID != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("telematikID", telematikID)); + } + + if (professionOID != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("professionOID", professionOID)); + } + + if (usage != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("usage", usage)); + } + + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - if (professionOID != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("professionOID", professionOID)); + @SuppressWarnings("rawtypes") + private okhttp3.Call readDirectoryCertificatesValidateBeforeCall(String uid, String certificateEntryID, String entryType, String telematikID, + String professionOID, String usage, final ApiCallback _callback) throws ApiException { + + okhttp3.Call localVarCall = readDirectoryCertificatesCall(uid, certificateEntryID, entryType, telematikID, professionOID, usage, _callback); + return localVarCall; + } - if (usage != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("usage", usage)); + /** + * Zertifikat lesen Liefert alle Zertifikate, die dem Filter (siehe 'parameters') entsprechen. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (optional) + * @param certificateEntryID ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) + * Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. + * (optional) + * @param entryType Erlaubt die Suche mit Hilfe des Attributs entryType. (optional) + * @param telematikID telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. (optional) + * @param professionOID Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. (optional) + * @param usage Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im + * Attribut usage (array) des Zertifikatseintrags enthalten ist. (optional) + * @return List<UserCertificate> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public List<UserCertificate> readDirectoryCertificates(String uid, String certificateEntryID, String entryType, String telematikID, + String professionOID, String usage) throws ApiException { + ApiResponse<List<UserCertificate>> localVarResp = readDirectoryCertificatesWithHttpInfo(uid, certificateEntryID, entryType, telematikID, + professionOID, usage); + return localVarResp.getData(); } - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - "application/json" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); + /** + * Zertifikat lesen Liefert alle Zertifikate, die dem Filter (siehe 'parameters') entsprechen. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (optional) + * @param certificateEntryID ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) + * Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. + * (optional) + * @param entryType Erlaubt die Suche mit Hilfe des Attributs entryType. (optional) + * @param telematikID telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. (optional) + * @param professionOID Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. (optional) + * @param usage Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im + * Attribut usage (array) des Zertifikatseintrags enthalten ist. (optional) + * @return ApiResponse<List<UserCertificate>> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public ApiResponse<List<UserCertificate>> readDirectoryCertificatesWithHttpInfo(String uid, String certificateEntryID, String entryType, + String telematikID, String professionOID, String usage) throws ApiException { + okhttp3.Call localVarCall = readDirectoryCertificatesValidateBeforeCall(uid, certificateEntryID, entryType, telematikID, professionOID, usage, + null); + Type localVarReturnType = new TypeToken<List<UserCertificate>>() { + }.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); } - final String[] localVarContentTypes = { - - }; - final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, - localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); - } - - @SuppressWarnings("rawtypes") - private okhttp3.Call readDirectoryCertificatesValidateBeforeCall(String uid, String certificateEntryID, String entryType, String telematikID, - String professionOID, String usage, final ApiCallback _callback) throws ApiException { - - okhttp3.Call localVarCall = readDirectoryCertificatesCall(uid, certificateEntryID, entryType, telematikID, professionOID, usage, _callback); - return localVarCall; - - } - - /** - * Zertifikat lesen Liefert alle Zertifikate, die dem Filter (siehe 'parameters') entsprechen. - * - * @param uid ID vom übergeordneten Verzeichniseintrag (optional) - * @param certificateEntryID ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) - * Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. - * (optional) - * @param entryType Erlaubt die Suche mit Hilfe des Attributs entryType. (optional) - * @param telematikID telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. (optional) - * @param professionOID Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene - * professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. (optional) - * @param usage Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im - * Attribut usage (array) des Zertifikatseintrags enthalten ist. (optional) - * @return List<UserCertificate> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public List<UserCertificate> readDirectoryCertificates(String uid, String certificateEntryID, String entryType, String telematikID, - String professionOID, String usage) throws ApiException { - ApiResponse<List<UserCertificate>> localVarResp = readDirectoryCertificatesWithHttpInfo(uid, certificateEntryID, entryType, telematikID, - professionOID, usage); - return localVarResp.getData(); - } - - /** - * Zertifikat lesen Liefert alle Zertifikate, die dem Filter (siehe 'parameters') entsprechen. - * - * @param uid ID vom übergeordneten Verzeichniseintrag (optional) - * @param certificateEntryID ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) - * Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. - * (optional) - * @param entryType Erlaubt die Suche mit Hilfe des Attributs entryType. (optional) - * @param telematikID telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. (optional) - * @param professionOID Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene - * professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. (optional) - * @param usage Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im - * Attribut usage (array) des Zertifikatseintrags enthalten ist. (optional) - * @return ApiResponse<List<UserCertificate>> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public ApiResponse<List<UserCertificate>> readDirectoryCertificatesWithHttpInfo(String uid, String certificateEntryID, String entryType, - String telematikID, String professionOID, String usage) throws ApiException { - okhttp3.Call localVarCall = readDirectoryCertificatesValidateBeforeCall(uid, certificateEntryID, entryType, telematikID, professionOID, usage, - null); - Type localVarReturnType = new TypeToken<List<UserCertificate>>() { - }.getType(); - return localVarApiClient.execute(localVarCall, localVarReturnType); - } - - /** - * Zertifikat lesen (asynchronously) Liefert alle Zertifikate, die dem Filter (siehe 'parameters') entsprechen. - * - * @param uid ID vom übergeordneten Verzeichniseintrag (optional) - * @param certificateEntryID ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) - * Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. - * (optional) - * @param entryType Erlaubt die Suche mit Hilfe des Attributs entryType. (optional) - * @param telematikID telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. (optional) - * @param professionOID Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene - * professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. (optional) - * @param usage Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im - * Attribut usage (array) des Zertifikatseintrags enthalten ist. (optional) - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call readDirectoryCertificatesAsync(String uid, String certificateEntryID, String entryType, String telematikID, - String professionOID, String usage, final ApiCallback<List<UserCertificate>> _callback) throws ApiException { - - okhttp3.Call localVarCall = readDirectoryCertificatesValidateBeforeCall(uid, certificateEntryID, entryType, telematikID, professionOID, usage, - _callback); - Type localVarReturnType = new TypeToken<List<UserCertificate>>() { - }.getType(); - localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); - return localVarCall; - } + /** + * Zertifikat lesen (asynchronously) Liefert alle Zertifikate, die dem Filter (siehe 'parameters') entsprechen. + * + * @param uid ID vom übergeordneten Verzeichniseintrag (optional) + * @param certificateEntryID ID von dem Zertifikat (dn.cn vom Zertifikatseintrag) Wenn angegeben wird das adressierte (certificateEntryID) + * Zertifikat geliefert. Wenn nicht angegeben werden alle Zertifikate des übergeordneten Verzeichniseintrags geliefert. + * (optional) + * @param entryType Erlaubt die Suche mit Hilfe des Attributs entryType. (optional) + * @param telematikID telematikID von dem Zertifikat Erlaubt die Suche nach Zertifikatseinträgen einer telematikID. (optional) + * @param professionOID Erlaubt die Suche mit Hilfe des Attributs professionOID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * professionOID im Attribut professionOID (array) des Zertifikatseintrags enthalten ist. (optional) + * @param usage Erlaubt die Suche mit Hilfe des Attributs usage. Der Verzeichniseintrag wird selektiert, wenn die angegebene usage im + * Attribut usage (array) des Zertifikatseintrags enthalten ist. (optional) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call readDirectoryCertificatesAsync(String uid, String certificateEntryID, String entryType, String telematikID, + String professionOID, String usage, final ApiCallback<List<UserCertificate>> _callback) throws ApiException { + + okhttp3.Call localVarCall = readDirectoryCertificatesValidateBeforeCall(uid, certificateEntryID, entryType, telematikID, professionOID, usage, + _callback); + Type localVarReturnType = new TypeToken<List<UserCertificate>>() { + }.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApi.java b/src/main/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApi.java index 45e81ad..e1fded9 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApi.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApi.java @@ -29,17 +29,26 @@ package de.gematik.ti.epa.vzd.client.api; -import com.google.gson.reflect.TypeToken; import de.gematik.ti.epa.vzd.client.invoker.ApiCallback; import de.gematik.ti.epa.vzd.client.invoker.ApiClient; import de.gematik.ti.epa.vzd.client.invoker.ApiException; import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; import de.gematik.ti.epa.vzd.client.invoker.Configuration; import de.gematik.ti.epa.vzd.client.invoker.Pair; +import de.gematik.ti.epa.vzd.client.invoker.ProgressRequestBody; +import de.gematik.ti.epa.vzd.client.invoker.ProgressResponseBody; + +import com.google.gson.reflect.TypeToken; + +import java.io.IOException; + + import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.client.model.Error; + import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; @@ -48,782 +57,792 @@ public class DirectoryEntryAdministrationApi { - private ApiClient localVarApiClient; - - public DirectoryEntryAdministrationApi() { - this(Configuration.getDefaultApiClient()); - } - - public DirectoryEntryAdministrationApi(ApiClient apiClient) { - this.localVarApiClient = apiClient; - } - - public ApiClient getApiClient() { - return localVarApiClient; - } - - public void setApiClient(ApiClient apiClient) { - this.localVarApiClient = apiClient; - } - - /** - * Build call for addDirectoryEntry - * - * @param createDirectoryEntry Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn. Leer/nicht vorhanden (wird vom - * Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht vorhanden (wird vom - * Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) displayName Kann optional belegt - * werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. localityName Kann optional - * belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt werden. organization Kann - * optional belegt werden. otherName Kann optional belegt werden. specialization Kann optional belegt werden. domainID - * Kann optional belegt werden. personalEntry Nicht vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority - * Nicht vorhanden (wird vom Verzeichnisdienst belegt) userCertificates Kann optional belegt werden. dn.uid Leer/nicht - * vorhanden (wird vom Verzeichnisdienst belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou - * Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst - * belegt) telematikID Kann optional belegt werden. Wird telematikID und userCertificate angegeben, dann muss diese - * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die - * Operation mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID - * Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Kann optional - * belegt werden (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird ein - * Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in dieser - * Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im Verzeichniseintrag - * gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden kann). (required) - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 201 </td><td> Created </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call addDirectoryEntryCall(CreateDirectoryEntry createDirectoryEntry, final ApiCallback _callback) throws ApiException { - Object localVarPostBody = createDirectoryEntry; - - // create path and map variables - String localVarPath = "/DirectoryEntries"; - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - "application/json" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); + private ApiClient localVarApiClient; + + public DirectoryEntryAdministrationApi() { + this(Configuration.getDefaultApiClient()); } - final String[] localVarContentTypes = { - "application/json" - }; - final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, - localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); - } - - @SuppressWarnings("rawtypes") - private okhttp3.Call addDirectoryEntryValidateBeforeCall(CreateDirectoryEntry createDirectoryEntry, final ApiCallback _callback) - throws ApiException { - - // verify the required parameter 'createDirectoryEntry' is set - if (createDirectoryEntry == null) { - throw new ApiException("Missing the required parameter 'createDirectoryEntry' when calling addDirectoryEntry(Async)"); + public DirectoryEntryAdministrationApi(ApiClient apiClient) { + this.localVarApiClient = apiClient; } - okhttp3.Call localVarCall = addDirectoryEntryCall(createDirectoryEntry, _callback); - return localVarCall; - - } - - /** - * Einen Eintrag zum Verzeichnisdienst hinzufügen - * - * @param createDirectoryEntry Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn. Leer/nicht vorhanden (wird vom - * Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht vorhanden (wird vom - * Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) displayName Kann optional belegt - * werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. localityName Kann optional - * belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt werden. organization Kann - * optional belegt werden. otherName Kann optional belegt werden. specialization Kann optional belegt werden. domainID - * Kann optional belegt werden. personalEntry Nicht vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority - * Nicht vorhanden (wird vom Verzeichnisdienst belegt) userCertificates Kann optional belegt werden. dn.uid Leer/nicht - * vorhanden (wird vom Verzeichnisdienst belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou - * Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst - * belegt) telematikID Kann optional belegt werden. Wird telematikID und userCertificate angegeben, dann muss diese - * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die - * Operation mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID - * Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Kann optional - * belegt werden (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird ein - * Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in dieser - * Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im Verzeichniseintrag - * gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden kann). (required) - * @return DistinguishedName - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 201 </td><td> Created </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> - * </table> - */ - public DistinguishedName addDirectoryEntry(CreateDirectoryEntry createDirectoryEntry) throws ApiException { - ApiResponse<DistinguishedName> localVarResp = addDirectoryEntryWithHttpInfo(createDirectoryEntry); - return localVarResp.getData(); - } - - /** - * Einen Eintrag zum Verzeichnisdienst hinzufügen - * - * @param createDirectoryEntry Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn. Leer/nicht vorhanden (wird vom - * Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht vorhanden (wird vom - * Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) displayName Kann optional belegt - * werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. localityName Kann optional - * belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt werden. organization Kann - * optional belegt werden. otherName Kann optional belegt werden. specialization Kann optional belegt werden. domainID - * Kann optional belegt werden. personalEntry Nicht vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority - * Nicht vorhanden (wird vom Verzeichnisdienst belegt) userCertificates Kann optional belegt werden. dn.uid Leer/nicht - * vorhanden (wird vom Verzeichnisdienst belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou - * Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst - * belegt) telematikID Kann optional belegt werden. Wird telematikID und userCertificate angegeben, dann muss diese - * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die - * Operation mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID - * Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Kann optional - * belegt werden (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird ein - * Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in dieser - * Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im Verzeichniseintrag - * gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden kann). (required) - * @return ApiResponse<DistinguishedName> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 201 </td><td> Created </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> - * </table> - */ - public ApiResponse<DistinguishedName> addDirectoryEntryWithHttpInfo(CreateDirectoryEntry createDirectoryEntry) throws ApiException { - okhttp3.Call localVarCall = addDirectoryEntryValidateBeforeCall(createDirectoryEntry, null); - Type localVarReturnType = new TypeToken<DistinguishedName>() { - }.getType(); - return localVarApiClient.execute(localVarCall, localVarReturnType); - } - - /** - * Einen Eintrag zum Verzeichnisdienst hinzufügen (asynchronously) - * - * @param createDirectoryEntry Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn. Leer/nicht vorhanden (wird vom - * Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht vorhanden (wird vom - * Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) displayName Kann optional belegt - * werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. localityName Kann optional - * belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt werden. organization Kann - * optional belegt werden. otherName Kann optional belegt werden. specialization Kann optional belegt werden. domainID - * Kann optional belegt werden. personalEntry Nicht vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority - * Nicht vorhanden (wird vom Verzeichnisdienst belegt) userCertificates Kann optional belegt werden. dn.uid Leer/nicht - * vorhanden (wird vom Verzeichnisdienst belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou - * Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst - * belegt) telematikID Kann optional belegt werden. Wird telematikID und userCertificate angegeben, dann muss diese - * telematikID mit der telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die - * Operation mit Fehlercode 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID - * Nicht vorhanden (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Kann optional - * belegt werden (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird ein - * Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in dieser - * Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im Verzeichniseintrag - * gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden kann). (required) - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 201 </td><td> Created </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call addDirectoryEntryAsync(CreateDirectoryEntry createDirectoryEntry, final ApiCallback<DistinguishedName> _callback) - throws ApiException { - - okhttp3.Call localVarCall = addDirectoryEntryValidateBeforeCall(createDirectoryEntry, _callback); - Type localVarReturnType = new TypeToken<DistinguishedName>() { - }.getType(); - localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); - return localVarCall; - } - - /** - * Build call for deleteDirectoryEntry - * - * @param uid ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate - * und Fachdaten. (required) - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call deleteDirectoryEntryCall(String uid, final ApiCallback _callback) throws ApiException { - Object localVarPostBody = null; - - // create path and map variables - String localVarPath = "/DirectoryEntries/{uid}" - .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); + public ApiClient getApiClient() { + return localVarApiClient; } - final String[] localVarContentTypes = { + public void setApiClient(ApiClient apiClient) { + this.localVarApiClient = apiClient; + } - }; - final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); + /** + * Build call for addDirectoryEntry + * + * @param createDirectoryEntry Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn.* Leer/nicht vorhanden + * (wird vom Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) + * displayName Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional + * belegt werden. localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt werden. title + * Kann optional belegt werden. organization Kann optional belegt werden. otherName Kann optional belegt werden. + * specialization Kann optional belegt werden. domainID Kann optional belegt werden. personalEntry Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority Nicht vorhanden (wird vom Verzeichnisdienst belegt) + * userCertificates Kann optional belegt werden. dn.uid Leer/nicht vorhanden (wird vom Verzeichnisdienst + * belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou Leer/nicht vorhanden (wird vom + * Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) telematikID Kann + * optional belegt werden. Wird telematikID und userCertificate angegeben, dann muss diese telematikID mit der + * telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode + * 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden + * (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Kann optional + * belegt werden (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird + * ein Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in + * dieser Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im + * Verzeichniseintrag gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden + * kann). (required) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call addDirectoryEntryCall(CreateDirectoryEntry createDirectoryEntry, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = createDirectoryEntry; + + // create path and map variables + String localVarPath = "/DirectoryEntries"; + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "DELETE", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, - localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); - } + @SuppressWarnings("rawtypes") + private okhttp3.Call addDirectoryEntryValidateBeforeCall(CreateDirectoryEntry createDirectoryEntry, final ApiCallback _callback) + throws ApiException { - @SuppressWarnings("rawtypes") - private okhttp3.Call deleteDirectoryEntryValidateBeforeCall(String uid, final ApiCallback _callback) throws ApiException { + // verify the required parameter 'createDirectoryEntry' is set + if (createDirectoryEntry == null) { + throw new ApiException("Missing the required parameter 'createDirectoryEntry' when calling addDirectoryEntry(Async)"); + } - // verify the required parameter 'uid' is set - if (uid == null) { - throw new ApiException("Missing the required parameter 'uid' when calling deleteDirectoryEntry(Async)"); - } + okhttp3.Call localVarCall = addDirectoryEntryCall(createDirectoryEntry, _callback); + return localVarCall; - okhttp3.Call localVarCall = deleteDirectoryEntryCall(uid, _callback); - return localVarCall; - - } - - /** - * Gesamten Verzeichniseintrag löschen - * - * @param uid ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate und - * Fachdaten. (required) - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public void deleteDirectoryEntry(String uid) throws ApiException { - deleteDirectoryEntryWithHttpInfo(uid); - } - - /** - * Gesamten Verzeichniseintrag löschen - * - * @param uid ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate und - * Fachdaten. (required) - * @return ApiResponse<Void> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public ApiResponse<Void> deleteDirectoryEntryWithHttpInfo(String uid) throws ApiException { - okhttp3.Call localVarCall = deleteDirectoryEntryValidateBeforeCall(uid, null); - return localVarApiClient.execute(localVarCall); - } - - /** - * Gesamten Verzeichniseintrag löschen (asynchronously) - * - * @param uid ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate - * und Fachdaten. (required) - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call deleteDirectoryEntryAsync(String uid, final ApiCallback<Void> _callback) throws ApiException { - - okhttp3.Call localVarCall = deleteDirectoryEntryValidateBeforeCall(uid, _callback); - localVarApiClient.executeAsync(localVarCall, _callback); - return localVarCall; - } - - /** - * Build call for modifyDirectoryEntry - * - * @param uid ID von dem Verzeichniseintrag (required) - * @param baseDirectoryEntry Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn. Nicht vorhanden - * (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn Nicht vorhanden. displayName - * Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. - * localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt - * werden. organization Kann optional belegt werden. otherName Nicht vorhanden. specialization Kann optional belegt - * werden. domainID Kann optional belegt werden. personalEntry Nicht vorhanden. dataFromAuthority Nicht vorhanden - * (required) - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call modifyDirectoryEntryCall(String uid, BaseDirectoryEntry baseDirectoryEntry, final ApiCallback _callback) throws ApiException { - Object localVarPostBody = baseDirectoryEntry; - - // create path and map variables - String localVarPath = "/DirectoryEntries/{uid}/baseDirectoryEntries" - .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - "application/json" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); } - final String[] localVarContentTypes = { - "application/json" - }; - final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "PUT", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, - localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); - } - - @SuppressWarnings("rawtypes") - private okhttp3.Call modifyDirectoryEntryValidateBeforeCall(String uid, BaseDirectoryEntry baseDirectoryEntry, final ApiCallback _callback) - throws ApiException { - - // verify the required parameter 'uid' is set - if (uid == null) { - throw new ApiException("Missing the required parameter 'uid' when calling modifyDirectoryEntry(Async)"); + /** + * Einen Eintrag zum Verzeichnisdienst hinzufügen + * + * @param createDirectoryEntry Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn.* Leer/nicht vorhanden + * (wird vom Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) + * displayName Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional + * belegt werden. localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt werden. title + * Kann optional belegt werden. organization Kann optional belegt werden. otherName Kann optional belegt werden. + * specialization Kann optional belegt werden. domainID Kann optional belegt werden. personalEntry Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority Nicht vorhanden (wird vom Verzeichnisdienst belegt) + * userCertificates Kann optional belegt werden. dn.uid Leer/nicht vorhanden (wird vom Verzeichnisdienst + * belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou Leer/nicht vorhanden (wird vom + * Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) telematikID Kann + * optional belegt werden. Wird telematikID und userCertificate angegeben, dann muss diese telematikID mit der + * telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode + * 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden + * (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Kann optional + * belegt werden (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird + * ein Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in + * dieser Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im + * Verzeichniseintrag gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden + * kann). (required) + * @return DistinguishedName + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public DistinguishedName addDirectoryEntry(CreateDirectoryEntry createDirectoryEntry) throws ApiException { + ApiResponse<DistinguishedName> localVarResp = addDirectoryEntryWithHttpInfo(createDirectoryEntry); + return localVarResp.getData(); } - // verify the required parameter 'baseDirectoryEntry' is set - if (baseDirectoryEntry == null) { - throw new ApiException("Missing the required parameter 'baseDirectoryEntry' when calling modifyDirectoryEntry(Async)"); + /** + * Einen Eintrag zum Verzeichnisdienst hinzufügen + * + * @param createDirectoryEntry Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn.* Leer/nicht vorhanden + * (wird vom Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) + * displayName Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional + * belegt werden. localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt werden. title + * Kann optional belegt werden. organization Kann optional belegt werden. otherName Kann optional belegt werden. + * specialization Kann optional belegt werden. domainID Kann optional belegt werden. personalEntry Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority Nicht vorhanden (wird vom Verzeichnisdienst belegt) + * userCertificates Kann optional belegt werden. dn.uid Leer/nicht vorhanden (wird vom Verzeichnisdienst + * belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou Leer/nicht vorhanden (wird vom + * Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) telematikID Kann + * optional belegt werden. Wird telematikID und userCertificate angegeben, dann muss diese telematikID mit der + * telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode + * 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden + * (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Kann optional + * belegt werden (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird + * ein Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in + * dieser Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im + * Verzeichniseintrag gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden + * kann). (required) + * @return ApiResponse<DistinguishedName> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public ApiResponse<DistinguishedName> addDirectoryEntryWithHttpInfo(CreateDirectoryEntry createDirectoryEntry) throws ApiException { + okhttp3.Call localVarCall = addDirectoryEntryValidateBeforeCall(createDirectoryEntry, null); + Type localVarReturnType = new TypeToken<DistinguishedName>() { + }.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); } - okhttp3.Call localVarCall = modifyDirectoryEntryCall(uid, baseDirectoryEntry, _callback); - return localVarCall; - - } - - /** - * Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. - * - * @param uid ID von dem Verzeichniseintrag (required) - * @param baseDirectoryEntry Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn. Nicht vorhanden - * (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn Nicht vorhanden. displayName - * Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. - * localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt - * werden. organization Kann optional belegt werden. otherName Nicht vorhanden. specialization Kann optional belegt - * werden. domainID Kann optional belegt werden. personalEntry Nicht vorhanden. dataFromAuthority Nicht vorhanden - * (required) - * @return DistinguishedName - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * </table> - */ - public DistinguishedName modifyDirectoryEntry(String uid, BaseDirectoryEntry baseDirectoryEntry) throws ApiException { - ApiResponse<DistinguishedName> localVarResp = modifyDirectoryEntryWithHttpInfo(uid, baseDirectoryEntry); - return localVarResp.getData(); - } - - /** - * Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. - * - * @param uid ID von dem Verzeichniseintrag (required) - * @param baseDirectoryEntry Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn. Nicht vorhanden - * (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn Nicht vorhanden. displayName - * Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. - * localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt - * werden. organization Kann optional belegt werden. otherName Nicht vorhanden. specialization Kann optional belegt - * werden. domainID Kann optional belegt werden. personalEntry Nicht vorhanden. dataFromAuthority Nicht vorhanden - * (required) - * @return ApiResponse<DistinguishedName> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * </table> - */ - public ApiResponse<DistinguishedName> modifyDirectoryEntryWithHttpInfo(String uid, BaseDirectoryEntry baseDirectoryEntry) throws ApiException { - okhttp3.Call localVarCall = modifyDirectoryEntryValidateBeforeCall(uid, baseDirectoryEntry, null); - Type localVarReturnType = new TypeToken<DistinguishedName>() { - }.getType(); - return localVarApiClient.execute(localVarCall, localVarReturnType); - } - - /** - * Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. (asynchronously) - * - * @param uid ID von dem Verzeichniseintrag (required) - * @param baseDirectoryEntry Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn. Nicht vorhanden - * (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn Nicht vorhanden. displayName - * Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional belegt werden. - * localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt werden. title Kann optional belegt - * werden. organization Kann optional belegt werden. otherName Nicht vorhanden. specialization Kann optional belegt - * werden. domainID Kann optional belegt werden. personalEntry Nicht vorhanden. dataFromAuthority Nicht vorhanden - * (required) - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call modifyDirectoryEntryAsync(String uid, BaseDirectoryEntry baseDirectoryEntry, final ApiCallback<DistinguishedName> _callback) - throws ApiException { - - okhttp3.Call localVarCall = modifyDirectoryEntryValidateBeforeCall(uid, baseDirectoryEntry, _callback); - Type localVarReturnType = new TypeToken<DistinguishedName>() { - }.getType(); - localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); - return localVarCall; - } - - /** - * Build call for readDirectoryEntry - * - * @param uid ID von dem Verzeichniseintrag (distinguishedName.uid) (optional) - * @param givenName Erlaubt die Suche mit Hilfe des Attributs givenName. (optional) - * @param sn Erlaubt die Suche mit Hilfe des Attributs sn. (optional) - * @param cn Erlaubt die Suche mit Hilfe des Attributs cn. (optional) - * @param displayName Erlaubt die Suche mit Hilfe des Attributs displayName. (optional) - * @param streetAddress Erlaubt die Suche mit Hilfe des Attributs streetAddress. (optional) - * @param postalCode Erlaubt die Suche mit Hilfe des Attributs postalCode. (optional) - * @param localityName Erlaubt die Suche mit Hilfe des Attributs localityName. (optional) - * @param stateOrProvinceName Erlaubt die Suche mit Hilfe des Attributs stateOrProvinceName. (optional) - * @param title Erlaubt die Suche mit Hilfe des Attributs title. (optional) - * @param organization Erlaubt die Suche mit Hilfe des Attributs organization. (optional) - * @param otherName Erlaubt die Suche mit Hilfe des Attributs otherName. (optional) - * @param specialization Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die angegebene - * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) - * @param domainID Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene - * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) - * @param personalEntry Erlaubt die Suche mit Hilfe des Attributs personalEntry. (optional) - * @param dataFromAuthority Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. (optional) - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call readDirectoryEntryCall(String uid, String givenName, String sn, String cn, String displayName, String streetAddress, - String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, String specialization, - String domainID, String personalEntry, String dataFromAuthority, final ApiCallback _callback) throws ApiException { - Object localVarPostBody = null; - - // create path and map variables - String localVarPath = "/DirectoryEntries"; - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - if (uid != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("uid", uid)); + /** + * Einen Eintrag zum Verzeichnisdienst hinzufügen (asynchronously) + * + * @param createDirectoryEntry Datensatz für den neuen Eintrag. Die Attribute müssen wie folgt belegt sein dn.* Leer/nicht vorhanden + * (wird vom Verzeichnisdienst belegt) givenName Nicht vorhanden (wird vom Verzeichnisdienst belegt) sn Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) cn Nicht vorhanden (wird vom Verzeichnisdienst belegt) + * displayName Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode Kann optional + * belegt werden. localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt werden. title + * Kann optional belegt werden. organization Kann optional belegt werden. otherName Kann optional belegt werden. + * specialization Kann optional belegt werden. domainID Kann optional belegt werden. personalEntry Nicht + * vorhanden (wird vom Verzeichnisdienst belegt) dataFromAuthority Nicht vorhanden (wird vom Verzeichnisdienst belegt) + * userCertificates Kann optional belegt werden. dn.uid Leer/nicht vorhanden (wird vom Verzeichnisdienst + * belegt) dn.dc Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) dn.ou Leer/nicht vorhanden (wird vom + * Verzeichnisdienst belegt) dn.cn Leer/nicht vorhanden (wird vom Verzeichnisdienst belegt) telematikID Kann + * optional belegt werden. Wird telematikID und userCertificate angegeben, dann muss diese telematikID mit der + * telematikID im userCertificate übereinstimmen. Bei unterschiedlichen telematikID wird die Operation mit Fehlercode + * 422 abgelehnt. entryType Nicht vorhanden (wird vom Verzeichnisdienst belegt) professionOID Nicht vorhanden + * (wird vom Verzeichnisdienst belegt) usage Kann optional belegt werden. userCertificate Kann optional + * belegt werden (Format DER, Base64 kodiert) description Kann optional belegt werden. Entsprechend gemSpec_VZD wird + * ein Teil der Attribute durch den Verzeichnisdienst automatisch mit Werten aus dem Zertifikat gefüllt. Wenn in + * dieser Operation Attribute - für die dies erlaubt ist - mit einem Wert belegt werden, wird dieser Wert im + * Verzeichniseintrag gespeichert (auch wenn der Wert durch den Verzeichnisdienst aus dem Zertifikat entnommen werden + * kann). (required) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 201 </td><td> Created </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * <tr><td> 422 </td><td> Unprocessable Entity </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call addDirectoryEntryAsync(CreateDirectoryEntry createDirectoryEntry, final ApiCallback<DistinguishedName> _callback) + throws ApiException { + + okhttp3.Call localVarCall = addDirectoryEntryValidateBeforeCall(createDirectoryEntry, _callback); + Type localVarReturnType = new TypeToken<DistinguishedName>() { + }.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; } - if (givenName != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("givenName", givenName)); + /** + * Build call for deleteDirectoryEntry + * + * @param uid ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate + * und Fachdaten. (required) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call deleteDirectoryEntryCall(String uid, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "DELETE", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - if (sn != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("sn", sn)); - } + @SuppressWarnings("rawtypes") + private okhttp3.Call deleteDirectoryEntryValidateBeforeCall(String uid, final ApiCallback _callback) throws ApiException { - if (cn != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("cn", cn)); - } + // verify the required parameter 'uid' is set + if (uid == null) { + throw new ApiException("Missing the required parameter 'uid' when calling deleteDirectoryEntry(Async)"); + } + + okhttp3.Call localVarCall = deleteDirectoryEntryCall(uid, _callback); + return localVarCall; - if (displayName != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("displayName", displayName)); } - if (streetAddress != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("streetAddress", streetAddress)); + /** + * Gesamten Verzeichniseintrag löschen + * + * @param uid ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate und + * Fachdaten. (required) + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public void deleteDirectoryEntry(String uid) throws ApiException { + deleteDirectoryEntryWithHttpInfo(uid); } - if (postalCode != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("postalCode", postalCode)); + /** + * Gesamten Verzeichniseintrag löschen + * + * @param uid ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate und + * Fachdaten. (required) + * @return ApiResponse<Void> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public ApiResponse<Void> deleteDirectoryEntryWithHttpInfo(String uid) throws ApiException { + okhttp3.Call localVarCall = deleteDirectoryEntryValidateBeforeCall(uid, null); + return localVarApiClient.execute(localVarCall); } - if (localityName != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("localityName", localityName)); + /** + * Gesamten Verzeichniseintrag löschen (asynchronously) + * + * @param uid ID von dem zu löschenden Verzeichniseintrag Gelöscht werden der Basis Verzeichniseintrag sowie alle dazugehörenden Zertifikate + * und Fachdaten. (required) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call deleteDirectoryEntryAsync(String uid, final ApiCallback<Void> _callback) throws ApiException { + + okhttp3.Call localVarCall = deleteDirectoryEntryValidateBeforeCall(uid, _callback); + localVarApiClient.executeAsync(localVarCall, _callback); + return localVarCall; } - if (stateOrProvinceName != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("stateOrProvinceName", stateOrProvinceName)); + /** + * Build call for modifyDirectoryEntry + * + * @param uid ID von dem Verzeichniseintrag (required) + * @param baseDirectoryEntry Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn.* Nicht + * vorhanden (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn + * Nicht vorhanden. displayName Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode + * Kann optional belegt werden. localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt + * werden. title Kann optional belegt werden. organization Kann optional belegt werden. otherName Nicht + * vorhanden. specialization Kann optional belegt werden. domainID Kann optional belegt werden. personalEntry + * Nicht vorhanden. dataFromAuthority Nicht vorhanden (required) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call modifyDirectoryEntryCall(String uid, BaseDirectoryEntry baseDirectoryEntry, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = baseDirectoryEntry; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/baseDirectoryEntries" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "PUT", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - if (title != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("title", title)); + @SuppressWarnings("rawtypes") + private okhttp3.Call modifyDirectoryEntryValidateBeforeCall(String uid, BaseDirectoryEntry baseDirectoryEntry, final ApiCallback _callback) + throws ApiException { + + // verify the required parameter 'uid' is set + if (uid == null) { + throw new ApiException("Missing the required parameter 'uid' when calling modifyDirectoryEntry(Async)"); + } + + // verify the required parameter 'baseDirectoryEntry' is set + if (baseDirectoryEntry == null) { + throw new ApiException("Missing the required parameter 'baseDirectoryEntry' when calling modifyDirectoryEntry(Async)"); + } + + okhttp3.Call localVarCall = modifyDirectoryEntryCall(uid, baseDirectoryEntry, _callback); + return localVarCall; + } - if (organization != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("organization", organization)); + /** + * Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. + * + * @param uid ID von dem Verzeichniseintrag (required) + * @param baseDirectoryEntry Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn.* Nicht + * vorhanden (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn + * Nicht vorhanden. displayName Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode + * Kann optional belegt werden. localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt + * werden. title Kann optional belegt werden. organization Kann optional belegt werden. otherName Nicht + * vorhanden. specialization Kann optional belegt werden. domainID Kann optional belegt werden. personalEntry + * Nicht vorhanden. dataFromAuthority Nicht vorhanden (required) + * @return DistinguishedName + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * </table> + */ + public DistinguishedName modifyDirectoryEntry(String uid, BaseDirectoryEntry baseDirectoryEntry) throws ApiException { + ApiResponse<DistinguishedName> localVarResp = modifyDirectoryEntryWithHttpInfo(uid, baseDirectoryEntry); + return localVarResp.getData(); } - if (otherName != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("otherName", otherName)); + /** + * Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. + * + * @param uid ID von dem Verzeichniseintrag (required) + * @param baseDirectoryEntry Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn.* Nicht + * vorhanden (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn + * Nicht vorhanden. displayName Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode + * Kann optional belegt werden. localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt + * werden. title Kann optional belegt werden. organization Kann optional belegt werden. otherName Nicht + * vorhanden. specialization Kann optional belegt werden. domainID Kann optional belegt werden. personalEntry + * Nicht vorhanden. dataFromAuthority Nicht vorhanden (required) + * @return ApiResponse<DistinguishedName> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * </table> + */ + public ApiResponse<DistinguishedName> modifyDirectoryEntryWithHttpInfo(String uid, BaseDirectoryEntry baseDirectoryEntry) throws ApiException { + okhttp3.Call localVarCall = modifyDirectoryEntryValidateBeforeCall(uid, baseDirectoryEntry, null); + Type localVarReturnType = new TypeToken<DistinguishedName>() { + }.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); } - if (specialization != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("specialization", specialization)); + /** + * Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. (asynchronously) + * + * @param uid ID von dem Verzeichniseintrag (required) + * @param baseDirectoryEntry Datensatz für die Aktualisierung des Eintrags Die Attribute müssen wie folgt belegt sein dn.* Nicht + * vorhanden (Adressierung erfolgt über uid in Path). givenName Nicht vorhanden. sn Nicht vorhanden. cn + * Nicht vorhanden. displayName Kann optional belegt werden. streetAddress Kann optional belegt werden. postalCode + * Kann optional belegt werden. localityName Kann optional belegt werden. stateOrProvinceName Kann optional belegt + * werden. title Kann optional belegt werden. organization Kann optional belegt werden. otherName Nicht + * vorhanden. specialization Kann optional belegt werden. domainID Kann optional belegt werden. personalEntry + * Nicht vorhanden. dataFromAuthority Nicht vorhanden (required) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * <tr><td> 405 </td><td> Method Not Allowed </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call modifyDirectoryEntryAsync(String uid, BaseDirectoryEntry baseDirectoryEntry, final ApiCallback<DistinguishedName> _callback) + throws ApiException { + + okhttp3.Call localVarCall = modifyDirectoryEntryValidateBeforeCall(uid, baseDirectoryEntry, _callback); + Type localVarReturnType = new TypeToken<DistinguishedName>() { + }.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; } - if (domainID != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("domainID", domainID)); + /** + * Build call for readDirectoryEntry + * + * @param uid ID von dem Verzeichniseintrag (distinguishedName.uid) (optional) + * @param givenName Erlaubt die Suche mit Hilfe des Attributs givenName. (optional) + * @param sn Erlaubt die Suche mit Hilfe des Attributs sn. (optional) + * @param cn Erlaubt die Suche mit Hilfe des Attributs cn. (optional) + * @param displayName Erlaubt die Suche mit Hilfe des Attributs displayName. (optional) + * @param streetAddress Erlaubt die Suche mit Hilfe des Attributs streetAddress. (optional) + * @param postalCode Erlaubt die Suche mit Hilfe des Attributs postalCode. (optional) + * @param localityName Erlaubt die Suche mit Hilfe des Attributs localityName. (optional) + * @param stateOrProvinceName Erlaubt die Suche mit Hilfe des Attributs stateOrProvinceName. (optional) + * @param title Erlaubt die Suche mit Hilfe des Attributs title. (optional) + * @param organization Erlaubt die Suche mit Hilfe des Attributs organization. (optional) + * @param otherName Erlaubt die Suche mit Hilfe des Attributs otherName. (optional) + * @param specialization Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die + * angegebene domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param domainID Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param personalEntry Erlaubt die Suche mit Hilfe des Attributs personalEntry. (optional) + * @param dataFromAuthority Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. (optional) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call readDirectoryEntryCall(String uid, String givenName, String sn, String cn, String displayName, String streetAddress, + String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, + String specialization, String domainID, String personalEntry, String dataFromAuthority, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries"; + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + if (uid != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("uid", uid)); + } + + if (givenName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("givenName", givenName)); + } + + if (sn != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("sn", sn)); + } + + if (cn != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("cn", cn)); + } + + if (displayName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("displayName", displayName)); + } + + if (streetAddress != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("streetAddress", streetAddress)); + } + + if (postalCode != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("postalCode", postalCode)); + } + + if (localityName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("localityName", localityName)); + } + + if (stateOrProvinceName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("stateOrProvinceName", stateOrProvinceName)); + } + + if (title != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("title", title)); + } + + if (organization != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("organization", organization)); + } + + if (otherName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("otherName", otherName)); + } + + if (specialization != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("specialization", specialization)); + } + + if (domainID != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("domainID", domainID)); + } + + if (personalEntry != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("personalEntry", personalEntry)); + } + + if (dataFromAuthority != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("dataFromAuthority", dataFromAuthority)); + } + + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - if (personalEntry != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("personalEntry", personalEntry)); + @SuppressWarnings("rawtypes") + private okhttp3.Call readDirectoryEntryValidateBeforeCall(String uid, String givenName, String sn, String cn, String displayName, + String streetAddress, String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, + String specialization, String domainID, String personalEntry, String dataFromAuthority, final ApiCallback _callback) throws ApiException { + + okhttp3.Call localVarCall = readDirectoryEntryCall(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, + stateOrProvinceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority, _callback); + return localVarCall; + } - if (dataFromAuthority != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("dataFromAuthority", dataFromAuthority)); + /** + * Gesamten Verzeichniseintrag lesen Liefert alle zum Filter passenden Verzeichniseinträge. Die angegebenen Parameter werden mit logischen UND + * verknüpft. + * + * @param uid ID von dem Verzeichniseintrag (distinguishedName.uid) (optional) + * @param givenName Erlaubt die Suche mit Hilfe des Attributs givenName. (optional) + * @param sn Erlaubt die Suche mit Hilfe des Attributs sn. (optional) + * @param cn Erlaubt die Suche mit Hilfe des Attributs cn. (optional) + * @param displayName Erlaubt die Suche mit Hilfe des Attributs displayName. (optional) + * @param streetAddress Erlaubt die Suche mit Hilfe des Attributs streetAddress. (optional) + * @param postalCode Erlaubt die Suche mit Hilfe des Attributs postalCode. (optional) + * @param localityName Erlaubt die Suche mit Hilfe des Attributs localityName. (optional) + * @param stateOrProvinceName Erlaubt die Suche mit Hilfe des Attributs stateOrProvinceName. (optional) + * @param title Erlaubt die Suche mit Hilfe des Attributs title. (optional) + * @param organization Erlaubt die Suche mit Hilfe des Attributs organization. (optional) + * @param otherName Erlaubt die Suche mit Hilfe des Attributs otherName. (optional) + * @param specialization Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die + * angegebene domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param domainID Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param personalEntry Erlaubt die Suche mit Hilfe des Attributs personalEntry. (optional) + * @param dataFromAuthority Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. (optional) + * @return List<DirectoryEntry> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public List<DirectoryEntry> readDirectoryEntry(String uid, String givenName, String sn, String cn, String displayName, String streetAddress, + String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, + String specialization, String domainID, String personalEntry, String dataFromAuthority) throws ApiException { + ApiResponse<List<DirectoryEntry>> localVarResp = readDirectoryEntryWithHttpInfo(uid, givenName, sn, cn, displayName, streetAddress, + postalCode, localityName, stateOrProvinceName, title, organization, otherName, specialization, domainID, personalEntry, + dataFromAuthority); + return localVarResp.getData(); } - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - "application/json" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); + /** + * Gesamten Verzeichniseintrag lesen Liefert alle zum Filter passenden Verzeichniseinträge. Die angegebenen Parameter werden mit logischen UND + * verknüpft. + * + * @param uid ID von dem Verzeichniseintrag (distinguishedName.uid) (optional) + * @param givenName Erlaubt die Suche mit Hilfe des Attributs givenName. (optional) + * @param sn Erlaubt die Suche mit Hilfe des Attributs sn. (optional) + * @param cn Erlaubt die Suche mit Hilfe des Attributs cn. (optional) + * @param displayName Erlaubt die Suche mit Hilfe des Attributs displayName. (optional) + * @param streetAddress Erlaubt die Suche mit Hilfe des Attributs streetAddress. (optional) + * @param postalCode Erlaubt die Suche mit Hilfe des Attributs postalCode. (optional) + * @param localityName Erlaubt die Suche mit Hilfe des Attributs localityName. (optional) + * @param stateOrProvinceName Erlaubt die Suche mit Hilfe des Attributs stateOrProvinceName. (optional) + * @param title Erlaubt die Suche mit Hilfe des Attributs title. (optional) + * @param organization Erlaubt die Suche mit Hilfe des Attributs organization. (optional) + * @param otherName Erlaubt die Suche mit Hilfe des Attributs otherName. (optional) + * @param specialization Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die + * angegebene domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param domainID Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param personalEntry Erlaubt die Suche mit Hilfe des Attributs personalEntry. (optional) + * @param dataFromAuthority Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. (optional) + * @return ApiResponse<List<DirectoryEntry>> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public ApiResponse<List<DirectoryEntry>> readDirectoryEntryWithHttpInfo(String uid, String givenName, String sn, String cn, String displayName, + String streetAddress, String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, + String specialization, String domainID, String personalEntry, String dataFromAuthority) throws ApiException { + okhttp3.Call localVarCall = readDirectoryEntryValidateBeforeCall(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, + stateOrProvinceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority, null); + Type localVarReturnType = new TypeToken<List<DirectoryEntry>>() { + }.getType(); + return localVarApiClient.execute(localVarCall, localVarReturnType); } - final String[] localVarContentTypes = { - - }; - final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, - localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); - } - - @SuppressWarnings("rawtypes") - private okhttp3.Call readDirectoryEntryValidateBeforeCall(String uid, String givenName, String sn, String cn, String displayName, - String streetAddress, String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, - String specialization, String domainID, String personalEntry, String dataFromAuthority, final ApiCallback _callback) throws ApiException { - - okhttp3.Call localVarCall = readDirectoryEntryCall(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, - stateOrProvinceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority, _callback); - return localVarCall; - - } - - /** - * Gesamten Verzeichniseintrag lesen Liefert alle zum Filter passenden Verzeichniseinträge. Die angegebenen Parameter werden mit logischen UND - * verknüpft. - * - * @param uid ID von dem Verzeichniseintrag (distinguishedName.uid) (optional) - * @param givenName Erlaubt die Suche mit Hilfe des Attributs givenName. (optional) - * @param sn Erlaubt die Suche mit Hilfe des Attributs sn. (optional) - * @param cn Erlaubt die Suche mit Hilfe des Attributs cn. (optional) - * @param displayName Erlaubt die Suche mit Hilfe des Attributs displayName. (optional) - * @param streetAddress Erlaubt die Suche mit Hilfe des Attributs streetAddress. (optional) - * @param postalCode Erlaubt die Suche mit Hilfe des Attributs postalCode. (optional) - * @param localityName Erlaubt die Suche mit Hilfe des Attributs localityName. (optional) - * @param stateOrProvinceName Erlaubt die Suche mit Hilfe des Attributs stateOrProvinceName. (optional) - * @param title Erlaubt die Suche mit Hilfe des Attributs title. (optional) - * @param organization Erlaubt die Suche mit Hilfe des Attributs organization. (optional) - * @param otherName Erlaubt die Suche mit Hilfe des Attributs otherName. (optional) - * @param specialization Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die angegebene - * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) - * @param domainID Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene - * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) - * @param personalEntry Erlaubt die Suche mit Hilfe des Attributs personalEntry. (optional) - * @param dataFromAuthority Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. (optional) - * @return List<DirectoryEntry> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public List<DirectoryEntry> readDirectoryEntry(String uid, String givenName, String sn, String cn, String displayName, String streetAddress, - String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, String specialization, - String domainID, String personalEntry, String dataFromAuthority) throws ApiException { - ApiResponse<List<DirectoryEntry>> localVarResp = readDirectoryEntryWithHttpInfo(uid, givenName, sn, cn, displayName, streetAddress, postalCode, - localityName, stateOrProvinceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority); - return localVarResp.getData(); - } - - /** - * Gesamten Verzeichniseintrag lesen Liefert alle zum Filter passenden Verzeichniseinträge. Die angegebenen Parameter werden mit logischen UND - * verknüpft. - * - * @param uid ID von dem Verzeichniseintrag (distinguishedName.uid) (optional) - * @param givenName Erlaubt die Suche mit Hilfe des Attributs givenName. (optional) - * @param sn Erlaubt die Suche mit Hilfe des Attributs sn. (optional) - * @param cn Erlaubt die Suche mit Hilfe des Attributs cn. (optional) - * @param displayName Erlaubt die Suche mit Hilfe des Attributs displayName. (optional) - * @param streetAddress Erlaubt die Suche mit Hilfe des Attributs streetAddress. (optional) - * @param postalCode Erlaubt die Suche mit Hilfe des Attributs postalCode. (optional) - * @param localityName Erlaubt die Suche mit Hilfe des Attributs localityName. (optional) - * @param stateOrProvinceName Erlaubt die Suche mit Hilfe des Attributs stateOrProvinceName. (optional) - * @param title Erlaubt die Suche mit Hilfe des Attributs title. (optional) - * @param organization Erlaubt die Suche mit Hilfe des Attributs organization. (optional) - * @param otherName Erlaubt die Suche mit Hilfe des Attributs otherName. (optional) - * @param specialization Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die angegebene - * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) - * @param domainID Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene - * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) - * @param personalEntry Erlaubt die Suche mit Hilfe des Attributs personalEntry. (optional) - * @param dataFromAuthority Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. (optional) - * @return ApiResponse<List<DirectoryEntry>> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public ApiResponse<List<DirectoryEntry>> readDirectoryEntryWithHttpInfo(String uid, String givenName, String sn, String cn, String displayName, - String streetAddress, String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, - String specialization, String domainID, String personalEntry, String dataFromAuthority) throws ApiException { - okhttp3.Call localVarCall = readDirectoryEntryValidateBeforeCall(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, - stateOrProvinceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority, null); - Type localVarReturnType = new TypeToken<List<DirectoryEntry>>() { - }.getType(); - return localVarApiClient.execute(localVarCall, localVarReturnType); - } - - /** - * Gesamten Verzeichniseintrag lesen (asynchronously) Liefert alle zum Filter passenden Verzeichniseinträge. Die angegebenen Parameter werden mit - * logischen UND verknüpft. - * - * @param uid ID von dem Verzeichniseintrag (distinguishedName.uid) (optional) - * @param givenName Erlaubt die Suche mit Hilfe des Attributs givenName. (optional) - * @param sn Erlaubt die Suche mit Hilfe des Attributs sn. (optional) - * @param cn Erlaubt die Suche mit Hilfe des Attributs cn. (optional) - * @param displayName Erlaubt die Suche mit Hilfe des Attributs displayName. (optional) - * @param streetAddress Erlaubt die Suche mit Hilfe des Attributs streetAddress. (optional) - * @param postalCode Erlaubt die Suche mit Hilfe des Attributs postalCode. (optional) - * @param localityName Erlaubt die Suche mit Hilfe des Attributs localityName. (optional) - * @param stateOrProvinceName Erlaubt die Suche mit Hilfe des Attributs stateOrProvinceName. (optional) - * @param title Erlaubt die Suche mit Hilfe des Attributs title. (optional) - * @param organization Erlaubt die Suche mit Hilfe des Attributs organization. (optional) - * @param otherName Erlaubt die Suche mit Hilfe des Attributs otherName. (optional) - * @param specialization Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die angegebene - * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) - * @param domainID Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene - * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) - * @param personalEntry Erlaubt die Suche mit Hilfe des Attributs personalEntry. (optional) - * @param dataFromAuthority Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. (optional) - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details <table summary="Response Details" border="1"> - * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> - * <tr><td> 200 </td><td> OK </td><td> - </td></tr> - * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> - * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> - * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> - * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> - * </table> - */ - public okhttp3.Call readDirectoryEntryAsync(String uid, String givenName, String sn, String cn, String displayName, String streetAddress, - String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, String specialization, - String domainID, String personalEntry, String dataFromAuthority, final ApiCallback<List<DirectoryEntry>> _callback) throws ApiException { - - okhttp3.Call localVarCall = readDirectoryEntryValidateBeforeCall(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, - stateOrProvinceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority, _callback); - Type localVarReturnType = new TypeToken<List<DirectoryEntry>>() { - }.getType(); - localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); - return localVarCall; - } + /** + * Gesamten Verzeichniseintrag lesen (asynchronously) Liefert alle zum Filter passenden Verzeichniseinträge. Die angegebenen Parameter werden mit + * logischen UND verknüpft. + * + * @param uid ID von dem Verzeichniseintrag (distinguishedName.uid) (optional) + * @param givenName Erlaubt die Suche mit Hilfe des Attributs givenName. (optional) + * @param sn Erlaubt die Suche mit Hilfe des Attributs sn. (optional) + * @param cn Erlaubt die Suche mit Hilfe des Attributs cn. (optional) + * @param displayName Erlaubt die Suche mit Hilfe des Attributs displayName. (optional) + * @param streetAddress Erlaubt die Suche mit Hilfe des Attributs streetAddress. (optional) + * @param postalCode Erlaubt die Suche mit Hilfe des Attributs postalCode. (optional) + * @param localityName Erlaubt die Suche mit Hilfe des Attributs localityName. (optional) + * @param stateOrProvinceName Erlaubt die Suche mit Hilfe des Attributs stateOrProvinceName. (optional) + * @param title Erlaubt die Suche mit Hilfe des Attributs title. (optional) + * @param organization Erlaubt die Suche mit Hilfe des Attributs organization. (optional) + * @param otherName Erlaubt die Suche mit Hilfe des Attributs otherName. (optional) + * @param specialization Erlaubt die Suche mit Hilfe des Attributs specialization. Der Verzeichniseintrag wird selektiert, wenn die + * angegebene domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param domainID Erlaubt die Suche mit Hilfe des Attributs domainID. Der Verzeichniseintrag wird selektiert, wenn die angegebene + * domainID im Attribut domainID (array) des Verzeichniseintrags enthalten ist. (optional) + * @param personalEntry Erlaubt die Suche mit Hilfe des Attributs personalEntry. (optional) + * @param dataFromAuthority Erlaubt die Suche mit Hilfe des Attributs dataFromAuthority. (optional) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details <table summary="Response Details" border="1"> + * <tr><td> Status Code </td><td> Description </td><td> Response Headers </td></tr> + * <tr><td> 200 </td><td> OK </td><td> - </td></tr> + * <tr><td> 400 </td><td> Bad Request </td><td> - </td></tr> + * <tr><td> 401 </td><td> Unauthorized </td><td> - </td></tr> + * <tr><td> 403 </td><td> Forbidden </td><td> - </td></tr> + * <tr><td> 404 </td><td> Not Found </td><td> - </td></tr> + * </table> + */ + public okhttp3.Call readDirectoryEntryAsync(String uid, String givenName, String sn, String cn, String displayName, String streetAddress, + String postalCode, String localityName, String stateOrProvinceName, String title, String organization, String otherName, + String specialization, String domainID, String personalEntry, String dataFromAuthority, final ApiCallback<List<DirectoryEntry>> _callback) + throws ApiException { + + okhttp3.Call localVarCall = readDirectoryEntryValidateBeforeCall(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, + stateOrProvinceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority, _callback); + Type localVarReturnType = new TypeToken<List<DirectoryEntry>>() { + }.getType(); + localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiCallback.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiCallback.java index b513c74..e2cd72b 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiCallback.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiCallback.java @@ -29,8 +29,10 @@ package de.gematik.ti.epa.vzd.client.invoker; -import java.util.List; +import java.io.IOException; + import java.util.Map; +import java.util.List; /** * Callback for asynchronous API call. @@ -39,39 +41,39 @@ */ public interface ApiCallback<T> { - /** - * This is called when the API call fails. - * - * @param e The exception causing the failure - * @param statusCode Status code of the response if available, otherwise it would be 0 - * @param responseHeaders Headers of the response if available, otherwise it would be null - */ - void onFailure(ApiException e, int statusCode, Map<String, List<String>> responseHeaders); + /** + * This is called when the API call fails. + * + * @param e The exception causing the failure + * @param statusCode Status code of the response if available, otherwise it would be 0 + * @param responseHeaders Headers of the response if available, otherwise it would be null + */ + void onFailure(ApiException e, int statusCode, Map<String, List<String>> responseHeaders); - /** - * This is called when the API call succeeded. - * - * @param result The result deserialized from response - * @param statusCode Status code of the response - * @param responseHeaders Headers of the response - */ - void onSuccess(T result, int statusCode, Map<String, List<String>> responseHeaders); + /** + * This is called when the API call succeeded. + * + * @param result The result deserialized from response + * @param statusCode Status code of the response + * @param responseHeaders Headers of the response + */ + void onSuccess(T result, int statusCode, Map<String, List<String>> responseHeaders); - /** - * This is called when the API upload processing. - * - * @param bytesWritten bytes Written - * @param contentLength content length of request body - * @param done write end - */ - void onUploadProgress(long bytesWritten, long contentLength, boolean done); + /** + * This is called when the API upload processing. + * + * @param bytesWritten bytes Written + * @param contentLength content length of request body + * @param done write end + */ + void onUploadProgress(long bytesWritten, long contentLength, boolean done); - /** - * This is called when the API downlond processing. - * - * @param bytesRead bytes Read - * @param contentLength content lenngth of the response - * @param done Read end - */ - void onDownloadProgress(long bytesRead, long contentLength, boolean done); + /** + * This is called when the API downlond processing. + * + * @param bytesRead bytes Read + * @param contentLength content lenngth of the response + * @param done Read end + */ + void onDownloadProgress(long bytesRead, long contentLength, boolean done); } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiClient.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiClient.java index 65363ce..1a36bd7 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiClient.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiClient.java @@ -29,1356 +29,1346 @@ package de.gematik.ti.epa.vzd.client.invoker; -import de.gematik.ti.epa.vzd.client.invoker.auth.ApiKeyAuth; -import de.gematik.ti.epa.vzd.client.invoker.auth.Authentication; -import de.gematik.ti.epa.vzd.client.invoker.auth.HttpBasicAuth; -import de.gematik.ti.epa.vzd.client.invoker.auth.OAuth; -import de.gematik.ti.epa.vzd.client.invoker.auth.OAuthFlow; -import de.gematik.ti.epa.vzd.client.invoker.auth.RetryingOAuth; +import okhttp3.*; +import okhttp3.internal.http.HttpMethod; +import okhttp3.internal.tls.OkHostnameVerifier; +import okhttp3.logging.HttpLoggingInterceptor; +import okhttp3.logging.HttpLoggingInterceptor.Level; +import okio.BufferedSink; +import okio.Okio; +import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder; +import org.apache.oltu.oauth2.common.message.types.GrantType; + +import javax.net.ssl.*; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.lang.reflect.Type; import java.net.URLConnection; import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.SecureRandom; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.text.DateFormat; import java.time.LocalDate; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.Headers; -import okhttp3.Interceptor; -import okhttp3.MediaType; -import okhttp3.MultipartBody; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.internal.http.HttpMethod; -import okhttp3.internal.tls.OkHostnameVerifier; -import okhttp3.logging.HttpLoggingInterceptor; -import okhttp3.logging.HttpLoggingInterceptor.Level; -import okio.BufferedSink; -import okio.Okio; -import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder; + +import de.gematik.ti.epa.vzd.client.invoker.auth.Authentication; +import de.gematik.ti.epa.vzd.client.invoker.auth.HttpBasicAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.HttpBearerAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.ApiKeyAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.OAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.RetryingOAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.OAuthFlow; public class ApiClient { - private String basePath = "https://to.be.defined"; - private boolean debugging = false; - private final Map<String, String> defaultHeaderMap = new HashMap<String, String>(); - private final Map<String, String> defaultCookieMap = new HashMap<String, String>(); - private String tempFolderPath = null; - - private Map<String, Authentication> authentications; - - private DateFormat dateFormat; - private DateFormat datetimeFormat; - private boolean lenientDatetimeFormat; - private int dateLength; - - private InputStream sslCaCert; - private boolean verifyingSsl; - private KeyManager[] keyManagers; - - private OkHttpClient httpClient; - private JSON json; - - private HttpLoggingInterceptor loggingInterceptor; - - /* - * Basic constructor for ApiClient - */ - public ApiClient() { - init(); - - // Setup authentications (key: authentication name, value: authentication). - authentications.put("OAuth2", new OAuth()); - // Prevent the authentications from being modified. - authentications = Collections.unmodifiableMap(authentications); - } - - /* - * Constructor for ApiClient to support access token retry on 401/403 configured with client ID - */ - public ApiClient(String clientId) { - this(clientId, null, null); - } - - /* - * Constructor for ApiClient to support access token retry on 401/403 configured with client ID and additional parameters - */ - public ApiClient(String clientId, Map<String, String> parameters) { - this(clientId, null, parameters); - } - - /* - * Constructor for ApiClient to support access token retry on 401/403 configured with client ID, secret, and additional parameters - */ - public ApiClient(String clientId, String clientSecret, Map<String, String> parameters) { - init(); - - RetryingOAuth retryingOAuth = new RetryingOAuth("https://to.be.defined/oauth/token", clientId, OAuthFlow.application, clientSecret, parameters); - authentications.put( - "OAuth2", - retryingOAuth - ); - httpClient.interceptors().add(retryingOAuth); - - // Prevent the authentications from being modified. - authentications = Collections.unmodifiableMap(authentications); - } - - private void init() { - OkHttpClient.Builder builder = new OkHttpClient.Builder(); - builder.addNetworkInterceptor(getProgressInterceptor()); - httpClient = builder.build(); - - verifyingSsl = true; - - json = new JSON(); - - // Set default User-Agent. - setUserAgent("OpenAPI-Generator/1.1.1/java"); - - authentications = new HashMap<String, Authentication>(); - } - - /** - * Get base path - * - * @return Base path - */ - public String getBasePath() { - return basePath; - } - - /** - * Set base path - * - * @param basePath Base path of the URL (e.g https://to.be.defined - * @return An instance of OkHttpClient - */ - public ApiClient setBasePath(String basePath) { - this.basePath = basePath; - return this; - } - - /** - * Get HTTP client - * - * @return An instance of OkHttpClient - */ - public OkHttpClient getHttpClient() { - return httpClient; - } - - /** - * Set HTTP client, which must never be null. - * - * @param newHttpClient An instance of OkHttpClient - * @return Api Client - * @throws NullPointerException when newHttpClient is null - */ - public ApiClient setHttpClient(OkHttpClient newHttpClient) { - this.httpClient = Objects.requireNonNull(newHttpClient, "HttpClient must not be null!"); - return this; - } - - /** - * Get JSON - * - * @return JSON object - */ - public JSON getJSON() { - return json; - } - - /** - * Set JSON - * - * @param json JSON object - * @return Api client - */ - public ApiClient setJSON(JSON json) { - this.json = json; - return this; - } - - /** - * True if isVerifyingSsl flag is on - * - * @return True if isVerifySsl flag is on - */ - public boolean isVerifyingSsl() { - return verifyingSsl; - } - - /** - * Configure whether to verify certificate and hostname when making https requests. Default to true. NOTE: Do NOT set to false in production code, - * otherwise you would face multiple types of cryptographic attacks. - * - * @param verifyingSsl True to verify TLS/SSL connection - * @return ApiClient - */ - public ApiClient setVerifyingSsl(boolean verifyingSsl) { - this.verifyingSsl = verifyingSsl; - applySslSettings(); - return this; - } - - /** - * Get SSL CA cert. - * - * @return Input stream to the SSL CA cert - */ - public InputStream getSslCaCert() { - return sslCaCert; - } - - /** - * Configure the CA certificate to be trusted when making https requests. Use null to reset to default. - * - * @param sslCaCert input stream for SSL CA cert - * @return ApiClient - */ - public ApiClient setSslCaCert(InputStream sslCaCert) { - this.sslCaCert = sslCaCert; - applySslSettings(); - return this; - } - - public KeyManager[] getKeyManagers() { - return keyManagers; - } - - /** - * Configure client keys to use for authorization in an SSL session. Use null to reset to default. - * - * @param managers The KeyManagers to use - * @return ApiClient - */ - public ApiClient setKeyManagers(KeyManager[] managers) { - this.keyManagers = managers; - applySslSettings(); - return this; - } - - public DateFormat getDateFormat() { - return dateFormat; - } - - public ApiClient setDateFormat(DateFormat dateFormat) { - this.json.setDateFormat(dateFormat); - return this; - } - - public ApiClient setSqlDateFormat(DateFormat dateFormat) { - this.json.setSqlDateFormat(dateFormat); - return this; - } - - public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { - this.json.setOffsetDateTimeFormat(dateFormat); - return this; - } - - public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { - this.json.setLocalDateFormat(dateFormat); - return this; - } - - public ApiClient setLenientOnJson(boolean lenientOnJson) { - this.json.setLenientOnJson(lenientOnJson); - return this; - } - - /** - * Get authentications (key: authentication name, value: authentication). - * - * @return Map of authentication objects - */ - public Map<String, Authentication> getAuthentications() { - return authentications; - } - - /** - * Get authentication for the given name. - * - * @param authName The authentication name - * @return The authentication, null if not found - */ - public Authentication getAuthentication(String authName) { - return authentications.get(authName); - } - - /** - * Helper method to set username for the first HTTP basic authentication. - * - * @param username Username - */ - public void setUsername(String username) { - for (Authentication auth : authentications.values()) { - if (auth instanceof HttpBasicAuth) { - ((HttpBasicAuth) auth).setUsername(username); - return; - } - } - throw new RuntimeException("No HTTP basic authentication configured!"); - } - - /** - * Helper method to set password for the first HTTP basic authentication. - * - * @param password Password - */ - public void setPassword(String password) { - for (Authentication auth : authentications.values()) { - if (auth instanceof HttpBasicAuth) { - ((HttpBasicAuth) auth).setPassword(password); - return; - } - } - throw new RuntimeException("No HTTP basic authentication configured!"); - } - - /** - * Helper method to set API key value for the first API key authentication. - * - * @param apiKey API key - */ - public void setApiKey(String apiKey) { - for (Authentication auth : authentications.values()) { - if (auth instanceof ApiKeyAuth) { - ((ApiKeyAuth) auth).setApiKey(apiKey); - return; - } - } - throw new RuntimeException("No API key authentication configured!"); - } - - /** - * Helper method to set API key prefix for the first API key authentication. - * - * @param apiKeyPrefix API key prefix - */ - public void setApiKeyPrefix(String apiKeyPrefix) { - for (Authentication auth : authentications.values()) { - if (auth instanceof ApiKeyAuth) { - ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); - return; - } - } - throw new RuntimeException("No API key authentication configured!"); - } - - /** - * Helper method to set access token for the first OAuth2 authentication. - * - * @param accessToken Access token - */ - public void setAccessToken(String accessToken) { - for (Authentication auth : authentications.values()) { - if (auth instanceof OAuth) { - ((OAuth) auth).setAccessToken(accessToken); - return; - } - } - throw new RuntimeException("No OAuth2 authentication configured!"); - } - - /** - * Set the User-Agent header's value (by adding to the default header map). - * - * @param userAgent HTTP request's user agent - * @return ApiClient - */ - public ApiClient setUserAgent(String userAgent) { - addDefaultHeader("User-Agent", userAgent); - return this; - } - - /** - * Add a default header. - * - * @param key The header's key - * @param value The header's value - * @return ApiClient - */ - public ApiClient addDefaultHeader(String key, String value) { - defaultHeaderMap.put(key, value); - return this; - } - - /** - * Add a default cookie. - * - * @param key The cookie's key - * @param value The cookie's value - * @return ApiClient - */ - public ApiClient addDefaultCookie(String key, String value) { - defaultCookieMap.put(key, value); - return this; - } - - /** - * Check that whether debugging is enabled for this API client. - * - * @return True if debugging is enabled, false otherwise. - */ - public boolean isDebugging() { - return debugging; - } - - /** - * Enable/disable debugging for this API client. - * - * @param debugging To enable (true) or disable (false) debugging - * @return ApiClient - */ - public ApiClient setDebugging(boolean debugging) { - if (debugging != this.debugging) { - if (debugging) { - loggingInterceptor = new HttpLoggingInterceptor(); - loggingInterceptor.setLevel(Level.BODY); - httpClient = httpClient.newBuilder().addInterceptor(loggingInterceptor).build(); - } else { - httpClient.interceptors().remove(loggingInterceptor); - loggingInterceptor = null; - } - } - this.debugging = debugging; - return this; - } - - /** - * The path of temporary folder used to store downloaded files from endpoints with file response. The default value is <code>null</code>, i.e. using - * the system's default tempopary folder. - * - * @return Temporary folder path - * @see <a href="https://docs.oracle.com/javase/7/docs/api/java/io/File.html#createTempFile">createTempFile</a> - */ - public String getTempFolderPath() { - return tempFolderPath; - } - - /** - * Set the temporary folder path (for downloading files) - * - * @param tempFolderPath Temporary folder path - * @return ApiClient - */ - public ApiClient setTempFolderPath(String tempFolderPath) { - this.tempFolderPath = tempFolderPath; - return this; - } - - /** - * Get connection timeout (in milliseconds). - * - * @return Timeout in milliseconds - */ - public int getConnectTimeout() { - return httpClient.connectTimeoutMillis(); - } - - /** - * Sets the connect timeout (in milliseconds). A value of 0 means no timeout, otherwise values must be between 1 and {@link Integer#MAX_VALUE}. - * - * @param connectionTimeout connection timeout in milliseconds - * @return Api client - */ - public ApiClient setConnectTimeout(int connectionTimeout) { - httpClient = httpClient.newBuilder().connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS).build(); - return this; - } - - /** - * Get read timeout (in milliseconds). - * - * @return Timeout in milliseconds - */ - public int getReadTimeout() { - return httpClient.readTimeoutMillis(); - } - - /** - * Sets the read timeout (in milliseconds). A value of 0 means no timeout, otherwise values must be between 1 and {@link Integer#MAX_VALUE}. - * - * @param readTimeout read timeout in milliseconds - * @return Api client - */ - public ApiClient setReadTimeout(int readTimeout) { - httpClient = httpClient.newBuilder().readTimeout(readTimeout, TimeUnit.MILLISECONDS).build(); - return this; - } - - /** - * Get write timeout (in milliseconds). - * - * @return Timeout in milliseconds - */ - public int getWriteTimeout() { - return httpClient.writeTimeoutMillis(); - } - - /** - * Sets the write timeout (in milliseconds). A value of 0 means no timeout, otherwise values must be between 1 and {@link Integer#MAX_VALUE}. - * - * @param writeTimeout connection timeout in milliseconds - * @return Api client - */ - public ApiClient setWriteTimeout(int writeTimeout) { - httpClient = httpClient.newBuilder().writeTimeout(writeTimeout, TimeUnit.MILLISECONDS).build(); - return this; - } - - /** - * Helper method to configure the token endpoint of the first oauth found in the apiAuthorizations (there should be only one) - * - * @return Token request builder - */ - public TokenRequestBuilder getTokenEndPoint() { - for (Authentication apiAuth : authentications.values()) { - if (apiAuth instanceof RetryingOAuth) { - RetryingOAuth retryingOAuth = (RetryingOAuth) apiAuth; - return retryingOAuth.getTokenRequestBuilder(); - } - } - return null; - } - - /** - * Format the given parameter object into string. - * - * @param param Parameter - * @return String representation of the parameter - */ - public String parameterToString(Object param) { - if (param == null) { - return ""; - } else if (param instanceof Date || param instanceof OffsetDateTime || param instanceof LocalDate) { - //Serialize to json string and remove the " enclosing characters - String jsonStr = json.serialize(param); - return jsonStr.substring(1, jsonStr.length() - 1); - } else if (param instanceof Collection) { - StringBuilder b = new StringBuilder(); - for (Object o : (Collection) param) { - if (b.length() > 0) { - b.append(","); + private String basePath = "https://to.be.defined"; + private boolean debugging = false; + private Map<String, String> defaultHeaderMap = new HashMap<String, String>(); + private Map<String, String> defaultCookieMap = new HashMap<String, String>(); + private String tempFolderPath = null; + + private Map<String, Authentication> authentications; + + private DateFormat dateFormat; + private DateFormat datetimeFormat; + private boolean lenientDatetimeFormat; + private int dateLength; + + private InputStream sslCaCert; + private boolean verifyingSsl; + private KeyManager[] keyManagers; + + private OkHttpClient httpClient; + private JSON json; + + private HttpLoggingInterceptor loggingInterceptor; + + /* + * Basic constructor for ApiClient + */ + public ApiClient() { + init(); + + // Setup authentications (key: authentication name, value: authentication). + authentications.put("OAuth2", new OAuth()); + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID + */ + public ApiClient(String clientId) { + this(clientId, null, null); + } + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID and additional parameters + */ + public ApiClient(String clientId, Map<String, String> parameters) { + this(clientId, null, parameters); + } + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID, secret, and additional parameters + */ + public ApiClient(String clientId, String clientSecret, Map<String, String> parameters) { + init(); + + RetryingOAuth retryingOAuth = new RetryingOAuth("https://to.be.defined/oauth/token", clientId, OAuthFlow.application, clientSecret, + parameters); + authentications.put( + "OAuth2", + retryingOAuth + ); + httpClient.interceptors().add(retryingOAuth); + + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + private void init() { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.addNetworkInterceptor(getProgressInterceptor()); + httpClient = builder.build(); + + verifyingSsl = true; + + json = new JSON(); + + // Set default User-Agent. + setUserAgent("OpenAPI-Generator/1.1.1/java"); + + authentications = new HashMap<String, Authentication>(); + } + + /** + * Get base path + * + * @return Base path + */ + public String getBasePath() { + return basePath; + } + + /** + * Set base path + * + * @param basePath Base path of the URL (e.g https://to.be.defined + * @return An instance of OkHttpClient + */ + public ApiClient setBasePath(String basePath) { + this.basePath = basePath; + return this; + } + + /** + * Get HTTP client + * + * @return An instance of OkHttpClient + */ + public OkHttpClient getHttpClient() { + return httpClient; + } + + /** + * Set HTTP client, which must never be null. + * + * @param newHttpClient An instance of OkHttpClient + * @return Api Client + * @throws NullPointerException when newHttpClient is null + */ + public ApiClient setHttpClient(OkHttpClient newHttpClient) { + this.httpClient = Objects.requireNonNull(newHttpClient, "HttpClient must not be null!"); + return this; + } + + /** + * Get JSON + * + * @return JSON object + */ + public JSON getJSON() { + return json; + } + + /** + * Set JSON + * + * @param json JSON object + * @return Api client + */ + public ApiClient setJSON(JSON json) { + this.json = json; + return this; + } + + /** + * True if isVerifyingSsl flag is on + * + * @return True if isVerifySsl flag is on + */ + public boolean isVerifyingSsl() { + return verifyingSsl; + } + + /** + * Configure whether to verify certificate and hostname when making https requests. Default to true. NOTE: Do NOT set to false in production code, + * otherwise you would face multiple types of cryptographic attacks. + * + * @param verifyingSsl True to verify TLS/SSL connection + * @return ApiClient + */ + public ApiClient setVerifyingSsl(boolean verifyingSsl) { + this.verifyingSsl = verifyingSsl; + applySslSettings(); + return this; + } + + /** + * Get SSL CA cert. + * + * @return Input stream to the SSL CA cert + */ + public InputStream getSslCaCert() { + return sslCaCert; + } + + /** + * Configure the CA certificate to be trusted when making https requests. Use null to reset to default. + * + * @param sslCaCert input stream for SSL CA cert + * @return ApiClient + */ + public ApiClient setSslCaCert(InputStream sslCaCert) { + this.sslCaCert = sslCaCert; + applySslSettings(); + return this; + } + + public KeyManager[] getKeyManagers() { + return keyManagers; + } + + /** + * Configure client keys to use for authorization in an SSL session. Use null to reset to default. + * + * @param managers The KeyManagers to use + * @return ApiClient + */ + public ApiClient setKeyManagers(KeyManager[] managers) { + this.keyManagers = managers; + applySslSettings(); + return this; + } + + public DateFormat getDateFormat() { + return dateFormat; + } + + public ApiClient setDateFormat(DateFormat dateFormat) { + this.json.setDateFormat(dateFormat); + return this; + } + + public ApiClient setSqlDateFormat(DateFormat dateFormat) { + this.json.setSqlDateFormat(dateFormat); + return this; + } + + public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + this.json.setOffsetDateTimeFormat(dateFormat); + return this; + } + + public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) { + this.json.setLocalDateFormat(dateFormat); + return this; + } + + public ApiClient setLenientOnJson(boolean lenientOnJson) { + this.json.setLenientOnJson(lenientOnJson); + return this; + } + + /** + * Get authentications (key: authentication name, value: authentication). + * + * @return Map of authentication objects + */ + public Map<String, Authentication> getAuthentications() { + return authentications; + } + + /** + * Get authentication for the given name. + * + * @param authName The authentication name + * @return The authentication, null if not found + */ + public Authentication getAuthentication(String authName) { + return authentications.get(authName); + } + + /** + * Helper method to set username for the first HTTP basic authentication. + * + * @param username Username + */ + public void setUsername(String username) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setUsername(username); + return; + } } - b.append(o); - } - return b.toString(); - } else { - return String.valueOf(param); - } - } - - /** - * Formats the specified query parameter to a list containing a single {@code Pair} object. - * <p> - * Note that {@code value} must not be a collection. - * - * @param name The name of the parameter. - * @param value The value of the parameter. - * @return A list containing a single {@code Pair} object. - */ - public List<Pair> parameterToPair(String name, Object value) { - List<Pair> params = new ArrayList<Pair>(); - - // preconditions - if (name == null || name.isEmpty() || value == null || value instanceof Collection) { - return params; - } - - params.add(new Pair(name, parameterToString(value))); - return params; - } - - /** - * Formats the specified collection query parameters to a list of {@code Pair} objects. - * <p> - * Note that the values of each of the returned Pair objects are percent-encoded. - * - * @param collectionFormat The collection format of the parameter. - * @param name The name of the parameter. - * @param value The value of the parameter. - * @return A list of {@code Pair} objects. - */ - public List<Pair> parameterToPairs(String collectionFormat, String name, Collection value) { - List<Pair> params = new ArrayList<Pair>(); - - // preconditions - if (name == null || name.isEmpty() || value == null || value.isEmpty()) { - return params; - } - - // create the params based on the collection format - if ("multi".equals(collectionFormat)) { - for (Object item : value) { - params.add(new Pair(name, escapeString(parameterToString(item)))); - } - return params; - } - - // collectionFormat is assumed to be "csv" by default - String delimiter = ","; - - // escape all delimiters except commas, which are URI reserved - // characters - if ("ssv".equals(collectionFormat)) { - delimiter = escapeString(" "); - } else if ("tsv".equals(collectionFormat)) { - delimiter = escapeString("\t"); - } else if ("pipes".equals(collectionFormat)) { - delimiter = escapeString("|"); - } - - StringBuilder sb = new StringBuilder(); - for (Object item : value) { - sb.append(delimiter); - sb.append(escapeString(parameterToString(item))); - } - - params.add(new Pair(name, sb.substring(delimiter.length()))); - - return params; - } - - /** - * Formats the specified collection path parameter to a string value. - * - * @param collectionFormat The collection format of the parameter. - * @param value The value of the parameter. - * @return String representation of the parameter - */ - public String collectionPathParameterToString(String collectionFormat, Collection value) { - // create the value based on the collection format - if ("multi".equals(collectionFormat)) { - // not valid for path params - return parameterToString(value); - } - - // collectionFormat is assumed to be "csv" by default - String delimiter = ","; - - if ("ssv".equals(collectionFormat)) { - delimiter = " "; - } else if ("tsv".equals(collectionFormat)) { - delimiter = "\t"; - } else if ("pipes".equals(collectionFormat)) { - delimiter = "|"; - } - - StringBuilder sb = new StringBuilder(); - for (Object item : value) { - sb.append(delimiter); - sb.append(parameterToString(item)); - } - - return sb.substring(delimiter.length()); - } - - /** - * Sanitize filename by removing path. e.g. ../../sun.gif becomes sun.gif - * - * @param filename The filename to be sanitized - * @return The sanitized filename - */ - public String sanitizeFilename(String filename) { - return filename.replaceAll(".*[/\\\\]", ""); - } - - /** - * Check if the given MIME is a JSON MIME. JSON MIME examples: application/json application/json; charset=UTF8 APPLICATION/JSON - * application/vnd.company+json "* / *" is also default to JSON - * - * @param mime MIME (Multipurpose Internet Mail Extensions) - * @return True if the given MIME is JSON, false otherwise. - */ - public boolean isJsonMime(String mime) { - String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; - return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); - } - - /** - * Select the Accept header's value from the given accepts array: if JSON exists in the given array, use it; otherwise use all of them (joining into - * a string) - * - * @param accepts The accepts array to select from - * @return The Accept header to use. If the given array is empty, null will be returned (not to set the Accept header explicitly). - */ - public String selectHeaderAccept(String[] accepts) { - if (accepts.length == 0) { - return null; - } - for (String accept : accepts) { - if (isJsonMime(accept)) { - return accept; - } - } - return StringUtil.join(accepts, ","); - } - - /** - * Select the Content-Type header's value from the given array: if JSON exists in the given array, use it; otherwise use the first one of the - * array. - * - * @param contentTypes The Content-Type array to select from - * @return The Content-Type header to use. If the given array is empty, or matches "any", JSON will be used. - */ - public String selectHeaderContentType(String[] contentTypes) { - if (contentTypes.length == 0 || contentTypes[0].equals("*/*")) { - return "application/json"; - } - for (String contentType : contentTypes) { - if (isJsonMime(contentType)) { - return contentType; - } - } - return contentTypes[0]; - } - - /** - * Escape the given string to be used as URL query value. - * - * @param str String to be escaped - * @return Escaped string - */ - public String escapeString(String str) { - return URLEncoder.encode(str, StandardCharsets.UTF_8).replaceAll("\\+", "%20"); - } - - /** - * Deserialize response body to Java object, according to the return type and the Content-Type response header. - * - * @param <T> Type - * @param response HTTP response - * @param returnType The type of the Java object - * @return The deserialized Java object - * @throws ApiException If fail to deserialize response body, i.e. cannot read response body or the Content-Type of the response is not supported. - */ - @SuppressWarnings("unchecked") - public <T> T deserialize(Response response, Type returnType) throws ApiException { - if (response == null || returnType == null) { - return null; - } - - if ("byte[]".equals(returnType.toString())) { - // Handle binary response (byte array). - try { - return (T) response.body().bytes(); - } catch (IOException e) { - throw new ApiException(e); - } - } else if (returnType.equals(File.class)) { - // Handle file downloading. - return (T) downloadFileFromResponse(response); - } - - String respBody; - try { - if (response.body() != null) { - respBody = response.body().string(); - } else { - respBody = null; - } - } catch (IOException e) { - throw new ApiException(e); - } - - if (respBody == null || "".equals(respBody)) { - return null; - } - - String contentType = response.headers().get("Content-Type"); - if (contentType == null) { - // ensuring a default content type - contentType = "application/json"; - } - if (isJsonMime(contentType)) { - return json.deserialize(respBody, returnType); - } else if (returnType.equals(String.class)) { - // Expecting string, return the raw response body. - return (T) respBody; - } else { - throw new ApiException( - "Content type \"" + contentType + "\" is not supported for type: " + returnType, - response.code(), - response.headers().toMultimap(), - respBody); - } - } - - /** - * Serialize the given Java object into request body according to the object's class and the request Content-Type. - * - * @param obj The Java object - * @param contentType The request Content-Type - * @return The serialized request body - * @throws ApiException If fail to serialize the given object - */ - public RequestBody serialize(Object obj, String contentType) throws ApiException { - if (obj instanceof byte[]) { - // Binary (byte array) body parameter support. - return RequestBody.create(MediaType.parse(contentType), (byte[]) obj); - } else if (obj instanceof File) { - // File body parameter support. - return RequestBody.create(MediaType.parse(contentType), (File) obj); - } else if (isJsonMime(contentType)) { - String content; - if (obj != null) { - content = json.serialize(obj); - } else { - content = null; - } - return RequestBody.create(MediaType.parse(contentType), content); - } else { - throw new ApiException("Content type \"" + contentType + "\" is not supported"); - } - } - - /** - * Download file from the given response. - * - * @param response An instance of the Response object - * @return Downloaded file - * @throws ApiException If fail to read file content from response and write to disk - */ - public File downloadFileFromResponse(Response response) throws ApiException { - try { - File file = prepareDownloadFile(response); - BufferedSink sink = Okio.buffer(Okio.sink(file)); - sink.writeAll(response.body().source()); - sink.close(); - return file; - } catch (IOException e) { - throw new ApiException(e); - } - } - - /** - * Prepare file for download - * - * @param response An instance of the Response object - * @return Prepared file for the download - * @throws IOException If fail to prepare file for download - */ - public File prepareDownloadFile(Response response) throws IOException { - String filename = null; - String contentDisposition = response.header("Content-Disposition"); - if (contentDisposition != null && !"".equals(contentDisposition)) { - // Get filename from the Content-Disposition header. - Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?"); - Matcher matcher = pattern.matcher(contentDisposition); - if (matcher.find()) { - filename = sanitizeFilename(matcher.group(1)); - } - } - - String prefix = null; - String suffix = null; - if (filename == null) { - prefix = "download-"; - suffix = ""; - } else { - int pos = filename.lastIndexOf("."); - if (pos == -1) { - prefix = filename + "-"; - } else { - prefix = filename.substring(0, pos) + "-"; - suffix = filename.substring(pos); - } - // File.createTempFile requires the prefix to be at least three characters long - if (prefix.length() < 3) { - prefix = "download-"; - } - } - - if (tempFolderPath == null) { - return File.createTempFile(prefix, suffix); - } else { - return File.createTempFile(prefix, suffix, new File(tempFolderPath)); - } - } - - /** - * {@link #execute(Call, Type)} - * - * @param <T> Type - * @param call An instance of the Call object - * @return ApiResponse<T> - * @throws ApiException If fail to execute the call - */ - public <T> ApiResponse<T> execute(Call call) throws ApiException { - return execute(call, null); - } - - /** - * Execute HTTP call and deserialize the HTTP response body into the given return type. - * - * @param returnType The return type used to deserialize HTTP response body - * @param <T> The return type corresponding to (same with) returnType - * @param call Call - * @return ApiResponse object containing response status, headers and data, which is a Java object deserialized from response body and would be null - * when returnType is null. - * @throws ApiException If fail to execute the call - */ - public <T> ApiResponse<T> execute(Call call, Type returnType) throws ApiException { - try { - Response response = call.execute(); - T data = handleResponse(response, returnType); - return new ApiResponse<T>(response.code(), response.headers().toMultimap(), data); - } catch (IOException e) { - throw new ApiException(e); - } - } - - /** - * {@link #executeAsync(Call, Type, ApiCallback)} - * - * @param <T> Type - * @param call An instance of the Call object - * @param callback ApiCallback<T> - */ - public <T> void executeAsync(Call call, ApiCallback<T> callback) { - executeAsync(call, null, callback); - } - - /** - * Execute HTTP call asynchronously. - * - * @param <T> Type - * @param call The callback to be executed when the API call finishes - * @param returnType Return type - * @param callback ApiCallback - * @see #execute(Call, Type) - */ - @SuppressWarnings("unchecked") - public <T> void executeAsync(Call call, final Type returnType, final ApiCallback<T> callback) { - call.enqueue(new Callback() { - @Override - public void onFailure(Call call, IOException e) { - callback.onFailure(new ApiException(e), 0, null); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - T result; - try { - result = handleResponse(response, returnType); - } catch (ApiException e) { - callback.onFailure(e, response.code(), response.headers().toMultimap()); - return; + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set password for the first HTTP basic authentication. + * + * @param password Password + */ + public void setPassword(String password) { + for (Authentication auth : authentications.values()) { + if (auth instanceof HttpBasicAuth) { + ((HttpBasicAuth) auth).setPassword(password); + return; + } } - callback.onSuccess(result, response.code(), response.headers().toMultimap()); - } - }); - } - - /** - * Handle the given response, return the deserialized object when the response is successful. - * - * @param <T> Type - * @param response Response - * @param returnType Return type - * @return Type - * @throws ApiException If the response has an unsuccessful status code or fail to deserialize the response body - */ - public <T> T handleResponse(Response response, Type returnType) throws ApiException { - if (response.isSuccessful()) { - if (returnType == null || response.code() == 204) { - // returning null if the returnType is not defined, - // or the status code is 204 (No Content) - if (response.body() != null) { - try { - response.body().close(); - } catch (Exception e) { - throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); - } + throw new RuntimeException("No HTTP basic authentication configured!"); + } + + /** + * Helper method to set API key value for the first API key authentication. + * + * @param apiKey API key + */ + public void setApiKey(String apiKey) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKey(apiKey); + return; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set API key prefix for the first API key authentication. + * + * @param apiKeyPrefix API key prefix + */ + public void setApiKeyPrefix(String apiKeyPrefix) { + for (Authentication auth : authentications.values()) { + if (auth instanceof ApiKeyAuth) { + ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); + return; + } + } + throw new RuntimeException("No API key authentication configured!"); + } + + /** + * Helper method to set access token for the first OAuth2 authentication. + * + * @param accessToken Access token + */ + public void setAccessToken(String accessToken) { + for (Authentication auth : authentications.values()) { + if (auth instanceof OAuth) { + ((OAuth) auth).setAccessToken(accessToken); + return; + } + } + throw new RuntimeException("No OAuth2 authentication configured!"); + } + + /** + * Set the User-Agent header's value (by adding to the default header map). + * + * @param userAgent HTTP request's user agent + * @return ApiClient + */ + public ApiClient setUserAgent(String userAgent) { + addDefaultHeader("User-Agent", userAgent); + return this; + } + + /** + * Add a default header. + * + * @param key The header's key + * @param value The header's value + * @return ApiClient + */ + public ApiClient addDefaultHeader(String key, String value) { + defaultHeaderMap.put(key, value); + return this; + } + + /** + * Add a default cookie. + * + * @param key The cookie's key + * @param value The cookie's value + * @return ApiClient + */ + public ApiClient addDefaultCookie(String key, String value) { + defaultCookieMap.put(key, value); + return this; + } + + /** + * Check that whether debugging is enabled for this API client. + * + * @return True if debugging is enabled, false otherwise. + */ + public boolean isDebugging() { + return debugging; + } + + /** + * Enable/disable debugging for this API client. + * + * @param debugging To enable (true) or disable (false) debugging + * @return ApiClient + */ + public ApiClient setDebugging(boolean debugging) { + if (debugging != this.debugging) { + if (debugging) { + loggingInterceptor = new HttpLoggingInterceptor(); + loggingInterceptor.setLevel(Level.BODY); + httpClient = httpClient.newBuilder().addInterceptor(loggingInterceptor).build(); + } else { + httpClient.interceptors().remove(loggingInterceptor); + loggingInterceptor = null; + } + } + this.debugging = debugging; + return this; + } + + /** + * The path of temporary folder used to store downloaded files from endpoints with file response. The default value is <code>null</code>, i.e. + * using the system's default tempopary folder. + * + * @return Temporary folder path + * @see <a href="https://docs.oracle.com/javase/7/docs/api/java/io/File.html#createTempFile">createTempFile</a> + */ + public String getTempFolderPath() { + return tempFolderPath; + } + + /** + * Set the temporary folder path (for downloading files) + * + * @param tempFolderPath Temporary folder path + * @return ApiClient + */ + public ApiClient setTempFolderPath(String tempFolderPath) { + this.tempFolderPath = tempFolderPath; + return this; + } + + /** + * Get connection timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getConnectTimeout() { + return httpClient.connectTimeoutMillis(); + } + + /** + * Sets the connect timeout (in milliseconds). A value of 0 means no timeout, otherwise values must be between 1 and {@link Integer#MAX_VALUE}. + * + * @param connectionTimeout connection timeout in milliseconds + * @return Api client + */ + public ApiClient setConnectTimeout(int connectionTimeout) { + httpClient = httpClient.newBuilder().connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS).build(); + return this; + } + + /** + * Get read timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getReadTimeout() { + return httpClient.readTimeoutMillis(); + } + + /** + * Sets the read timeout (in milliseconds). A value of 0 means no timeout, otherwise values must be between 1 and {@link Integer#MAX_VALUE}. + * + * @param readTimeout read timeout in milliseconds + * @return Api client + */ + public ApiClient setReadTimeout(int readTimeout) { + httpClient = httpClient.newBuilder().readTimeout(readTimeout, TimeUnit.MILLISECONDS).build(); + return this; + } + + /** + * Get write timeout (in milliseconds). + * + * @return Timeout in milliseconds + */ + public int getWriteTimeout() { + return httpClient.writeTimeoutMillis(); + } + + /** + * Sets the write timeout (in milliseconds). A value of 0 means no timeout, otherwise values must be between 1 and {@link Integer#MAX_VALUE}. + * + * @param writeTimeout connection timeout in milliseconds + * @return Api client + */ + public ApiClient setWriteTimeout(int writeTimeout) { + httpClient = httpClient.newBuilder().writeTimeout(writeTimeout, TimeUnit.MILLISECONDS).build(); + return this; + } + + /** + * Helper method to configure the token endpoint of the first oauth found in the apiAuthorizations (there should be only one) + * + * @return Token request builder + */ + public TokenRequestBuilder getTokenEndPoint() { + for (Authentication apiAuth : authentications.values()) { + if (apiAuth instanceof RetryingOAuth) { + RetryingOAuth retryingOAuth = (RetryingOAuth) apiAuth; + return retryingOAuth.getTokenRequestBuilder(); + } } return null; - } else { - return deserialize(response, returnType); - } - } else { - String respBody = null; - if (response.body() != null) { + } + + /** + * Format the given parameter object into string. + * + * @param param Parameter + * @return String representation of the parameter + */ + public String parameterToString(Object param) { + if (param == null) { + return ""; + } else if (param instanceof Date || param instanceof OffsetDateTime || param instanceof LocalDate) { + //Serialize to json string and remove the " enclosing characters + String jsonStr = json.serialize(param); + return jsonStr.substring(1, jsonStr.length() - 1); + } else if (param instanceof Collection) { + StringBuilder b = new StringBuilder(); + for (Object o : (Collection) param) { + if (b.length() > 0) { + b.append(","); + } + b.append(String.valueOf(o)); + } + return b.toString(); + } else { + return String.valueOf(param); + } + } + + /** + * Formats the specified query parameter to a list containing a single {@code Pair} object. + * <p> + * Note that {@code value} must not be a collection. + * + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list containing a single {@code Pair} object. + */ + public List<Pair> parameterToPair(String name, Object value) { + List<Pair> params = new ArrayList<Pair>(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value instanceof Collection) { + return params; + } + + params.add(new Pair(name, parameterToString(value))); + return params; + } + + /** + * Formats the specified collection query parameters to a list of {@code Pair} objects. + * <p> + * Note that the values of each of the returned Pair objects are percent-encoded. + * + * @param collectionFormat The collection format of the parameter. + * @param name The name of the parameter. + * @param value The value of the parameter. + * @return A list of {@code Pair} objects. + */ + public List<Pair> parameterToPairs(String collectionFormat, String name, Collection value) { + List<Pair> params = new ArrayList<Pair>(); + + // preconditions + if (name == null || name.isEmpty() || value == null || value.isEmpty()) { + return params; + } + + // create the params based on the collection format + if ("multi".equals(collectionFormat)) { + for (Object item : value) { + params.add(new Pair(name, escapeString(parameterToString(item)))); + } + return params; + } + + // collectionFormat is assumed to be "csv" by default + String delimiter = ","; + + // escape all delimiters except commas, which are URI reserved + // characters + if ("ssv".equals(collectionFormat)) { + delimiter = escapeString(" "); + } else if ("tsv".equals(collectionFormat)) { + delimiter = escapeString("\t"); + } else if ("pipes".equals(collectionFormat)) { + delimiter = escapeString("|"); + } + + StringBuilder sb = new StringBuilder(); + for (Object item : value) { + sb.append(delimiter); + sb.append(escapeString(parameterToString(item))); + } + + params.add(new Pair(name, sb.substring(delimiter.length()))); + + return params; + } + + /** + * Formats the specified collection path parameter to a string value. + * + * @param collectionFormat The collection format of the parameter. + * @param value The value of the parameter. + * @return String representation of the parameter + */ + public String collectionPathParameterToString(String collectionFormat, Collection value) { + // create the value based on the collection format + if ("multi".equals(collectionFormat)) { + // not valid for path params + return parameterToString(value); + } + + // collectionFormat is assumed to be "csv" by default + String delimiter = ","; + + if ("ssv".equals(collectionFormat)) { + delimiter = " "; + } else if ("tsv".equals(collectionFormat)) { + delimiter = "\t"; + } else if ("pipes".equals(collectionFormat)) { + delimiter = "|"; + } + + StringBuilder sb = new StringBuilder(); + for (Object item : value) { + sb.append(delimiter); + sb.append(parameterToString(item)); + } + + return sb.substring(delimiter.length()); + } + + /** + * Sanitize filename by removing path. e.g. ../../sun.gif becomes sun.gif + * + * @param filename The filename to be sanitized + * @return The sanitized filename + */ + public String sanitizeFilename(String filename) { + return filename.replaceAll(".*[/\\\\]", ""); + } + + /** + * Check if the given MIME is a JSON MIME. JSON MIME examples: application/json application/json; charset=UTF8 APPLICATION/JSON + * application/vnd.company+json "* / *" is also default to JSON + * + * @param mime MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public boolean isJsonMime(String mime) { + String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; + return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); + } + + /** + * Select the Accept header's value from the given accepts array: if JSON exists in the given array, use it; otherwise use all of them (joining + * into a string) + * + * @param accepts The accepts array to select from + * @return The Accept header to use. If the given array is empty, null will be returned (not to set the Accept header explicitly). + */ + public String selectHeaderAccept(String[] accepts) { + if (accepts.length == 0) { + return null; + } + for (String accept : accepts) { + if (isJsonMime(accept)) { + return accept; + } + } + return StringUtil.join(accepts, ","); + } + + /** + * Select the Content-Type header's value from the given array: if JSON exists in the given array, use it; otherwise use the first one of the + * array. + * + * @param contentTypes The Content-Type array to select from + * @return The Content-Type header to use. If the given array is empty, or matches "any", JSON will be used. + */ + public String selectHeaderContentType(String[] contentTypes) { + if (contentTypes.length == 0 || contentTypes[0].equals("*/*")) { + return "application/json"; + } + for (String contentType : contentTypes) { + if (isJsonMime(contentType)) { + return contentType; + } + } + return contentTypes[0]; + } + + /** + * Escape the given string to be used as URL query value. + * + * @param str String to be escaped + * @return Escaped string + */ + public String escapeString(String str) { try { - respBody = response.body().string(); + return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20"); + } catch (UnsupportedEncodingException e) { + return str; + } + } + + /** + * Deserialize response body to Java object, according to the return type and the Content-Type response header. + * + * @param <T> Type + * @param response HTTP response + * @param returnType The type of the Java object + * @return The deserialized Java object + * @throws ApiException If fail to deserialize response body, i.e. cannot read response body or the Content-Type of the response is not + * supported. + */ + @SuppressWarnings("unchecked") + public <T> T deserialize(Response response, Type returnType) throws ApiException { + if (response == null || returnType == null) { + return null; + } + + if ("byte[]".equals(returnType.toString())) { + // Handle binary response (byte array). + try { + return (T) response.body().bytes(); + } catch (IOException e) { + throw new ApiException(e); + } + } else if (returnType.equals(File.class)) { + // Handle file downloading. + return (T) downloadFileFromResponse(response); + } + + String respBody; + try { + if (response.body() != null) { + respBody = response.body().string(); + } else { + respBody = null; + } } catch (IOException e) { - throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + throw new ApiException(e); } - } - throw new ApiException(response.message(), response.code(), response.headers().toMultimap(), respBody); - } - } - - /** - * Build HTTP call with the given options. - * - * @param path The sub-path of the HTTP URL - * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" - * @param queryParams The query parameters - * @param collectionQueryParams The collection query parameters - * @param body The request body object - * @param headerParams The header parameters - * @param cookieParams The cookie parameters - * @param formParams The form parameters - * @param authNames The authentications to apply - * @param callback Callback for upload/download progress - * @return The HTTP call - * @throws ApiException If fail to serialize the request body object - */ - public Call buildCall(String path, String method, List<Pair> queryParams, List<Pair> collectionQueryParams, Object body, - Map<String, String> headerParams, Map<String, String> cookieParams, Map<String, Object> formParams, String[] authNames, ApiCallback callback) - throws ApiException { - Request request = buildRequest(path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, authNames, - callback); - - return httpClient.newCall(request); - } - - /** - * Build an HTTP request with the given options. - * - * @param path The sub-path of the HTTP URL - * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" - * @param queryParams The query parameters - * @param collectionQueryParams The collection query parameters - * @param body The request body object - * @param headerParams The header parameters - * @param cookieParams The cookie parameters - * @param formParams The form parameters - * @param authNames The authentications to apply - * @param callback Callback for upload/download progress - * @return The HTTP request - * @throws ApiException If fail to serialize the request body object - */ - public Request buildRequest(String path, String method, List<Pair> queryParams, List<Pair> collectionQueryParams, Object body, - Map<String, String> headerParams, Map<String, String> cookieParams, Map<String, Object> formParams, String[] authNames, ApiCallback callback) - throws ApiException { - updateParamsForAuth(authNames, queryParams, headerParams, cookieParams); - - final String url = buildUrl(path, queryParams, collectionQueryParams); - final Request.Builder reqBuilder = new Request.Builder().url(url); - processHeaderParams(headerParams, reqBuilder); - processCookieParams(cookieParams, reqBuilder); - - String contentType = headerParams.get("Content-Type"); - // ensuring a default content type - if (contentType == null) { - contentType = "application/json"; - } - - RequestBody reqBody; - if (!HttpMethod.permitsRequestBody(method)) { - reqBody = null; - } else if ("application/x-www-form-urlencoded".equals(contentType)) { - reqBody = buildRequestBodyFormEncoding(formParams); - } else if ("multipart/form-data".equals(contentType)) { - reqBody = buildRequestBodyMultipart(formParams); - } else if (body == null) { - if ("DELETE".equals(method)) { - // allow calling DELETE without sending a request body - reqBody = null; - } else { - // use an empty request body (for POST, PUT and PATCH) - reqBody = RequestBody.create(MediaType.parse(contentType), ""); - } - } else { - reqBody = serialize(body, contentType); - } - - // Associate callback with request (if not null) so interceptor can - // access it when creating ProgressResponseBody - reqBuilder.tag(callback); - - Request request = null; - - if (callback != null && reqBody != null) { - ProgressRequestBody progressRequestBody = new ProgressRequestBody(reqBody, callback); - request = reqBuilder.method(method, progressRequestBody).build(); - } else { - request = reqBuilder.method(method, reqBody).build(); - } - - return request; - } - - /** - * Build full URL by concatenating base path, the given sub path and query parameters. - * - * @param path The sub path - * @param queryParams The query parameters - * @param collectionQueryParams The collection query parameters - * @return The full URL - */ - public String buildUrl(String path, List<Pair> queryParams, List<Pair> collectionQueryParams) { - final StringBuilder url = new StringBuilder(); - url.append(basePath).append(path); - - if (queryParams != null && !queryParams.isEmpty()) { - // support (constant) query string in `path`, e.g. "/posts?draft=1" - String prefix = path.contains("?") ? "&" : "?"; - for (Pair param : queryParams) { - if (param.getValue() != null) { - if (prefix != null) { - url.append(prefix); - prefix = null; - } else { - url.append("&"); - } - String value = parameterToString(param.getValue()); - url.append(escapeString(param.getName())).append("=").append(escapeString(value)); + + if (respBody == null || "".equals(respBody)) { + return null; } - } - } - - if (collectionQueryParams != null && !collectionQueryParams.isEmpty()) { - String prefix = url.toString().contains("?") ? "&" : "?"; - for (Pair param : collectionQueryParams) { - if (param.getValue() != null) { - if (prefix != null) { - url.append(prefix); - prefix = null; - } else { - url.append("&"); - } - String value = parameterToString(param.getValue()); - // collection query parameter value already escaped as part of parameterToPairs - url.append(escapeString(param.getName())).append("=").append(value); + + String contentType = response.headers().get("Content-Type"); + if (contentType == null) { + // ensuring a default content type + contentType = "application/json"; } - } - } - - return url.toString(); - } - - /** - * Set header parameters to the request builder, including default headers. - * - * @param headerParams Header parameters in the form of Map - * @param reqBuilder Request.Builder - */ - public void processHeaderParams(Map<String, String> headerParams, Request.Builder reqBuilder) { - for (Entry<String, String> param : headerParams.entrySet()) { - reqBuilder.header(param.getKey(), parameterToString(param.getValue())); - } - for (Entry<String, String> header : defaultHeaderMap.entrySet()) { - if (!headerParams.containsKey(header.getKey())) { - reqBuilder.header(header.getKey(), parameterToString(header.getValue())); - } - } - } - - /** - * Set cookie parameters to the request builder, including default cookies. - * - * @param cookieParams Cookie parameters in the form of Map - * @param reqBuilder Request.Builder - */ - public void processCookieParams(Map<String, String> cookieParams, Request.Builder reqBuilder) { - for (Entry<String, String> param : cookieParams.entrySet()) { - reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue())); - } - for (Entry<String, String> param : defaultCookieMap.entrySet()) { - if (!cookieParams.containsKey(param.getKey())) { - reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue())); - } - } - } - - /** - * Update query and header parameters based on authentication settings. - * - * @param authNames The authentications to apply - * @param queryParams List of query parameters - * @param headerParams Map of header parameters - * @param cookieParams Map of cookie parameters - */ - public void updateParamsForAuth(String[] authNames, List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { - for (String authName : authNames) { - Authentication auth = authentications.get(authName); - if (auth == null) { - throw new RuntimeException("Authentication undefined: " + authName); - } - auth.applyToParams(queryParams, headerParams, cookieParams); - } - } - - /** - * Build a form-encoding request body with the given form parameters. - * - * @param formParams Form parameters in the form of Map - * @return RequestBody - */ - public RequestBody buildRequestBodyFormEncoding(Map<String, Object> formParams) { - okhttp3.FormBody.Builder formBuilder = new okhttp3.FormBody.Builder(); - for (Entry<String, Object> param : formParams.entrySet()) { - formBuilder.add(param.getKey(), parameterToString(param.getValue())); - } - return formBuilder.build(); - } - - /** - * Build a multipart (file uploading) request body with the given form parameters, which could contain text fields and file fields. - * - * @param formParams Form parameters in the form of Map - * @return RequestBody - */ - public RequestBody buildRequestBodyMultipart(Map<String, Object> formParams) { - MultipartBody.Builder mpBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); - for (Entry<String, Object> param : formParams.entrySet()) { - if (param.getValue() instanceof File) { - File file = (File) param.getValue(); - Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\"; filename=\"" + file.getName() + "\""); - MediaType mediaType = MediaType.parse(guessContentTypeFromFile(file)); - mpBuilder.addPart(partHeaders, RequestBody.create(mediaType, file)); - } else { - Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\""); - mpBuilder.addPart(partHeaders, RequestBody.create(null, parameterToString(param.getValue()))); - } - } - return mpBuilder.build(); - } - - /** - * Guess Content-Type header from the given file (defaults to "application/octet-stream"). - * - * @param file The given file - * @return The guessed Content-Type - */ - public String guessContentTypeFromFile(File file) { - String contentType = URLConnection.guessContentTypeFromName(file.getName()); - if (contentType == null) { - return "application/octet-stream"; - } else { - return contentType; - } - } - - /** - * Get network interceptor to add it to the httpClient to track download progress for async requests. - */ - public Interceptor getProgressInterceptor() { - return new Interceptor() { - @Override - public Response intercept(Interceptor.Chain chain) throws IOException { - final Request request = chain.request(); - final Response originalResponse = chain.proceed(request); - if (request.tag() instanceof ApiCallback) { - final ApiCallback callback = (ApiCallback) request.tag(); - return originalResponse.newBuilder() - .body(new ProgressResponseBody(originalResponse.body(), callback)) - .build(); + if (isJsonMime(contentType)) { + return json.deserialize(respBody, returnType); + } else if (returnType.equals(String.class)) { + // Expecting string, return the raw response body. + return (T) respBody; + } else { + throw new ApiException( + "Content type \"" + contentType + "\" is not supported for type: " + returnType, + response.code(), + response.headers().toMultimap(), + respBody); } - return originalResponse; - } - }; - } - - /** - * Apply SSL related settings to httpClient according to the current values of verifyingSsl and sslCaCert. - */ - private void applySslSettings() { - try { - TrustManager[] trustManagers; - HostnameVerifier hostnameVerifier; - if (!verifyingSsl) { - trustManagers = new TrustManager[]{ - new X509TrustManager() { - @Override - public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { - } - - @Override - public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { - } - - @Override - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return new java.security.cert.X509Certificate[]{}; - } + } + + /** + * Serialize the given Java object into request body according to the object's class and the request Content-Type. + * + * @param obj The Java object + * @param contentType The request Content-Type + * @return The serialized request body + * @throws ApiException If fail to serialize the given object + */ + public RequestBody serialize(Object obj, String contentType) throws ApiException { + if (obj instanceof byte[]) { + // Binary (byte array) body parameter support. + return RequestBody.create(MediaType.parse(contentType), (byte[]) obj); + } else if (obj instanceof File) { + // File body parameter support. + return RequestBody.create(MediaType.parse(contentType), (File) obj); + } else if (isJsonMime(contentType)) { + String content; + if (obj != null) { + content = json.serialize(obj); + } else { + content = null; } - }; - hostnameVerifier = new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }; - } else { - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + return RequestBody.create(MediaType.parse(contentType), content); + } else { + throw new ApiException("Content type \"" + contentType + "\" is not supported"); + } + } + + /** + * Download file from the given response. + * + * @param response An instance of the Response object + * @return Downloaded file + * @throws ApiException If fail to read file content from response and write to disk + */ + public File downloadFileFromResponse(Response response) throws ApiException { + try { + File file = prepareDownloadFile(response); + BufferedSink sink = Okio.buffer(Okio.sink(file)); + sink.writeAll(response.body().source()); + sink.close(); + return file; + } catch (IOException e) { + throw new ApiException(e); + } + } + + /** + * Prepare file for download + * + * @param response An instance of the Response object + * @return Prepared file for the download + * @throws IOException If fail to prepare file for download + */ + public File prepareDownloadFile(Response response) throws IOException { + String filename = null; + String contentDisposition = response.header("Content-Disposition"); + if (contentDisposition != null && !"".equals(contentDisposition)) { + // Get filename from the Content-Disposition header. + Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?"); + Matcher matcher = pattern.matcher(contentDisposition); + if (matcher.find()) { + filename = sanitizeFilename(matcher.group(1)); + } + } + + String prefix = null; + String suffix = null; + if (filename == null) { + prefix = "download-"; + suffix = ""; + } else { + int pos = filename.lastIndexOf("."); + if (pos == -1) { + prefix = filename + "-"; + } else { + prefix = filename.substring(0, pos) + "-"; + suffix = filename.substring(pos); + } + // File.createTempFile requires the prefix to be at least three characters long + if (prefix.length() < 3) { + prefix = "download-"; + } + } + + if (tempFolderPath == null) { + return File.createTempFile(prefix, suffix); + } else { + return File.createTempFile(prefix, suffix, new File(tempFolderPath)); + } + } + + /** + * {@link #execute(Call, Type)} + * + * @param <T> Type + * @param call An instance of the Call object + * @return ApiResponse<T> + * @throws ApiException If fail to execute the call + */ + public <T> ApiResponse<T> execute(Call call) throws ApiException { + return execute(call, null); + } + + /** + * Execute HTTP call and deserialize the HTTP response body into the given return type. + * + * @param returnType The return type used to deserialize HTTP response body + * @param <T> The return type corresponding to (same with) returnType + * @param call Call + * @return ApiResponse object containing response status, headers and data, which is a Java object deserialized from response body and would be + * null when returnType is null. + * @throws ApiException If fail to execute the call + */ + public <T> ApiResponse<T> execute(Call call, Type returnType) throws ApiException { + try { + Response response = call.execute(); + T data = handleResponse(response, returnType); + return new ApiResponse<T>(response.code(), response.headers().toMultimap(), data); + } catch (IOException e) { + throw new ApiException(e); + } + } + + /** + * {@link #executeAsync(Call, Type, ApiCallback)} + * + * @param <T> Type + * @param call An instance of the Call object + * @param callback ApiCallback<T> + */ + public <T> void executeAsync(Call call, ApiCallback<T> callback) { + executeAsync(call, null, callback); + } + + /** + * Execute HTTP call asynchronously. + * + * @param <T> Type + * @param call The callback to be executed when the API call finishes + * @param returnType Return type + * @param callback ApiCallback + * @see #execute(Call, Type) + */ + @SuppressWarnings("unchecked") + public <T> void executeAsync(Call call, final Type returnType, final ApiCallback<T> callback) { + call.enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + callback.onFailure(new ApiException(e), 0, null); + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + T result; + try { + result = (T) handleResponse(response, returnType); + } catch (ApiException e) { + callback.onFailure(e, response.code(), response.headers().toMultimap()); + return; + } + callback.onSuccess(result, response.code(), response.headers().toMultimap()); + } + }); + } + + /** + * Handle the given response, return the deserialized object when the response is successful. + * + * @param <T> Type + * @param response Response + * @param returnType Return type + * @return Type + * @throws ApiException If the response has an unsuccessful status code or fail to deserialize the response body + */ + public <T> T handleResponse(Response response, Type returnType) throws ApiException { + if (response.isSuccessful()) { + if (returnType == null || response.code() == 204) { + // returning null if the returnType is not defined, + // or the status code is 204 (No Content) + if (response.body() != null) { + try { + response.body().close(); + } catch (Exception e) { + throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + } + } + return null; + } else { + return deserialize(response, returnType); + } + } else { + String respBody = null; + if (response.body() != null) { + try { + respBody = response.body().string(); + } catch (IOException e) { + throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap()); + } + } + throw new ApiException(response.message(), response.code(), response.headers().toMultimap(), respBody); + } + } + + /** + * Build HTTP call with the given options. + * + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param cookieParams The cookie parameters + * @param formParams The form parameters + * @param authNames The authentications to apply + * @param callback Callback for upload/download progress + * @return The HTTP call + * @throws ApiException If fail to serialize the request body object + */ + public Call buildCall(String path, String method, List<Pair> queryParams, List<Pair> collectionQueryParams, Object body, + Map<String, String> headerParams, Map<String, String> cookieParams, Map<String, Object> formParams, String[] authNames, ApiCallback callback) + throws ApiException { + Request request = buildRequest(path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, authNames, + callback); + + return httpClient.newCall(request); + } + + /** + * Build an HTTP request with the given options. + * + * @param path The sub-path of the HTTP URL + * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE" + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @param body The request body object + * @param headerParams The header parameters + * @param cookieParams The cookie parameters + * @param formParams The form parameters + * @param authNames The authentications to apply + * @param callback Callback for upload/download progress + * @return The HTTP request + * @throws ApiException If fail to serialize the request body object + */ + public Request buildRequest(String path, String method, List<Pair> queryParams, List<Pair> collectionQueryParams, Object body, + Map<String, String> headerParams, Map<String, String> cookieParams, Map<String, Object> formParams, String[] authNames, ApiCallback callback) + throws ApiException { + updateParamsForAuth(authNames, queryParams, headerParams, cookieParams); + + final String url = buildUrl(path, queryParams, collectionQueryParams); + final Request.Builder reqBuilder = new Request.Builder().url(url); + processHeaderParams(headerParams, reqBuilder); + processCookieParams(cookieParams, reqBuilder); + + String contentType = (String) headerParams.get("Content-Type"); + // ensuring a default content type + if (contentType == null) { + contentType = "application/json"; + } - if (sslCaCert == null) { - trustManagerFactory.init((KeyStore) null); + RequestBody reqBody; + if (!HttpMethod.permitsRequestBody(method)) { + reqBody = null; + } else if ("application/x-www-form-urlencoded".equals(contentType)) { + reqBody = buildRequestBodyFormEncoding(formParams); + } else if ("multipart/form-data".equals(contentType)) { + reqBody = buildRequestBodyMultipart(formParams); + } else if (body == null) { + if ("DELETE".equals(method)) { + // allow calling DELETE without sending a request body + reqBody = null; + } else { + // use an empty request body (for POST, PUT and PATCH) + reqBody = RequestBody.create(MediaType.parse(contentType), ""); + } + } else { + reqBody = serialize(body, contentType); + } + + // Associate callback with request (if not null) so interceptor can + // access it when creating ProgressResponseBody + reqBuilder.tag(callback); + + Request request = null; + + if (callback != null && reqBody != null) { + ProgressRequestBody progressRequestBody = new ProgressRequestBody(reqBody, callback); + request = reqBuilder.method(method, progressRequestBody).build(); } else { - char[] password = null; // Any password will work. - CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); - Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(sslCaCert); - if (certificates.isEmpty()) { - throw new IllegalArgumentException("expected non-empty set of trusted certificates"); - } - KeyStore caKeyStore = newEmptyKeyStore(password); - int index = 0; - for (Certificate certificate : certificates) { - String certificateAlias = "ca" + index++; - caKeyStore.setCertificateEntry(certificateAlias, certificate); - } - trustManagerFactory.init(caKeyStore); + request = reqBuilder.method(method, reqBody).build(); } - trustManagers = trustManagerFactory.getTrustManagers(); - hostnameVerifier = OkHostnameVerifier.INSTANCE; - } - - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(keyManagers, trustManagers, new SecureRandom()); - httpClient = httpClient.newBuilder() - .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0]) - .hostnameVerifier(hostnameVerifier) - .build(); - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - } - - private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { - try { - KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - keyStore.load(null, password); - return keyStore; - } catch (IOException e) { - throw new AssertionError(e); - } - } + + return request; + } + + /** + * Build full URL by concatenating base path, the given sub path and query parameters. + * + * @param path The sub path + * @param queryParams The query parameters + * @param collectionQueryParams The collection query parameters + * @return The full URL + */ + public String buildUrl(String path, List<Pair> queryParams, List<Pair> collectionQueryParams) { + final StringBuilder url = new StringBuilder(); + url.append(basePath).append(path); + + if (queryParams != null && !queryParams.isEmpty()) { + // support (constant) query string in `path`, e.g. "/posts?draft=1" + String prefix = path.contains("?") ? "&" : "?"; + for (Pair param : queryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + url.append(escapeString(param.getName())).append("=").append(escapeString(value)); + } + } + } + + if (collectionQueryParams != null && !collectionQueryParams.isEmpty()) { + String prefix = url.toString().contains("?") ? "&" : "?"; + for (Pair param : collectionQueryParams) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + String value = parameterToString(param.getValue()); + // collection query parameter value already escaped as part of parameterToPairs + url.append(escapeString(param.getName())).append("=").append(value); + } + } + } + + return url.toString(); + } + + /** + * Set header parameters to the request builder, including default headers. + * + * @param headerParams Header parameters in the form of Map + * @param reqBuilder Request.Builder + */ + public void processHeaderParams(Map<String, String> headerParams, Request.Builder reqBuilder) { + for (Entry<String, String> param : headerParams.entrySet()) { + reqBuilder.header(param.getKey(), parameterToString(param.getValue())); + } + for (Entry<String, String> header : defaultHeaderMap.entrySet()) { + if (!headerParams.containsKey(header.getKey())) { + reqBuilder.header(header.getKey(), parameterToString(header.getValue())); + } + } + } + + /** + * Set cookie parameters to the request builder, including default cookies. + * + * @param cookieParams Cookie parameters in the form of Map + * @param reqBuilder Request.Builder + */ + public void processCookieParams(Map<String, String> cookieParams, Request.Builder reqBuilder) { + for (Entry<String, String> param : cookieParams.entrySet()) { + reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue())); + } + for (Entry<String, String> param : defaultCookieMap.entrySet()) { + if (!cookieParams.containsKey(param.getKey())) { + reqBuilder.addHeader("Cookie", String.format("%s=%s", param.getKey(), param.getValue())); + } + } + } + + /** + * Update query and header parameters based on authentication settings. + * + * @param authNames The authentications to apply + * @param queryParams List of query parameters + * @param headerParams Map of header parameters + * @param cookieParams Map of cookie parameters + */ + public void updateParamsForAuth(String[] authNames, List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { + for (String authName : authNames) { + Authentication auth = authentications.get(authName); + if (auth == null) { + throw new RuntimeException("Authentication undefined: " + authName); + } + auth.applyToParams(queryParams, headerParams, cookieParams); + } + } + + /** + * Build a form-encoding request body with the given form parameters. + * + * @param formParams Form parameters in the form of Map + * @return RequestBody + */ + public RequestBody buildRequestBodyFormEncoding(Map<String, Object> formParams) { + okhttp3.FormBody.Builder formBuilder = new okhttp3.FormBody.Builder(); + for (Entry<String, Object> param : formParams.entrySet()) { + formBuilder.add(param.getKey(), parameterToString(param.getValue())); + } + return formBuilder.build(); + } + + /** + * Build a multipart (file uploading) request body with the given form parameters, which could contain text fields and file fields. + * + * @param formParams Form parameters in the form of Map + * @return RequestBody + */ + public RequestBody buildRequestBodyMultipart(Map<String, Object> formParams) { + MultipartBody.Builder mpBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); + for (Entry<String, Object> param : formParams.entrySet()) { + if (param.getValue() instanceof File) { + File file = (File) param.getValue(); + Headers partHeaders = Headers + .of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\"; filename=\"" + file.getName() + "\""); + MediaType mediaType = MediaType.parse(guessContentTypeFromFile(file)); + mpBuilder.addPart(partHeaders, RequestBody.create(mediaType, file)); + } else { + Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\""); + mpBuilder.addPart(partHeaders, RequestBody.create(null, parameterToString(param.getValue()))); + } + } + return mpBuilder.build(); + } + + /** + * Guess Content-Type header from the given file (defaults to "application/octet-stream"). + * + * @param file The given file + * @return The guessed Content-Type + */ + public String guessContentTypeFromFile(File file) { + String contentType = URLConnection.guessContentTypeFromName(file.getName()); + if (contentType == null) { + return "application/octet-stream"; + } else { + return contentType; + } + } + + /** + * Get network interceptor to add it to the httpClient to track download progress for async requests. + */ + public Interceptor getProgressInterceptor() { + return new Interceptor() { + @Override + public Response intercept(Interceptor.Chain chain) throws IOException { + final Request request = chain.request(); + final Response originalResponse = chain.proceed(request); + if (request.tag() instanceof ApiCallback) { + final ApiCallback callback = (ApiCallback) request.tag(); + return originalResponse.newBuilder() + .body(new ProgressResponseBody(originalResponse.body(), callback)) + .build(); + } + return originalResponse; + } + }; + } + + /** + * Apply SSL related settings to httpClient according to the current values of verifyingSsl and sslCaCert. + */ + private void applySslSettings() { + try { + TrustManager[] trustManagers; + HostnameVerifier hostnameVerifier; + if (!verifyingSsl) { + trustManagers = new TrustManager[]{ + new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[]{}; + } + } + }; + hostnameVerifier = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + } else { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + + if (sslCaCert == null) { + trustManagerFactory.init((KeyStore) null); + } else { + char[] password = null; // Any password will work. + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(sslCaCert); + if (certificates.isEmpty()) { + throw new IllegalArgumentException("expected non-empty set of trusted certificates"); + } + KeyStore caKeyStore = newEmptyKeyStore(password); + int index = 0; + for (Certificate certificate : certificates) { + String certificateAlias = "ca" + Integer.toString(index++); + caKeyStore.setCertificateEntry(certificateAlias, certificate); + } + trustManagerFactory.init(caKeyStore); + } + trustManagers = trustManagerFactory.getTrustManagers(); + hostnameVerifier = OkHostnameVerifier.INSTANCE; + } + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagers, trustManagers, new SecureRandom()); + httpClient = httpClient.newBuilder() + .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0]) + .hostnameVerifier(hostnameVerifier) + .build(); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { + try { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, password); + return keyStore; + } catch (IOException e) { + throw new AssertionError(e); + } + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiException.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiException.java index bbb8b68..238fc69 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiException.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiException.java @@ -29,81 +29,81 @@ package de.gematik.ti.epa.vzd.client.invoker; -import java.util.List; import java.util.Map; +import java.util.List; public class ApiException extends Exception { - private int code = 0; - private Map<String, List<String>> responseHeaders = null; - private String responseBody = null; - - public ApiException() { - } - - public ApiException(Throwable throwable) { - super(throwable); - } - - public ApiException(String message) { - super(message); - } - - public ApiException(String message, Throwable throwable, int code, Map<String, List<String>> responseHeaders, String responseBody) { - super(message, throwable); - this.code = code; - this.responseHeaders = responseHeaders; - this.responseBody = responseBody; - } - - public ApiException(String message, int code, Map<String, List<String>> responseHeaders, String responseBody) { - this(message, null, code, responseHeaders, responseBody); - } - - public ApiException(String message, Throwable throwable, int code, Map<String, List<String>> responseHeaders) { - this(message, throwable, code, responseHeaders, null); - } - - public ApiException(int code, Map<String, List<String>> responseHeaders, String responseBody) { - this(null, null, code, responseHeaders, responseBody); - } - - public ApiException(int code, String message) { - super(message); - this.code = code; - } - - public ApiException(int code, String message, Map<String, List<String>> responseHeaders, String responseBody) { - this(code, message); - this.responseHeaders = responseHeaders; - this.responseBody = responseBody; - } - - /** - * Get the HTTP status code. - * - * @return HTTP status code - */ - public int getCode() { - return code; - } - - /** - * Get the HTTP response headers. - * - * @return A map of list of string - */ - public Map<String, List<String>> getResponseHeaders() { - return responseHeaders; - } - - /** - * Get the HTTP response body. - * - * @return Response body in the form of string - */ - public String getResponseBody() { - return responseBody; - } + private int code = 0; + private Map<String, List<String>> responseHeaders = null; + private String responseBody = null; + + public ApiException() { + } + + public ApiException(Throwable throwable) { + super(throwable); + } + + public ApiException(String message) { + super(message); + } + + public ApiException(String message, Throwable throwable, int code, Map<String, List<String>> responseHeaders, String responseBody) { + super(message, throwable); + this.code = code; + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + public ApiException(String message, int code, Map<String, List<String>> responseHeaders, String responseBody) { + this(message, (Throwable) null, code, responseHeaders, responseBody); + } + + public ApiException(String message, Throwable throwable, int code, Map<String, List<String>> responseHeaders) { + this(message, throwable, code, responseHeaders, null); + } + + public ApiException(int code, Map<String, List<String>> responseHeaders, String responseBody) { + this((String) null, (Throwable) null, code, responseHeaders, responseBody); + } + + public ApiException(int code, String message) { + super(message); + this.code = code; + } + + public ApiException(int code, String message, Map<String, List<String>> responseHeaders, String responseBody) { + this(code, message); + this.responseHeaders = responseHeaders; + this.responseBody = responseBody; + } + + /** + * Get the HTTP status code. + * + * @return HTTP status code + */ + public int getCode() { + return code; + } + + /** + * Get the HTTP response headers. + * + * @return A map of list of string + */ + public Map<String, List<String>> getResponseHeaders() { + return responseHeaders; + } + + /** + * Get the HTTP response body. + * + * @return Response body in the form of string + */ + public String getResponseBody() { + return responseBody; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiResponse.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiResponse.java index da68d2a..ed295dc 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiResponse.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiResponse.java @@ -39,38 +39,38 @@ */ public class ApiResponse<T> { - final private int statusCode; - final private Map<String, List<String>> headers; - final private T data; + final private int statusCode; + final private Map<String, List<String>> headers; + final private T data; - /** - * @param statusCode The status code of HTTP response - * @param headers The headers of HTTP response - */ - public ApiResponse(int statusCode, Map<String, List<String>> headers) { - this(statusCode, headers, null); - } + /** + * @param statusCode The status code of HTTP response + * @param headers The headers of HTTP response + */ + public ApiResponse(int statusCode, Map<String, List<String>> headers) { + this(statusCode, headers, null); + } - /** - * @param statusCode The status code of HTTP response - * @param headers The headers of HTTP response - * @param data The object deserialized from response bod - */ - public ApiResponse(int statusCode, Map<String, List<String>> headers, T data) { - this.statusCode = statusCode; - this.headers = headers; - this.data = data; - } + /** + * @param statusCode The status code of HTTP response + * @param headers The headers of HTTP response + * @param data The object deserialized from response bod + */ + public ApiResponse(int statusCode, Map<String, List<String>> headers, T data) { + this.statusCode = statusCode; + this.headers = headers; + this.data = data; + } - public int getStatusCode() { - return statusCode; - } + public int getStatusCode() { + return statusCode; + } - public Map<String, List<String>> getHeaders() { - return headers; - } + public Map<String, List<String>> getHeaders() { + return headers; + } - public T getData() { - return data; - } + public T getData() { + return data; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Configuration.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Configuration.java index 94e41bc..75e91c9 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Configuration.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Configuration.java @@ -32,23 +32,23 @@ public class Configuration { - private static ApiClient defaultApiClient = new ApiClient(); - - /** - * Get the default API client, which would be used when creating API instances without providing an API client. - * - * @return Default API client - */ - public static ApiClient getDefaultApiClient() { - return defaultApiClient; - } - - /** - * Set the default API client, which would be used when creating API instances without providing an API client. - * - * @param apiClient API client - */ - public static void setDefaultApiClient(ApiClient apiClient) { - defaultApiClient = apiClient; - } + private static ApiClient defaultApiClient = new ApiClient(); + + /** + * Get the default API client, which would be used when creating API instances without providing an API client. + * + * @return Default API client + */ + public static ApiClient getDefaultApiClient() { + return defaultApiClient; + } + + /** + * Set the default API client, which would be used when creating API instances without providing an API client. + * + * @param apiClient API client + */ + public static void setDefaultApiClient(ApiClient apiClient) { + defaultApiClient = apiClient; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/GzipRequestInterceptor.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/GzipRequestInterceptor.java index beebb21..346c211 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/GzipRequestInterceptor.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/GzipRequestInterceptor.java @@ -29,17 +29,14 @@ package de.gematik.ti.epa.vzd.client.invoker; -import java.io.IOException; -import okhttp3.Interceptor; -import okhttp3.MediaType; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; +import okhttp3.*; import okio.Buffer; import okio.BufferedSink; import okio.GzipSink; import okio.Okio; +import java.io.IOException; + /** * Encodes request bodies using gzip. * <p> @@ -47,59 +44,59 @@ */ class GzipRequestInterceptor implements Interceptor { - @Override - public Response intercept(Chain chain) throws IOException { - Request originalRequest = chain.request(); - if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) { - return chain.proceed(originalRequest); - } + @Override + public Response intercept(Chain chain) throws IOException { + Request originalRequest = chain.request(); + if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) { + return chain.proceed(originalRequest); + } - Request compressedRequest = originalRequest.newBuilder() - .header("Content-Encoding", "gzip") - .method(originalRequest.method(), forceContentLength(gzip(originalRequest.body()))) - .build(); - return chain.proceed(compressedRequest); - } + Request compressedRequest = originalRequest.newBuilder() + .header("Content-Encoding", "gzip") + .method(originalRequest.method(), forceContentLength(gzip(originalRequest.body()))) + .build(); + return chain.proceed(compressedRequest); + } - private RequestBody forceContentLength(final RequestBody requestBody) throws IOException { - final Buffer buffer = new Buffer(); - requestBody.writeTo(buffer); - return new RequestBody() { - @Override - public MediaType contentType() { - return requestBody.contentType(); - } + private RequestBody forceContentLength(final RequestBody requestBody) throws IOException { + final Buffer buffer = new Buffer(); + requestBody.writeTo(buffer); + return new RequestBody() { + @Override + public MediaType contentType() { + return requestBody.contentType(); + } - @Override - public long contentLength() { - return buffer.size(); - } + @Override + public long contentLength() { + return buffer.size(); + } - @Override - public void writeTo(BufferedSink sink) throws IOException { - sink.write(buffer.snapshot()); - } - }; - } + @Override + public void writeTo(BufferedSink sink) throws IOException { + sink.write(buffer.snapshot()); + } + }; + } - private RequestBody gzip(final RequestBody body) { - return new RequestBody() { - @Override - public MediaType contentType() { - return body.contentType(); - } + private RequestBody gzip(final RequestBody body) { + return new RequestBody() { + @Override + public MediaType contentType() { + return body.contentType(); + } - @Override - public long contentLength() { - return -1; // We don't know the compressed length in advance! - } + @Override + public long contentLength() { + return -1; // We don't know the compressed length in advance! + } - @Override - public void writeTo(BufferedSink sink) throws IOException { - BufferedSink gzipSink = Okio.buffer(new GzipSink(sink)); - body.writeTo(gzipSink); - gzipSink.close(); - } - }; - } + @Override + public void writeTo(BufferedSink sink) throws IOException { + BufferedSink gzipSink = Okio.buffer(new GzipSink(sink)); + body.writeTo(gzipSink); + gzipSink.close(); + } + }; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/JSON.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/JSON.java index 97a813c..8e393f8 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/JSON.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/JSON.java @@ -31,13 +31,18 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import com.google.gson.TypeAdapter; import com.google.gson.internal.bind.util.ISO8601Utils; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; +import com.google.gson.JsonElement; import io.gsonfire.GsonFireBuilder; +import io.gsonfire.TypeSelector; + +import de.gematik.ti.epa.vzd.client.model.*; +import okio.ByteString; + import java.io.IOException; import java.io.StringReader; import java.lang.reflect.Type; @@ -50,357 +55,357 @@ import java.util.Date; import java.util.Locale; import java.util.Map; -import okio.ByteString; +import java.util.HashMap; public class JSON { - private Gson gson; - private boolean isLenientOnJson = false; - private final DateTypeAdapter dateTypeAdapter = new DateTypeAdapter(); - private final SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter(); - private final OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(); - private final LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); - private final ByteArrayAdapter byteArrayAdapter = new ByteArrayAdapter(); - - public static GsonBuilder createGson() { - GsonFireBuilder fireBuilder = new GsonFireBuilder(); - GsonBuilder builder = fireBuilder.createGsonBuilder(); - return builder; - } - - private static String getDiscriminatorValue(JsonElement readElement, String discriminatorField) { - JsonElement element = readElement.getAsJsonObject().get(discriminatorField); - if (null == element) { - throw new IllegalArgumentException("missing discriminator field: <" + discriminatorField + ">"); + private Gson gson; + private boolean isLenientOnJson = false; + private DateTypeAdapter dateTypeAdapter = new DateTypeAdapter(); + private SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter(); + private OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(); + private LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); + private ByteArrayAdapter byteArrayAdapter = new ByteArrayAdapter(); + + public static GsonBuilder createGson() { + GsonFireBuilder fireBuilder = new GsonFireBuilder(); + GsonBuilder builder = fireBuilder.createGsonBuilder(); + return builder; } - return element.getAsString(); - } - private static Class getClassByDiscriminator(Map classByDiscriminatorValue, String discriminatorValue) { - Class clazz = (Class) classByDiscriminatorValue.get(discriminatorValue.toUpperCase(Locale.ROOT)); - if (null == clazz) { - throw new IllegalArgumentException("cannot determine model class of name: <" + discriminatorValue + ">"); - } - return clazz; - } - - public JSON() { - gson = createGson() - .registerTypeAdapter(Date.class, dateTypeAdapter) - .registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter) - .registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter) - .registerTypeAdapter(LocalDate.class, localDateTypeAdapter) - .registerTypeAdapter(byte[].class, byteArrayAdapter) - .create(); - } - - /** - * Get Gson. - * - * @return Gson - */ - public Gson getGson() { - return gson; - } - - /** - * Set Gson. - * - * @param gson Gson - * @return JSON - */ - public JSON setGson(Gson gson) { - this.gson = gson; - return this; - } - - public JSON setLenientOnJson(boolean lenientOnJson) { - isLenientOnJson = lenientOnJson; - return this; - } - - /** - * Serialize the given Java object into JSON string. - * - * @param obj Object - * @return String representation of the JSON - */ - public String serialize(Object obj) { - return gson.toJson(obj); - } - - /** - * Deserialize the given JSON string to Java object. - * - * @param <T> Type - * @param body The JSON string - * @param returnType The type to deserialize into - * @return The deserialized Java object - */ - @SuppressWarnings("unchecked") - public <T> T deserialize(String body, Type returnType) { - try { - if (isLenientOnJson) { - JsonReader jsonReader = new JsonReader(new StringReader(body)); - // see https://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/stream/JsonReader.html#setLenient(boolean) - jsonReader.setLenient(true); - return gson.fromJson(jsonReader, returnType); - } else { - return gson.fromJson(body, returnType); - } - } catch (JsonParseException e) { - // Fallback processing when failed to parse JSON form response body: - // return the response body string directly for the String return type; - if (returnType.equals(String.class)) { - return (T) body; - } else { - throw (e); - } - } - } - - /** - * Gson TypeAdapter for Byte Array type - */ - public class ByteArrayAdapter extends TypeAdapter<byte[]> { - - @Override - public void write(JsonWriter out, byte[] value) throws IOException { - if (value == null) { - out.nullValue(); - } else { - out.value(ByteString.of(value).base64()); - } + private static String getDiscriminatorValue(JsonElement readElement, String discriminatorField) { + JsonElement element = readElement.getAsJsonObject().get(discriminatorField); + if (null == element) { + throw new IllegalArgumentException("missing discriminator field: <" + discriminatorField + ">"); + } + return element.getAsString(); } - @Override - public byte[] read(JsonReader in) throws IOException { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String bytesAsBase64 = in.nextString(); - ByteString byteString = ByteString.decodeBase64(bytesAsBase64); - return byteString.toByteArray(); - } + private static Class getClassByDiscriminator(Map classByDiscriminatorValue, String discriminatorValue) { + Class clazz = (Class) classByDiscriminatorValue.get(discriminatorValue.toUpperCase(Locale.ROOT)); + if (null == clazz) { + throw new IllegalArgumentException("cannot determine model class of name: <" + discriminatorValue + ">"); + } + return clazz; } - } - /** - * Gson TypeAdapter for JSR310 OffsetDateTime type - */ - public static class OffsetDateTimeTypeAdapter extends TypeAdapter<OffsetDateTime> { + public JSON() { + gson = createGson() + .registerTypeAdapter(Date.class, dateTypeAdapter) + .registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter) + .registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter) + .registerTypeAdapter(LocalDate.class, localDateTypeAdapter) + .registerTypeAdapter(byte[].class, byteArrayAdapter) + .create(); + } - private DateTimeFormatter formatter; + /** + * Get Gson. + * + * @return Gson + */ + public Gson getGson() { + return gson; + } - public OffsetDateTimeTypeAdapter() { - this(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + /** + * Set Gson. + * + * @param gson Gson + * @return JSON + */ + public JSON setGson(Gson gson) { + this.gson = gson; + return this; } - public OffsetDateTimeTypeAdapter(DateTimeFormatter formatter) { - this.formatter = formatter; + public JSON setLenientOnJson(boolean lenientOnJson) { + isLenientOnJson = lenientOnJson; + return this; } - public void setFormat(DateTimeFormatter dateFormat) { - this.formatter = dateFormat; + /** + * Serialize the given Java object into JSON string. + * + * @param obj Object + * @return String representation of the JSON + */ + public String serialize(Object obj) { + return gson.toJson(obj); } - @Override - public void write(JsonWriter out, OffsetDateTime date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - out.value(formatter.format(date)); - } + /** + * Deserialize the given JSON string to Java object. + * + * @param <T> Type + * @param body The JSON string + * @param returnType The type to deserialize into + * @return The deserialized Java object + */ + @SuppressWarnings("unchecked") + public <T> T deserialize(String body, Type returnType) { + try { + if (isLenientOnJson) { + JsonReader jsonReader = new JsonReader(new StringReader(body)); + // see https://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/stream/JsonReader.html#setLenient(boolean) + jsonReader.setLenient(true); + return gson.fromJson(jsonReader, returnType); + } else { + return gson.fromJson(body, returnType); + } + } catch (JsonParseException e) { + // Fallback processing when failed to parse JSON form response body: + // return the response body string directly for the String return type; + if (returnType.equals(String.class)) { + return (T) body; + } else { + throw (e); + } + } } - @Override - public OffsetDateTime read(JsonReader in) throws IOException { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); - if (date.endsWith("+0000")) { - date = date.substring(0, date.length() - 5) + "Z"; - } - return OffsetDateTime.parse(date, formatter); - } + /** + * Gson TypeAdapter for Byte Array type + */ + public class ByteArrayAdapter extends TypeAdapter<byte[]> { + + @Override + public void write(JsonWriter out, byte[] value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(ByteString.of(value).base64()); + } + } + + @Override + public byte[] read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String bytesAsBase64 = in.nextString(); + ByteString byteString = ByteString.decodeBase64(bytesAsBase64); + return byteString.toByteArray(); + } + } } - } - /** - * Gson TypeAdapter for JSR310 LocalDate type - */ - public class LocalDateTypeAdapter extends TypeAdapter<LocalDate> { + /** + * Gson TypeAdapter for JSR310 OffsetDateTime type + */ + public static class OffsetDateTimeTypeAdapter extends TypeAdapter<OffsetDateTime> { - private DateTimeFormatter formatter; + private DateTimeFormatter formatter; - public LocalDateTypeAdapter() { - this(DateTimeFormatter.ISO_LOCAL_DATE); - } + public OffsetDateTimeTypeAdapter() { + this(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } - public LocalDateTypeAdapter(DateTimeFormatter formatter) { - this.formatter = formatter; - } + public OffsetDateTimeTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } - public void setFormat(DateTimeFormatter dateFormat) { - this.formatter = dateFormat; - } + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } - @Override - public void write(JsonWriter out, LocalDate date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - out.value(formatter.format(date)); - } - } + @Override + public void write(JsonWriter out, OffsetDateTime date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } - @Override - public LocalDate read(JsonReader in) throws IOException { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); - return LocalDate.parse(date, formatter); - } + @Override + public OffsetDateTime read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + if (date.endsWith("+0000")) { + date = date.substring(0, date.length() - 5) + "Z"; + } + return OffsetDateTime.parse(date, formatter); + } + } } - } - public JSON setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { - offsetDateTimeTypeAdapter.setFormat(dateFormat); - return this; - } + /** + * Gson TypeAdapter for JSR310 LocalDate type + */ + public class LocalDateTypeAdapter extends TypeAdapter<LocalDate> { - public JSON setLocalDateFormat(DateTimeFormatter dateFormat) { - localDateTypeAdapter.setFormat(dateFormat); - return this; - } + private DateTimeFormatter formatter; - /** - * Gson TypeAdapter for java.sql.Date type If the dateFormat is null, a simple "yyyy-MM-dd" format will be used (more efficient than - * SimpleDateFormat). - */ - public static class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> { + public LocalDateTypeAdapter() { + this(DateTimeFormatter.ISO_LOCAL_DATE); + } + + public LocalDateTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } - private DateFormat dateFormat; + @Override + public void write(JsonWriter out, LocalDate date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } - public SqlDateTypeAdapter() { + @Override + public LocalDate read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return LocalDate.parse(date, formatter); + } + } } - public SqlDateTypeAdapter(DateFormat dateFormat) { - this.dateFormat = dateFormat; + public JSON setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + offsetDateTimeTypeAdapter.setFormat(dateFormat); + return this; } - public void setFormat(DateFormat dateFormat) { - this.dateFormat = dateFormat; + public JSON setLocalDateFormat(DateTimeFormatter dateFormat) { + localDateTypeAdapter.setFormat(dateFormat); + return this; } - @Override - public void write(JsonWriter out, java.sql.Date date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - String value; - if (dateFormat != null) { - value = dateFormat.format(date); - } else { - value = date.toString(); + /** + * Gson TypeAdapter for java.sql.Date type If the dateFormat is null, a simple "yyyy-MM-dd" format will be used (more efficient than + * SimpleDateFormat). + */ + public static class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> { + + private DateFormat dateFormat; + + public SqlDateTypeAdapter() { + } + + public SqlDateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, java.sql.Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = date.toString(); + } + out.value(value); + } } - out.value(value); - } - } - @Override - public java.sql.Date read(JsonReader in) throws IOException { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); - try { - if (dateFormat != null) { - return new java.sql.Date(dateFormat.parse(date).getTime()); + @Override + public java.sql.Date read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return new java.sql.Date(dateFormat.parse(date).getTime()); + } + return new java.sql.Date(ISO8601Utils.parse(date, new ParsePosition(0)).getTime()); + } catch (ParseException e) { + throw new JsonParseException(e); + } } - return new java.sql.Date(ISO8601Utils.parse(date, new ParsePosition(0)).getTime()); - } catch (ParseException e) { - throw new JsonParseException(e); - } - } + } } - } - /** - * Gson TypeAdapter for java.util.Date type If the dateFormat is null, ISO8601Utils will be used. - */ - public static class DateTypeAdapter extends TypeAdapter<Date> { + /** + * Gson TypeAdapter for java.util.Date type If the dateFormat is null, ISO8601Utils will be used. + */ + public static class DateTypeAdapter extends TypeAdapter<Date> { - private DateFormat dateFormat; + private DateFormat dateFormat; - public DateTypeAdapter() { - } + public DateTypeAdapter() { + } - public DateTypeAdapter(DateFormat dateFormat) { - this.dateFormat = dateFormat; - } + public DateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } - public void setFormat(DateFormat dateFormat) { - this.dateFormat = dateFormat; - } + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } - @Override - public void write(JsonWriter out, Date date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - String value; - if (dateFormat != null) { - value = dateFormat.format(date); - } else { - value = ISO8601Utils.format(date, true); + @Override + public void write(JsonWriter out, Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = ISO8601Utils.format(date, true); + } + out.value(value); + } } - out.value(value); - } - } - @Override - public Date read(JsonReader in) throws IOException { - try { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); + @Override + public Date read(JsonReader in) throws IOException { try { - if (dateFormat != null) { - return dateFormat.parse(date); - } - return ISO8601Utils.parse(date, new ParsePosition(0)); - } catch (ParseException e) { - throw new JsonParseException(e); + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return dateFormat.parse(date); + } + return ISO8601Utils.parse(date, new ParsePosition(0)); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } catch (IllegalArgumentException e) { + throw new JsonParseException(e); } } - } catch (IllegalArgumentException e) { - throw new JsonParseException(e); - } } - } - public JSON setDateFormat(DateFormat dateFormat) { - dateTypeAdapter.setFormat(dateFormat); - return this; - } + public JSON setDateFormat(DateFormat dateFormat) { + dateTypeAdapter.setFormat(dateFormat); + return this; + } - public JSON setSqlDateFormat(DateFormat dateFormat) { - sqlDateTypeAdapter.setFormat(dateFormat); - return this; - } + public JSON setSqlDateFormat(DateFormat dateFormat) { + sqlDateTypeAdapter.setFormat(dateFormat); + return this; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Pair.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Pair.java index 1eda4e8..f48eeef 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Pair.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/Pair.java @@ -32,43 +32,47 @@ public class Pair { - private String name = ""; - private String value = ""; + private String name = ""; + private String value = ""; - public Pair(String name, String value) { - setName(name); - setValue(value); - } - - private void setName(String name) { - if (!isValidString(name)) { - return; + public Pair(String name, String value) { + setName(name); + setValue(value); } - this.name = name; - } + private void setName(String name) { + if (!isValidString(name)) { + return; + } - private void setValue(String value) { - if (!isValidString(value)) { - return; + this.name = name; } - this.value = value; - } + private void setValue(String value) { + if (!isValidString(value)) { + return; + } - public String getName() { - return this.name; - } + this.value = value; + } - public String getValue() { - return this.value; - } + public String getName() { + return this.name; + } - private boolean isValidString(String arg) { - if (arg == null) { - return false; + public String getValue() { + return this.value; } - return !arg.trim().isEmpty(); - } + private boolean isValidString(String arg) { + if (arg == null) { + return false; + } + + if (arg.trim().isEmpty()) { + return false; + } + + return true; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressRequestBody.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressRequestBody.java index 3b481b7..88096f7 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressRequestBody.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressRequestBody.java @@ -29,9 +29,11 @@ package de.gematik.ti.epa.vzd.client.invoker; -import java.io.IOException; import okhttp3.MediaType; import okhttp3.RequestBody; + +import java.io.IOException; + import okio.Buffer; import okio.BufferedSink; import okio.ForwardingSink; @@ -40,48 +42,48 @@ public class ProgressRequestBody extends RequestBody { - private final RequestBody requestBody; - - private final ApiCallback callback; - - public ProgressRequestBody(RequestBody requestBody, ApiCallback callback) { - this.requestBody = requestBody; - this.callback = callback; - } - - @Override - public MediaType contentType() { - return requestBody.contentType(); - } - - @Override - public long contentLength() throws IOException { - return requestBody.contentLength(); - } - - @Override - public void writeTo(BufferedSink sink) throws IOException { - BufferedSink bufferedSink = Okio.buffer(sink(sink)); - requestBody.writeTo(bufferedSink); - bufferedSink.flush(); - } - - private Sink sink(Sink sink) { - return new ForwardingSink(sink) { - - long bytesWritten = 0L; - long contentLength = 0L; - - @Override - public void write(Buffer source, long byteCount) throws IOException { - super.write(source, byteCount); - if (contentLength == 0) { - contentLength = contentLength(); - } - - bytesWritten += byteCount; - callback.onUploadProgress(bytesWritten, contentLength, bytesWritten == contentLength); - } - }; - } + private final RequestBody requestBody; + + private final ApiCallback callback; + + public ProgressRequestBody(RequestBody requestBody, ApiCallback callback) { + this.requestBody = requestBody; + this.callback = callback; + } + + @Override + public MediaType contentType() { + return requestBody.contentType(); + } + + @Override + public long contentLength() throws IOException { + return requestBody.contentLength(); + } + + @Override + public void writeTo(BufferedSink sink) throws IOException { + BufferedSink bufferedSink = Okio.buffer(sink(sink)); + requestBody.writeTo(bufferedSink); + bufferedSink.flush(); + } + + private Sink sink(Sink sink) { + return new ForwardingSink(sink) { + + long bytesWritten = 0L; + long contentLength = 0L; + + @Override + public void write(Buffer source, long byteCount) throws IOException { + super.write(source, byteCount); + if (contentLength == 0) { + contentLength = contentLength(); + } + + bytesWritten += byteCount; + callback.onUploadProgress(bytesWritten, contentLength, bytesWritten == contentLength); + } + }; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressResponseBody.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressResponseBody.java index fa2f950..5ca684c 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressResponseBody.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressResponseBody.java @@ -29,9 +29,11 @@ package de.gematik.ti.epa.vzd.client.invoker; -import java.io.IOException; import okhttp3.MediaType; import okhttp3.ResponseBody; + +import java.io.IOException; + import okio.Buffer; import okio.BufferedSource; import okio.ForwardingSource; @@ -40,47 +42,47 @@ public class ProgressResponseBody extends ResponseBody { - private final ResponseBody responseBody; - private final ApiCallback callback; - private BufferedSource bufferedSource; - - public ProgressResponseBody(ResponseBody responseBody, ApiCallback callback) { - this.responseBody = responseBody; - this.callback = callback; - } - - @Override - public MediaType contentType() { - return responseBody.contentType(); - } - - @Override - public long contentLength() { - return responseBody.contentLength(); - } - - @Override - public BufferedSource source() { - if (bufferedSource == null) { - bufferedSource = Okio.buffer(source(responseBody.source())); + private final ResponseBody responseBody; + private final ApiCallback callback; + private BufferedSource bufferedSource; + + public ProgressResponseBody(ResponseBody responseBody, ApiCallback callback) { + this.responseBody = responseBody; + this.callback = callback; + } + + @Override + public MediaType contentType() { + return responseBody.contentType(); + } + + @Override + public long contentLength() { + return responseBody.contentLength(); + } + + @Override + public BufferedSource source() { + if (bufferedSource == null) { + bufferedSource = Okio.buffer(source(responseBody.source())); + } + return bufferedSource; + } + + private Source source(Source source) { + return new ForwardingSource(source) { + long totalBytesRead = 0L; + + @Override + public long read(Buffer sink, long byteCount) throws IOException { + long bytesRead = super.read(sink, byteCount); + // read() returns the number of bytes read, or -1 if this source is exhausted. + totalBytesRead += bytesRead != -1 ? bytesRead : 0; + callback.onDownloadProgress(totalBytesRead, responseBody.contentLength(), bytesRead == -1); + return bytesRead; + } + }; } - return bufferedSource; - } - - private Source source(Source source) { - return new ForwardingSource(source) { - long totalBytesRead = 0L; - - @Override - public long read(Buffer sink, long byteCount) throws IOException { - long bytesRead = super.read(sink, byteCount); - // read() returns the number of bytes read, or -1 if this source is exhausted. - totalBytesRead += bytesRead != -1 ? bytesRead : 0; - callback.onDownloadProgress(totalBytesRead, responseBody.contentLength(), bytesRead == -1); - return bytesRead; - } - }; - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/StringUtil.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/StringUtil.java index d17a928..5fd8f1d 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/StringUtil.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/StringUtil.java @@ -32,46 +32,46 @@ public class StringUtil { - /** - * Check if the given array contains the given value (with case-insensitive comparison). - * - * @param array The array - * @param value The value to search - * @return true if the array contains the value - */ - public static boolean containsIgnoreCase(String[] array, String value) { - for (String str : array) { - if (value == null && str == null) { - return true; - } - if (value != null && value.equalsIgnoreCase(str)) { - return true; - } + /** + * Check if the given array contains the given value (with case-insensitive comparison). + * + * @param array The array + * @param value The value to search + * @return true if the array contains the value + */ + public static boolean containsIgnoreCase(String[] array, String value) { + for (String str : array) { + if (value == null && str == null) { + return true; + } + if (value != null && value.equalsIgnoreCase(str)) { + return true; + } + } + return false; } - return false; - } - /** - * Join an array of strings with the given separator. - * <p> - * Note: This might be replaced by utility method from commons-lang or guava someday if one of those libraries is added as dependency. - * </p> - * - * @param array The array of strings - * @param separator The separator - * @return the resulting string - */ - public static String join(String[] array, String separator) { - int len = array.length; - if (len == 0) { - return ""; - } + /** + * Join an array of strings with the given separator. + * <p> + * Note: This might be replaced by utility method from commons-lang or guava someday if one of those libraries is added as dependency. + * </p> + * + * @param array The array of strings + * @param separator The separator + * @return the resulting string + */ + public static String join(String[] array, String separator) { + int len = array.length; + if (len == 0) { + return ""; + } - StringBuilder out = new StringBuilder(); - out.append(array[0]); - for (int i = 1; i < len; i++) { - out.append(separator).append(array[i]); + StringBuilder out = new StringBuilder(); + out.append(array[0]); + for (int i = 1; i < len; i++) { + out.append(separator).append(array[i]); + } + return out.toString(); } - return out.toString(); - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/ApiKeyAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/ApiKeyAuth.java index 6b11d3f..afecfb6 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/ApiKeyAuth.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/ApiKeyAuth.java @@ -30,64 +30,65 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; import de.gematik.ti.epa.vzd.client.invoker.Pair; -import java.util.List; + import java.util.Map; +import java.util.List; public class ApiKeyAuth implements Authentication { - private final String location; - private final String paramName; + private final String location; + private final String paramName; - private String apiKey; - private String apiKeyPrefix; + private String apiKey; + private String apiKeyPrefix; - public ApiKeyAuth(String location, String paramName) { - this.location = location; - this.paramName = paramName; - } - - public String getLocation() { - return location; - } - - public String getParamName() { - return paramName; - } + public ApiKeyAuth(String location, String paramName) { + this.location = location; + this.paramName = paramName; + } - public String getApiKey() { - return apiKey; - } + public String getLocation() { + return location; + } - public void setApiKey(String apiKey) { - this.apiKey = apiKey; - } + public String getParamName() { + return paramName; + } - public String getApiKeyPrefix() { - return apiKeyPrefix; - } + public String getApiKey() { + return apiKey; + } - public void setApiKeyPrefix(String apiKeyPrefix) { - this.apiKeyPrefix = apiKeyPrefix; - } + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } - @Override - public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { - if (apiKey == null) { - return; + public String getApiKeyPrefix() { + return apiKeyPrefix; } - String value; - if (apiKeyPrefix != null) { - value = apiKeyPrefix + " " + apiKey; - } else { - value = apiKey; + + public void setApiKeyPrefix(String apiKeyPrefix) { + this.apiKeyPrefix = apiKeyPrefix; } - if ("query".equals(location)) { - queryParams.add(new Pair(paramName, value)); - } else if ("header".equals(location)) { - headerParams.put(paramName, value); - } else if ("cookie".equals(location)) { - cookieParams.put(paramName, value); + + @Override + public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { + if (apiKey == null) { + return; + } + String value; + if (apiKeyPrefix != null) { + value = apiKeyPrefix + " " + apiKey; + } else { + value = apiKey; + } + if ("query".equals(location)) { + queryParams.add(new Pair(paramName, value)); + } else if ("header".equals(location)) { + headerParams.put(paramName, value); + } else if ("cookie".equals(location)) { + cookieParams.put(paramName, value); + } } - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/Authentication.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/Authentication.java index 0c3dfa7..226e311 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/Authentication.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/Authentication.java @@ -30,17 +30,18 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; import de.gematik.ti.epa.vzd.client.invoker.Pair; -import java.util.List; + import java.util.Map; +import java.util.List; public interface Authentication { - /** - * Apply authentication settings to header and query params. - * - * @param queryParams List of query parameters - * @param headerParams Map of header parameters - * @param cookieParams Map of cookie parameters - */ - void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams); + /** + * Apply authentication settings to header and query params. + * + * @param queryParams List of query parameters + * @param headerParams Map of header parameters + * @param cookieParams Map of cookie parameters + */ + void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams); } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBasicAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBasicAuth.java index 2904481..652c31c 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBasicAuth.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBasicAuth.java @@ -30,38 +30,42 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; import de.gematik.ti.epa.vzd.client.invoker.Pair; -import java.util.List; -import java.util.Map; + import okhttp3.Credentials; +import java.util.Map; +import java.util.List; + +import java.io.UnsupportedEncodingException; + public class HttpBasicAuth implements Authentication { - private String username; - private String password; + private String username; + private String password; - public String getUsername() { - return username; - } + public String getUsername() { + return username; + } - public void setUsername(String username) { - this.username = username; - } + public void setUsername(String username) { + this.username = username; + } - public String getPassword() { - return password; - } + public String getPassword() { + return password; + } - public void setPassword(String password) { - this.password = password; - } + public void setPassword(String password) { + this.password = password; + } - @Override - public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { - if (username == null && password == null) { - return; + @Override + public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { + if (username == null && password == null) { + return; + } + headerParams.put("Authorization", Credentials.basic( + username == null ? "" : username, + password == null ? "" : password)); } - headerParams.put("Authorization", Credentials.basic( - username == null ? "" : username, - password == null ? "" : password)); - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBearerAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBearerAuth.java index 6cccd8c..682e788 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBearerAuth.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBearerAuth.java @@ -30,47 +30,48 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; import de.gematik.ti.epa.vzd.client.invoker.Pair; -import java.util.List; + import java.util.Map; +import java.util.List; public class HttpBearerAuth implements Authentication { - private final String scheme; - private String bearerToken; + private final String scheme; + private String bearerToken; - public HttpBearerAuth(String scheme) { - this.scheme = scheme; - } - - /** - * Gets the token, which together with the scheme, will be sent as the value of the Authorization header. - * - * @return The bearer token - */ - public String getBearerToken() { - return bearerToken; - } + public HttpBearerAuth(String scheme) { + this.scheme = scheme; + } - /** - * Sets the token, which together with the scheme, will be sent as the value of the Authorization header. - * - * @param bearerToken The bearer token to send in the Authorization header - */ - public void setBearerToken(String bearerToken) { - this.bearerToken = bearerToken; - } + /** + * Gets the token, which together with the scheme, will be sent as the value of the Authorization header. + * + * @return The bearer token + */ + public String getBearerToken() { + return bearerToken; + } - @Override - public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { - if (bearerToken == null) { - return; + /** + * Sets the token, which together with the scheme, will be sent as the value of the Authorization header. + * + * @param bearerToken The bearer token to send in the Authorization header + */ + public void setBearerToken(String bearerToken) { + this.bearerToken = bearerToken; } - headerParams.put("Authorization", (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken); - } + @Override + public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { + if (bearerToken == null) { + return; + } + + headerParams.put("Authorization", (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken); + } - private static String upperCaseBearer(String scheme) { - return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme; - } + private static String upperCaseBearer(String scheme) { + return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuth.java index 95d30ee..4eca415 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuth.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuth.java @@ -30,26 +30,27 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; import de.gematik.ti.epa.vzd.client.invoker.Pair; -import java.util.List; + import java.util.Map; +import java.util.List; public class OAuth implements Authentication { - private String accessToken; + private String accessToken; - public String getAccessToken() { - return accessToken; - } + public String getAccessToken() { + return accessToken; + } - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } - @Override - public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { - if (accessToken != null) { - headerParams.put("Authorization", "Bearer " + accessToken); + @Override + public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { + if (accessToken != null) { + headerParams.put("Authorization", "Bearer " + accessToken); + } } - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthFlow.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthFlow.java index 1db3f44..7936549 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthFlow.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthFlow.java @@ -30,5 +30,5 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; public enum OAuthFlow { - accessCode, implicit, password, application + accessCode, implicit, password, application } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthOkHttpClient.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthOkHttpClient.java index fb165f8..f28399e 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthOkHttpClient.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthOkHttpClient.java @@ -16,14 +16,12 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; -import java.io.IOException; -import java.util.Map; -import java.util.Map.Entry; -import okhttp3.MediaType; import okhttp3.OkHttpClient; +import okhttp3.MediaType; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; + import org.apache.oltu.oauth2.client.HttpClient; import org.apache.oltu.oauth2.client.request.OAuthClientRequest; import org.apache.oltu.oauth2.client.response.OAuthClientResponse; @@ -31,54 +29,58 @@ import org.apache.oltu.oauth2.common.exception.OAuthProblemException; import org.apache.oltu.oauth2.common.exception.OAuthSystemException; +import java.io.IOException; +import java.util.Map; +import java.util.Map.Entry; + public class OAuthOkHttpClient implements HttpClient { - private final OkHttpClient client; + private OkHttpClient client; - public OAuthOkHttpClient() { - this.client = new OkHttpClient(); - } + public OAuthOkHttpClient() { + this.client = new OkHttpClient(); + } - public OAuthOkHttpClient(OkHttpClient client) { - this.client = client; - } + public OAuthOkHttpClient(OkHttpClient client) { + this.client = client; + } - @Override - public <T extends OAuthClientResponse> T execute(OAuthClientRequest request, Map<String, String> headers, - String requestMethod, Class<T> responseClass) - throws OAuthSystemException, OAuthProblemException { + @Override + public <T extends OAuthClientResponse> T execute(OAuthClientRequest request, Map<String, String> headers, + String requestMethod, Class<T> responseClass) + throws OAuthSystemException, OAuthProblemException { - MediaType mediaType = MediaType.parse("application/json"); - Request.Builder requestBuilder = new Request.Builder().url(request.getLocationUri()); + MediaType mediaType = MediaType.parse("application/json"); + Request.Builder requestBuilder = new Request.Builder().url(request.getLocationUri()); - if (headers != null) { - for (Entry<String, String> entry : headers.entrySet()) { - if (entry.getKey().equalsIgnoreCase("Content-Type")) { - mediaType = MediaType.parse(entry.getValue()); - } else { - requestBuilder.addHeader(entry.getKey(), entry.getValue()); + if (headers != null) { + for (Entry<String, String> entry : headers.entrySet()) { + if (entry.getKey().equalsIgnoreCase("Content-Type")) { + mediaType = MediaType.parse(entry.getValue()); + } else { + requestBuilder.addHeader(entry.getKey(), entry.getValue()); + } + } } - } - } - RequestBody body = request.getBody() != null ? RequestBody.create(mediaType, request.getBody()) : null; - requestBuilder.method(requestMethod, body); + RequestBody body = request.getBody() != null ? RequestBody.create(mediaType, request.getBody()) : null; + requestBuilder.method(requestMethod, body); - try { - Response response = client.newCall(requestBuilder.build()).execute(); - return OAuthClientResponseFactory.createCustomResponse( - response.body().string(), - response.body().contentType().toString(), - response.code(), - response.headers().toMultimap(), - responseClass); - } catch (IOException e) { - throw new OAuthSystemException(e); + try { + Response response = client.newCall(requestBuilder.build()).execute(); + return OAuthClientResponseFactory.createCustomResponse( + response.body().string(), + response.body().contentType().toString(), + response.code(), + response.headers().toMultimap(), + responseClass); + } catch (IOException e) { + throw new OAuthSystemException(e); + } } - } - @Override - public void shutdown() { - // Nothing to do here - } + @Override + public void shutdown() { + // Nothing to do here + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/RetryingOAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/RetryingOAuth.java index 14b6f55..fa3acea 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/RetryingOAuth.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/RetryingOAuth.java @@ -17,14 +17,12 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; import de.gematik.ti.epa.vzd.client.invoker.Pair; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.util.List; -import java.util.Map; + import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; + import org.apache.oltu.oauth2.client.OAuthClient; import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest; import org.apache.oltu.oauth2.client.request.OAuthClientRequest; @@ -34,154 +32,159 @@ import org.apache.oltu.oauth2.common.exception.OAuthSystemException; import org.apache.oltu.oauth2.common.message.types.GrantType; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.Map; +import java.util.List; + public class RetryingOAuth extends OAuth implements Interceptor { - private final OAuthClient oAuthClient; - - private TokenRequestBuilder tokenRequestBuilder; - - public RetryingOAuth(OkHttpClient client, TokenRequestBuilder tokenRequestBuilder) { - this.oAuthClient = new OAuthClient(new OAuthOkHttpClient(client)); - this.tokenRequestBuilder = tokenRequestBuilder; - } - - public RetryingOAuth(TokenRequestBuilder tokenRequestBuilder) { - this(new OkHttpClient(), tokenRequestBuilder); - } - - public RetryingOAuth( - String tokenUrl, - String clientId, - OAuthFlow flow, - String clientSecret, - Map<String, String> parameters - ) { - this(OAuthClientRequest.tokenLocation(tokenUrl) - .setClientId(clientId) - .setClientSecret(clientSecret)); - setFlow(flow); - if (parameters != null) { - for (String paramName : parameters.keySet()) { - tokenRequestBuilder.setParameter(paramName, parameters.get(paramName)); - } - } - } - - public void setFlow(OAuthFlow flow) { - switch (flow) { - case accessCode: - tokenRequestBuilder.setGrantType(GrantType.AUTHORIZATION_CODE); - break; - case implicit: - tokenRequestBuilder.setGrantType(GrantType.IMPLICIT); - break; - case password: - tokenRequestBuilder.setGrantType(GrantType.PASSWORD); - break; - case application: - tokenRequestBuilder.setGrantType(GrantType.CLIENT_CREDENTIALS); - break; - default: - break; - } - } + private OAuthClient oAuthClient; - @Override - public Response intercept(Chain chain) throws IOException { - return retryingIntercept(chain, true); - } + private TokenRequestBuilder tokenRequestBuilder; - private Response retryingIntercept(Chain chain, boolean updateTokenAndRetryOnAuthorizationFailure) throws IOException { - Request request = chain.request(); + public RetryingOAuth(OkHttpClient client, TokenRequestBuilder tokenRequestBuilder) { + this.oAuthClient = new OAuthClient(new OAuthOkHttpClient(client)); + this.tokenRequestBuilder = tokenRequestBuilder; + } - // If the request already has an authorization (e.g. Basic auth), proceed with the request as is - if (request.header("Authorization") != null) { - return chain.proceed(request); + public RetryingOAuth(TokenRequestBuilder tokenRequestBuilder) { + this(new OkHttpClient(), tokenRequestBuilder); } - // Get the token if it has not yet been acquired - if (getAccessToken() == null) { - updateAccessToken(null); + public RetryingOAuth( + String tokenUrl, + String clientId, + OAuthFlow flow, + String clientSecret, + Map<String, String> parameters + ) { + this(OAuthClientRequest.tokenLocation(tokenUrl) + .setClientId(clientId) + .setClientSecret(clientSecret)); + setFlow(flow); + if (parameters != null) { + for (String paramName : parameters.keySet()) { + tokenRequestBuilder.setParameter(paramName, parameters.get(paramName)); + } + } } - OAuthClientRequest oAuthRequest; - if (getAccessToken() != null) { - // Build the request - Request.Builder requestBuilder = request.newBuilder(); - - String requestAccessToken = getAccessToken(); - try { - oAuthRequest = - new OAuthBearerClientRequest(request.url().toString()). - setAccessToken(requestAccessToken). - buildHeaderMessage(); - } catch (OAuthSystemException e) { - throw new IOException(e); - } - - Map<String, String> headers = oAuthRequest.getHeaders(); - for (String headerName : headers.keySet()) { - requestBuilder.addHeader(headerName, headers.get(headerName)); - } - requestBuilder.url(oAuthRequest.getLocationUri()); - - // Execute the request - Response response = chain.proceed(requestBuilder.build()); - - // 401/403 response codes most likely indicate an expired access token, unless it happens two times in a row - if ( - response != null && - (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED || - response.code() == HttpURLConnection.HTTP_FORBIDDEN) && - updateTokenAndRetryOnAuthorizationFailure - ) { - try { - if (updateAccessToken(requestAccessToken)) { - response.body().close(); - return retryingIntercept(chain, false); - } - } catch (Exception e) { - response.body().close(); - throw e; + public void setFlow(OAuthFlow flow) { + switch (flow) { + case accessCode: + tokenRequestBuilder.setGrantType(GrantType.AUTHORIZATION_CODE); + break; + case implicit: + tokenRequestBuilder.setGrantType(GrantType.IMPLICIT); + break; + case password: + tokenRequestBuilder.setGrantType(GrantType.PASSWORD); + break; + case application: + tokenRequestBuilder.setGrantType(GrantType.CLIENT_CREDENTIALS); + break; + default: + break; } - } - return response; - } else { - return chain.proceed(chain.request()); } - } - - /* - * Returns true if the access token has been updated - */ - public synchronized boolean updateAccessToken(String requestAccessToken) throws IOException { - if (getAccessToken() == null || getAccessToken().equals(requestAccessToken)) { - try { - OAuthJSONAccessTokenResponse accessTokenResponse = - oAuthClient.accessToken(tokenRequestBuilder.buildBodyMessage()); - if (accessTokenResponse != null && accessTokenResponse.getAccessToken() != null) { - setAccessToken(accessTokenResponse.getAccessToken()); - return !getAccessToken().equals(requestAccessToken); + + @Override + public Response intercept(Chain chain) throws IOException { + return retryingIntercept(chain, true); + } + + private Response retryingIntercept(Chain chain, boolean updateTokenAndRetryOnAuthorizationFailure) throws IOException { + Request request = chain.request(); + + // If the request already has an authorization (e.g. Basic auth), proceed with the request as is + if (request.header("Authorization") != null) { + return chain.proceed(request); + } + + // Get the token if it has not yet been acquired + if (getAccessToken() == null) { + updateAccessToken(null); + } + + OAuthClientRequest oAuthRequest; + if (getAccessToken() != null) { + // Build the request + Request.Builder requestBuilder = request.newBuilder(); + + String requestAccessToken = getAccessToken(); + try { + oAuthRequest = + new OAuthBearerClientRequest(request.url().toString()). + setAccessToken(requestAccessToken). + buildHeaderMessage(); + } catch (OAuthSystemException e) { + throw new IOException(e); + } + + Map<String, String> headers = oAuthRequest.getHeaders(); + for (String headerName : headers.keySet()) { + requestBuilder.addHeader(headerName, headers.get(headerName)); + } + requestBuilder.url(oAuthRequest.getLocationUri()); + + // Execute the request + Response response = chain.proceed(requestBuilder.build()); + + // 401/403 response codes most likely indicate an expired access token, unless it happens two times in a row + if ( + response != null && + (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED || + response.code() == HttpURLConnection.HTTP_FORBIDDEN) && + updateTokenAndRetryOnAuthorizationFailure + ) { + try { + if (updateAccessToken(requestAccessToken)) { + response.body().close(); + return retryingIntercept(chain, false); + } + } catch (Exception e) { + response.body().close(); + throw e; + } + } + return response; + } else { + return chain.proceed(chain.request()); } - } catch (OAuthSystemException | OAuthProblemException e) { - throw new IOException(e); - } } - return false; - } + /* + * Returns true if the access token has been updated + */ + public synchronized boolean updateAccessToken(String requestAccessToken) throws IOException { + if (getAccessToken() == null || getAccessToken().equals(requestAccessToken)) { + try { + OAuthJSONAccessTokenResponse accessTokenResponse = + oAuthClient.accessToken(tokenRequestBuilder.buildBodyMessage()); + if (accessTokenResponse != null && accessTokenResponse.getAccessToken() != null) { + setAccessToken(accessTokenResponse.getAccessToken()); + return !getAccessToken().equals(requestAccessToken); + } + } catch (OAuthSystemException | OAuthProblemException e) { + throw new IOException(e); + } + } - public TokenRequestBuilder getTokenRequestBuilder() { - return tokenRequestBuilder; - } + return false; + } - public void setTokenRequestBuilder(TokenRequestBuilder tokenRequestBuilder) { - this.tokenRequestBuilder = tokenRequestBuilder; - } + public TokenRequestBuilder getTokenRequestBuilder() { + return tokenRequestBuilder; + } - // Applying authorization to parameters is performed in the retryingIntercept method - @Override - public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { - // No implementation necessary - } + public void setTokenRequestBuilder(TokenRequestBuilder tokenRequestBuilder) { + this.tokenRequestBuilder = tokenRequestBuilder; + } + + // Applying authorization to parameters is performed in the retryingIntercept method + @Override + public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams, Map<String, String> cookieParams) { + // No implementation necessary + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntry.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntry.java index 77908a5..492fc16 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntry.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntry.java @@ -29,11 +29,19 @@ package de.gematik.ti.epa.vzd.client.model; +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * BaseDirectoryEntry @@ -41,491 +49,491 @@ public class BaseDirectoryEntry { - public static final String SERIALIZED_NAME_DN = "dn"; - @SerializedName(SERIALIZED_NAME_DN) - private DistinguishedName dn; + public static final String SERIALIZED_NAME_DN = "dn"; + @SerializedName(SERIALIZED_NAME_DN) + private DistinguishedName dn; - public static final String SERIALIZED_NAME_GIVEN_NAME = "givenName"; - @SerializedName(SERIALIZED_NAME_GIVEN_NAME) - private String givenName; + public static final String SERIALIZED_NAME_GIVEN_NAME = "givenName"; + @SerializedName(SERIALIZED_NAME_GIVEN_NAME) + private String givenName; - public static final String SERIALIZED_NAME_SN = "sn"; - @SerializedName(SERIALIZED_NAME_SN) - private String sn; + public static final String SERIALIZED_NAME_SN = "sn"; + @SerializedName(SERIALIZED_NAME_SN) + private String sn; - public static final String SERIALIZED_NAME_CN = "cn"; - @SerializedName(SERIALIZED_NAME_CN) - private String cn; + public static final String SERIALIZED_NAME_CN = "cn"; + @SerializedName(SERIALIZED_NAME_CN) + private String cn; - public static final String SERIALIZED_NAME_DISPLAY_NAME = "displayName"; - @SerializedName(SERIALIZED_NAME_DISPLAY_NAME) - private String displayName; + public static final String SERIALIZED_NAME_DISPLAY_NAME = "displayName"; + @SerializedName(SERIALIZED_NAME_DISPLAY_NAME) + private String displayName; - public static final String SERIALIZED_NAME_STREET_ADDRESS = "streetAddress"; - @SerializedName(SERIALIZED_NAME_STREET_ADDRESS) - private String streetAddress; + public static final String SERIALIZED_NAME_STREET_ADDRESS = "streetAddress"; + @SerializedName(SERIALIZED_NAME_STREET_ADDRESS) + private String streetAddress; - public static final String SERIALIZED_NAME_POSTAL_CODE = "postalCode"; - @SerializedName(SERIALIZED_NAME_POSTAL_CODE) - private String postalCode; + public static final String SERIALIZED_NAME_POSTAL_CODE = "postalCode"; + @SerializedName(SERIALIZED_NAME_POSTAL_CODE) + private String postalCode; - public static final String SERIALIZED_NAME_LOCALITY_NAME = "localityName"; - @SerializedName(SERIALIZED_NAME_LOCALITY_NAME) - private String localityName; + public static final String SERIALIZED_NAME_LOCALITY_NAME = "localityName"; + @SerializedName(SERIALIZED_NAME_LOCALITY_NAME) + private String localityName; - public static final String SERIALIZED_NAME_STATE_OR_PROVINCE_NAME = "stateOrProvinceName"; - @SerializedName(SERIALIZED_NAME_STATE_OR_PROVINCE_NAME) - private String stateOrProvinceName; + public static final String SERIALIZED_NAME_STATE_OR_PROVINCE_NAME = "stateOrProvinceName"; + @SerializedName(SERIALIZED_NAME_STATE_OR_PROVINCE_NAME) + private String stateOrProvinceName; - public static final String SERIALIZED_NAME_TITLE = "title"; - @SerializedName(SERIALIZED_NAME_TITLE) - private String title; + public static final String SERIALIZED_NAME_TITLE = "title"; + @SerializedName(SERIALIZED_NAME_TITLE) + private String title; - public static final String SERIALIZED_NAME_ORGANIZATION = "organization"; - @SerializedName(SERIALIZED_NAME_ORGANIZATION) - private String organization; + public static final String SERIALIZED_NAME_ORGANIZATION = "organization"; + @SerializedName(SERIALIZED_NAME_ORGANIZATION) + private String organization; - public static final String SERIALIZED_NAME_OTHER_NAME = "otherName"; - @SerializedName(SERIALIZED_NAME_OTHER_NAME) - private String otherName; + public static final String SERIALIZED_NAME_OTHER_NAME = "otherName"; + @SerializedName(SERIALIZED_NAME_OTHER_NAME) + private String otherName; - public static final String SERIALIZED_NAME_SPECIALIZATION = "specialization"; - @SerializedName(SERIALIZED_NAME_SPECIALIZATION) - private List<String> specialization = null; + public static final String SERIALIZED_NAME_SPECIALIZATION = "specialization"; + @SerializedName(SERIALIZED_NAME_SPECIALIZATION) + private List<String> specialization = null; - public static final String SERIALIZED_NAME_DOMAIN_I_D = "domainID"; - @SerializedName(SERIALIZED_NAME_DOMAIN_I_D) - private List<String> domainID = null; + public static final String SERIALIZED_NAME_DOMAIN_I_D = "domainID"; + @SerializedName(SERIALIZED_NAME_DOMAIN_I_D) + private List<String> domainID = null; - public static final String SERIALIZED_NAME_PERSONAL_ENTRY = "personalEntry"; - @SerializedName(SERIALIZED_NAME_PERSONAL_ENTRY) - private Boolean personalEntry; + public static final String SERIALIZED_NAME_PERSONAL_ENTRY = "personalEntry"; + @SerializedName(SERIALIZED_NAME_PERSONAL_ENTRY) + private Boolean personalEntry; - public static final String SERIALIZED_NAME_DATA_FROM_AUTHORITY = "dataFromAuthority"; - @SerializedName(SERIALIZED_NAME_DATA_FROM_AUTHORITY) - private Boolean dataFromAuthority; + public static final String SERIALIZED_NAME_DATA_FROM_AUTHORITY = "dataFromAuthority"; + @SerializedName(SERIALIZED_NAME_DATA_FROM_AUTHORITY) + private Boolean dataFromAuthority; - public BaseDirectoryEntry dn(DistinguishedName dn) { + public BaseDirectoryEntry dn(DistinguishedName dn) { - this.dn = dn; - return this; - } + this.dn = dn; + return this; + } - /** - * Get dn - * - * @return dn - **/ - @ApiModelProperty(required = true, value = "") + /** + * Get dn + * + * @return dn + **/ + @ApiModelProperty(required = true, value = "") - public DistinguishedName getDn() { - return dn; - } + public DistinguishedName getDn() { + return dn; + } - public void setDn(DistinguishedName dn) { - this.dn = dn; - } + public void setDn(DistinguishedName dn) { + this.dn = dn; + } - /** - * HBA: Vorname, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet - * - * @return givenName - **/ - @javax.annotation.Nullable - @ApiModelProperty(example = "Vorname", value = "HBA: Vorname, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet") + /** + * HBA: Vorname, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet + * + * @return givenName + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "Vorname", value = "HBA: Vorname, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet") - public String getGivenName() { - return givenName; - } + public String getGivenName() { + return givenName; + } - /** - * HBA: Name, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet - * - * @return sn - **/ - @javax.annotation.Nullable - @ApiModelProperty(example = "Nachname", value = "HBA: Name, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet") + /** + * HBA: Name, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet + * + * @return sn + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "Nachname", value = "HBA: Name, obligatorisch, wird aus dem Zertifikat übernommen / SMC-B: nicht verwendet") - public String getSn() { - return sn; - } + public String getSn() { + return sn; + } - /** - * HBA: Vorname und Nachname / SMC-B: Bezeichner: Name Wird vom VZD aus dem Zertifikatsattribut commonName übernommen. - * - * @return cn - **/ - @ApiModelProperty(example = "Vorname Nachname", required = true, value = "HBA: Vorname und Nachname / SMC-B: Bezeichner: Name Wird vom VZD aus dem Zertifikatsattribut commonName übernommen.") + /** + * HBA: Vorname und Nachname / SMC-B: Bezeichner: Name Wird vom VZD aus dem Zertifikatsattribut commonName übernommen. + * + * @return cn + **/ + @ApiModelProperty(example = "Vorname Nachname", required = true, value = "HBA: Vorname und Nachname / SMC-B: Bezeichner: Name Wird vom VZD aus dem Zertifikatsattribut commonName übernommen.") - public String getCn() { - return cn; - } + public String getCn() { + return cn; + } - public void setCn(String cn) { - this.cn = cn; - } + public void setCn(String cn) { + this.cn = cn; + } - public BaseDirectoryEntry displayName(String displayName) { + public BaseDirectoryEntry displayName(String displayName) { - this.displayName = displayName; - return this; - } + this.displayName = displayName; + return this; + } - /** - * Anzeigename, kann geändert werden. Dieses Attribut wird genutzt um den Namen der Organisation gegenüber dem Anwender darzustellen (Verwendung als - * Filter-Attribut um die Suche einzuschränken und bei der Darstellung des Ergebnisses). Der Wert wird von der pflegenden Stelle festgelegt. - * - * @return displayName - **/ - @javax.annotation.Nullable - @ApiModelProperty(example = "Vorname Nachname", value = "Anzeigename, kann geändert werden. Dieses Attribut wird genutzt um den Namen der Organisation gegenüber dem Anwender darzustellen (Verwendung als Filter-Attribut um die Suche einzuschränken und bei der Darstellung des Ergebnisses). Der Wert wird von der pflegenden Stelle festgelegt.") + /** + * Anzeigename, kann geändert werden. Dieses Attribut wird genutzt um den Namen der Organisation gegenüber dem Anwender darzustellen (Verwendung + * als Filter-Attribut um die Suche einzuschränken und bei der Darstellung des Ergebnisses). Der Wert wird von der pflegenden Stelle festgelegt. + * + * @return displayName + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "Vorname Nachname", value = "Anzeigename, kann geändert werden. Dieses Attribut wird genutzt um den Namen der Organisation gegenüber dem Anwender darzustellen (Verwendung als Filter-Attribut um die Suche einzuschränken und bei der Darstellung des Ergebnisses). Der Wert wird von der pflegenden Stelle festgelegt.") + + public String getDisplayName() { + return displayName; + } - public String getDisplayName() { - return displayName; - } + public void setDisplayName(String displayName) { + this.displayName = displayName; + } - public void setDisplayName(String displayName) { - this.displayName = displayName; - } + public BaseDirectoryEntry streetAddress(String streetAddress) { - public BaseDirectoryEntry streetAddress(String streetAddress) { + this.streetAddress = streetAddress; + return this; + } - this.streetAddress = streetAddress; - return this; - } + /** + * Straße und Hausnummer Der Wert wird von der pflegenden Stelle festgelegt + * + * @return streetAddress + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "Friedrichstraße 136", value = "Straße und Hausnummer Der Wert wird von der pflegenden Stelle festgelegt") - /** - * Straße und Hausnummer Der Wert wird von der pflegenden Stelle festgelegt - * - * @return streetAddress - **/ - @javax.annotation.Nullable - @ApiModelProperty(example = "Friedrichstraße 136", value = "Straße und Hausnummer Der Wert wird von der pflegenden Stelle festgelegt") + public String getStreetAddress() { + return streetAddress; + } - public String getStreetAddress() { - return streetAddress; - } + public void setStreetAddress(String streetAddress) { + this.streetAddress = streetAddress; + } - public void setStreetAddress(String streetAddress) { - this.streetAddress = streetAddress; - } + public BaseDirectoryEntry postalCode(String postalCode) { - public BaseDirectoryEntry postalCode(String postalCode) { + this.postalCode = postalCode; + return this; + } - this.postalCode = postalCode; - return this; - } + /** + * Postleitzahl Der Wert wird von der pflegenden Stelle festgelegt + * + * @return postalCode + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "10117", value = "Postleitzahl Der Wert wird von der pflegenden Stelle festgelegt") - /** - * Postleitzahl Der Wert wird von der pflegenden Stelle festgelegt - * - * @return postalCode - **/ - @javax.annotation.Nullable - @ApiModelProperty(example = "10117", value = "Postleitzahl Der Wert wird von der pflegenden Stelle festgelegt") + public String getPostalCode() { + return postalCode; + } - public String getPostalCode() { - return postalCode; - } + public void setPostalCode(String postalCode) { + this.postalCode = postalCode; + } - public void setPostalCode(String postalCode) { - this.postalCode = postalCode; - } + public BaseDirectoryEntry localityName(String localityName) { - public BaseDirectoryEntry localityName(String localityName) { + this.localityName = localityName; + return this; + } - this.localityName = localityName; - return this; - } + /** + * Ort Der Wert wird von der pflegenden Stelle festgelegt + * + * @return localityName + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "Berlin", value = "Ort Der Wert wird von der pflegenden Stelle festgelegt") - /** - * Ort Der Wert wird von der pflegenden Stelle festgelegt - * - * @return localityName - **/ - @javax.annotation.Nullable - @ApiModelProperty(example = "Berlin", value = "Ort Der Wert wird von der pflegenden Stelle festgelegt") + public String getLocalityName() { + return localityName; + } - public String getLocalityName() { - return localityName; - } + public void setLocalityName(String localityName) { + this.localityName = localityName; + } - public void setLocalityName(String localityName) { - this.localityName = localityName; - } + public BaseDirectoryEntry stateOrProvinceName(String stateOrProvinceName) { - public BaseDirectoryEntry stateOrProvinceName(String stateOrProvinceName) { + this.stateOrProvinceName = stateOrProvinceName; + return this; + } - this.stateOrProvinceName = stateOrProvinceName; - return this; - } + /** + * Bundesland Der Wert wird von der pflegenden Stelle festgelegt + * + * @return stateOrProvinceName + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "Berlin", value = "Bundesland Der Wert wird von der pflegenden Stelle festgelegt") - /** - * Bundesland Der Wert wird von der pflegenden Stelle festgelegt - * - * @return stateOrProvinceName - **/ - @javax.annotation.Nullable - @ApiModelProperty(example = "Berlin", value = "Bundesland Der Wert wird von der pflegenden Stelle festgelegt") + public String getStateOrProvinceName() { + return stateOrProvinceName; + } - public String getStateOrProvinceName() { - return stateOrProvinceName; - } + public void setStateOrProvinceName(String stateOrProvinceName) { + this.stateOrProvinceName = stateOrProvinceName; + } - public void setStateOrProvinceName(String stateOrProvinceName) { - this.stateOrProvinceName = stateOrProvinceName; - } + public BaseDirectoryEntry title(String title) { - public BaseDirectoryEntry title(String title) { + this.title = title; + return this; + } - this.title = title; - return this; - } + /** + * HBA: Titel, optional / SMC-B: nicht verwendet + * + * @return title + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "HBA: Titel, optional / SMC-B: nicht verwendet") - /** - * HBA: Titel, optional / SMC-B: nicht verwendet - * - * @return title - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "HBA: Titel, optional / SMC-B: nicht verwendet") + public String getTitle() { + return title; + } - public String getTitle() { - return title; - } + public void setTitle(String title) { + this.title = title; + } - public void setTitle(String title) { - this.title = title; - } + public BaseDirectoryEntry organization(String organization) { - public BaseDirectoryEntry organization(String organization) { + this.organization = organization; + return this; + } - this.organization = organization; - return this; - } + /** + * Organisation Der Wert wird von der pflegenden Stelle festgelegt + * + * @return organization + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "12345670", value = "Organisation Der Wert wird von der pflegenden Stelle festgelegt") - /** - * Organisation Der Wert wird von der pflegenden Stelle festgelegt - * - * @return organization - **/ - @javax.annotation.Nullable - @ApiModelProperty(example = "12345670", value = "Organisation Der Wert wird von der pflegenden Stelle festgelegt") + public String getOrganization() { + return organization; + } - public String getOrganization() { - return organization; - } + public void setOrganization(String organization) { + this.organization = organization; + } - public void setOrganization(String organization) { - this.organization = organization; - } + public BaseDirectoryEntry otherName(String otherName) { - public BaseDirectoryEntry otherName(String otherName) { + this.otherName = otherName; + return this; + } - this.otherName = otherName; - return this; - } + /** + * Anderer Name. Wird vom VZD aus dem Zertifikatsattribut otherName übernommen. + * + * @return otherName + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Anderer Name. Wird vom VZD aus dem Zertifikatsattribut otherName übernommen.") - /** - * Anderer Name. Wird vom VZD aus dem Zertifikatsattribut otherName übernommen. - * - * @return otherName - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "Anderer Name. Wird vom VZD aus dem Zertifikatsattribut otherName übernommen.") + public String getOtherName() { + return otherName; + } - public String getOtherName() { - return otherName; - } + public void setOtherName(String otherName) { + this.otherName = otherName; + } - public void setOtherName(String otherName) { - this.otherName = otherName; - } + public BaseDirectoryEntry specialization(List<String> specialization) { - public BaseDirectoryEntry specialization(List<String> specialization) { + this.specialization = specialization; + return this; + } + + public BaseDirectoryEntry addSpecializationItem(String specializationItem) { + if (this.specialization == null) { + this.specialization = new ArrayList<>(); + } + this.specialization.add(specializationItem); + return this; + } + + /** + * Fachgebiet Der Wert wird von der pflegenden Stelle festgelegt + * + * @return specialization + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Fachgebiet Der Wert wird von der pflegenden Stelle festgelegt") + + public List<String> getSpecialization() { + return specialization; + } + + + public void setSpecialization(List<String> specialization) { + this.specialization = specialization; + } + + + public BaseDirectoryEntry domainID(List<String> domainID) { + + this.domainID = domainID; + return this; + } + + public BaseDirectoryEntry addDomainIDItem(String domainIDItem) { + if (this.domainID == null) { + this.domainID = new ArrayList<>(); + } + this.domainID.add(domainIDItem); + return this; + } + + /** + * Ärzte: Betriebsstättennummer Der Wert wird aus dem Zertifikat übernommen (Attribut organizationName) + * + * @return domainID + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Ärzte: Betriebsstättennummer Der Wert wird aus dem Zertifikat übernommen (Attribut organizationName)") - this.specialization = specialization; - return this; - } + public List<String> getDomainID() { + return domainID; + } + + + public void setDomainID(List<String> domainID) { + this.domainID = domainID; + } + + + /** + * Wird vom VZD eingetragen / Wert == TRUE, wenn alle Zertifikate den entryType 1 haben (Berufsgruppe), Wert == FALSE sonst + * + * @return personalEntry + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "true", value = "Wird vom VZD eingetragen / Wert == TRUE, wenn alle Zertifikate den entryType 1 haben (Berufsgruppe), Wert == FALSE sonst") + + public Boolean getPersonalEntry() { + return personalEntry; + } - public BaseDirectoryEntry addSpecializationItem(String specializationItem) { - if (this.specialization == null) { - this.specialization = new ArrayList<>(); + + /** + * Wird vom VZD eingetragen / Wert == TRUE, wenn der Verzeichnisdienst_Eintrag von dem Kartenherausgeber geschrieben wurde, Wert + * == FALSE sonst + * + * @return dataFromAuthority + **/ + @javax.annotation.Nullable + @ApiModelProperty(example = "true", value = "Wird vom VZD eingetragen / Wert == TRUE, wenn der Verzeichnisdienst_Eintrag von dem Kartenherausgeber geschrieben wurde, Wert == FALSE sonst") + + public Boolean getDataFromAuthority() { + return dataFromAuthority; + } + + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BaseDirectoryEntry baseDirectoryEntry = (BaseDirectoryEntry) o; + return Objects.equals(this.dn, baseDirectoryEntry.dn) && + Objects.equals(this.givenName, baseDirectoryEntry.givenName) && + Objects.equals(this.sn, baseDirectoryEntry.sn) && + Objects.equals(this.cn, baseDirectoryEntry.cn) && + Objects.equals(this.displayName, baseDirectoryEntry.displayName) && + Objects.equals(this.streetAddress, baseDirectoryEntry.streetAddress) && + Objects.equals(this.postalCode, baseDirectoryEntry.postalCode) && + Objects.equals(this.localityName, baseDirectoryEntry.localityName) && + Objects.equals(this.stateOrProvinceName, baseDirectoryEntry.stateOrProvinceName) && + Objects.equals(this.title, baseDirectoryEntry.title) && + Objects.equals(this.organization, baseDirectoryEntry.organization) && + Objects.equals(this.otherName, baseDirectoryEntry.otherName) && + Objects.equals(this.specialization, baseDirectoryEntry.specialization) && + Objects.equals(this.domainID, baseDirectoryEntry.domainID) && + Objects.equals(this.personalEntry, baseDirectoryEntry.personalEntry) && + Objects.equals(this.dataFromAuthority, baseDirectoryEntry.dataFromAuthority); } - this.specialization.add(specializationItem); - return this; - } - /** - * Fachgebiet Der Wert wird von der pflegenden Stelle festgelegt - * - * @return specialization - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "Fachgebiet Der Wert wird von der pflegenden Stelle festgelegt") + @Override + public int hashCode() { + return Objects + .hash(dn, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, + stateOrProvinceName, title, organization, otherName, specialization, domainID, + personalEntry, dataFromAuthority); + } - public List<String> getSpecialization() { - return specialization; - } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class BaseDirectoryEntry {\n"); + sb.append(" dn: ").append(toIndentedString(dn)).append("\n"); + sb.append(" givenName: ").append(toIndentedString(givenName)).append("\n"); + sb.append(" sn: ").append(toIndentedString(sn)).append("\n"); + sb.append(" cn: ").append(toIndentedString(cn)).append("\n"); + sb.append(" displayName: ").append(toIndentedString(displayName)).append("\n"); + sb.append(" streetAddress: ").append(toIndentedString(streetAddress)).append("\n"); + sb.append(" postalCode: ").append(toIndentedString(postalCode)).append("\n"); + sb.append(" localityName: ").append(toIndentedString(localityName)).append("\n"); + sb.append(" stateOrProvinceName: ").append(toIndentedString(stateOrProvinceName)) + .append("\n"); + sb.append(" title: ").append(toIndentedString(title)).append("\n"); + sb.append(" organization: ").append(toIndentedString(organization)).append("\n"); + sb.append(" otherName: ").append(toIndentedString(otherName)).append("\n"); + sb.append(" specialization: ").append(toIndentedString(specialization)).append("\n"); + sb.append(" domainID: ").append(toIndentedString(domainID)).append("\n"); + sb.append(" personalEntry: ").append(toIndentedString(personalEntry)).append("\n"); + sb.append(" dataFromAuthority: ").append(toIndentedString(dataFromAuthority)) + .append("\n"); + sb.append("}"); + return sb.toString(); + } - public void setSpecialization(List<String> specialization) { - this.specialization = specialization; - } - - - public BaseDirectoryEntry domainID(List<String> domainID) { - - this.domainID = domainID; - return this; - } - - public BaseDirectoryEntry addDomainIDItem(String domainIDItem) { - if (this.domainID == null) { - this.domainID = new ArrayList<>(); - } - this.domainID.add(domainIDItem); - return this; - } - - /** - * Ärzte: Betriebsstättennummer Der Wert wird aus dem Zertifikat übernommen (Attribut organizationName) - * - * @return domainID - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "Ärzte: Betriebsstättennummer Der Wert wird aus dem Zertifikat übernommen (Attribut organizationName)") - - public List<String> getDomainID() { - return domainID; - } - - - public void setDomainID(List<String> domainID) { - this.domainID = domainID; - } - - - /** - * Wird vom VZD eingetragen / Wert == TRUE, wenn alle Zertifikate den entryType 1 haben (Berufsgruppe), Wert == FALSE sonst - * - * @return personalEntry - **/ - @javax.annotation.Nullable - @ApiModelProperty(example = "true", value = "Wird vom VZD eingetragen / Wert == TRUE, wenn alle Zertifikate den entryType 1 haben (Berufsgruppe), Wert == FALSE sonst") - - public Boolean getPersonalEntry() { - return personalEntry; - } - - - /** - * Wird vom VZD eingetragen / Wert == TRUE, wenn der Verzeichnisdienst_Eintrag von dem Kartenherausgeber geschrieben wurde, Wert - * == FALSE sonst - * - * @return dataFromAuthority - **/ - @javax.annotation.Nullable - @ApiModelProperty(example = "true", value = "Wird vom VZD eingetragen / Wert == TRUE, wenn der Verzeichnisdienst_Eintrag von dem Kartenherausgeber geschrieben wurde, Wert == FALSE sonst") - - public Boolean getDataFromAuthority() { - return dataFromAuthority; - } - - - @Override - public boolean equals(java.lang.Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - BaseDirectoryEntry baseDirectoryEntry = (BaseDirectoryEntry) o; - return Objects.equals(this.dn, baseDirectoryEntry.dn) && - Objects.equals(this.givenName, baseDirectoryEntry.givenName) && - Objects.equals(this.sn, baseDirectoryEntry.sn) && - Objects.equals(this.cn, baseDirectoryEntry.cn) && - Objects.equals(this.displayName, baseDirectoryEntry.displayName) && - Objects.equals(this.streetAddress, baseDirectoryEntry.streetAddress) && - Objects.equals(this.postalCode, baseDirectoryEntry.postalCode) && - Objects.equals(this.localityName, baseDirectoryEntry.localityName) && - Objects.equals(this.stateOrProvinceName, baseDirectoryEntry.stateOrProvinceName) && - Objects.equals(this.title, baseDirectoryEntry.title) && - Objects.equals(this.organization, baseDirectoryEntry.organization) && - Objects.equals(this.otherName, baseDirectoryEntry.otherName) && - Objects.equals(this.specialization, baseDirectoryEntry.specialization) && - Objects.equals(this.domainID, baseDirectoryEntry.domainID) && - Objects.equals(this.personalEntry, baseDirectoryEntry.personalEntry) && - Objects.equals(this.dataFromAuthority, baseDirectoryEntry.dataFromAuthority); - } - - @Override - public int hashCode() { - return Objects - .hash(dn, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, - stateOrProvinceName, title, organization, otherName, specialization, domainID, - personalEntry, dataFromAuthority); - } - - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class BaseDirectoryEntry {\n"); - sb.append(" dn: ").append(toIndentedString(dn)).append("\n"); - sb.append(" givenName: ").append(toIndentedString(givenName)).append("\n"); - sb.append(" sn: ").append(toIndentedString(sn)).append("\n"); - sb.append(" cn: ").append(toIndentedString(cn)).append("\n"); - sb.append(" displayName: ").append(toIndentedString(displayName)).append("\n"); - sb.append(" streetAddress: ").append(toIndentedString(streetAddress)).append("\n"); - sb.append(" postalCode: ").append(toIndentedString(postalCode)).append("\n"); - sb.append(" localityName: ").append(toIndentedString(localityName)).append("\n"); - sb.append(" stateOrProvinceName: ").append(toIndentedString(stateOrProvinceName)) - .append("\n"); - sb.append(" title: ").append(toIndentedString(title)).append("\n"); - sb.append(" organization: ").append(toIndentedString(organization)).append("\n"); - sb.append(" otherName: ").append(toIndentedString(otherName)).append("\n"); - sb.append(" specialization: ").append(toIndentedString(specialization)).append("\n"); - sb.append(" domainID: ").append(toIndentedString(domainID)).append("\n"); - sb.append(" personalEntry: ").append(toIndentedString(personalEntry)).append("\n"); - sb.append(" dataFromAuthority: ").append(toIndentedString(dataFromAuthority)) - .append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(java.lang.Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntry.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntry.java index 5a42fa2..ac69ca4 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntry.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntry.java @@ -29,11 +29,20 @@ package de.gematik.ti.epa.vzd.client.model; +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * CreateDirectoryEntry @@ -41,109 +50,109 @@ public class CreateDirectoryEntry { - public static final String SERIALIZED_NAME_DIRECTORY_ENTRY_BASE = "DirectoryEntryBase"; - @SerializedName(SERIALIZED_NAME_DIRECTORY_ENTRY_BASE) - private BaseDirectoryEntry directoryEntryBase; + public static final String SERIALIZED_NAME_DIRECTORY_ENTRY_BASE = "DirectoryEntryBase"; + @SerializedName(SERIALIZED_NAME_DIRECTORY_ENTRY_BASE) + private BaseDirectoryEntry directoryEntryBase; - public static final String SERIALIZED_NAME_USER_CERTIFICATES = "userCertificates"; - @SerializedName(SERIALIZED_NAME_USER_CERTIFICATES) - private List<UserCertificate> userCertificates = null; + public static final String SERIALIZED_NAME_USER_CERTIFICATES = "userCertificates"; + @SerializedName(SERIALIZED_NAME_USER_CERTIFICATES) + private List<UserCertificate> userCertificates = null; - public CreateDirectoryEntry directoryEntryBase(BaseDirectoryEntry directoryEntryBase) { + public CreateDirectoryEntry directoryEntryBase(BaseDirectoryEntry directoryEntryBase) { - this.directoryEntryBase = directoryEntryBase; - return this; - } + this.directoryEntryBase = directoryEntryBase; + return this; + } - /** - * Get directoryEntryBase - * - * @return directoryEntryBase - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "") + /** + * Get directoryEntryBase + * + * @return directoryEntryBase + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") - public BaseDirectoryEntry getDirectoryEntryBase() { - return directoryEntryBase; - } + public BaseDirectoryEntry getDirectoryEntryBase() { + return directoryEntryBase; + } - public void setDirectoryEntryBase(BaseDirectoryEntry directoryEntryBase) { - this.directoryEntryBase = directoryEntryBase; - } + public void setDirectoryEntryBase(BaseDirectoryEntry directoryEntryBase) { + this.directoryEntryBase = directoryEntryBase; + } - public CreateDirectoryEntry userCertificates(List<UserCertificate> userCertificates) { + public CreateDirectoryEntry userCertificates(List<UserCertificate> userCertificates) { - this.userCertificates = userCertificates; - return this; - } + this.userCertificates = userCertificates; + return this; + } - public CreateDirectoryEntry addUserCertificatesItem(UserCertificate userCertificatesItem) { - if (this.userCertificates == null) { - this.userCertificates = new ArrayList<>(); + public CreateDirectoryEntry addUserCertificatesItem(UserCertificate userCertificatesItem) { + if (this.userCertificates == null) { + this.userCertificates = new ArrayList<>(); + } + this.userCertificates.add(userCertificatesItem); + return this; } - this.userCertificates.add(userCertificatesItem); - return this; - } - /** - * Get userCertificates - * - * @return userCertificates - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "") + /** + * Get userCertificates + * + * @return userCertificates + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") - public List<UserCertificate> getUserCertificates() { - return userCertificates; - } + public List<UserCertificate> getUserCertificates() { + return userCertificates; + } - public void setUserCertificates(List<UserCertificate> userCertificates) { - this.userCertificates = userCertificates; - } + public void setUserCertificates(List<UserCertificate> userCertificates) { + this.userCertificates = userCertificates; + } + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CreateDirectoryEntry createDirectoryEntry = (CreateDirectoryEntry) o; + return Objects.equals(this.directoryEntryBase, createDirectoryEntry.directoryEntryBase) && + Objects.equals(this.userCertificates, createDirectoryEntry.userCertificates); + } - @Override - public boolean equals(java.lang.Object o) { - if (this == o) { - return true; + @Override + public int hashCode() { + return Objects.hash(directoryEntryBase, userCertificates); } - if (o == null || getClass() != o.getClass()) { - return false; + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CreateDirectoryEntry {\n"); + sb.append(" directoryEntryBase: ").append(toIndentedString(directoryEntryBase)).append("\n"); + sb.append(" userCertificates: ").append(toIndentedString(userCertificates)).append("\n"); + sb.append("}"); + return sb.toString(); } - CreateDirectoryEntry createDirectoryEntry = (CreateDirectoryEntry) o; - return Objects.equals(this.directoryEntryBase, createDirectoryEntry.directoryEntryBase) && - Objects.equals(this.userCertificates, createDirectoryEntry.userCertificates); - } - - @Override - public int hashCode() { - return Objects.hash(directoryEntryBase, userCertificates); - } - - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class CreateDirectoryEntry {\n"); - sb.append(" directoryEntryBase: ").append(toIndentedString(directoryEntryBase)).append("\n"); - sb.append(" userCertificates: ").append(toIndentedString(userCertificates)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(java.lang.Object o) { - if (o == null) { - return "null"; + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); } - return o.toString().replace("\n", "\n "); - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntry.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntry.java index 1e64fb5..f94e47f 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntry.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntry.java @@ -29,11 +29,21 @@ package de.gematik.ti.epa.vzd.client.model; +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.Fachdaten; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * DirectoryEntry @@ -41,147 +51,147 @@ public class DirectoryEntry { - public static final String SERIALIZED_NAME_DIRECTORY_ENTRY_BASE = "DirectoryEntryBase"; - @SerializedName(SERIALIZED_NAME_DIRECTORY_ENTRY_BASE) - private BaseDirectoryEntry directoryEntryBase; + public static final String SERIALIZED_NAME_DIRECTORY_ENTRY_BASE = "DirectoryEntryBase"; + @SerializedName(SERIALIZED_NAME_DIRECTORY_ENTRY_BASE) + private BaseDirectoryEntry directoryEntryBase; - public static final String SERIALIZED_NAME_USER_CERTIFICATES = "userCertificates"; - @SerializedName(SERIALIZED_NAME_USER_CERTIFICATES) - private List<UserCertificate> userCertificates = null; + public static final String SERIALIZED_NAME_USER_CERTIFICATES = "userCertificates"; + @SerializedName(SERIALIZED_NAME_USER_CERTIFICATES) + private List<UserCertificate> userCertificates = null; - public static final String SERIALIZED_NAME_FACHDATEN = "Fachdaten"; - @SerializedName(SERIALIZED_NAME_FACHDATEN) - private List<Fachdaten> fachdaten = null; + public static final String SERIALIZED_NAME_FACHDATEN = "Fachdaten"; + @SerializedName(SERIALIZED_NAME_FACHDATEN) + private List<Fachdaten> fachdaten = null; - public DirectoryEntry directoryEntryBase(BaseDirectoryEntry directoryEntryBase) { + public DirectoryEntry directoryEntryBase(BaseDirectoryEntry directoryEntryBase) { - this.directoryEntryBase = directoryEntryBase; - return this; - } + this.directoryEntryBase = directoryEntryBase; + return this; + } - /** - * Get directoryEntryBase - * - * @return directoryEntryBase - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "") + /** + * Get directoryEntryBase + * + * @return directoryEntryBase + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") - public BaseDirectoryEntry getDirectoryEntryBase() { - return directoryEntryBase; - } + public BaseDirectoryEntry getDirectoryEntryBase() { + return directoryEntryBase; + } - public void setDirectoryEntryBase(BaseDirectoryEntry directoryEntryBase) { - this.directoryEntryBase = directoryEntryBase; - } + public void setDirectoryEntryBase(BaseDirectoryEntry directoryEntryBase) { + this.directoryEntryBase = directoryEntryBase; + } - public DirectoryEntry userCertificates(List<UserCertificate> userCertificates) { + public DirectoryEntry userCertificates(List<UserCertificate> userCertificates) { - this.userCertificates = userCertificates; - return this; - } + this.userCertificates = userCertificates; + return this; + } - public DirectoryEntry addUserCertificatesItem(UserCertificate userCertificatesItem) { - if (this.userCertificates == null) { - this.userCertificates = new ArrayList<>(); + public DirectoryEntry addUserCertificatesItem(UserCertificate userCertificatesItem) { + if (this.userCertificates == null) { + this.userCertificates = new ArrayList<>(); + } + this.userCertificates.add(userCertificatesItem); + return this; } - this.userCertificates.add(userCertificatesItem); - return this; - } - /** - * Get userCertificates - * - * @return userCertificates - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "") + /** + * Get userCertificates + * + * @return userCertificates + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") - public List<UserCertificate> getUserCertificates() { - return userCertificates; - } + public List<UserCertificate> getUserCertificates() { + return userCertificates; + } - public void setUserCertificates(List<UserCertificate> userCertificates) { - this.userCertificates = userCertificates; - } + public void setUserCertificates(List<UserCertificate> userCertificates) { + this.userCertificates = userCertificates; + } - public DirectoryEntry fachdaten(List<Fachdaten> fachdaten) { + public DirectoryEntry fachdaten(List<Fachdaten> fachdaten) { - this.fachdaten = fachdaten; - return this; - } + this.fachdaten = fachdaten; + return this; + } - public DirectoryEntry addFachdatenItem(Fachdaten fachdatenItem) { - if (this.fachdaten == null) { - this.fachdaten = new ArrayList<>(); + public DirectoryEntry addFachdatenItem(Fachdaten fachdatenItem) { + if (this.fachdaten == null) { + this.fachdaten = new ArrayList<>(); + } + this.fachdaten.add(fachdatenItem); + return this; } - this.fachdaten.add(fachdatenItem); - return this; - } - /** - * Get fachdaten - * - * @return fachdaten - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "") + /** + * Get fachdaten + * + * @return fachdaten + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public List<Fachdaten> getFachdaten() { + return fachdaten; + } - public List<Fachdaten> getFachdaten() { - return fachdaten; - } + public void setFachdaten(List<Fachdaten> fachdaten) { + this.fachdaten = fachdaten; + } - public void setFachdaten(List<Fachdaten> fachdaten) { - this.fachdaten = fachdaten; - } + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DirectoryEntry directoryEntry = (DirectoryEntry) o; + return Objects.equals(this.directoryEntryBase, directoryEntry.directoryEntryBase) && + Objects.equals(this.userCertificates, directoryEntry.userCertificates) && + Objects.equals(this.fachdaten, directoryEntry.fachdaten); + } - @Override - public boolean equals(java.lang.Object o) { - if (this == o) { - return true; + @Override + public int hashCode() { + return Objects.hash(directoryEntryBase, userCertificates, fachdaten); } - if (o == null || getClass() != o.getClass()) { - return false; + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class DirectoryEntry {\n"); + sb.append(" directoryEntryBase: ").append(toIndentedString(directoryEntryBase)).append("\n"); + sb.append(" userCertificates: ").append(toIndentedString(userCertificates)).append("\n"); + sb.append(" fachdaten: ").append(toIndentedString(fachdaten)).append("\n"); + sb.append("}"); + return sb.toString(); } - DirectoryEntry directoryEntry = (DirectoryEntry) o; - return Objects.equals(this.directoryEntryBase, directoryEntry.directoryEntryBase) && - Objects.equals(this.userCertificates, directoryEntry.userCertificates) && - Objects.equals(this.fachdaten, directoryEntry.fachdaten); - } - - @Override - public int hashCode() { - return Objects.hash(directoryEntryBase, userCertificates, fachdaten); - } - - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class DirectoryEntry {\n"); - sb.append(" directoryEntryBase: ").append(toIndentedString(directoryEntryBase)).append("\n"); - sb.append(" userCertificates: ").append(toIndentedString(userCertificates)).append("\n"); - sb.append(" fachdaten: ").append(toIndentedString(fachdaten)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(java.lang.Object o) { - if (o == null) { - return "null"; + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); } - return o.toString().replace("\n", "\n "); - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/DistinguishedName.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/DistinguishedName.java index 9106b91..04cdc29 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/DistinguishedName.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/DistinguishedName.java @@ -29,11 +29,18 @@ package de.gematik.ti.epa.vzd.client.model; +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * DistinguishedName @@ -41,177 +48,177 @@ public class DistinguishedName { - public static final String SERIALIZED_NAME_UID = "uid"; - @SerializedName(SERIALIZED_NAME_UID) - private String uid; + public static final String SERIALIZED_NAME_UID = "uid"; + @SerializedName(SERIALIZED_NAME_UID) + private String uid; - public static final String SERIALIZED_NAME_DC = "dc"; - @SerializedName(SERIALIZED_NAME_DC) - private List<String> dc = null; + public static final String SERIALIZED_NAME_DC = "dc"; + @SerializedName(SERIALIZED_NAME_DC) + private List<String> dc = null; - public static final String SERIALIZED_NAME_OU = "ou"; - @SerializedName(SERIALIZED_NAME_OU) - private List<String> ou = null; + public static final String SERIALIZED_NAME_OU = "ou"; + @SerializedName(SERIALIZED_NAME_OU) + private List<String> ou = null; - public static final String SERIALIZED_NAME_CN = "cn"; - @SerializedName(SERIALIZED_NAME_CN) - private String cn; + public static final String SERIALIZED_NAME_CN = "cn"; + @SerializedName(SERIALIZED_NAME_CN) + private String cn; - public DistinguishedName uid(String uid) { + public DistinguishedName uid(String uid) { - this.uid = uid; - return this; - } + this.uid = uid; + return this; + } - /** - * entryID: Name/ID, den den Eintrag eindeutig identifiziert. Hat für den Verzeichnisdienst_Eintrag, Certificate, KOM-LE_Fachdaten und FAD1 eines - * Verzeichniseintrags den gleichen Wert. - * - * @return uid - **/ - @ApiModelProperty(required = true, value = "entryID: Name/ID, den den Eintrag eindeutig identifiziert. Hat für den Verzeichnisdienst_Eintrag, Certificate, KOM-LE_Fachdaten und FAD1 eines Verzeichniseintrags den gleichen Wert.") + /** + * entryID: Name/ID, den den Eintrag eindeutig identifiziert. Hat für den Verzeichnisdienst_Eintrag, Certificate, KOM-LE_Fachdaten und FAD1 eines + * Verzeichniseintrags den gleichen Wert. + * + * @return uid + **/ + @ApiModelProperty(required = true, value = "entryID: Name/ID, den den Eintrag eindeutig identifiziert. Hat für den Verzeichnisdienst_Eintrag, Certificate, KOM-LE_Fachdaten und FAD1 eines Verzeichniseintrags den gleichen Wert.") - public String getUid() { - return uid; - } + public String getUid() { + return uid; + } - public void setUid(String uid) { - this.uid = uid; - } + public void setUid(String uid) { + this.uid = uid; + } - public DistinguishedName dc(List<String> dc) { + public DistinguishedName dc(List<String> dc) { - this.dc = dc; - return this; - } + this.dc = dc; + return this; + } - public DistinguishedName addDcItem(String dcItem) { - if (this.dc == null) { - this.dc = new ArrayList<>(); + public DistinguishedName addDcItem(String dcItem) { + if (this.dc == null) { + this.dc = new ArrayList<>(); + } + this.dc.add(dcItem); + return this; } - this.dc.add(dcItem); - return this; - } - /** - * Get dc - * - * @return dc - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "") + /** + * Get dc + * + * @return dc + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") - public List<String> getDc() { - return dc; - } + public List<String> getDc() { + return dc; + } - public void setDc(List<String> dc) { - this.dc = dc; - } + public void setDc(List<String> dc) { + this.dc = dc; + } - public DistinguishedName ou(List<String> ou) { + public DistinguishedName ou(List<String> ou) { - this.ou = ou; - return this; - } + this.ou = ou; + return this; + } - public DistinguishedName addOuItem(String ouItem) { - if (this.ou == null) { - this.ou = new ArrayList<>(); + public DistinguishedName addOuItem(String ouItem) { + if (this.ou == null) { + this.ou = new ArrayList<>(); + } + this.ou.add(ouItem); + return this; } - this.ou.add(ouItem); - return this; - } - /** - * Get ou - * - * @return ou - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "") + /** + * Get ou + * + * @return ou + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") - public List<String> getOu() { - return ou; - } + public List<String> getOu() { + return ou; + } - public void setOu(List<String> ou) { - this.ou = ou; - } + public void setOu(List<String> ou) { + this.ou = ou; + } - public DistinguishedName cn(String cn) { + public DistinguishedName cn(String cn) { - this.cn = cn; - return this; - } + this.cn = cn; + return this; + } - /** - * Common Name - * - * @return cn - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "Common Name") + /** + * Common Name + * + * @return cn + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Common Name") - public String getCn() { - return cn; - } + public String getCn() { + return cn; + } - public void setCn(String cn) { - this.cn = cn; - } + public void setCn(String cn) { + this.cn = cn; + } - @Override - public boolean equals(java.lang.Object o) { - if (this == o) { - return true; + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DistinguishedName distinguishedName = (DistinguishedName) o; + return Objects.equals(this.uid, distinguishedName.uid) && + Objects.equals(this.dc, distinguishedName.dc) && + Objects.equals(this.ou, distinguishedName.ou) && + Objects.equals(this.cn, distinguishedName.cn); } - if (o == null || getClass() != o.getClass()) { - return false; + + @Override + public int hashCode() { + return Objects.hash(uid, dc, ou, cn); } - DistinguishedName distinguishedName = (DistinguishedName) o; - return Objects.equals(this.uid, distinguishedName.uid) && - Objects.equals(this.dc, distinguishedName.dc) && - Objects.equals(this.ou, distinguishedName.ou) && - Objects.equals(this.cn, distinguishedName.cn); - } - - @Override - public int hashCode() { - return Objects.hash(uid, dc, ou, cn); - } - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class DistinguishedName {\n"); - sb.append(" uid: ").append(toIndentedString(uid)).append("\n"); - sb.append(" dc: ").append(toIndentedString(dc)).append("\n"); - sb.append(" ou: ").append(toIndentedString(ou)).append("\n"); - sb.append(" cn: ").append(toIndentedString(cn)).append("\n"); - sb.append("}"); - return sb.toString(); - } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class DistinguishedName {\n"); + sb.append(" uid: ").append(toIndentedString(uid)).append("\n"); + sb.append(" dc: ").append(toIndentedString(dc)).append("\n"); + sb.append(" ou: ").append(toIndentedString(ou)).append("\n"); + sb.append(" cn: ").append(toIndentedString(cn)).append("\n"); + sb.append("}"); + return sb.toString(); + } - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(java.lang.Object o) { - if (o == null) { - return "null"; + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); } - return o.toString().replace("\n", "\n "); - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/Error.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/Error.java index 33d7ed8..fb7925c 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/Error.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/Error.java @@ -29,9 +29,16 @@ package de.gematik.ti.epa.vzd.client.model; +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import java.util.Objects; +import java.io.IOException; /** * Error @@ -39,101 +46,101 @@ public class Error { - public static final String SERIALIZED_NAME_ATTRIBUTE_NAME = "attributeName"; - @SerializedName(SERIALIZED_NAME_ATTRIBUTE_NAME) - private String attributeName; + public static final String SERIALIZED_NAME_ATTRIBUTE_NAME = "attributeName"; + @SerializedName(SERIALIZED_NAME_ATTRIBUTE_NAME) + private String attributeName; - public static final String SERIALIZED_NAME_ATTRIBUTE_ERROR = "attributeError"; - @SerializedName(SERIALIZED_NAME_ATTRIBUTE_ERROR) - private String attributeError; + public static final String SERIALIZED_NAME_ATTRIBUTE_ERROR = "attributeError"; + @SerializedName(SERIALIZED_NAME_ATTRIBUTE_ERROR) + private String attributeError; - public Error attributeName(String attributeName) { + public Error attributeName(String attributeName) { - this.attributeName = attributeName; - return this; - } + this.attributeName = attributeName; + return this; + } - /** - * Name des Attributs, in dem ein Fehler erkannt wurde - * - * @return attributeName - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "Name des Attributs, in dem ein Fehler erkannt wurde") + /** + * Name des Attributs, in dem ein Fehler erkannt wurde + * + * @return attributeName + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Name des Attributs, in dem ein Fehler erkannt wurde") - public String getAttributeName() { - return attributeName; - } + public String getAttributeName() { + return attributeName; + } - public void setAttributeName(String attributeName) { - this.attributeName = attributeName; - } + public void setAttributeName(String attributeName) { + this.attributeName = attributeName; + } - public Error attributeError(String attributeError) { + public Error attributeError(String attributeError) { - this.attributeError = attributeError; - return this; - } + this.attributeError = attributeError; + return this; + } - /** - * Beschreibung des erkannten Fehlers - * - * @return attributeError - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "Beschreibung des erkannten Fehlers") + /** + * Beschreibung des erkannten Fehlers + * + * @return attributeError + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Beschreibung des erkannten Fehlers") - public String getAttributeError() { - return attributeError; - } + public String getAttributeError() { + return attributeError; + } - public void setAttributeError(String attributeError) { - this.attributeError = attributeError; - } + public void setAttributeError(String attributeError) { + this.attributeError = attributeError; + } + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Error error = (Error) o; + return Objects.equals(this.attributeName, error.attributeName) && + Objects.equals(this.attributeError, error.attributeError); + } - @Override - public boolean equals(java.lang.Object o) { - if (this == o) { - return true; + @Override + public int hashCode() { + return Objects.hash(attributeName, attributeError); } - if (o == null || getClass() != o.getClass()) { - return false; + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Error {\n"); + sb.append(" attributeName: ").append(toIndentedString(attributeName)).append("\n"); + sb.append(" attributeError: ").append(toIndentedString(attributeError)).append("\n"); + sb.append("}"); + return sb.toString(); } - Error error = (Error) o; - return Objects.equals(this.attributeName, error.attributeName) && - Objects.equals(this.attributeError, error.attributeError); - } - - @Override - public int hashCode() { - return Objects.hash(attributeName, attributeError); - } - - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class Error {\n"); - sb.append(" attributeName: ").append(toIndentedString(attributeName)).append("\n"); - sb.append(" attributeError: ").append(toIndentedString(attributeError)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(java.lang.Object o) { - if (o == null) { - return "null"; + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); } - return o.toString().replace("\n", "\n "); - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/FAD1.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/FAD1.java index bc63e56..d54c1a6 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/FAD1.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/FAD1.java @@ -29,10 +29,19 @@ package de.gematik.ti.epa.vzd.client.model; +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; +import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * FAD1 @@ -40,89 +49,89 @@ public class FAD1 { - public static final String SERIALIZED_NAME_DN = "dn"; - @SerializedName(SERIALIZED_NAME_DN) - private DistinguishedName dn; + public static final String SERIALIZED_NAME_DN = "dn"; + @SerializedName(SERIALIZED_NAME_DN) + private DistinguishedName dn; - public static final String SERIALIZED_NAME_MAIL = "mail"; - @SerializedName(SERIALIZED_NAME_MAIL) - private List<String> mail = null; + public static final String SERIALIZED_NAME_MAIL = "mail"; + @SerializedName(SERIALIZED_NAME_MAIL) + private List<String> mail = null; - public FAD1 dn(DistinguishedName dn) { + public FAD1 dn(DistinguishedName dn) { - this.dn = dn; - return this; - } + this.dn = dn; + return this; + } - /** - * Get dn - * - * @return dn - **/ - @ApiModelProperty(required = true, value = "") + /** + * Get dn + * + * @return dn + **/ + @ApiModelProperty(required = true, value = "") - public DistinguishedName getDn() { - return dn; - } + public DistinguishedName getDn() { + return dn; + } - public void setDn(DistinguishedName dn) { - this.dn = dn; - } + public void setDn(DistinguishedName dn) { + this.dn = dn; + } - /** - * Get mail - * - * @return mail - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "") + /** + * Get mail + * + * @return mail + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") - public List<String> getMail() { - return mail; - } + public List<String> getMail() { + return mail; + } - @Override - public boolean equals(java.lang.Object o) { - if (this == o) { - return true; + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FAD1 FAD1 = (FAD1) o; + return Objects.equals(this.dn, FAD1.dn) && + Objects.equals(this.mail, FAD1.mail); } - if (o == null || getClass() != o.getClass()) { - return false; + + @Override + public int hashCode() { + return Objects.hash(dn, mail); } - FAD1 FAD1 = (FAD1) o; - return Objects.equals(this.dn, FAD1.dn) && - Objects.equals(this.mail, FAD1.mail); - } - - @Override - public int hashCode() { - return Objects.hash(dn, mail); - } - - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class FAD1 {\n"); - sb.append(" dn: ").append(toIndentedString(dn)).append("\n"); - sb.append(" mail: ").append(toIndentedString(mail)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(java.lang.Object o) { - if (o == null) { - return "null"; + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FAD1 {\n"); + sb.append(" dn: ").append(toIndentedString(dn)).append("\n"); + sb.append(" mail: ").append(toIndentedString(mail)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); } - return o.toString().replace("\n", "\n "); - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/Fachdaten.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/Fachdaten.java index 0c1836c..7edcbc8 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/Fachdaten.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/Fachdaten.java @@ -29,11 +29,20 @@ package de.gematik.ti.epa.vzd.client.model; +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.client.model.FAD1; +import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * Fachdaten @@ -41,108 +50,108 @@ public class Fachdaten { - public static final String SERIALIZED_NAME_DN = "dn"; - @SerializedName(SERIALIZED_NAME_DN) - private DistinguishedName dn; + public static final String SERIALIZED_NAME_DN = "dn"; + @SerializedName(SERIALIZED_NAME_DN) + private DistinguishedName dn; - public static final String SERIALIZED_NAME_F_A_D1 = "FAD1"; - @SerializedName(SERIALIZED_NAME_F_A_D1) - private List<FAD1> FAD1 = null; + public static final String SERIALIZED_NAME_F_A_D1 = "FAD1"; + @SerializedName(SERIALIZED_NAME_F_A_D1) + private List<FAD1> FAD1 = null; - public Fachdaten dn(DistinguishedName dn) { + public Fachdaten dn(DistinguishedName dn) { - this.dn = dn; - return this; - } + this.dn = dn; + return this; + } - /** - * Get dn - * - * @return dn - **/ - @ApiModelProperty(required = true, value = "") + /** + * Get dn + * + * @return dn + **/ + @ApiModelProperty(required = true, value = "") - public DistinguishedName getDn() { - return dn; - } + public DistinguishedName getDn() { + return dn; + } - public void setDn(DistinguishedName dn) { - this.dn = dn; - } + public void setDn(DistinguishedName dn) { + this.dn = dn; + } - public Fachdaten FAD1(List<FAD1> FAD1) { + public Fachdaten FAD1(List<FAD1> FAD1) { - this.FAD1 = FAD1; - return this; - } + this.FAD1 = FAD1; + return this; + } - public Fachdaten addFAD1Item(FAD1 FAD1Item) { - if (this.FAD1 == null) { - this.FAD1 = new ArrayList<>(); + public Fachdaten addFAD1Item(FAD1 FAD1Item) { + if (this.FAD1 == null) { + this.FAD1 = new ArrayList<>(); + } + this.FAD1.add(FAD1Item); + return this; } - this.FAD1.add(FAD1Item); - return this; - } - /** - * Get FAD1 - * - * @return FAD1 - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "") + /** + * Get FAD1 + * + * @return FAD1 + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") - public List<FAD1> getFAD1() { - return FAD1; - } + public List<FAD1> getFAD1() { + return FAD1; + } - public void setFAD1(List<FAD1> FAD1) { - this.FAD1 = FAD1; - } + public void setFAD1(List<FAD1> FAD1) { + this.FAD1 = FAD1; + } + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Fachdaten fachdaten = (Fachdaten) o; + return Objects.equals(this.dn, fachdaten.dn) && + Objects.equals(this.FAD1, fachdaten.FAD1); + } - @Override - public boolean equals(java.lang.Object o) { - if (this == o) { - return true; + @Override + public int hashCode() { + return Objects.hash(dn, FAD1); } - if (o == null || getClass() != o.getClass()) { - return false; + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Fachdaten {\n"); + sb.append(" dn: ").append(toIndentedString(dn)).append("\n"); + sb.append(" FAD1: ").append(toIndentedString(FAD1)).append("\n"); + sb.append("}"); + return sb.toString(); } - Fachdaten fachdaten = (Fachdaten) o; - return Objects.equals(this.dn, fachdaten.dn) && - Objects.equals(this.FAD1, fachdaten.FAD1); - } - - @Override - public int hashCode() { - return Objects.hash(dn, FAD1); - } - - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class Fachdaten {\n"); - sb.append(" dn: ").append(toIndentedString(dn)).append("\n"); - sb.append(" FAD1: ").append(toIndentedString(FAD1)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(java.lang.Object o) { - if (o == null) { - return "null"; + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); } - return o.toString().replace("\n", "\n "); - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/UserCertificate.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/UserCertificate.java index e46d9bb..6a091b8 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/UserCertificate.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/UserCertificate.java @@ -29,17 +29,19 @@ package de.gematik.ti.epa.vzd.client.model; +import java.util.Objects; +import java.util.Arrays; import com.google.gson.TypeAdapter; import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * Jeder Verzeichniseintrag muss mindestens ein Zertifikat enthalten. @@ -48,289 +50,289 @@ public class UserCertificate { - public static final String SERIALIZED_NAME_DN = "dn"; - @SerializedName(SERIALIZED_NAME_DN) - private DistinguishedName dn; + public static final String SERIALIZED_NAME_DN = "dn"; + @SerializedName(SERIALIZED_NAME_DN) + private DistinguishedName dn; - public static final String SERIALIZED_NAME_ENTRY_TYPE = "entryType"; - @SerializedName(SERIALIZED_NAME_ENTRY_TYPE) - private String entryType; + public static final String SERIALIZED_NAME_ENTRY_TYPE = "entryType"; + @SerializedName(SERIALIZED_NAME_ENTRY_TYPE) + private String entryType; - public static final String SERIALIZED_NAME_TELEMATIK_I_D = "telematikID"; - @SerializedName(SERIALIZED_NAME_TELEMATIK_I_D) - private String telematikID; + public static final String SERIALIZED_NAME_TELEMATIK_I_D = "telematikID"; + @SerializedName(SERIALIZED_NAME_TELEMATIK_I_D) + private String telematikID; - public static final String SERIALIZED_NAME_PROFESSION_O_I_D = "professionOID"; - @SerializedName(SERIALIZED_NAME_PROFESSION_O_I_D) - private List<String> professionOID = null; + public static final String SERIALIZED_NAME_PROFESSION_O_I_D = "professionOID"; + @SerializedName(SERIALIZED_NAME_PROFESSION_O_I_D) + private List<String> professionOID = null; - /** - * Gets or Sets usage - */ - @JsonAdapter(UsageEnum.Adapter.class) - public enum UsageEnum { - KOM_LE("KOM-LE"), + /** + * Gets or Sets usage + */ + @JsonAdapter(UsageEnum.Adapter.class) + public enum UsageEnum { + KOM_LE("KOM-LE"), - EPA("ePA"); + EPA("ePA"); - private String value; + private String value; - UsageEnum(String value) { - this.value = value; - } + UsageEnum(String value) { + this.value = value; + } - public String getValue() { - return value; - } + public String getValue() { + return value; + } - @Override - public String toString() { - return String.valueOf(value); - } + @Override + public String toString() { + return String.valueOf(value); + } - public static UsageEnum fromValue(String value) { - for (UsageEnum b : UsageEnum.values()) { - if (b.value.equals(value)) { - return b; + public static UsageEnum fromValue(String value) { + for (UsageEnum b : UsageEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); } - } - throw new IllegalArgumentException("Unexpected value '" + value + "'"); - } - public static class Adapter extends TypeAdapter<UsageEnum> { + public static class Adapter extends TypeAdapter<UsageEnum> { - @Override - public void write(final JsonWriter jsonWriter, final UsageEnum enumeration) throws IOException { - jsonWriter.value(enumeration.getValue()); - } + @Override + public void write(final JsonWriter jsonWriter, final UsageEnum enumeration) throws IOException { + jsonWriter.value(enumeration.getValue()); + } - @Override - public UsageEnum read(final JsonReader jsonReader) throws IOException { - String value = jsonReader.nextString(); - return UsageEnum.fromValue(value); - } + @Override + public UsageEnum read(final JsonReader jsonReader) throws IOException { + String value = jsonReader.nextString(); + return UsageEnum.fromValue(value); + } + } } - } - public static final String SERIALIZED_NAME_USAGE = "usage"; - @SerializedName(SERIALIZED_NAME_USAGE) - private List<UsageEnum> usage = null; + public static final String SERIALIZED_NAME_USAGE = "usage"; + @SerializedName(SERIALIZED_NAME_USAGE) + private List<UsageEnum> usage = null; - public static final String SERIALIZED_NAME_USER_CERTIFICATE = "userCertificate"; - @SerializedName(SERIALIZED_NAME_USER_CERTIFICATE) - private String userCertificate; + public static final String SERIALIZED_NAME_USER_CERTIFICATE = "userCertificate"; + @SerializedName(SERIALIZED_NAME_USER_CERTIFICATE) + private String userCertificate; - public static final String SERIALIZED_NAME_DESCRIPTION = "description"; - @SerializedName(SERIALIZED_NAME_DESCRIPTION) - private String description; + public static final String SERIALIZED_NAME_DESCRIPTION = "description"; + @SerializedName(SERIALIZED_NAME_DESCRIPTION) + private String description; - public UserCertificate dn(DistinguishedName dn) { + public UserCertificate dn(DistinguishedName dn) { - this.dn = dn; - return this; - } + this.dn = dn; + return this; + } - /** - * Get dn - * - * @return dn - **/ - @ApiModelProperty(required = true, value = "") + /** + * Get dn + * + * @return dn + **/ + @ApiModelProperty(required = true, value = "") - public DistinguishedName getDn() { - return dn; - } + public DistinguishedName getDn() { + return dn; + } - public void setDn(DistinguishedName dn) { - this.dn = dn; - } + public void setDn(DistinguishedName dn) { + this.dn = dn; + } - /** - * Eintragstyp Wird vom VZD anhand der in dem Zertifikat enthaltenen OID (Extension Admission, Attribut ProfessionOID) und der Spalte Eintragstyp in - * Tab_VZD_Mapping_Eintragstyp_und_ProfessionOID automatisch eingetragen. Siehe auch [gemSpecOID]# Tab_PKI_402 und Tab_PKI_403 - * - * @return entryType - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "Eintragstyp Wird vom VZD anhand der in dem Zertifikat enthaltenen OID (Extension Admission, Attribut ProfessionOID) und der Spalte Eintragstyp in Tab_VZD_Mapping_Eintragstyp_und_ProfessionOID automatisch eingetragen. Siehe auch [gemSpecOID]# Tab_PKI_402 und Tab_PKI_403") + /** + * Eintragstyp Wird vom VZD anhand der in dem Zertifikat enthaltenen OID (Extension Admission, Attribut ProfessionOID) und der Spalte Eintragstyp + * in Tab_VZD_Mapping_Eintragstyp_und_ProfessionOID automatisch eingetragen. Siehe auch [gemSpecOID]# Tab_PKI_402 und Tab_PKI_403 + * + * @return entryType + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Eintragstyp Wird vom VZD anhand der in dem Zertifikat enthaltenen OID (Extension Admission, Attribut ProfessionOID) und der Spalte Eintragstyp in Tab_VZD_Mapping_Eintragstyp_und_ProfessionOID automatisch eingetragen. Siehe auch [gemSpecOID]# Tab_PKI_402 und Tab_PKI_403") - public String getEntryType() { - return entryType; - } + public String getEntryType() { + return entryType; + } - public UserCertificate telematikID(String telematikID) { + public UserCertificate telematikID(String telematikID) { - this.telematikID = telematikID; - return this; - } + this.telematikID = telematikID; + return this; + } - /** - * Wird beim Anlegen des Eintrags vom VZD aus dem Zertifikat übernommen (Feld registrationNumber der Extension Admission). Falls der Basiseintrag - * (baseDirectoryEntry) ohne Zertifikat angelegt wird, kann in Operation add_Directory_Entry die telematikID angegeben werden. Damit ist der - * Verzeichniseintrag bereits über die telematikID auffindbar. - * - * @return telematikID - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "Wird beim Anlegen des Eintrags vom VZD aus dem Zertifikat übernommen (Feld registrationNumber der Extension Admission). Falls der Basiseintrag (baseDirectoryEntry) ohne Zertifikat angelegt wird, kann in Operation add_Directory_Entry die telematikID angegeben werden. Damit ist der Verzeichniseintrag bereits über die telematikID auffindbar.") + /** + * Wird beim Anlegen des Eintrags vom VZD aus dem Zertifikat übernommen (Feld registrationNumber der Extension Admission). Falls der Basiseintrag + * (baseDirectoryEntry) ohne Zertifikat angelegt wird, kann in Operation add_Directory_Entry die telematikID angegeben werden. Damit ist der + * Verzeichniseintrag bereits über die telematikID auffindbar. + * + * @return telematikID + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Wird beim Anlegen des Eintrags vom VZD aus dem Zertifikat übernommen (Feld registrationNumber der Extension Admission). Falls der Basiseintrag (baseDirectoryEntry) ohne Zertifikat angelegt wird, kann in Operation add_Directory_Entry die telematikID angegeben werden. Damit ist der Verzeichniseintrag bereits über die telematikID auffindbar.") + + public String getTelematikID() { + return telematikID; + } - public String getTelematikID() { - return telematikID; - } + public void setTelematikID(String telematikID) { + this.telematikID = telematikID; + } - public void setTelematikID(String telematikID) { - this.telematikID = telematikID; - } + /** + * Get professionOID + * + * @return professionOID + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") - /** - * Get professionOID - * - * @return professionOID - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "") + public List<String> getProfessionOID() { + return professionOID; + } - public List<String> getProfessionOID() { - return professionOID; - } + public UserCertificate usage(List<UsageEnum> usage) { - public UserCertificate usage(List<UsageEnum> usage) { + this.usage = usage; + return this; + } - this.usage = usage; - return this; - } + public UserCertificate addUsageItem(UsageEnum usageItem) { + if (this.usage == null) { + this.usage = new ArrayList<>(); + } + this.usage.add(usageItem); + return this; + } - public UserCertificate addUsageItem(UsageEnum usageItem) { - if (this.usage == null) { - this.usage = new ArrayList<>(); + /** + * Nutzungskennzeichnung kann pro Zertifikat mehrfach vergeben werden. Vorgegebener Wertebereich [KOM-LE, ePA]. Obligatorisch für LEI und KTR mit + * vorgegebenem Wert usage=ePA + * + * @return usage + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Nutzungskennzeichnung kann pro Zertifikat mehrfach vergeben werden. Vorgegebener Wertebereich [KOM-LE, ePA]. Obligatorisch für LEI und KTR mit vorgegebenem Wert usage=ePA") + + public List<UsageEnum> getUsage() { + return usage; } - this.usage.add(usageItem); - return this; - } - /** - * Nutzungskennzeichnung kann pro Zertifikat mehrfach vergeben werden. Vorgegebener Wertebereich [KOM-LE, ePA]. Obligatorisch für LEI und KTR mit - * vorgegebenem Wert usage=ePA - * - * @return usage - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "Nutzungskennzeichnung kann pro Zertifikat mehrfach vergeben werden. Vorgegebener Wertebereich [KOM-LE, ePA]. Obligatorisch für LEI und KTR mit vorgegebenem Wert usage=ePA") - public List<UsageEnum> getUsage() { - return usage; - } + public void setUsage(List<UsageEnum> usage) { + this.usage = usage; + } - public void setUsage(List<UsageEnum> usage) { - this.usage = usage; - } + public UserCertificate userCertificate(String userCertificate) { + this.userCertificate = userCertificate; + return this; + } - public UserCertificate userCertificate(String userCertificate) { + /** + * Zertifikat im DER Format. Base64 kodiert. Die pflegende Stelle erhält das Zertifikat vom TSP oder falls das nicht möglich ist wird ein + * Ersatzverfahren abgestimmt. + * + * @return userCertificate + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Zertifikat im DER Format. Base64 kodiert. Die pflegende Stelle erhält das Zertifikat vom TSP oder falls das nicht möglich ist wird ein Ersatzverfahren abgestimmt.") + + public String getUserCertificate() { + return userCertificate; + } - this.userCertificate = userCertificate; - return this; - } - /** - * Zertifikat im DER Format. Base64 kodiert. Die pflegende Stelle erhält das Zertifikat vom TSP oder falls das nicht möglich ist wird ein - * Ersatzverfahren abgestimmt. - * - * @return userCertificate - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "Zertifikat im DER Format. Base64 kodiert. Die pflegende Stelle erhält das Zertifikat vom TSP oder falls das nicht möglich ist wird ein Ersatzverfahren abgestimmt.") + public void setUserCertificate(String userCertificate) { + this.userCertificate = userCertificate; + } - public String getUserCertificate() { - return userCertificate; - } + public UserCertificate description(String description) { - public void setUserCertificate(String userCertificate) { - this.userCertificate = userCertificate; - } + this.description = description; + return this; + } + /** + * Dieses Attribut ermöglicht das Zertifikat zu beschreiben, um die Administration des VZD Eintrags zu vereinfachen. + * + * @return description + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Dieses Attribut ermöglicht das Zertifikat zu beschreiben, um die Administration des VZD Eintrags zu vereinfachen.") - public UserCertificate description(String description) { + public String getDescription() { + return description; + } - this.description = description; - return this; - } - /** - * Dieses Attribut ermöglicht das Zertifikat zu beschreiben, um die Administration des VZD Eintrags zu vereinfachen. - * - * @return description - **/ - @javax.annotation.Nullable - @ApiModelProperty(value = "Dieses Attribut ermöglicht das Zertifikat zu beschreiben, um die Administration des VZD Eintrags zu vereinfachen.") + public void setDescription(String description) { + this.description = description; + } - public String getDescription() { - return description; - } + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UserCertificate userCertificate = (UserCertificate) o; + return Objects.equals(this.dn, userCertificate.dn) && + Objects.equals(this.entryType, userCertificate.entryType) && + Objects.equals(this.telematikID, userCertificate.telematikID) && + Objects.equals(this.professionOID, userCertificate.professionOID) && + Objects.equals(this.usage, userCertificate.usage) && + Objects.equals(this.userCertificate, userCertificate.userCertificate) && + Objects.equals(this.description, userCertificate.description); + } - public void setDescription(String description) { - this.description = description; - } + @Override + public int hashCode() { + return Objects.hash(dn, entryType, telematikID, professionOID, usage, userCertificate, description); + } - @Override - public boolean equals(java.lang.Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class UserCertificate {\n"); + sb.append(" dn: ").append(toIndentedString(dn)).append("\n"); + sb.append(" entryType: ").append(toIndentedString(entryType)).append("\n"); + sb.append(" telematikID: ").append(toIndentedString(telematikID)).append("\n"); + sb.append(" professionOID: ").append(toIndentedString(professionOID)).append("\n"); + sb.append(" usage: ").append(toIndentedString(usage)).append("\n"); + sb.append(" userCertificate: ").append(toIndentedString(userCertificate)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append("}"); + return sb.toString(); } - UserCertificate userCertificate = (UserCertificate) o; - return Objects.equals(this.dn, userCertificate.dn) && - Objects.equals(this.entryType, userCertificate.entryType) && - Objects.equals(this.telematikID, userCertificate.telematikID) && - Objects.equals(this.professionOID, userCertificate.professionOID) && - Objects.equals(this.usage, userCertificate.usage) && - Objects.equals(this.userCertificate, userCertificate.userCertificate) && - Objects.equals(this.description, userCertificate.description); - } - - @Override - public int hashCode() { - return Objects.hash(dn, entryType, telematikID, professionOID, usage, userCertificate, description); - } - - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class UserCertificate {\n"); - sb.append(" dn: ").append(toIndentedString(dn)).append("\n"); - sb.append(" entryType: ").append(toIndentedString(entryType)).append("\n"); - sb.append(" telematikID: ").append(toIndentedString(telematikID)).append("\n"); - sb.append(" professionOID: ").append(toIndentedString(professionOID)).append("\n"); - sb.append(" usage: ").append(toIndentedString(usage)).append("\n"); - sb.append(" userCertificate: ").append(toIndentedString(userCertificate)).append("\n"); - sb.append(" description: ").append(toIndentedString(description)).append("\n"); - sb.append("}"); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(java.lang.Object o) { - if (o == null) { - return "null"; + + /** + * Convert the given object to string with each line indented by 4 spaces (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); } - return o.toString().replace("\n", "\n "); - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/CommandNamesEnum.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/CommandNamesEnum.java index cadcfba..02af16b 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/CommandNamesEnum.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/CommandNamesEnum.java @@ -18,31 +18,31 @@ public enum CommandNamesEnum { - ADD_DIR_ENTRY("addDirectoryEntries"), - READ_DIR_ENTRY("readDirectoryEntries"), - MOD_DIR_ENTRY("modifyDirectoryEntries"), - DEL_DIR_ENTRY("deleteDirectoryEntries"), - ADD_DIR_CERT("addDirectoryEntryCertificate"), - READ_DIR_CERT("readDirectoryEntryCertificate"), - MOD_DIR_CERT("modifyDirectoryEntryCertificate"), - DEL_DIR_CERT("deleteDirectoryEntryCertificate"); + ADD_DIR_ENTRY("addDirectoryEntries"), + READ_DIR_ENTRY("readDirectoryEntries"), + MOD_DIR_ENTRY("modifyDirectoryEntries"), + DEL_DIR_ENTRY("deleteDirectoryEntries"), + ADD_DIR_CERT("addDirectoryEntryCertificate"), + READ_DIR_CERT("readDirectoryEntryCertificate"), + MOD_DIR_CERT("modifyDirectoryEntryCertificate"), + DEL_DIR_CERT("deleteDirectoryEntryCertificate"); - private final String name; + private final String name; - CommandNamesEnum(String name) { - this.name = name; - } + CommandNamesEnum(String name) { + this.name = name; + } - public String getName() { - return this.name; - } + public String getName() { + return this.name; + } - public static CommandNamesEnum getEntry(String name) { - for (CommandNamesEnum cn : CommandNamesEnum.values()) { - if (name.equals(cn.getName())) { - return cn; - } + public static CommandNamesEnum getEntry(String name) { + for (CommandNamesEnum cn : CommandNamesEnum.values()) { + if (name.equals(cn.getName())) { + return cn; + } + } + return null; } - return null; - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/GemStringUtils.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/GemStringUtils.java index 387b214..d05f943 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/GemStringUtils.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/GemStringUtils.java @@ -22,35 +22,35 @@ public class GemStringUtils { - private static final String gothic = "____ ______________________ _________ .__ .__ __ \n\\ \\ / /\\____ /\\______ \\ \\_ ___ \\| | |__| ____ _____/ |_ \n \\ Y / / / | | \\ ______ / \\ \\/| | | |/ __ \\ / \\ __\\ \n \\ / / /_ | ` \\ /_____/ \\ \\___| |_| \\ ___/| | \\ |\n \\___/ /_______ \\/_______ / \\______ /____/__|\\___ >___| /__|\n \\/ \\/ \\/ \\/ \\/ \n"; - private static final String goofy = " __ __ __ ___________ __ _____ __ __ ___ __ __\n| | | | (___ ) | \\ / __) \\ | (_ _) \\ ___) | \\ | | (__ __) \n| | | | / / | | ___ | / | | | | | (__ | | \\ | | | \n| | | | / / | | (___) | | | | | | | __) | | \\ \\| | | | \n \\ \\/ / / /__ | | | \\__ | |__ _| |_ | (___ | | \\ | | | \n__\\ /___( )_| /_________\\ )_/ )_( )_/ )_ | |___\\ |____| |____\n"; - private static final String fuzzy = ".-..-..----..---. .--. .-. _ .-. \n: :: :`--. :: . : : .--': : :_; .' `.\n: :: : ,',': :: : _____ : : : : .-. .--. ,-.,-.`. .'\n: `' ;.'.'_ : :; ::_____:: :__ : :_ : :' '_.': ,. : : : \n `.,' :____;:___.' `.__.'`.__;:_;`.__.':_;:_; :_;\n"; - private static final String fourtops = "| |~~/|~~\\ /~~|' | \n \\ / / | |---| ||/~/|/~\\~|~\n \\/ /__|__/ \\__||\\/_| || \n"; - private static final String fender = "\\\\ // |'''''/ '||'''|. .|'''', '||` || \n \\\\ // // || || || || '' || \n \\\\ // // || || --- || || || .|''|, `||''|, ''||'' \n \\\\// // || || || || || ||..|| || || || \n \\/ /.....| .||...|' `|....' .||. .||. `|... .|| ||. `|..' \n"; - private static final String univers = "8b d8 888888888888 88888888ba, ,ad8888ba, 88 88 \n`8b d8' ,88 88 `\"8b d8\"' `\"8b 88 \"\" ,d \n `8b d8' ,88\" 88 `8b d8' 88 88 \n `8b d8' ,88\" 88 88 88 88 88 ,adPPYba, 8b,dPPYba, MM88MMM \n `8b d8' ,88\" 88 88 aaaaaaaa 88 88 88 a8P_____88 88P' `\"8a 88 \n `8b d8' ,88\" 88 8P \"\"\"\"\"\"\"\" Y8, 88 88 8PP\"\"\"\"\"\"\" 88 88 88 \n `888' 88\" 88 .a8P Y8a. .a8P 88 88 \"8b, ,aa 88 88 88, \n `8' 888888888888 88888888Y\"' `\"Y8888Y\"' 88 88 `\\\"Ybbd8\\\"'88 88 \\\"Y888 \"\n"; - public static final List<String> pics = Arrays - .asList(gothic, goofy, fuzzy, fourtops, fender, univers); + private static final String gothic = "____ ______________________ _________ .__ .__ __ \n\\ \\ / /\\____ /\\______ \\ \\_ ___ \\| | |__| ____ _____/ |_ \n \\ Y / / / | | \\ ______ / \\ \\/| | | |/ __ \\ / \\ __\\ \n \\ / / /_ | ` \\ /_____/ \\ \\___| |_| \\ ___/| | \\ |\n \\___/ /_______ \\/_______ / \\______ /____/__|\\___ >___| /__|\n \\/ \\/ \\/ \\/ \\/ \n"; + private static final String goofy = " __ __ __ ___________ __ _____ __ __ ___ __ __\n| | | | (___ ) | \\ / __) \\ | (_ _) \\ ___) | \\ | | (__ __) \n| | | | / / | | ___ | / | | | | | (__ | | \\ | | | \n| | | | / / | | (___) | | | | | | | __) | | \\ \\| | | | \n \\ \\/ / / /__ | | | \\__ | |__ _| |_ | (___ | | \\ | | | \n__\\ /___( )_| /_________\\ )_/ )_( )_/ )_ | |___\\ |____| |____\n"; + private static final String fuzzy = ".-..-..----..---. .--. .-. _ .-. \n: :: :`--. :: . : : .--': : :_; .' `.\n: :: : ,',': :: : _____ : : : : .-. .--. ,-.,-.`. .'\n: `' ;.'.'_ : :; ::_____:: :__ : :_ : :' '_.': ,. : : : \n `.,' :____;:___.' `.__.'`.__;:_;`.__.':_;:_; :_;\n"; + private static final String fourtops = "| |~~/|~~\\ /~~|' | \n \\ / / | |---| ||/~/|/~\\~|~\n \\/ /__|__/ \\__||\\/_| || \n"; + private static final String fender = "\\\\ // |'''''/ '||'''|. .|'''', '||` || \n \\\\ // // || || || || '' || \n \\\\ // // || || --- || || || .|''|, `||''|, ''||'' \n \\\\// // || || || || || ||..|| || || || \n \\/ /.....| .||...|' `|....' .||. .||. `|... .|| ||. `|..' \n"; + private static final String univers = "8b d8 888888888888 88888888ba, ,ad8888ba, 88 88 \n`8b d8' ,88 88 `\"8b d8\"' `\"8b 88 \"\" ,d \n `8b d8' ,88\" 88 `8b d8' 88 88 \n `8b d8' ,88\" 88 88 88 88 88 ,adPPYba, 8b,dPPYba, MM88MMM \n `8b d8' ,88\" 88 88 aaaaaaaa 88 88 88 a8P_____88 88P' `\"8a 88 \n `8b d8' ,88\" 88 8P \"\"\"\"\"\"\"\" Y8, 88 88 8PP\"\"\"\"\"\"\" 88 88 88 \n `888' 88\" 88 .a8P Y8a. .a8P 88 88 \"8b, ,aa 88 88 88, \n `8' 888888888888 88888888Y\"' `\"Y8888Y\"' 88 88 `\\\"Ybbd8\\\"'88 88 \\\"Y888 \"\n"; + private static final List<String> pics = Arrays + .asList(gothic, goofy, fuzzy, fourtops, fender, univers); - public static String listToString(List<String> list) { - StringBuilder sb = new StringBuilder(); - for (String s : list) { - sb.append(s + ","); + public static String listToString(List<String> list) { + StringBuilder sb = new StringBuilder(); + for (String s : list) { + sb.append(s + ","); + } + if (sb.length() > 0) { + sb.setLength(sb.length() - 1); + } + return sb.toString(); } - if (sb.length() > 0) { - sb.setLength(sb.length() - 1); - } - return sb.toString(); - } - public static String getPic() { - StringBuffer asciPic = new StringBuffer(); - asciPic.append("\n=====================================================================\n"); - asciPic.append("\t\t\t=========================================================\n"); - asciPic.append("=====================================================================\n"); - asciPic.append(pics.get(new Random().nextInt(GemStringUtils.pics.size()))); - asciPic.append("=====================================================================\n"); - asciPic.append("\t\t\t=========================================================\n"); - asciPic.append("=====================================================================\n"); - return asciPic.toString(); - } + public static String getPic() { + StringBuffer asciPic = new StringBuffer(); + asciPic.append("\n=====================================================================\n"); + asciPic.append("\t\t\t=========================================================\n"); + asciPic.append("=====================================================================\n"); + asciPic.append(pics.get(new Random().nextInt(GemStringUtils.pics.size()))); + asciPic.append("=====================================================================\n"); + asciPic.append("\t\t\t=========================================================\n"); + asciPic.append("=====================================================================\n"); + return asciPic.toString(); + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/Main.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/Main.java index c10de94..882dfa0 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/Main.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/Main.java @@ -28,31 +28,31 @@ public final class Main { - private static final Logger LOG = LoggerFactory.getLogger(Main.class); + private static final Logger LOG = LoggerFactory.getLogger(Main.class); - public static void main(final String[] args) { - LOG.info("VZD-Client started"); - LOG.info(GemStringUtils.getPic()); - ConfigHandler.init(args); - start(); - } + public static void main(final String[] args) { + LOG.info("VZD-Client started"); + LOG.info(GemStringUtils.getPic()); + ConfigHandler.init(args); + start(); + } - private static void start() { - ExecutionCollection.init(new GemApiClient()); - List<CommandType> commands = new CommandsBuilder().buildCommands(); - ConfigHandler configHandler = ConfigHandler.getInstance(); - LOG.debug("============ Execution parameter ============"); - LOG.debug("Server: " + configHandler.getBasePath()); - LOG.debug("OAuth Server: " + configHandler.getRetryingOAuthPath()); - LOG.debug("Command data: " + configHandler.getCommandsPath()); - LOG.debug("Commands in progress: " + commands.size()); - LOG.debug("============================================="); - new ExecutionController().execute(commands); - } + private static void start() { + ExecutionCollection.init(new GemApiClient()); + List<CommandType> commands = new CommandsBuilder().buildCommands(); + ConfigHandler configHandler = ConfigHandler.getInstance(); + LOG.debug("============ Execution parameter ============"); + LOG.debug("Server: " + configHandler.getBasePath()); + LOG.debug("OAuth Server: " + configHandler.getRetryingOAuthPath()); + LOG.debug("Command data: " + configHandler.getCommandsPath()); + LOG.debug("Commands in progress: " + commands.size()); + LOG.debug("============================================="); + new ExecutionController().execute(commands); + } - // <editor-fold desc="Private Constructor"> - private Main() { - super(); - } - // </editor-fold> + // <editor-fold desc="Private Constructor"> + private Main() { + super(); + } + // </editor-fold> } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemCertificateAdministrationApi.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemCertificateAdministrationApi.java index ba9db31..0d12288 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemCertificateAdministrationApi.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemCertificateAdministrationApi.java @@ -33,198 +33,198 @@ */ public class GemCertificateAdministrationApi extends CertificateAdministrationApi { - private GemApiClient localVarApiClient; + private GemApiClient localVarApiClient; - public GemCertificateAdministrationApi(GemApiClient apiClient) { - this.localVarApiClient = apiClient; - } - - @Override - public okhttp3.Call readDirectoryCertificatesCall(String uid, String certificateEntryID, - String entryType, String telematikID, String professionOID, String usage, - final ApiCallback _callback) throws ApiException { - Object localVarPostBody = null; - - // create path and map variables - String localVarPath = "/DirectoryEntries/Certificates"; - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - if (uid != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("uid", uid)); - } - - if (certificateEntryID != null) { - localVarQueryParams.addAll( - localVarApiClient.parameterToPair("certificateEntryID", certificateEntryID)); - } - - if (entryType != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("entryType", entryType)); + public GemCertificateAdministrationApi(GemApiClient apiClient) { + this.localVarApiClient = apiClient; } - if (telematikID != null) { - localVarQueryParams - .addAll(localVarApiClient.parameterToPair("telematikID", telematikID)); + @Override + public okhttp3.Call readDirectoryCertificatesCall(String uid, String certificateEntryID, + String entryType, String telematikID, String professionOID, String usage, + final ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries/Certificates"; + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + if (uid != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("uid", uid)); + } + + if (certificateEntryID != null) { + localVarQueryParams.addAll( + localVarApiClient.parameterToPair("certificateEntryID", certificateEntryID)); + } + + if (entryType != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("entryType", entryType)); + } + + if (telematikID != null) { + localVarQueryParams + .addAll(localVarApiClient.parameterToPair("telematikID", telematikID)); + } + + if (professionOID != null) { + localVarQueryParams + .addAll(localVarApiClient.parameterToPair("professionOID", professionOID)); + } + + if (usage != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("usage", usage)); + } + + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = {"application/json"}; + + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + //Setze Auth token + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient + .selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, + localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, + localVarAuthNames, _callback); } - if (professionOID != null) { - localVarQueryParams - .addAll(localVarApiClient.parameterToPair("professionOID", professionOID)); + @Override + public okhttp3.Call addDirectoryEntryCertificateCall(String uid, + UserCertificate userCertificate, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = userCertificate; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/Certificates" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + //Setze Auth token + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient + .selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, + localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, + localVarAuthNames, _callback); } - if (usage != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("usage", usage)); + @Override + public okhttp3.Call deleteDirectoryEntryCertificateCall(String uid, String certificateEntryID, + final ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/Certificates/{certificateEntryID}" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())) + .replaceAll("\\{" + "certificateEntryID" + "\\}", + localVarApiClient.escapeString(certificateEntryID.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient + .selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + //Set OAuth2 token + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "DELETE", localVarQueryParams, localVarCollectionQueryParams, + localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, + localVarAuthNames, _callback); } - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = {"application/json"}; - - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); - } - //Setze Auth token - final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); - localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); - - final String[] localVarContentTypes = { - - }; - final String localVarContentType = localVarApiClient - .selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, - localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, - localVarAuthNames, _callback); - } - - @Override - public okhttp3.Call addDirectoryEntryCertificateCall(String uid, - UserCertificate userCertificate, final ApiCallback _callback) throws ApiException { - Object localVarPostBody = userCertificate; - - // create path and map variables - String localVarPath = "/DirectoryEntries/{uid}/Certificates" - .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - "application/json" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); - } - - //Setze Auth token - final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); - localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); - - final String[] localVarContentTypes = { - "application/json" - }; - final String localVarContentType = localVarApiClient - .selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, - localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, - localVarAuthNames, _callback); - } - - @Override - public okhttp3.Call deleteDirectoryEntryCertificateCall(String uid, String certificateEntryID, - final ApiCallback _callback) throws ApiException { - Object localVarPostBody = null; - - // create path and map variables - String localVarPath = "/DirectoryEntries/{uid}/Certificates/{certificateEntryID}" - .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())) - .replaceAll("\\{" + "certificateEntryID" + "\\}", - localVarApiClient.escapeString(certificateEntryID.toString())); - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - "application/json" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); + @Override + public okhttp3.Call modifyDirectoryEntryCertificateCall(String uid, String certificateEntryID, + UserCertificate userCertificate, final ApiCallback _callback) throws ApiException { + Object localVarPostBody = userCertificate; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/Certificates/{certificateEntryID}" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())) + .replaceAll("\\{" + "certificateEntryID" + "\\}", + localVarApiClient.escapeString(certificateEntryID.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient + .selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + //Set OAuth2 token + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "PUT", localVarQueryParams, localVarCollectionQueryParams, + localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, + localVarAuthNames, _callback); } - - final String[] localVarContentTypes = { - - }; - final String localVarContentType = localVarApiClient - .selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - - //Set OAuth2 token - final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); - localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "DELETE", localVarQueryParams, localVarCollectionQueryParams, - localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, - localVarAuthNames, _callback); - } - - @Override - public okhttp3.Call modifyDirectoryEntryCertificateCall(String uid, String certificateEntryID, - UserCertificate userCertificate, final ApiCallback _callback) throws ApiException { - Object localVarPostBody = userCertificate; - - // create path and map variables - String localVarPath = "/DirectoryEntries/{uid}/Certificates/{certificateEntryID}" - .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())) - .replaceAll("\\{" + "certificateEntryID" + "\\}", - localVarApiClient.escapeString(certificateEntryID.toString())); - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - "application/json" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); - } - - final String[] localVarContentTypes = { - "application/json" - }; - final String localVarContentType = localVarApiClient - .selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - - //Set OAuth2 token - final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); - localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "PUT", localVarQueryParams, localVarCollectionQueryParams, - localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, - localVarAuthNames, _callback); - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemDirectoryEntryAdministrationApi.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemDirectoryEntryAdministrationApi.java index ec3bef2..8e467b9 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemDirectoryEntryAdministrationApi.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemDirectoryEntryAdministrationApi.java @@ -29,232 +29,233 @@ import java.util.List; import java.util.Map; import okhttp3.Call; +import okhttp3.Request; /** * Overrides all functions of DirectoryEntryAdministration api that build calls for the different commands to add the OAuth2 Token to the header */ public class GemDirectoryEntryAdministrationApi extends DirectoryEntryAdministrationApi { - private GemApiClient localVarApiClient; - - public GemDirectoryEntryAdministrationApi(GemApiClient apiClient) { - this.localVarApiClient = apiClient; - } - - @Override - public Call addDirectoryEntryCall(CreateDirectoryEntry createDirectoryEntry, - ApiCallback _callback) throws ApiException { - Object localVarPostBody = createDirectoryEntry; - - // create path and map variables - String localVarPath = "/DirectoryEntries"; - - List<Pair> localVarQueryParams = new ArrayList<>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<>(); - Map<String, String> localVarHeaderParams = new HashMap<>(); - Map<String, String> localVarCookieParams = new HashMap<>(); - Map<String, Object> localVarFormParams = new HashMap<>(); - final String[] localVarAccepts = { - "application/json" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); - } - - final String[] localVarContentTypes = { - "application/json" - }; - final String localVarContentType = localVarApiClient - .selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - // add OAuth2 token for authorization - final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); - localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, - localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, - localVarAuthNames, _callback); - } - - @Override - public Call deleteDirectoryEntryCall(String uid, ApiCallback _callback) - throws ApiException { - Object localVarPostBody = null; - - // create path and map variables - String localVarPath = "/DirectoryEntries/{uid}" - .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - "application/json;charset=UTF-8" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); - } - // add OAuth2 token for authorization - final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); - localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); - - final String[] localVarContentTypes = { - - }; - final String localVarContentType = localVarApiClient - .selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "DELETE", localVarQueryParams, localVarCollectionQueryParams, - localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, - localVarAuthNames, _callback); - } - - @Override - public Call modifyDirectoryEntryCall(String uid, BaseDirectoryEntry baseDirectoryEntry, - ApiCallback _callback) throws ApiException { - Object localVarPostBody = baseDirectoryEntry; - - // create path and map variables - String localVarPath = "/DirectoryEntries/{uid}/baseDirectoryEntries" - .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - "application/json" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); - } - // add OAuth2 token for authorization - final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); - localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); - - final String[] localVarContentTypes = { - "application/json" - }; - final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "PUT", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, - localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); - } - - @Override - public Call readDirectoryEntryCall(String uid, String givenName, String sn, String cn, - String displayName, String streetAddress, String postalCode, String localityName, - String stateOrProvienceName, String title, String organization, String otherName, - String specialization, String domainID, String personalEntry, String dataFromAuthority, - ApiCallback _callback) throws ApiException { - Object localVarPostBody = null; - - // create path and map variables - String localVarPath = "/DirectoryEntries"; - - List<Pair> localVarQueryParams = new ArrayList<Pair>(); - List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); - if (uid != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("uid", uid)); - } - - if (givenName != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("givenName", givenName)); - } - - if (sn != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("sn", sn)); - } - - if (cn != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("cn", cn)); - } - - if (displayName != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("displayName", displayName)); - } - - if (streetAddress != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("streetAddress", streetAddress)); - } + private GemApiClient localVarApiClient; - if (postalCode != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("postalCode", postalCode)); + public GemDirectoryEntryAdministrationApi(GemApiClient apiClient) { + this.localVarApiClient = apiClient; } - if (localityName != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("localityName", localityName)); + @Override + public Call addDirectoryEntryCall(CreateDirectoryEntry createDirectoryEntry, + ApiCallback _callback) throws ApiException { + Object localVarPostBody = createDirectoryEntry; + + // create path and map variables + String localVarPath = "/DirectoryEntries"; + + List<Pair> localVarQueryParams = new ArrayList<>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<>(); + Map<String, String> localVarHeaderParams = new HashMap<>(); + Map<String, String> localVarCookieParams = new HashMap<>(); + Map<String, Object> localVarFormParams = new HashMap<>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient + .selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + // add OAuth2 token for authorization + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, + localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, + localVarAuthNames, _callback); } - if (stateOrProvienceName != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("stateOrProvienceName", stateOrProvienceName)); + @Override + public Call deleteDirectoryEntryCall(String uid, ApiCallback _callback) + throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json;charset=UTF-8" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + // add OAuth2 token for authorization + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient + .selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "DELETE", localVarQueryParams, localVarCollectionQueryParams, + localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, + localVarAuthNames, _callback); } - if (title != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("title", title)); + @Override + public Call modifyDirectoryEntryCall(String uid, BaseDirectoryEntry baseDirectoryEntry, + ApiCallback _callback) throws ApiException { + Object localVarPostBody = baseDirectoryEntry; + + // create path and map variables + String localVarPath = "/DirectoryEntries/{uid}/baseDirectoryEntries" + .replaceAll("\\{" + "uid" + "\\}", localVarApiClient.escapeString(uid.toString())); + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + // add OAuth2 token for authorization + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "PUT", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - if (organization != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("organization", organization)); + @Override + public Call readDirectoryEntryCall(String uid, String givenName, String sn, String cn, + String displayName, String streetAddress, String postalCode, String localityName, + String stateOrProvienceName, String title, String organization, String otherName, + String specialization, String domainID, String personalEntry, String dataFromAuthority, + ApiCallback _callback) throws ApiException { + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/DirectoryEntries"; + + List<Pair> localVarQueryParams = new ArrayList<Pair>(); + List<Pair> localVarCollectionQueryParams = new ArrayList<Pair>(); + if (uid != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("uid", uid)); + } + + if (givenName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("givenName", givenName)); + } + + if (sn != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("sn", sn)); + } + + if (cn != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("cn", cn)); + } + + if (displayName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("displayName", displayName)); + } + + if (streetAddress != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("streetAddress", streetAddress)); + } + + if (postalCode != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("postalCode", postalCode)); + } + + if (localityName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("localityName", localityName)); + } + + if (stateOrProvienceName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("stateOrProvienceName", stateOrProvienceName)); + } + + if (title != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("title", title)); + } + + if (organization != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("organization", organization)); + } + + if (otherName != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("otherName", otherName)); + } + + if (specialization != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("specialization", specialization)); + } + + if (domainID != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("domainID", domainID)); + } + + if (personalEntry != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("personalEntry", personalEntry)); + } + + if (dataFromAuthority != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("dataFromAuthority", dataFromAuthority)); + } + + Map<String, String> localVarHeaderParams = new HashMap<String, String>(); + Map<String, String> localVarCookieParams = new HashMap<String, String>(); + Map<String, Object> localVarFormParams = new HashMap<String, Object>(); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + // add OAuth2 token for authorization + final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); + localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); + + final String[] localVarContentTypes = { + + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + localVarHeaderParams.put("Content-Type", localVarContentType); + + String[] localVarAuthNames = new String[]{"OAuth2"}; + return localVarApiClient + .buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, + localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - - if (otherName != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("otherName", otherName)); - } - - if (specialization != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("specialization", specialization)); - } - - if (domainID != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("domainID", domainID)); - } - - if (personalEntry != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("personalEntry", personalEntry)); - } - - if (dataFromAuthority != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("dataFromAuthority", dataFromAuthority)); - } - - Map<String, String> localVarHeaderParams = new HashMap<String, String>(); - Map<String, String> localVarCookieParams = new HashMap<String, String>(); - Map<String, Object> localVarFormParams = new HashMap<String, Object>(); - final String[] localVarAccepts = { - "application/json" - }; - final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); - if (localVarAccept != null) { - localVarHeaderParams.put("Accept", localVarAccept); - } - // add OAuth2 token for authorization - final OAuth oAuth2Token = (OAuth) localVarApiClient.getAuthentication("OAuth"); - localVarHeaderParams.put("Authorization", "Bearer " + oAuth2Token.getAccessToken()); - - final String[] localVarContentTypes = { - - }; - final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); - localVarHeaderParams.put("Content-Type", localVarContentType); - - String[] localVarAuthNames = new String[]{"OAuth2"}; - return localVarApiClient - .buildCall(localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, - localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilder.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilder.java index 92d78ab..b292f77 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilder.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilder.java @@ -16,20 +16,25 @@ package de.gematik.ti.epa.vzd.gemClient.command; -import de.gematik.ti.epa.vzd.gemClient.exceptions.ReadException; +import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; import de.gematik.ti.epa.vzd.gemClient.invoker.ConfigHandler; +import de.gematik.ti.epa.vzd.gemClient.exceptions.ReadException; import generated.CommandListType; import generated.CommandType; import generated.ObjectFactory; import java.io.File; import java.io.IOException; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; @@ -40,49 +45,84 @@ */ public class CommandsBuilder { - private JAXBContext jaxbContext; - private Logger LOGGER = LoggerFactory.getLogger(CommandsBuilder.class); + private JAXBContext jaxbContext; + private Logger LOG = LoggerFactory.getLogger(CommandsBuilder.class); + + private static DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); - private static DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + /** + * Standard constructor initialize an jaxbContext + */ + public CommandsBuilder() { + try { + jaxbContext = JAXBContext.newInstance(ObjectFactory.class); + } catch (JAXBException e) { + throw new ReadException("Error occurred by creating JAXBContext"); + } + } + + /** + * Reads the given command file and creates a list of commands to execute + * + * @return CommandListType - List of all commands to execute + */ + public List<CommandType> buildCommands() { + CommandListType commandList; + ConfigHandler configHandler = ConfigHandler.getInstance(); + try { + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + Document doc = builderFactory.newDocumentBuilder() + .parse(new File(configHandler.getCommandsPath())); + Object obj = unmarshaller.unmarshal(doc); + Object commands = ((JAXBElement) obj).getValue(); + if (commands instanceof CommandListType) { + commandList = (CommandListType) commands; + if(addId(commandList)){ + writeCommandDataWithIds(commandList); + } + LOG.debug("Commands have been build"); + return commandList.getCommand(); + } + } catch (ParserConfigurationException | SAXException | JAXBException e) { + throw new ReadException( + "An error have been occurred while reading your command file. Please check if this file is a valid .xml file"); + } catch (IOException e) { + throw new ReadException( + "A problem with your named file have occurred. Please if check " + configHandler.getCommandsPath() + " exist"); + } + return null; + } - /** - * Standard constructor initialize an jaxbContext - */ - public CommandsBuilder() { - try { - jaxbContext = JAXBContext.newInstance(ObjectFactory.class); - } catch (JAXBException e) { - throw new ReadException("Error occurred by creating JAXBContext"); + private boolean addId(CommandListType commandList) { + int counter = 1; + boolean idsSet = false; + Set<String> givenIds = new HashSet<>(); + for (CommandType command : commandList.getCommand()) { + if (!StringUtils.isBlank(command.getCommandId()) && !givenIds.add(command.getCommandId())) { + throw new CommandException("The predefined ID \"" + command.getCommandId() + "\" occurs twice"); + } + } + for (CommandType command : commandList.getCommand()) { + while (givenIds.contains(String.valueOf(counter))) { + counter++; + } + if (StringUtils.isBlank(command.getCommandId())) { + command.setCommandId(String.valueOf(counter)); + counter++; + idsSet = true; + } + } + if (idsSet) { + LOG.debug("IDs have been set automatically"); + } + return idsSet; } - } - /** - * Reads the given command file and creates a list of commands to execute - * - * @return CommandListType - List of all commands to execute - */ - public List<CommandType> buildCommands() { - CommandListType commandList; - ConfigHandler configHandler = ConfigHandler.getInstance(); - try { - Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); - Document doc = builderFactory.newDocumentBuilder() - .parse(new File(configHandler.getCommandsPath())); - Object obj = unmarshaller.unmarshal(doc); - Object commands = ((JAXBElement) obj).getValue(); - if (commands instanceof CommandListType) { - commandList = (CommandListType) commands; - LOGGER.debug("Commands have been build"); - return commandList.getCommand(); - } - } catch (ParserConfigurationException | SAXException | JAXBException e) { - throw new ReadException( - "An error have been occurred while reading your command file. Please check if this file is a valid .xml file"); - } catch (IOException e) { - throw new ReadException( - "A problem with your named file have occurred. Please if check " + configHandler.getCommandsPath() + " exist"); + private void writeCommandDataWithIds(CommandListType commandList) throws JAXBException { + JAXBElement<CommandListType> element = new ObjectFactory().createCommandList(commandList); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + marshaller.marshal(element, new File(ConfigHandler.getInstance().getCommandsPath())); } - return null; - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/ExecutionController.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/ExecutionController.java index a69e454..0417f0e 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/ExecutionController.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/ExecutionController.java @@ -21,6 +21,7 @@ import de.gematik.ti.epa.vzd.gemClient.command.commandExecutions.ExecutionCollection; import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; import generated.CommandType; + import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -34,101 +35,101 @@ */ public class ExecutionController { - private Logger LOG = LoggerFactory.getLogger(ExecutionController.class); + private Logger LOG = LoggerFactory.getLogger(ExecutionController.class); - /** - * Order the commands to the specific executions where they belong and than call the execution functions of the specific executors - * - * @param commandList - */ - public void execute(List<CommandType> commandList) { - Map<String, Boolean> report = new HashMap<>(); - loadCommands(commandList); - LOG.debug("Execution -> Run executions"); - boolean correctExecution = true; - for (ExecutionBase executor : ExecutionCollection.getInstance().getExecutors()) { - String executorName = extractExecutorName(executor); - if (!executor.executeCommands()) { - correctExecution = false; - report.put(executorName, false); - LOG.error("Error while execute commands in " + executorName); - } else { - report.put(executorName, true); - LOG.debug("All commands of " + executorName + " operated correctly"); - } - if (!executor.postCheck()) { - throw new CommandException("Command executed, but postcheck failed!"); - } + /** + * Order the commands to the specific executions where they belong and than call the execution functions of the specific executors + * + * @param commandList + */ + public void execute(List<CommandType> commandList) { + Map<String, Boolean> report = new HashMap<>(); + loadCommands(commandList); + LOG.debug("Execution -> Run executions"); + boolean correctExecution = true; + for (ExecutionBase executor : ExecutionCollection.getInstance().getExecutors()) { + String executorName = extractExecutorName(executor); + if (!executor.executeCommands()) { + correctExecution = false; + report.put(executorName, false); + LOG.error("Error while execute commands in " + executorName); + } else { + report.put(executorName, true); + LOG.debug("All commands of " + executorName + " operated correctly"); + } + if (!executor.postCheck()) { + throw new CommandException("Command executed, but postcheck failed!"); + } + } + logReport(report, correctExecution); + return; } - logReport(report, correctExecution); - return; - } - /** - * Gets the name of the specific executor for logging - * - * @param executor - * @return - */ - private String extractExecutorName(ExecutionBase executor) { - String[] splitClass = executor.getClass().getName().split("\\."); - return splitClass[splitClass.length - 1]; - } - - private void logReport(Map<String, Boolean> report, boolean correctExecution) { - Iterator<String> keys = report.keySet().iterator(); - while (keys.hasNext()) { - String key = keys.next(); - LOG.info(report.get(key) + " <-- All executions for " + key + " run correctly"); + /** + * Gets the name of the specific executor for logging + * + * @param executor + * @return + */ + private String extractExecutorName(ExecutionBase executor) { + String[] splitClass = executor.getClass().getName().split("\\."); + return splitClass[splitClass.length - 1]; } - String path = System.getProperties().getProperty("l4j.logDir") == null ? - System.getProperties().getProperty("java.io.tmpdir") + "logs" - : System.getProperties().getProperty("l4j.logDir"); - LOG.info("Execution -> All executions done" + (correctExecution ? " correctly" - : ". Some commands failed. Please look at the logfile at " + path)); - } - private void loadCommands(List<CommandType> commandList) { - LOG.debug("Execution -> Precheck started"); - boolean commandError = false; - for (CommandType command : commandList) { - boolean unknownCommand = true; - for (ExecutionBase specificExecutor : ExecutionCollection.getInstance() - .getExecutors()) { - if (specificExecutor - .canHandleCommand(CommandNamesEnum.getEntry(command.getName()))) { - unknownCommand = false; - if (!specificExecutor.preCheck(command)) { - commandError = true; - } + private void logReport(Map<String, Boolean> report, boolean correctExecution) { + Iterator<String> keys = report.keySet().iterator(); + while (keys.hasNext()) { + String key = keys.next(); + LOG.info(report.get(key) + " <-- All executions for " + key + " run correctly"); } - } - if (unknownCommand) { - LOG.error("Unknown command " + command.getName() + "\n" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - commandError = true; - } + String path = System.getProperties().getProperty("l4j.logDir") == null ? + System.getProperties().getProperty("java.io.tmpdir") + "logs" + : System.getProperties().getProperty("l4j.logDir"); + LOG.info("Execution -> All executions done" + (correctExecution ? " correctly" + : ". Some commands failed. Please look at the logfile at " + path)); } - if (commandError) { - throw new CommandException("Commands not executed, preCheck failed!"); + + private void loadCommands(List<CommandType> commandList) { + LOG.debug("Execution -> Precheck started"); + boolean commandError = false; + for (CommandType command : commandList) { + boolean unknownCommand = true; + for (ExecutionBase specificExecutor : ExecutionCollection.getInstance() + .getExecutors()) { + if (specificExecutor + .canHandleCommand(CommandNamesEnum.getEntry(command.getName()))) { + unknownCommand = false; + if (!specificExecutor.preCheck(command)) { + commandError = true; + } + } + } + if (unknownCommand) { + LOG.error("Unknown command " + command.getName() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + commandError = true; + } + } + if (commandError) { + throw new CommandException("Commands not executed, preCheck failed!"); + } + LOG.debug("Execution -> Precheck successful"); } - LOG.debug("Execution -> Precheck successful"); - } - /** - * If a command was identified with the wrong name, the name can be changed and reordered by this function. Only call this in pre-check phase! - * - * @param command with another name - * @return - */ - public boolean reorder(CommandType command) { - for (ExecutionBase specificExecutor : ExecutionCollection.getInstance().getExecutors()) { - if (specificExecutor.canHandleCommand(CommandNamesEnum.getEntry(command.getName()))) { - return specificExecutor.preCheck(command); - } + /** + * If a command was identified with the wrong name, the name can be changed and reordered by this function. Only call this in pre-check phase! + * + * @param command with another name + * @return + */ + public boolean reorder(CommandType command) { + for (ExecutionBase specificExecutor : ExecutionCollection.getInstance().getExecutors()) { + if (specificExecutor.canHandleCommand(CommandNamesEnum.getEntry(command.getName()))) { + return specificExecutor.preCheck(command); + } + } + return false; } - return false; - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/Transformer.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/Transformer.java index 8a0be01..969fa10 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/Transformer.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/Transformer.java @@ -32,88 +32,90 @@ */ public class Transformer { - /** - * Transforms CommandType to BaseDirectorEntry - * - * @param command <type>CommandType</type> - * @return baseDirectoryEntry <type>BaseDirectoryEntry</type> - */ - public static BaseDirectoryEntry getBaseDirectoryEntryFromCommandType(CommandType command) { - BaseDirectoryEntry baseDirectoryEntry = new BaseDirectoryEntry(); + /** + * Transforms CommandType to BaseDirectorEntry + * + * @param command <type>CommandType</type> + * @return baseDirectoryEntry <type>BaseDirectoryEntry</type> + */ + public static BaseDirectoryEntry getBaseDirectoryEntryFromCommandType(CommandType command) { + BaseDirectoryEntry baseDirectoryEntry = new BaseDirectoryEntry(); - if (command.getDn() != null) { - baseDirectoryEntry.setDn(getDnFromDnType(command.getDn())); + if (command.getDn() != null) { + baseDirectoryEntry.setDn(getDnFromDnType(command.getDn())); + } + baseDirectoryEntry.setDisplayName(command.getDisplayName()); + baseDirectoryEntry.setStreetAddress(command.getStreetAddress()); + baseDirectoryEntry.setPostalCode(command.getPostalCode()); + baseDirectoryEntry.setLocalityName(command.getLocalityName()); + baseDirectoryEntry.setStateOrProvinceName(command.getStateOrProvinceName()); + // This setter is manually added to the generated class BaseDirectoryEntry + baseDirectoryEntry.setCn(command.getCn()); + baseDirectoryEntry.setTitle(command.getTitle()); + baseDirectoryEntry.setOrganization(command.getOrganization()); + baseDirectoryEntry.setOtherName(command.getOtherName()); + if (!command.getSpecialization().isEmpty()) { + baseDirectoryEntry.setSpecialization(command.getSpecialization()); + } + if (!command.getDomainID().isEmpty()) { + baseDirectoryEntry.setDomainID(command.getDomainID()); + } + return baseDirectoryEntry; } - baseDirectoryEntry.setDisplayName(command.getDisplayName()); - baseDirectoryEntry.setStreetAddress(command.getStreetAddress()); - baseDirectoryEntry.setPostalCode(command.getPostalCode()); - baseDirectoryEntry.setLocalityName(command.getLocalityName()); - baseDirectoryEntry.setStateOrProvinceName(command.getStateOrProvinceName()); - // This setter is manually added to the generated class BaseDirectoryEntry - baseDirectoryEntry.setCn(command.getCn()); - baseDirectoryEntry.setTitle(command.getTitle()); - baseDirectoryEntry.setOrganization(command.getOrganization()); - baseDirectoryEntry.setOtherName(command.getOtherName()); - if (command.getSpecialization().size() != 0) { - baseDirectoryEntry.setSpecialization(command.getSpecialization()); - } - if (command.getDomainID().size() != 0) { - baseDirectoryEntry.setDomainID(command.getDomainID()); - } - return baseDirectoryEntry; - } - private static DistinguishedName getDnFromDnType(DistinguishedNameType dn) { - DistinguishedName distinguishedName = new DistinguishedName(); - distinguishedName.setUid(dn.getUid()); - distinguishedName.setCn(dn.getCn()); - if (dn.getDc().size() != 0) { - distinguishedName.setDc(dn.getDc()); - } - if (dn.getOu().size() != 0) { - distinguishedName.setOu(dn.getOu()); + private static DistinguishedName getDnFromDnType(DistinguishedNameType dn) { + DistinguishedName distinguishedName = new DistinguishedName(); + distinguishedName.setUid(dn.getUid()); + distinguishedName.setCn(dn.getCn()); + if (!dn.getDc().isEmpty()) { + distinguishedName.setDc(dn.getDc()); + } + if (!dn.getOu().isEmpty()) { + distinguishedName.setOu(dn.getOu()); + } + return distinguishedName; } - return distinguishedName; - } - /** - * Transforms CommandType to CreateDirectoryEntry - * - * @param command <type>CommandType</type> - * @return baseDirectoryEntry <type>BaseDirectoryEntry</type> - */ - public static CreateDirectoryEntry getCreateDirectoryEntry(CommandType command) { - CreateDirectoryEntry createDirectoryEntry = new CreateDirectoryEntry(); - createDirectoryEntry.setDirectoryEntryBase(getBaseDirectoryEntryFromCommandType(command)); - if (command.getUserCertificate() != null) { - createDirectoryEntry - .setUserCertificates(getUserCertificates(command.getUserCertificate())); + /** + * Transforms CommandType to CreateDirectoryEntry + * + * @param command <type>CommandType</type> + * @return baseDirectoryEntry <type>BaseDirectoryEntry</type> + */ + public static CreateDirectoryEntry getCreateDirectoryEntry(CommandType command) { + CreateDirectoryEntry createDirectoryEntry = new CreateDirectoryEntry(); + createDirectoryEntry.setDirectoryEntryBase(getBaseDirectoryEntryFromCommandType(command)); + if (!command.getUserCertificate().isEmpty()) { + createDirectoryEntry.setUserCertificates(new ArrayList<>()); + for (UserCertificateType cert:command.getUserCertificate()){ + createDirectoryEntry.getUserCertificates().add(getUserCertificate(cert)); + } + } + return createDirectoryEntry; } - return createDirectoryEntry; - } - private static List<UserCertificate> getUserCertificates( - UserCertificateType userCertificateType) { - List<UserCertificate> userCertificateList = new ArrayList<>(); - UserCertificate userCertificate = new UserCertificate(); + private static UserCertificate getUserCertificate( + UserCertificateType userCertificateType) { - if (userCertificateType.getDn() != null) { - userCertificate.setDn(getDnFromDnType(userCertificateType.getDn())); - } - userCertificate.setTelematikID(userCertificateType.getTelematikID()); - if (userCertificateType.getUsage().size() != 0) { - for (String usage : userCertificateType.getUsage()) { - userCertificate.addUsageItem(UserCertificate.UsageEnum.fromValue(usage)); - } - } - userCertificate.setDescription(userCertificateType.getDescription()); - if (StringUtils.isNoneBlank(userCertificateType.getUserCertificate())) { - userCertificate - .setUserCertificate(userCertificateType.getUserCertificate().replaceAll("\\n", "")); - } + UserCertificate userCertificate = new UserCertificate(); + + if (userCertificateType.getDn() != null) { + userCertificate.setDn(getDnFromDnType(userCertificateType.getDn())); + } + userCertificate.setTelematikID(userCertificateType.getTelematikID()); + if (!userCertificateType.getUsage().isEmpty()) { + for (String usage : userCertificateType.getUsage()) { + userCertificate.addUsageItem(UserCertificate.UsageEnum.fromValue(usage)); + } + } + userCertificate.setDescription(userCertificateType.getDescription()); + if (StringUtils.isNoneBlank(userCertificateType.getUserCertificate())) { + String cert = userCertificateType.getUserCertificate().replaceAll("[\n\r]", "").trim(); + userCertificate.setUserCertificate(cert); + } - userCertificateList.add(userCertificate); - return userCertificateList; - } + + return userCertificate; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryCertExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryCertExecution.java index e190c77..e267244 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryCertExecution.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryCertExecution.java @@ -30,7 +30,10 @@ import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; import generated.CommandType; import generated.DistinguishedNameType; +import generated.UserCertificateType; +import java.security.cert.CertificateRevokedException; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,138 +42,138 @@ */ public class AddDirEntryCertExecution extends ExecutionBase { - private CertificateAdministrationApi certificateAdministrationApi; - private Logger LOG = LoggerFactory.getLogger(AddDirEntryExecution.class); + private CertificateAdministrationApi certificateAdministrationApi; + private Logger LOG = LoggerFactory.getLogger(CertificateAdministrationApi.class); - public AddDirEntryCertExecution(GemApiClient api) { - super(api, CommandNamesEnum.ADD_DIR_CERT); - certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); - } - - @Override - public boolean checkValidation(CommandType command) { - if (command.getUserCertificate() == null) { - return false; - } - if (StringUtils.isBlank(command.getUserCertificate().getUserCertificate())) { - return false; - } - String uid = null; - if (command.getDn() != null) { - uid = command.getDn().getUid(); - } - if (StringUtils.isBlank(uid)) { - DistinguishedNameType certDn = command.getUserCertificate().getDn(); - if (certDn != null) { - uid = certDn.getUid(); - } - } - if (StringUtils.isBlank(uid)) { - return false; - } - return true; - } - - @Override - public boolean executeCommands() { - boolean runSuccessful = true; - if (commands.size() == 0) { - return true; + public AddDirEntryCertExecution(GemApiClient api) { + super(api, CommandNamesEnum.ADD_DIR_CERT); + certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); } - for (CommandType command : commands) { - if (isEntryPresent(command)) { - try { - executeCommand(command); - } catch (Exception ex) { - LOG.error("An error have occured: " + ex.getMessage()); - runSuccessful = false; + + @Override + public boolean checkValidation(CommandType command) { + if (command.getUserCertificate() == null) { + LOG.error("No certificate element found"); + return false; } - } else { - runSuccessful = doModify(command); - } + if (command.getUserCertificate().isEmpty()) { + LOG.error("No certificate delivered"); + return false; + } + String uid= null; + for (UserCertificateType cert : command.getUserCertificate()) { + String uidDn = null; + if (command.getDn() != null) { + uidDn = command.getDn().getUid(); + } + if (StringUtils.isBlank(uidDn)) { + DistinguishedNameType certDn = cert.getDn(); + if (certDn != null) { + uid = certDn.getUid(); + } + } + if(StringUtils.isBlank(uid)){ + uid = uidDn; + } + + if (StringUtils.isBlank(uid)) { + LOG.error("No uid delivered"); + return false; + } + if(!StringUtils.isNotBlank(uid) && !StringUtils.isNotBlank(uidDn) && !uid.equals(uidDn)) { + LOG.error("Mismatching uid delivered"); + return false; + } + } + return true; } - return runSuccessful; - } - - private ApiResponse<DistinguishedName> executeCommand(CommandType command) { - apiClient.validateToken(); - boolean runSucsessfull = true; - CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); - - ApiResponse<DistinguishedName> response = null; - for (UserCertificate userCertificate : createDirectoryEntry.getUserCertificates()) { - try { - String uid = getUid(createDirectoryEntry.getDirectoryEntryBase(), userCertificate); - response = addSingleCertificate(uid, userCertificate); - if (response.getStatusCode() == 201) { - LOG.debug( - "Certificate successful added: \n" + userCertificate); + + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + for (CommandType command : commands) { + LOG.debug("--- Command " + command.getCommandId() + " ---"); + if (isEntryPresent(command)) { + try { + executeCommand(command); + } catch (Exception ex) { + LOG.error("An error have occured: " + ex.getMessage()); + runSuccessful = false; + } + } else { + LOG.error("Entry for this Certificate could not be found: " + + Transformer.getCreateDirectoryEntry(command)); + runSuccessful = false; + } + LOG.debug("--- Command " + command.getCommandId() + " end ---"); } - } catch (ApiException e) { - runSucsessfull = false; - LOG.error( - "Something went wrong will adding certificate. Responsecode: " + e.getCode() - + " certificate: " + userCertificate - .getUserCertificate()); - } + return runSuccessful; } - if (!runSucsessfull) { - throw new CommandException( - "At least one certificate could not be added in:" + "\n" + Transformer - .getCreateDirectoryEntry(command)); + + private ApiResponse<DistinguishedName> executeCommand(CommandType command) { + apiClient.validateToken(); + + boolean runSuccessful = true; + CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); + + ApiResponse<DistinguishedName> response = null; + for (UserCertificate userCertificate : createDirectoryEntry.getUserCertificates()) { + try { + String uid = getUid(createDirectoryEntry.getDirectoryEntryBase(), userCertificate); + response = addSingleCertificate(uid, userCertificate); + if (response.getStatusCode() == HttpStatus.SC_CREATED) { + LOG.debug( + "Certificate successful added: \n" + userCertificate); + } + } catch (ApiException e) { + runSuccessful = false; + LOG.error( + "Something went wrong will adding certificate. Responsecode: " + e.getCode() + + " certificate: " + userCertificate.getUserCertificate()); + } + } + if (!runSuccessful) { + throw new CommandException( + "At least one certificate could not be added in:" + "\n" + Transformer + .getCreateDirectoryEntry(command)); + } + + return response; } - return response; - } + private String getUid(BaseDirectoryEntry directoryEntryBase, UserCertificate userCertificate) { + // uid present because checked in validation + String uidCert = null; + String uidEntry = null; - private String getUid(BaseDirectoryEntry directoryEntryBase, UserCertificate userCertificate) { - // uid present because checked in validation - String uidCert = null; - String uidEntry = null; + if (userCertificate.getDn() != null) { + uidCert = userCertificate.getDn().getUid(); + } - if (userCertificate.getDn() != null) { - uidCert = userCertificate.getDn().getUid(); + if (directoryEntryBase != null) { + DistinguishedName dn = directoryEntryBase.getDn(); + if (dn != null) { + uidEntry = dn.getUid(); + } + } + return uidCert == null ? uidEntry : uidCert; } - if (directoryEntryBase != null) { - DistinguishedName dn = directoryEntryBase.getDn(); - if (dn != null) { - uidEntry = dn.getUid(); - } - } - return uidCert == null ? uidEntry : uidCert; - } - - private ApiResponse<DistinguishedName> addSingleCertificate(String uid, - UserCertificate userCertificate) throws ApiException { - return certificateAdministrationApi - .addDirectoryEntryCertificateWithHttpInfo(uid, userCertificate); - } - - private boolean doModify(CommandType command) { - LOG.debug( - "Certificate is already present in VZD. Will Proceed with modify certificate entry command"); - try { - ExecutionCollection.getInstance().getModifyDirEntryCertExecution() - .executeCommand(command); - return true; - } catch (Exception ex) { - LOG.error( - "Modify certificate entry execution failed. " + ex.getMessage() + "\n" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - return false; - } - } - - @Override - public boolean postCheck() { - try { - super.postCheck(); - return true; - } catch (Exception ex) { - ex.printStackTrace(); + private ApiResponse<DistinguishedName> addSingleCertificate(String uid, + UserCertificate userCertificate) throws ApiException { + return certificateAdministrationApi + .addDirectoryEntryCertificateWithHttpInfo(uid, userCertificate); } - return false; - } + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + } + + return false; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecution.java index 69dafa7..a310d60 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecution.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecution.java @@ -29,6 +29,7 @@ import generated.CommandType; import generated.UserCertificateType; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,108 +38,106 @@ */ public class AddDirEntryExecution extends ExecutionBase { - private final DirectoryEntryAdministrationApi directoryEntryAdministrationApi; - private Logger LOG = LoggerFactory.getLogger(AddDirEntryExecution.class); + private final DirectoryEntryAdministrationApi directoryEntryAdministrationApi; + private Logger LOG = LoggerFactory.getLogger(AddDirEntryExecution.class); - public AddDirEntryExecution(GemApiClient api) { - super(api, CommandNamesEnum.ADD_DIR_ENTRY); - directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); - } - - @Override - public boolean checkValidation(CommandType command) { - boolean check = true; - UserCertificateType userCertificateType = command.getUserCertificate(); - if (userCertificateType == null) { - LOG.error( - "Missing element \"UserCertificate\" " + command.getName() + "\n" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - check = false; - } else { - String telematikId = userCertificateType.getTelematikID(); - String userCertificate = userCertificateType.getUserCertificate(); - if ((StringUtils.isBlank(telematikId) && StringUtils - .isBlank(userCertificate))) { - check = false; - } - if (check == false) { - LOG.error( - "Missing argument -> telematikId or userCertificate for command " + command - .getName() - + "\n" + Transformer.getBaseDirectoryEntryFromCommandType(command)); - } + public AddDirEntryExecution(GemApiClient api) { + super(api, CommandNamesEnum.ADD_DIR_ENTRY); + directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); } - return check; - } - @Override - public boolean executeCommands() { - boolean runSuccessful = true; - if (commands.size() == 0) { - return true; - } - for (CommandType command : commands) { - if (!isEntryPresent(command)) { - try { - executeCommand(command); - } catch (Exception ex) { - LOG.error("An error have occured: " + ex.getMessage() + "\n" + Transformer - .getCreateDirectoryEntry(command)); - runSuccessful = false; + @Override + public boolean checkValidation(CommandType command) { + boolean check = true; + if (command.getUserCertificate().isEmpty()) { + check = false; + } else { + for (UserCertificateType userCertificateType : command.getUserCertificate()) { + String telematikId = userCertificateType.getTelematikID(); + String userCertificate = userCertificateType.getUserCertificate(); + if ((StringUtils.isBlank(telematikId) && StringUtils + .isBlank(userCertificate))) { + check = false; + } + } + } + if (StringUtils.isBlank(command.getCn())) { + check = false; } - } else { - runSuccessful = doModify(command); - } + if (check == false) { + LOG.error("Missing argument -> cn and telematikId or userCertificate have to be set " + + command.getName() + "\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command)); + } + return check; } - return runSuccessful; - } - /** - * Function that execute one command and logs the result - * - * @param command - * @return - * @throws ApiException - */ - protected ApiResponse<DistinguishedName> executeCommand(CommandType command) throws - ApiException { - apiClient.validateToken(); - CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); - ApiResponse<DistinguishedName> response = directoryEntryAdministrationApi - .addDirectoryEntryWithHttpInfo(createDirectoryEntry); - if (response.getStatusCode() == 201) { - LOG.debug("Add directory entry execution successful operated\n" + response.getData()); - } else { - throw new CommandException( - "Add directory entry execution failed. Response-Status was: " + response - .getStatusCode() + "\n" + Transformer.getCreateDirectoryEntry(command)); + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + for (CommandType command : commands) { + LOG.debug("--- Command " + command.getCommandId() + " ---"); + if (!isEntryPresent(command)) { + try { + executeCommand(command); + } catch (Exception ex) { + LOG.error("An error have occured: " + ex.getMessage() + "\n" + + Transformer.getCreateDirectoryEntry(command)); + runSuccessful = false; + } + } else { + runSuccessful = doModify(command); + } + LOG.debug("--- Command " + command.getCommandId() + " end ---"); + } + return runSuccessful; } - return response; - } - private boolean doModify(CommandType command) { - LOG.debug( - "Entry is already present in VZD. Will Proceed with modify directory entry command"); - try { - ExecutionCollection.getInstance().getModifyDirEntry().executeCommand(command); - return true; - } catch (Exception ex) { - LOG.error( - "Modify directory entry execution failed. " + ex.getMessage() + "\n" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - return false; + /** + * Function that execute one command and logs the result + * + * @param command + * @return + * @throws ApiException + */ + protected ApiResponse<DistinguishedName> executeCommand(CommandType command) throws + ApiException { + apiClient.validateToken(); + + CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); + ApiResponse<DistinguishedName> response = directoryEntryAdministrationApi + .addDirectoryEntryWithHttpInfo(createDirectoryEntry); + if (response.getStatusCode() == HttpStatus.SC_CREATED) { + LOG.debug("Add directory entry execution successful operated\n" + response.getData()); + } else { + throw new CommandException( + "Add directory entry execution failed. Response-Status was: " + response + .getStatusCode() + "\n" + Transformer.getCreateDirectoryEntry(command)); + } + return response; } - } - @Override - public boolean postCheck() { - try { - super.postCheck(); - return true; - } catch (Exception ex) { - ex.printStackTrace(); + private boolean doModify(CommandType command) { + LOG.debug( + "Entry is already present in VZD. Will Proceed with modify directory entry command"); + try { + ExecutionCollection.getInstance().getModifyDirEntry().executeCommand(command); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + return false; + } } - return false; - } + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + } + + return false; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecution.java index 5e0790c..9b5060f 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecution.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecution.java @@ -29,7 +29,9 @@ import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; import generated.CommandType; +import generated.UserCertificateType; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,114 +40,119 @@ */ public class DeleteDirEntryCertExecution extends ExecutionBase { - private CertificateAdministrationApi certificateAdministrationApi; - private Logger LOG = LoggerFactory.getLogger(AddDirEntryExecution.class); + private CertificateAdministrationApi certificateAdministrationApi; + private Logger LOG = LoggerFactory.getLogger(DeleteDirEntryCertExecution.class); - public DeleteDirEntryCertExecution(GemApiClient api) { - super(api, CommandNamesEnum.DEL_DIR_CERT); - certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); - } - - @Override - public boolean checkValidation(CommandType command) { - String uid = null; - String cn = null; - if (command.getUserCertificate() != null) { - if (command.getUserCertificate().getDn() != null) { - uid = command.getUserCertificate().getDn().getUid(); - cn = command.getUserCertificate().getDn().getCn(); - } - } - if (StringUtils.isBlank(uid)) { - if (command.getDomainID() != null) { - uid = command.getDn().getUid(); - } - } - if (StringUtils.isBlank(uid) || StringUtils.isBlank(cn)) { - return false; + public DeleteDirEntryCertExecution(GemApiClient api) { + super(api, CommandNamesEnum.DEL_DIR_CERT); + certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); } - return true; - } - - @Override - public boolean executeCommands() { - boolean runSuccessful = true; - if (commands.size() == 0) { - return true; - } - for (CommandType command : commands) { - System.out.println( - "Warning isEntryPresent abgeschlatet <- executeCommands " + this.getClass()); - try { - executeCommand(command); - } catch (Exception ex) { - LOG.error("An error have occured: " + ex.getMessage()); - runSuccessful = false; - } + + @Override + public boolean checkValidation(CommandType command) { + String uid = null; + String cn = null; + boolean check = true; + String totalUid = null; + for (UserCertificateType userCertificateType : command.getUserCertificate()) { + if (userCertificateType.getDn() != null) { + if (totalUid == null) { + totalUid = userCertificateType.getDn().getUid(); + } + uid = userCertificateType.getDn().getUid(); + cn = userCertificateType.getDn().getCn(); + } + if (StringUtils.isBlank(uid)) { + uid = command.getDn().getUid(); + } + if (StringUtils.isBlank(uid) || StringUtils.isBlank(cn) || !totalUid.equals(uid)) { + check = false; + } + } + return check; } - return runSuccessful; - } - - private void executeCommand(CommandType command) { - apiClient.validateToken(); - boolean runSucsessfull = true; - CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); - - ApiResponse<Void> response; - for (UserCertificate userCertificate : createDirectoryEntry.getUserCertificates()) { - //todo check if present mit einfügen - try { - String uid = getUid(createDirectoryEntry.getDirectoryEntryBase(), userCertificate); - String cn = command.getUserCertificate().getDn().getCn(); - response = deleteSingleCertificate(uid, cn); - if (response.getStatusCode() == 200) { - LOG.debug( - "Certificate successful deleted: \n" + userCertificate); + + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + if (commands.isEmpty()) { + return true; } - } catch (ApiException e) { - runSucsessfull = false; - LOG.error( - "Something went wrong will adding certificate. Responsecode: " + e.getCode() - + " certificate: " + userCertificate - .getUserCertificate()); - } + for (CommandType command : commands) { + LOG.debug("--- Command " + command.getCommandId() + " ---"); + if (searchByUserCertificate(command)) { + try { + executeCommand(command); + } catch (Exception ex) { + LOG.error("An error have occured: " + ex.getMessage()); + runSuccessful = false; + } + } + LOG.debug("--- Command " + command.getCommandId() + " end ---"); + } + return runSuccessful; } - if (!runSucsessfull) { - throw new CommandException( - "At least one certificate could not be added in:" + "\n" + Transformer - .getCreateDirectoryEntry(command)); + + private void executeCommand(CommandType command) { + apiClient.validateToken(); + + boolean runSucsessfull = true; + CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); + + ApiResponse<Void> response; + for (UserCertificate userCertificate : createDirectoryEntry.getUserCertificates()) { + try { + String uid = getUid(createDirectoryEntry.getDirectoryEntryBase(), userCertificate); + String cn = userCertificate.getDn().getCn(); + response = deleteSingleCertificate(uid, cn); + if (response.getStatusCode() == HttpStatus.SC_OK) { + LOG.debug( + "Certificate successful deleted: \n" + userCertificate); + } + } catch (ApiException e) { + runSucsessfull = false; + LOG.error( + "Something went wrong will deleting certificate. Responsecode: " + e.getCode() + + " certificate: " + userCertificate + .getUserCertificate()); + } + } + if (!runSucsessfull) { + throw new CommandException( + "At least one certificate could not be added in:" + "\n" + Transformer + .getCreateDirectoryEntry(command)); + } } - } - private String getUid(BaseDirectoryEntry directoryEntryBase, UserCertificate userCertificate) { - String uidCert = userCertificate.getDn().getUid(); - String uidEntry = null; + private String getUid(BaseDirectoryEntry directoryEntryBase, UserCertificate userCertificate) { + String uidCert = userCertificate.getDn().getUid(); + String uidEntry = null; - if (directoryEntryBase != null) { - DistinguishedName dn = directoryEntryBase.getDn(); - if (dn != null) { - uidEntry = dn.getUid(); - } + if (directoryEntryBase != null) { + DistinguishedName dn = directoryEntryBase.getDn(); + if (dn != null) { + uidEntry = dn.getUid(); + } + } + return uidCert == null ? uidEntry : uidCert; } - return uidCert == null ? uidEntry : uidCert; - } - - private ApiResponse<Void> deleteSingleCertificate(String uid, String certificateEntryId) - throws ApiException { - return certificateAdministrationApi - .deleteDirectoryEntryCertificateWithHttpInfo(uid, certificateEntryId); - } - - @Override - public boolean postCheck() { - try { - super.postCheck(); - return true; - } catch (Exception ex) { - ex.printStackTrace(); + + private ApiResponse<Void> deleteSingleCertificate(String uid, String certificateEntryId) + throws ApiException { + return certificateAdministrationApi + .deleteDirectoryEntryCertificateWithHttpInfo(uid, certificateEntryId); } - return false; - } + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + } + + return false; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecution.java index c30a289..8e045db 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecution.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecution.java @@ -25,6 +25,8 @@ import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; import generated.CommandType; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,82 +35,85 @@ */ public class DeleteDirEntryExecution extends ExecutionBase { - private Logger LOG = LoggerFactory.getLogger(DeleteDirEntryExecution.class); + private Logger LOG = LoggerFactory.getLogger(DeleteDirEntryExecution.class); - private final DirectoryEntryAdministrationApi directoryEntryAdministrationApi; + private final DirectoryEntryAdministrationApi directoryEntryAdministrationApi; - public DeleteDirEntryExecution(GemApiClient api) { - super(api, CommandNamesEnum.DEL_DIR_ENTRY); - directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); - } + public DeleteDirEntryExecution(GemApiClient api) { + super(api, CommandNamesEnum.DEL_DIR_ENTRY); + directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); + } - @Override - public boolean checkValidation(CommandType command) { - boolean check = true; - if (command.getDn() != null) { - if (command.getDn().getUid() == null || command.getDn().getUid().equals("")) { - LOG.error( - "Missing argument -> uid for command " + command.getName() + "\n" + Transformer + @Override + public boolean checkValidation(CommandType command) { + boolean check = true; + if (command.getDn() != null) { + if (StringUtils.isBlank(command.getDn().getUid())) { + LOG.error( + "Missing argument -> uid for command " + command.getName() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + check = false; + } + } else { + LOG.error("Missing element \"dn\" " + command.getName() + "\n" + Transformer .getBaseDirectoryEntryFromCommandType(command)); - check = false; - } - } else { - LOG.error("Missing element \"dn\" " + command.getName() + "\n" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - check = false; + check = false; + } + return check; } - return check; - } - @Override - public boolean executeCommands() { - boolean runSuccessful = true; - if (commands.size() == 0) { - return true; - } - for (CommandType command : commands) { - if (isEntryPresent(command)) { - try { - executeCommand(command); - } catch (Exception ex) { - LOG.error("An error have occured: " + ex.getMessage()); - runSuccessful = false; + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + if (commands.isEmpty()) { + return true; + } + for (CommandType command : commands) { + LOG.debug("--- Command " + command.getCommandId() + " ---"); + if (isEntryPresent(command)) { + try { + executeCommand(command); + } catch (Exception ex) { + LOG.error("An error have occured: " + ex.getMessage()); + runSuccessful = false; + } + } else { + LOG.debug(command.getDn().getUid() + " could not be found"); + runSuccessful = false; + } + LOG.debug("--- Command " + command.getCommandId() + " end ---"); } - } else { - LOG.debug(command.getDn().getUid() + " could not be found"); - runSuccessful = false; - } + return runSuccessful; } - return runSuccessful; - } - private void executeCommand(CommandType command) throws ApiException { - apiClient.validateToken(); - ApiResponse<Void> response = directoryEntryAdministrationApi - .deleteDirectoryEntryWithHttpInfo(command.getDn().getUid()); - if (response.getStatusCode() == 200) { - LOG.debug("Delete directory entry execution successful operated for " + command.getDn() - .getUid()); - } else if (response.getStatusCode() == 404) { - LOG.debug(command.getDn().getUid() + " could not be found"); - throw new CommandException(command.getDn().getUid() + " could not be found"); - } else { - throw new CommandException( - "Delete directory entry execution failed. Response-Status was: " + response - .getStatusCode() + "\n" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - } - } + private void executeCommand(CommandType command) throws ApiException { + apiClient.validateToken(); - @Override - public boolean postCheck() { - try { - super.postCheck(); - return true; - } catch (Exception ex) { - ex.printStackTrace(); + ApiResponse<Void> response = directoryEntryAdministrationApi + .deleteDirectoryEntryWithHttpInfo(command.getDn().getUid()); + if (response.getStatusCode() == HttpStatus.SC_OK) { + LOG.debug("Delete directory entry execution successful operated for " + command.getDn() + .getUid()); + } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) { + LOG.debug(command.getDn().getUid() + " could not be found"); + throw new CommandException(command.getDn().getUid() + " could not be found"); + } else { + throw new CommandException( + "Delete directory entry execution failed. Response-Status was: " + + response.getStatusCode() + "\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command)); + } } - return false; - } + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + } + + return false; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBase.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBase.java index 3748617..0a31197 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBase.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBase.java @@ -21,6 +21,7 @@ import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; import de.gematik.ti.epa.vzd.client.model.UserCertificate; import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; + import de.gematik.ti.epa.vzd.gemClient.command.Transformer; import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; import de.gematik.ti.epa.vzd.gemClient.exceptions.GemClientException; @@ -30,6 +31,7 @@ import generated.UserCertificateType; import java.util.ArrayList; import java.util.List; +import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,124 +40,168 @@ */ public abstract class ExecutionBase { - private Logger LOG = LoggerFactory.getLogger(ExecutionBase.class); - - protected GemApiClient apiClient; - protected CommandNamesEnum execCommand; - protected List<CommandType> commands; - - public ExecutionBase(GemApiClient api, CommandNamesEnum cmd) { - this.apiClient = api; - this.execCommand = cmd; - this.commands = new ArrayList<>(); - } - - public boolean canHandleCommand(CommandNamesEnum cmd) { - return this.execCommand.equals(cmd); - } - - /** - * Checks the given command for validation and adds it to the queue of commands to execute - * - * @param command - * @return - */ - public boolean preCheck(CommandType command) { - try { - if (!checkValidation(command)) { - throw new CommandException( - "Command invalid. Please check " + command.getName() + " " + command - .getUserCertificate()); - } - commands.add(command); - return true; - } catch (Exception ex) { - LOG.error(ex.getMessage()); - return false; + protected final int FIRST_INDEX = 0; + + private Logger LOG = LoggerFactory.getLogger(ExecutionBase.class); + + protected GemApiClient apiClient; + protected CommandNamesEnum execCommand; + protected List<CommandType> commands; + + public ExecutionBase(GemApiClient api, CommandNamesEnum cmd) { + this.apiClient = api; + this.execCommand = cmd; + this.commands = new ArrayList<>(); } - } - - /** - * Every single executor validates their commands and log the missing or wrong values - * - * @param command - * @return - */ - public abstract boolean checkValidation(CommandType command); - - /** - * Executes all commands in the queue - * - * @return - */ - public abstract boolean executeCommands(); - - /** - * Checks if the execution was successful and logs the result - * - * @return - */ - public boolean postCheck() { - try { - return true; - } catch (Exception ex) { - ex.printStackTrace(); + + public boolean canHandleCommand(CommandNamesEnum cmd) { + return this.execCommand.equals(cmd); } - return false; - } - - /** - * This function proceed a read command, to check if a entry without cert is already present - * - * @param command - * @return - */ - protected boolean isEntryPresent(CommandType command) { - if (command.getDn() != null) { - CommandType searchCommand = new CommandType(); - DistinguishedNameType dn = new DistinguishedNameType(); - dn.setUid(command.getDn().getUid()); - searchCommand.setDn(dn); - try { - ApiResponse<List<DirectoryEntry>> response = ExecutionCollection - .getInstance().getReadDirEntryExecution().executeCommand(searchCommand); - return response.getStatusCode() == 200 ? true : false; - } catch (ApiException ex) { - if (ex.getCode() == 0) { - throw new GemClientException( - "The server you address is probably not reachable at the moment"); + /** + * Checks the given command for validation and adds it to the queue of commands to execute + * + * @param command + * @return + */ + public boolean preCheck(CommandType command) { + try { + if (!checkValidation(command)) { + throw new CommandException( + "Command invalid. Please check " + command.getName()); + } + commands.add(command); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + return false; + } + } + + /** + * Every single executor validates their commands and log the missing or wrong values + * + * @param command + * @return + */ + public abstract boolean checkValidation(CommandType command); + + /** + * Executes all commands in the queue + * + * @return + */ + public abstract boolean executeCommands(); + + /** + * Checks if the execution was successful and logs the result + * + * @return + */ + public boolean postCheck() { + try { + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); } return false; - } - } else { - return serachByTelematikId(command); } - } - - /** - * This function proceed a read command, to check if a entry without cert is already present - * - * @param command - * @return - */ - private boolean serachByTelematikId(CommandType command) { - UserCertificateType userCertificate = command.getUserCertificate(); - if (userCertificate != null) { - try { - CommandType searchCommand = new CommandType(); - searchCommand.setUserCertificate(new UserCertificateType()); - searchCommand.getUserCertificate().setTelematikID(userCertificate.getTelematikID()); - ApiResponse<List<UserCertificate>> response = ExecutionCollection - .getInstance().getReadDirEntryCertExecution().executeCommand(searchCommand); - return response.getStatusCode() == 200 ? true : false; - } catch (ApiException ex) { - LOG.error(ex.getMessage()); + + /** + * This function proceed a read command, to check if a entry without cert is already present + * + * @param command + * @return + */ + protected boolean isEntryPresent(CommandType command) { + if (command.getDn() != null) { + CommandType searchCommand = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid(command.getDn().getUid()); + searchCommand.setDn(dn); + try { + ApiResponse<List<DirectoryEntry>> response = ExecutionCollection + .getInstance().getReadDirEntryExecution().executeCommand(searchCommand); + return response.getStatusCode() == HttpStatus.SC_OK ? true : false; + } catch (ApiException ex) { + if (ex.getCode() == 0) { + throw new GemClientException( + "The server you address is probably not reachable at the moment"); + } + LOG.error(ex.getMessage()); + return false; + } + } else { + return serachByTelematikId(command); + } + } + + /** + * This function proceed a read command, to check if a entry without cert is already present + * + * @param command + * @return + */ + private boolean serachByTelematikId(CommandType command) { + UserCertificateType userCertificate = command.getUserCertificate().get(FIRST_INDEX); + if (userCertificate != null) { + try { + CommandType searchCommand = new CommandType(); + searchCommand.getUserCertificate().add(new UserCertificateType()); + searchCommand.getUserCertificate().get(FIRST_INDEX).setTelematikID(userCertificate.getTelematikID()); + ApiResponse<List<UserCertificate>> response = ExecutionCollection + .getInstance().getReadDirEntryCertExecution().executeCommand(searchCommand); + return response.getStatusCode() == HttpStatus.SC_OK ? true : false; + } catch (ApiException ex) { + if (ex.getCode() == 0) { + throw new GemClientException( + "The server you address is probably not reachable at the moment"); + } + return false; + } + } + throw new GemClientException("No valid parameter found for present check" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + } + + public boolean searchByUserCertificate(CommandType command) { + ApiResponse<List<UserCertificate>> response; + try { + response = ExecutionCollection.getInstance().getReadDirEntryCertExecution() + .executeCommand(command); + } catch (ApiException ex) { + if (ex.getCode() == 0) { + throw new GemClientException( + "The server you address is probably not reachable at the moment"); + } + LOG.error(ex.getMessage()); + return false; + } + if (response.getData().size() == command.getUserCertificate().size()) { + return true; + } return false; - } } - throw new GemClientException("No valid parameter found for present check" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - } + public String getUidByTelematikId(String telematikId) { + ApiResponse<List<UserCertificate>> response; + CommandType command = new CommandType(); + command.getUserCertificate().add(new UserCertificateType()); + command.getUserCertificate().get(FIRST_INDEX).setTelematikID(telematikId); + try { + response = ExecutionCollection.getInstance().getReadDirEntryCertExecution() + .executeCommand(command); + } catch (ApiException ex) { + if (ex.getCode() == 0) { + throw new GemClientException( + "The server you address is probably not reachable at the moment"); + } + LOG.error(ex.getMessage()); + return null; + } + if (!response.getData().isEmpty()) { + return response.getData().get(FIRST_INDEX).getDn().getUid(); + } + return null; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionCollection.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionCollection.java index e674b44..0de8259 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionCollection.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionCollection.java @@ -27,94 +27,94 @@ */ public class ExecutionCollection { - private final ReadDirEntryExecution readDirEntryExecution; - private final ReadDirEntryCertExecution readDirEntryCertExecution; - private final AddDirEntryExecution addDirEntryExecution; - private final AddDirEntryCertExecution addDirEntryCertExecution; - private final ModifyDirEntryExecution modifyDirEntryExecution; - private final ModifyDirEntryCertExecution modifyDirEntryCertExecution; - - private static Logger LOG = LoggerFactory.getLogger(ExecutionCollection.class); - - private ArrayList<ExecutionBase> specificExecutors = new ArrayList<>(); - - private static ExecutionCollection executions; - - /** - * Gives the instance as long as it exists. - * - * @return - */ - public static ExecutionCollection getInstance() { - if (executions == null) { - throw new InstantiationError("Please instance a executor first. It needs an ApiClient"); + private final ReadDirEntryExecution readDirEntryExecution; + private final ReadDirEntryCertExecution readDirEntryCertExecution; + private final AddDirEntryExecution addDirEntryExecution; + private final AddDirEntryCertExecution addDirEntryCertExecution; + private final ModifyDirEntryExecution modifyDirEntryExecution; + private final ModifyDirEntryCertExecution modifyDirEntryCertExecution; + + private static Logger LOG = LoggerFactory.getLogger(ExecutionCollection.class); + + private ArrayList<ExecutionBase> specificExecutors = new ArrayList<>(); + + private static ExecutionCollection executions; + + /** + * Gives the instance as long as it exists. + * + * @return + */ + public static ExecutionCollection getInstance() { + if (executions == null) { + throw new InstantiationError("Please instance a executor first. It needs an ApiClient"); + } + return executions; } - return executions; - } - - /** - * Instances the executions - * - * @param apiClient - * @return - */ - public static ExecutionCollection init(GemApiClient apiClient) { - if (executions != null) { - LOG.error("Error occurred while initializing executions. Executor is already instanced"); - throw new InstantiationError("Executor is already instanced"); + + /** + * Instances the executions + * + * @param apiClient + * @return + */ + public static ExecutionCollection init(GemApiClient apiClient) { + if (executions != null) { + LOG.error("Error occurred while initializing executions. Executor is already instanced"); + throw new InstantiationError("Executor is already instanced"); + } + executions = new ExecutionCollection(apiClient); + LOG.debug("Executions have been initialized correctly"); + + return executions; + } + + private ExecutionCollection(GemApiClient apiClient) { + this.readDirEntryExecution = new ReadDirEntryExecution(apiClient); + this.readDirEntryCertExecution = new ReadDirEntryCertExecution(apiClient); + this.addDirEntryExecution = new AddDirEntryExecution(apiClient); + this.addDirEntryCertExecution = new AddDirEntryCertExecution(apiClient); + this.modifyDirEntryExecution = new ModifyDirEntryExecution(apiClient); + this.modifyDirEntryCertExecution = new ModifyDirEntryCertExecution(apiClient); + + specificExecutors.add(readDirEntryExecution); + specificExecutors.add(addDirEntryExecution); + specificExecutors.add(modifyDirEntryExecution); + specificExecutors.add(new DeleteDirEntryExecution(apiClient)); + specificExecutors.add(readDirEntryCertExecution); + specificExecutors.add(addDirEntryCertExecution); + specificExecutors.add(modifyDirEntryCertExecution); + specificExecutors.add(new DeleteDirEntryCertExecution(apiClient)); + } + + + //<editor-fold desc="Getter"> + public List<ExecutionBase> getExecutors() { + return this.specificExecutors; + } + + public ReadDirEntryExecution getReadDirEntryExecution() { + return this.readDirEntryExecution; + } + + public ReadDirEntryCertExecution getReadDirEntryCertExecution() { + return this.readDirEntryCertExecution; + } + + public AddDirEntryExecution getAddDirEntryExecution() { + return this.addDirEntryExecution; + } + + public AddDirEntryCertExecution getAddDirEntryCertExecution() { + return this.addDirEntryCertExecution; + } + + public ModifyDirEntryExecution getModifyDirEntry() { + return this.modifyDirEntryExecution; + } + + public ModifyDirEntryCertExecution getModifyDirEntryCertExecution() { + return this.modifyDirEntryCertExecution; } - executions = new ExecutionCollection(apiClient); - LOG.debug("Executions have been initialized correctly"); - - return executions; - } - - private ExecutionCollection(GemApiClient apiClient) { - this.readDirEntryExecution = new ReadDirEntryExecution(apiClient); - this.readDirEntryCertExecution = new ReadDirEntryCertExecution(apiClient); - this.addDirEntryExecution = new AddDirEntryExecution(apiClient); - this.addDirEntryCertExecution = new AddDirEntryCertExecution(apiClient); - this.modifyDirEntryExecution = new ModifyDirEntryExecution(apiClient); - this.modifyDirEntryCertExecution = new ModifyDirEntryCertExecution(apiClient); - - specificExecutors.add(readDirEntryExecution); - specificExecutors.add(addDirEntryExecution); - specificExecutors.add(modifyDirEntryExecution); - specificExecutors.add(new DeleteDirEntryExecution(apiClient)); - specificExecutors.add(readDirEntryCertExecution); - specificExecutors.add(addDirEntryCertExecution); - specificExecutors.add(modifyDirEntryCertExecution); - specificExecutors.add(new DeleteDirEntryCertExecution(apiClient)); - } - - - //<editor-fold desc="Getter"> - public List<ExecutionBase> getExecutors() { - return this.specificExecutors; - } - - public ReadDirEntryExecution getReadDirEntryExecution() { - return this.readDirEntryExecution; - } - - public ReadDirEntryCertExecution getReadDirEntryCertExecution() { - return this.readDirEntryCertExecution; - } - - public AddDirEntryExecution getAddDirEntryExecution() { - return this.addDirEntryExecution; - } - - public AddDirEntryCertExecution getAddDirEntryCertExecution() { - return this.addDirEntryCertExecution; - } - - public ModifyDirEntryExecution getModifyDirEntry() { - return this.modifyDirEntryExecution; - } - - public ModifyDirEntryCertExecution getModifyDirEntryCertExecution() { - return this.modifyDirEntryCertExecution; - } - //</editor-fold> + //</editor-fold> } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryCertExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryCertExecution.java index 8ffe6b5..dfa7503 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryCertExecution.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryCertExecution.java @@ -28,7 +28,9 @@ import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; import generated.CommandType; import generated.DistinguishedNameType; +import generated.UserCertificateType; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,81 +39,81 @@ */ public class ModifyDirEntryCertExecution extends ExecutionBase { - private Logger LOG = LoggerFactory.getLogger(ModifyDirEntryExecution.class); - private CertificateAdministrationApi certificateAdministrationApi; + private Logger LOG = LoggerFactory.getLogger(ModifyDirEntryCertExecution.class); + private CertificateAdministrationApi certificateAdministrationApi; - public ModifyDirEntryCertExecution(GemApiClient api) { - super(api, CommandNamesEnum.MOD_DIR_CERT); - certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); - } + public ModifyDirEntryCertExecution(GemApiClient api) { + super(api, CommandNamesEnum.MOD_DIR_CERT); + certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); + } - @Override - public boolean checkValidation(CommandType command) { - if (command.getUserCertificate() != null) { - if (command.getUserCertificate().getDn() != null) { - DistinguishedNameType dn = command.getUserCertificate().getDn(); - if (StringUtils.isBlank(dn.getUid()) || StringUtils.isBlank(dn.getCn())) { - return false; + @Override + public boolean checkValidation(CommandType command) { + if (command.getUserCertificate() != null) { + for (UserCertificateType cert : command.getUserCertificate()) { + if (checkSingleUserCertificate(cert)) { + return false; + } + } } - } + return true; } - return true; - } - - @Override - public boolean executeCommands() { - LOG.info("The execution for ModifyCertificate is exposed"); - return true; -// boolean runSuccessful = true; -// if (commands.size() == 0) { -// return true; -// } -// for (CommandType command : commands) { -// try { -// executeCommand(command); -// } catch (Exception ex) { -// runSuccessful = false; -// ex.printStackTrace(); -// } -// } -// return runSuccessful; - } - protected ApiResponse<UserCertificate> executeCommand(CommandType command) { - apiClient.validateToken(); - CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); - ApiResponse<UserCertificate> response = null; + private boolean checkSingleUserCertificate(UserCertificateType cert) { + if (cert.getDn() != null) { + DistinguishedNameType dn = cert.getDn(); + if (StringUtils.isBlank(dn.getUid()) || StringUtils.isBlank(dn.getCn())) { + return false; + } + } + return true; + } - for (UserCertificate userCertificate : createDirectoryEntry.getUserCertificates()) { - try { - response = certificateAdministrationApi - .modifyDirectoryEntryCertificateWithHttpInfo(userCertificate.getDn().getUid(), - userCertificate.getDn().getCn(), userCertificate); - } catch (ApiException e) { - e.printStackTrace(); - } - if (response.getStatusCode() == 200) { - LOG.debug( - "Modify directory entry execution successful operated\n" + response.getData()); - } else { - throw new CommandException( - "Modify directory entry execution failed. Response-Status was: " + response - .getStatusCode() + "\n" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - } + @Override + public boolean executeCommands() { + if (commands.size() == 0) { + return true; + } + LOG.info("The execution for ModifyCertificate is exposed"); + return true; } - return response; - } - @Override - public boolean postCheck() { - try { - super.postCheck(); - return true; - } catch (Exception ex) { - ex.printStackTrace(); + protected ApiResponse<UserCertificate> executeCommand(CommandType command) { + apiClient.validateToken(); + + CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); + ApiResponse<UserCertificate> response = null; + + for (UserCertificate userCertificate : createDirectoryEntry.getUserCertificates()) { + try { + response = certificateAdministrationApi + .modifyDirectoryEntryCertificateWithHttpInfo(userCertificate.getDn().getUid(), + userCertificate.getDn().getCn(), userCertificate); + if (response.getStatusCode() == HttpStatus.SC_OK) { + LOG.debug( + "Modify directory entry execution successful operated\n" + response.getData()); + } else { + throw new CommandException( + "Modify directory entry execution failed. Response-Status was: " + response + .getStatusCode() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + } + } catch (ApiException e) { + e.printStackTrace(); + } + } + return response; } - return false; - } + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + } + + return false; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecution.java index 93fcf77..4779372 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecution.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecution.java @@ -28,6 +28,7 @@ import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; import generated.CommandType; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,103 +37,101 @@ */ public class ModifyDirEntryExecution extends ExecutionBase { - private Logger LOG = LoggerFactory.getLogger(ModifyDirEntryExecution.class); - private final DirectoryEntryAdministrationApi directoryEntryAdministrationApi; + private Logger LOG = LoggerFactory.getLogger(ModifyDirEntryExecution.class); + private final DirectoryEntryAdministrationApi directoryEntryAdministrationApi; - public ModifyDirEntryExecution(GemApiClient api) { - super(api, CommandNamesEnum.MOD_DIR_ENTRY); - directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); - } + public ModifyDirEntryExecution(GemApiClient api) { + super(api, CommandNamesEnum.MOD_DIR_ENTRY); + directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); + } - @Override - public boolean checkValidation(CommandType command) { - boolean check = true; - if (command.getDn() != null) { - if (StringUtils.isBlank(command.getDn().getUid())) { - LOG.error( - "Missing argument -> uid for command " + command.getName() + "\n" + Transformer + @Override + public boolean checkValidation(CommandType command) { + boolean check = true; + if (command.getDn() != null) { + if (StringUtils.isBlank(command.getDn().getUid())) { + LOG.error( + "Missing argument -> uid for command " + command.getName() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + check = false; + } + } else { + LOG.error("Missing element \"dn\" " + command.getName() + "\n" + Transformer .getBaseDirectoryEntryFromCommandType(command)); - check = false; - } - } else { - LOG.error("Missing element \"dn\" " + command.getName() + "\n" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - check = false; + check = false; + } + return check; } - return check; - } - @Override - public boolean executeCommands() { - boolean runSuccessful = true; - if (commands.size() == 0) { - return true; - } - for (CommandType command : commands) { - if (isEntryPresent(command)) { - try { - executeCommand(command); - } catch (Exception ex) { - LOG.error("An error have occured: " + ex.getMessage()); - runSuccessful = false; + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + for (CommandType command : commands) { + LOG.debug("--- Command " + command.getCommandId() + " ---"); + if (isEntryPresent(command)) { + try { + executeCommand(command); + } catch (Exception ex) { + LOG.error("An error have occured: " + ex.getMessage()); + runSuccessful = false; + } + } else { + runSuccessful = doAdd(command); + } + LOG.debug("--- Command " + command.getCommandId() + " end ---"); } - } else { - runSuccessful = doAdd(command); - } + return runSuccessful; } - return runSuccessful; - } - /** - * Function that execute one command and logs the result - * - * @param command - * @return - * @throws ApiException - */ - protected ApiResponse<DistinguishedName> executeCommand(CommandType command) - throws ApiException { - apiClient.validateToken(); - BaseDirectoryEntry baseDirectoryEntry = Transformer - .getBaseDirectoryEntryFromCommandType(command); - ApiResponse<DistinguishedName> response = directoryEntryAdministrationApi - .modifyDirectoryEntryWithHttpInfo(command.getDn().getUid(), baseDirectoryEntry); - if (response.getStatusCode() == 200) { - LOG.debug( - "Modify directory entry execution successful operated\n" + response.getData()); - } else { - throw new CommandException( - "Modify directory entry execution failed. Response-Status was: " + response - .getStatusCode() + "\n" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - } - return response; - } + /** + * Function that execute one command and logs the result + * + * @param command + * @return + * @throws ApiException + */ + protected ApiResponse<DistinguishedName> executeCommand(CommandType command) + throws ApiException { + apiClient.validateToken(); - private boolean doAdd(CommandType command) { - LOG.info("Ended here!"); - return true; -// LOG.debug("Entry not present at VZD. Will proceed with add directory entry command"); -// try { -// ExecutionCollection.getInstance().getAddDirEntryExecution().executeCommand(command); -// return true; -// } catch (ApiException ex) { -// LOG.error("Add directory entry execution failed\n" + Transformer -// .getCreateDirectoryEntry(command)); -// return false; -// } + BaseDirectoryEntry baseDirectoryEntry = Transformer + .getBaseDirectoryEntryFromCommandType(command); + ApiResponse<DistinguishedName> response = directoryEntryAdministrationApi + .modifyDirectoryEntryWithHttpInfo(command.getDn().getUid(), baseDirectoryEntry); + if (response.getStatusCode() == HttpStatus.SC_OK) { + LOG.debug( + "Modify directory entry execution successful operated\n" + response.getData()); + } else { + throw new CommandException( + "Modify directory entry execution failed. Response status was: " + + response.getStatusCode() + "\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command)); + } + return response; + } - } + private boolean doAdd(CommandType command) { + LOG.debug("Entry not present at VZD. Will proceed with add directory entry command"); + try { + ExecutionCollection.getInstance().getAddDirEntryExecution().executeCommand(command); + return true; + } catch (ApiException ex) { + LOG.error("Add directory entry execution failed\n" + Transformer + .getCreateDirectoryEntry(command)); + return false; + } - @Override - public boolean postCheck() { - try { - super.postCheck(); - return true; - } catch (Exception ex) { - ex.printStackTrace(); } - return false; - } + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + } + + return false; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryCertExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryCertExecution.java index 5698308..18f86da 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryCertExecution.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryCertExecution.java @@ -38,89 +38,108 @@ */ public class ReadDirEntryCertExecution extends ExecutionBase { - private CertificateAdministrationApi certificateAdministrationApi; - private Logger LOG = LoggerFactory.getLogger(AddDirEntryExecution.class); + private CertificateAdministrationApi certificateAdministrationApi; + private Logger LOG = LoggerFactory.getLogger(ReadDirEntryCertExecution.class); - public ReadDirEntryCertExecution(GemApiClient api) { - super(api, CommandNamesEnum.READ_DIR_CERT); - certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); - } + public ReadDirEntryCertExecution(GemApiClient api) { + super(api, CommandNamesEnum.READ_DIR_CERT); + certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); + } - @Override - public boolean checkValidation(CommandType command) { - UserCertificateType cert = command.getUserCertificate(); - if (cert != null) { - List<String> params = new ArrayList<>(); - if (command.getDn() != null) { - params.add(command.getDn().getUid()); - } - params.add(cert.getEntryType()); - params.add(cert.getTelematikID()); - params.add(cert.getProfessionOID()); - if (cert.getUsage().size() != 0) { - params.add("usage Vorhanden"); - } - for (String param : params) { - if (!StringUtils.isBlank(param)) { - return true; + @Override + public boolean checkValidation(CommandType command) { + for (UserCertificateType cert : command.getUserCertificate()) { + if (checkSingleCertificate(cert) == false) { + return false; + } } - } + return true; } - return false; - } - @Override - public boolean executeCommands() { - boolean runSuccessful = true; - if (commands.size() == 0) { - return true; - } - for (CommandType command : commands) { - try { - for (UserCertificate userCertificate : executeCommand(command).getData()) { - LOG.debug("Entry found: " + userCertificate); + private boolean checkSingleCertificate(UserCertificateType cert) { + if (cert != null) { + List<String> params = new ArrayList<>(); + if (cert.getDn() != null) { + params.add(cert.getDn().getUid()); + } + params.add(cert.getEntryType()); + params.add(cert.getTelematikID()); + params.add(cert.getProfessionOID()); + if (cert.getUsage().isEmpty()) { + params.add("usage Vorhanden"); + } + for (String param : params) { + if (!StringUtils.isBlank(param)) { + return true; + } + } } - } catch (Exception ex) { - LOG.error("Read directory entry execution failed\n" + Transformer - .getCreateDirectoryEntry(command)); - runSuccessful = false; - } + return false; } - return runSuccessful; - } - protected ApiResponse<List<UserCertificate>> executeCommand(CommandType command) - throws ApiException { - UserCertificateType cert = command.getUserCertificate(); - String uid = null; - String cn = null; - if (cert.getDn() != null) { - uid = cert.getDn().getUid(); - cn = cert.getDn().getCn(); - } - String entryType = cert.getEntryType(); - String telematikID = cert.getTelematikID(); - String professionOID = cert.getProfessionOID(); - String usage = null; - if (cert.getUsage().size() != 0) { - usage = GemStringUtils.listToString(cert.getUsage()); + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + if (commands.isEmpty()) { + return true; + } + for (CommandType command : commands) { + LOG.debug("--- Command " + command.getCommandId() + " ---"); + try { + for (UserCertificate userCertificate : executeCommand(command).getData()) { + LOG.debug("Entry found: " + userCertificate); + } + } catch (Exception ex) { + LOG.error("Read directory entry execution failed\n" + Transformer + .getCreateDirectoryEntry(command)); + runSuccessful = false; + } + LOG.debug("--- Command " + command.getCommandId() + " end ---"); + } + return runSuccessful; } - ApiResponse<List<UserCertificate>> response = certificateAdministrationApi - .readDirectoryCertificatesWithHttpInfo(uid, cn, entryType, - telematikID, professionOID, usage); + protected ApiResponse<List<UserCertificate>> executeCommand(CommandType command) + throws ApiException { + apiClient.validateToken(); - return response; - } + ApiResponse<List<UserCertificate>> result = null; + for (UserCertificateType cert : command.getUserCertificate()) { - @Override - public boolean postCheck() { - try { - super.postCheck(); - return true; - } catch (Exception ex) { - ex.printStackTrace(); + String uid = null; + String cn = null; + if (cert.getDn() != null) { + uid = cert.getDn().getUid(); + cn = cert.getDn().getCn(); + } + String entryType = cert.getEntryType(); + String telematikID = cert.getTelematikID(); + String professionOID = cert.getProfessionOID(); + String usage = null; + if (cert.getUsage().isEmpty()) { + usage = GemStringUtils.listToString(cert.getUsage()); + } + ApiResponse<List<UserCertificate>> tempResult = certificateAdministrationApi + .readDirectoryCertificatesWithHttpInfo(uid, cn, entryType, + telematikID, professionOID, usage); + if (result == null) { + result = tempResult; + } else { + result.getData().addAll(tempResult.getData()); + } + } + + return result; + } + + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + } + return false; } - return false; - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecution.java index d0e0ffd..e9a80de 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecution.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecution.java @@ -20,6 +20,7 @@ import de.gematik.ti.epa.vzd.client.invoker.ApiException; import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; import de.gematik.ti.epa.vzd.gemClient.GemStringUtils; import de.gematik.ti.epa.vzd.gemClient.api.GemDirectoryEntryAdministrationApi; @@ -27,9 +28,11 @@ import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; import generated.CommandType; +import generated.UserCertificateType; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,109 +41,146 @@ */ public class ReadDirEntryExecution extends ExecutionBase { - private Logger LOG = LoggerFactory.getLogger(ReadDirEntryExecution.class); - private DirectoryEntryAdministrationApi directoryEntryAdministrationApi; + private Logger LOG = LoggerFactory.getLogger(ReadDirEntryExecution.class); + private DirectoryEntryAdministrationApi directoryEntryAdministrationApi; - public ReadDirEntryExecution(GemApiClient api) { - super(api, CommandNamesEnum.READ_DIR_ENTRY); - directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); - } - - @Override - public boolean checkValidation(CommandType command) { - List<String> params = new ArrayList<>(); - if (command.getDn() != null) { - params.add(command.getDn().getUid()); + public ReadDirEntryExecution(GemApiClient api) { + super(api, CommandNamesEnum.READ_DIR_ENTRY); + directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); } - params.add(command.getGivenName()); - params.add(command.getSn()); - params.add(command.getCn()); - params.add(command.getDisplayName()); - params.add(command.getStreetAddress()); - params.add(command.getPostalCode()); - params.add(command.getLocalityName()); - params.add(command.getStateOrProvinceName()); - params.add(command.getTitle()); - params.add(command.getOrganization()); - params.add(command.getOtherName()); - params.add(GemStringUtils.listToString(command.getSpecialization())); - params.add(GemStringUtils.listToString(command.getDomainID())); - params.add(command.getPersonalEntry()); - params.add(command.getDataFromAuthority()); - for (String parameter : params) { - if (!StringUtils.isBlank(parameter)) { - return true; - } + + @Override + public boolean checkValidation(CommandType command) { + boolean check = certificateCheck(command.getUserCertificate()); + if (!check) { + List<String> params = new ArrayList<>(); + if (command.getDn() != null) { + params.add(command.getDn().getUid()); + } + params.add(command.getGivenName()); + params.add(command.getSn()); + params.add(command.getCn()); + params.add(command.getDisplayName()); + params.add(command.getStreetAddress()); + params.add(command.getPostalCode()); + params.add(command.getLocalityName()); + params.add(command.getStateOrProvinceName()); + params.add(command.getTitle()); + params.add(command.getOrganization()); + params.add(command.getOtherName()); + params.add(GemStringUtils.listToString(command.getSpecialization())); + params.add(GemStringUtils.listToString(command.getDomainID())); + params.add(command.getPersonalEntry()); + params.add(command.getDataFromAuthority()); + + for (String parameter : params) { + if (!StringUtils.isBlank(parameter)) { + check = true; + } + } + } + if (!check) { + LOG.error("Missing argument -> The given command have no argument to search for or given different TelematikIds delivered " + command + .getName() + + "\n" + Transformer.getBaseDirectoryEntryFromCommandType(command)); + } + return check; } - LOG.error("Missing argument -> The given command have no argument to search for " + command - .getName() - + "\n" + Transformer.getBaseDirectoryEntryFromCommandType(command)); - return false; - } - @Override - public boolean executeCommands() { - boolean runSuccessful = true; - if (commands.size() == 0) { - return true; + private boolean certificateCheck(List<UserCertificateType> userCertificate) { + if (!userCertificate.isEmpty()) { + String telematikId = userCertificate.get(FIRST_INDEX).getTelematikID(); + for (UserCertificateType cert : userCertificate) { + if (!cert.getTelematikID().equals(telematikId) || StringUtils.isBlank(cert.getTelematikID())) { + return false; + } + } + return true; + } + return false; } - for (CommandType command : commands) { - try { - for (DirectoryEntry directoryEntry : executeCommand(command).getData()) { - LOG.debug("Entry found: " + directoryEntry); + + @Override + public boolean executeCommands() { + boolean runSuccessful = true; + for (CommandType command : commands) { + LOG.debug("--- Command " + command.getCommandId() + " ---"); + try { + for (DirectoryEntry directoryEntry : executeCommand(command).getData()) { + LOG.debug("Entry found: " + directoryEntry); + } + } catch (Exception ex) { + LOG.error("Read directory entry execution failed\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command)); + runSuccessful = false; + } + LOG.debug("--- Command " + command.getCommandId() + " end ---"); } - } catch (Exception ex) { - LOG.error("Read directory entry execution failed\n" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - runSuccessful = false; - } + return runSuccessful; } - return runSuccessful; - } - public ApiResponse<List<DirectoryEntry>> executeCommand(CommandType command) - throws ApiException { - apiClient.validateToken(); - String uid = command.getDn().getUid(); - String givenName = command.getGivenName(); - String sn = command.getSn(); - String cn = command.getCn(); - String displayName = command.getDisplayName(); - String streetAddress = command.getStreetAddress(); - String postalCode = command.getPostalCode(); - String localityName = command.getLocalityName(); - String stateOrProvinceName = command.getStateOrProvinceName(); - String title = command.getTitle(); - String organization = command.getOrganization(); - String otherName = command.getOtherName(); - String specialization = GemStringUtils.listToString(command.getSpecialization()); - String domainID = GemStringUtils.listToString(command.getDomainID()); - String personalEntry = command.getPersonalEntry(); - String dataFromAuthority = command.getDataFromAuthority(); + public ApiResponse<List<DirectoryEntry>> executeCommand(CommandType command) + throws ApiException { + apiClient.validateToken(); + + String uid = getUid(command); + String givenName = command.getGivenName(); + String sn = command.getSn(); + String cn = command.getCn(); + String displayName = command.getDisplayName(); + String streetAddress = command.getStreetAddress(); + String postalCode = command.getPostalCode(); + String localityName = command.getLocalityName(); + String stateOrProvinceName = command.getStateOrProvinceName(); + String title = command.getTitle(); + String organization = command.getOrganization(); + String otherName = command.getOtherName(); + String specialization = GemStringUtils.listToString(command.getSpecialization()); + String domainID = GemStringUtils.listToString(command.getDomainID()); + String personalEntry = command.getPersonalEntry(); + String dataFromAuthority = command.getDataFromAuthority(); - ApiResponse<List<DirectoryEntry>> response = directoryEntryAdministrationApi - .readDirectoryEntryWithHttpInfo(uid, givenName, sn, cn, displayName, streetAddress, - postalCode, localityName, stateOrProvinceName, title, organization, otherName, - specialization, domainID, personalEntry, dataFromAuthority); - if (response.getStatusCode() == 200) { - return response; - } else { - throw new CommandException( - "Modify directory entry execution failed. Response-Status was: " + response - .getStatusCode() + "\n" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); + ApiResponse<List<DirectoryEntry>> response = + directoryEntryAdministrationApi.readDirectoryEntryWithHttpInfo(uid, givenName, sn, cn, + displayName, streetAddress, postalCode, localityName, stateOrProvinceName, title, + organization, otherName, specialization, domainID, personalEntry, + dataFromAuthority); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return response; + } else { + throw new CommandException( + "Modify directory entry execution failed. Response status was: " + + response.getStatusCode() + "\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command)); + } } - } - @Override - public boolean postCheck() { - try { - super.postCheck(); - return true; - } catch (Exception ex) { - ex.printStackTrace(); + private String getUid(CommandType command) throws ApiException { + String cUid = null; + String tUid = null; + if (command.getDn() != null) { + cUid = command.getDn().getUid(); + } + if (!command.getUserCertificate().isEmpty()) { + tUid = getUidByTelematikId(command.getUserCertificate().get(FIRST_INDEX).getTelematikID()); + } + if (!StringUtils.isBlank(tUid) && !StringUtils.isBlank(cUid)) { + if (!tUid.equals(cUid)) { + throw new ApiException("UID delivered by TelematikId does not match the UID in commandfile"); + } + } + return StringUtils.isBlank(tUid) ? cUid : tUid; } - return false; - } + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + } + + return false; + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/CommandException.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/CommandException.java index f364421..17cb5ff 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/CommandException.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/CommandException.java @@ -21,22 +21,22 @@ */ public class CommandException extends RuntimeException { - public CommandException() { - } + public CommandException() { + } - public CommandException(String message) { - super(message); - } + public CommandException(String message) { + super(message); + } - public CommandException(String message, Throwable cause) { - super(message, cause); - } + public CommandException(String message, Throwable cause) { + super(message, cause); + } - public CommandException(Throwable cause) { - super(cause); - } + public CommandException(Throwable cause) { + super(cause); + } - protected CommandException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } + protected CommandException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/GemClientException.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/GemClientException.java index 86b837b..2a78b67 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/GemClientException.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/GemClientException.java @@ -18,22 +18,22 @@ public class GemClientException extends RuntimeException { - public GemClientException() { - } + public GemClientException() { + } - public GemClientException(String message) { - super(message); - } + public GemClientException(String message) { + super(message); + } - public GemClientException(String message, Throwable cause) { - super(message, cause); - } + public GemClientException(String message, Throwable cause) { + super(message, cause); + } - public GemClientException(Throwable cause) { - super(cause); - } + public GemClientException(Throwable cause) { + super(cause); + } - protected GemClientException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } + protected GemClientException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/ReadException.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/ReadException.java index 031fe11..ea130e4 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/ReadException.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/ReadException.java @@ -21,23 +21,23 @@ */ public class ReadException extends RuntimeException { - public ReadException() { - } + public ReadException() { + } - public ReadException(String message) { - super(message); - } + public ReadException(String message) { + super(message); + } - public ReadException(String message, Throwable cause) { - super(message, cause); - } + public ReadException(String message, Throwable cause) { + super(message, cause); + } - public ReadException(Throwable cause) { - super(cause); - } + public ReadException(Throwable cause) { + super(cause); + } - protected ReadException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } + protected ReadException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandler.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandler.java index 538b797..a057e83 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandler.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandler.java @@ -32,136 +32,135 @@ */ public final class ConfigHandler { - private static final Logger LOG = LoggerFactory.getLogger(ConfigHandler.class); - private static final String BASE_PATH = "base"; - private static final String RETRY_OAUTH = "retryingOAuth"; - private static final String COMMANDS = "commands"; - - private static ConfigHandler configHandler = null; - private String configPath; - private String basePath; - private String credentialPath; - private String commandsPath; - private String retryingOAuthPath; - - private ConfigHandler() { - } - - /** - * This function returns the instance of the ConfigHandler as long as it is initialized - * - * @return - */ - public static ConfigHandler getInstance() { - if (configHandler == null) { - throw new GemClientException("A ConfigHandler have to be initialized first"); + private static final Logger LOG = LoggerFactory.getLogger(ConfigHandler.class); + private static final String BASE_PATH = "base"; + private static final String RETRY_OAUTH = "retryingOAuth"; + private static final String COMMANDS = "commands"; + + private static ConfigHandler configHandler = null; + private String configPath; + private String basePath; + private String credentialPath; + private String commandsPath; + private String retryingOAuthPath; + + private ConfigHandler() { } - return configHandler; - } - - /** - * Create an instance of a ConfigHandler while reading the commandline - * - * @param args input parameter from commandline - */ - public static ConfigHandler init(String[] args) { - if (configHandler == null) { - configHandler = new ConfigHandler(); - for (int iIndex = 0; iIndex < args.length; iIndex++) { - switch (args[iIndex]) { - case "-p": - configHandler.configPath = new File((args[iIndex + 1])).getAbsolutePath(); - configHandler.setParams(configHandler.configPath); - break; - case "-c": - configHandler.credentialPath = new File((args[iIndex + 1])).getAbsolutePath(); - break; - case "-b": - configHandler.commandsPath = new File((args[iIndex + 1])).getAbsolutePath(); - break; - default: - break; + + /** + * This function returns the instance of the ConfigHandler as long as it is initialized + * + * @return + */ + public static ConfigHandler getInstance() { + if (configHandler == null) { + throw new GemClientException("A ConfigHandler have to be initialized first"); } - } - } else { - throw new GemClientException("Configurations are only allowed to set once"); - } - if (StringUtils.isBlank(configHandler.credentialPath) || StringUtils - .isBlank(configHandler.configPath)) { - LOG.error("At least CredentialPath or ConfigPath is missing and have to be set"); - throw new GemClientException( - "At least CredentialPath or ConfigPath is missing and have to be set"); + return configHandler; } - LOG.debug("Configurations have been set"); - return configHandler; - } - - - private void setParams(String arg) { - File file = new File(arg); - try { - BufferedReader br = new BufferedReader(new FileReader(file)); - String line = br.readLine(); - while (line != null) { - if (StringUtils.isNotBlank(line) && line.contains("=")) { - String[] param = line.split("="); - String name = param[0]; - String value = param[1]; - switch (name) { - case BASE_PATH: - configHandler.basePath = value; - break; - case RETRY_OAUTH: - configHandler.retryingOAuthPath = value; - break; - case COMMANDS: - if (StringUtils.isBlank(configHandler.commandsPath)) { - configHandler.commandsPath = new File(value).getAbsolutePath(); - } - break; - default: - break; - } + + /** + * Create an instance of a ConfigHandler while reading the commandline + * + * @param args input parameter from commandline + */ + public static ConfigHandler init(String[] args) { + if (configHandler == null) { + configHandler = new ConfigHandler(); + for (int iIndex = 0; iIndex < args.length; iIndex++) { + switch (args[iIndex]) { + case "-p": + configHandler.configPath = new File((args[iIndex + 1])).getAbsolutePath(); + configHandler.setParams(configHandler.configPath); + break; + case "-c": + configHandler.credentialPath = new File((args[iIndex + 1])).getAbsolutePath(); + break; + case "-b": + configHandler.commandsPath = new File((args[iIndex + 1])).getAbsolutePath(); + break; + default: + break; + } + } + } else { + throw new GemClientException("Configurations are only allowed to set once"); } - line = br.readLine(); - } - } catch (IOException e) { - LOG.error("File not found at " + file.getAbsolutePath()); - throw new IllegalArgumentException("No access to given file " + file.getAbsolutePath()); - } - if (StringUtils.isBlank(configHandler.retryingOAuthPath)) { - LOG.error("No authorization server named"); - throw new GemClientException("No authorization server named"); + if (StringUtils.isBlank(configHandler.credentialPath) || StringUtils + .isBlank(configHandler.configPath)) { + LOG.error("At least CredentialPath or ConfigPath is missing and have to be set"); + throw new GemClientException( + "At least CredentialPath or ConfigPath is missing and have to be set"); + } + LOG.debug("Configurations have been set"); + return configHandler; } - if (StringUtils.isBlank(configHandler.basePath)) { - LOG.error("No vzd server named"); - throw new GemClientException("No server named"); + + + private void setParams(String arg) { + File file = new File(arg); + try (BufferedReader br = new BufferedReader(new FileReader(file))){ + String line = br.readLine(); + while (line != null) { + if (StringUtils.isNotBlank(line) && line.contains("=")) { + String[] param = line.split("="); + String name = param[0]; + String value = param[1]; + switch (name) { + case BASE_PATH: + configHandler.basePath = value; + break; + case RETRY_OAUTH: + configHandler.retryingOAuthPath = value; + break; + case COMMANDS: + if (StringUtils.isBlank(configHandler.commandsPath)) { + configHandler.commandsPath = new File(value).getAbsolutePath(); + } + break; + default: + break; + } + } + line = br.readLine(); + } + } catch (IOException e) { + LOG.error("File not found at " + file.getAbsolutePath()); + throw new IllegalArgumentException("No access to given file " + file.getAbsolutePath()); + } + if (StringUtils.isBlank(configHandler.retryingOAuthPath)) { + LOG.error("No authorization server named"); + throw new GemClientException("No authorization server named"); + } + if (StringUtils.isBlank(configHandler.basePath)) { + LOG.error("No vzd server named"); + throw new GemClientException("No server named"); + } } - } - // <editor-fold desc="Getter & Setter"> - public static void setConfigHandler(ConfigHandler setConfigHandler) { - configHandler = setConfigHandler; - } + // <editor-fold desc="Getter & Setter"> + public static void setConfigHandler(ConfigHandler setConfigHandler) { + configHandler = setConfigHandler; + } - public String getRetryingOAuthPath() { - return retryingOAuthPath; - } + public String getRetryingOAuthPath() { + return retryingOAuthPath; + } - public String getConfigPath() { - return configPath; - } + public String getConfigPath() { + return configPath; + } - public String getCredentialPath() { - return credentialPath; - } + public String getCredentialPath() { + return credentialPath; + } - public String getBasePath() { - return basePath; - } + public String getBasePath() { + return basePath; + } - public String getCommandsPath() { - return commandsPath; - } - // </editor-fold> + public String getCommandsPath() { + return commandsPath; + } + // </editor-fold> } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/GemApiClient.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/GemApiClient.java index 6d4eefb..cc28486 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/GemApiClient.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/GemApiClient.java @@ -26,14 +26,7 @@ import de.gematik.ti.epa.vzd.client.invoker.auth.OAuthFlow; import de.gematik.ti.epa.vzd.client.invoker.auth.RetryingOAuth; import de.gematik.ti.epa.vzd.gemClient.exceptions.GemClientException; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; import java.time.LocalDateTime; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import okhttp3.OkHttpClient; import org.apache.oltu.oauth2.client.OAuthClient; import org.apache.oltu.oauth2.client.URLConnectionClient; @@ -46,203 +39,210 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + public class GemApiClient extends ApiClient { - private static final Logger LOG = LoggerFactory.getLogger(GemApiClient.class); - - private String retryingOAuthPath; - private Map<String, Authentication> authentications; - private LocalDateTime tokenvalidationDate; - - public GemApiClient() { - init(); - authentications = Collections.unmodifiableMap(authentications); - } - - /* - * Constructor for ApiClient to support access token retry on 401/403 configured with client ID - */ - public GemApiClient(final String clientId) { - this(clientId, null, null); - } - - /* - * Constructor for ApiClient to support access token retry on 401/403 configured with client ID and additional parameters - */ - public GemApiClient(final String clientId, final Map<String, String> parameters) { - this(clientId, null, parameters); - } - - /* - * Constructor for ApiClient to support access token retry on 401/403 configured with client ID, secret, and additional parameters - */ - public GemApiClient(final String clientId, final String clientSecret, - final Map<String, String> parameters) { - init(); - - final RetryingOAuth retryingOAuth = new RetryingOAuth(retryingOAuthPath, clientId, - OAuthFlow.application, clientSecret, parameters); - authentications.put("OAuth2", retryingOAuth); - getHttpClient().interceptors().add(retryingOAuth); - - // Prevent the authentications from being modified. - authentications = Collections.unmodifiableMap(authentications); - } - - /** - * The function <code> getProgressInterceptor </code> comes from the generated class ApiClient and maybe have to be set on public again. - * <p> - * The OAuth2 token is stored in authentications.get("OAuth") - */ - private void init() { - ConfigHandler configHandler = ConfigHandler.getInstance(); - setBasePath(configHandler.getBasePath()); - retryingOAuthPath = configHandler.getRetryingOAuthPath(); - - final OkHttpClient.Builder builder = new OkHttpClient.Builder(); - // Function have to be set public when client is regenerated - builder.addNetworkInterceptor(getProgressInterceptor()); - setHttpClient(builder.build()); - - setVerifyingSsl(true); - - setJSON(new JSON()); - - // Set default User-Agent. - setUserAgent("OpenAPI-Generator/1.0.0/java"); - - authentications = new HashMap<>(); - authentications - .put("HttpBasicAuth", getHttpBasicAuthFromFile(configHandler.getCredentialPath())); - try { - authentications.put("OAuth", getNewOAuth2Token()); - } catch (OAuthSystemException | OAuthProblemException e) { - throw new ExceptionInInitializerError("Error while getting Token"); + private static final Logger LOG = LoggerFactory.getLogger(GemApiClient.class); + + private String retryingOAuthPath; + private Map<String, Authentication> authentications; + private LocalDateTime tokenvalidationDate; + + public GemApiClient() { + modifiedWithOAuth(); + authentications = Collections.unmodifiableMap(authentications); } - } - - /** - * Requests an OAuth2 Token with Username and Password - * - * @return - * @throws OAuthSystemException - * @throws OAuthProblemException - */ - private OAuth getNewOAuth2Token() throws OAuthProblemException, OAuthSystemException { - LOG.debug("Trying to get new access token"); - HttpBasicAuth baseAuth = (HttpBasicAuth) getAuthentications().get("HttpBasicAuth"); - - OAuthClientRequest request = OAuthClientRequest - .tokenLocation(retryingOAuthPath) - .setClientId(baseAuth.getUsername()) - .setClientSecret(baseAuth.getPassword()) - .setGrantType(GrantType.CLIENT_CREDENTIALS) - .buildBodyMessage(); - - request.setHeader("Accept", "application/json"); - - OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient()); - OAuthAccessTokenResponse oAuthResponse = oAuthClient - .accessToken(request, OAuthJSONAccessTokenResponse.class); - - JsonObject jObj = new JsonParser().parse(oAuthResponse.getBody()).getAsJsonObject(); - OAuth oAuth = new OAuth(); - oAuth.setAccessToken(jObj.get("access_token").toString().replaceAll("\"", "")); - setTokenValidation(jObj.get("expires_in").toString()); - LOG.debug("Requesting new OAuth2 token successful"); - return oAuth; - } - - /** - * Sets the time - 10% until the token expires. This ensures that the token is every time valid - * - * @param expires_in is an String with numbers. For example 3600 equals 1 hour. - */ - private void setTokenValidation(String expires_in) { - int seconds = Integer.parseInt(expires_in); - int secureSeconds = (int) (seconds * 0.90); - tokenvalidationDate = LocalDateTime.now().plusSeconds(secureSeconds); - } - - /** - * Checks if the token is still valid. If not request a new one - * - * @return - */ - public boolean validateToken() { - if (LocalDateTime.now().isBefore(tokenvalidationDate)) { - return true; + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID + */ + public GemApiClient(final String clientId) { + this(clientId, null, null); } - try { - getNewOAuth2Token(); - } catch (OAuthProblemException | OAuthSystemException e) { - throw new GemClientException("Requesting a new OAuth2 token failed.", e); + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID and additional parameters + */ + public GemApiClient(final String clientId, final Map<String, String> parameters) { + this(clientId, null, parameters); } - return LocalDateTime.now().isBefore(tokenvalidationDate); - } - - /** - * Reads the credentialFile and stores the client_id and client_secret - * - * @param arg - * @return - */ - private HttpBasicAuth getHttpBasicAuthFromFile(String arg) { - String client_id = ""; - String client_secret = ""; - File file = new File(arg); - - try { - BufferedReader br = new BufferedReader(new FileReader(file)); - String line = br.readLine(); - while (line != null) { - String[] param = line.split("="); - switch (param[0]) { - case "id": - client_id = param[1]; - break; - case "secret": - client_secret = param[1]; - break; - default: - break; + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID, secret, and additional parameters + */ + public GemApiClient(final String clientId, final String clientSecret, + final Map<String, String> parameters) { + modifiedWithOAuth(); + + final RetryingOAuth retryingOAuth = new RetryingOAuth(retryingOAuthPath, clientId, + OAuthFlow.application, clientSecret, parameters); + authentications.put("OAuth2", retryingOAuth); + getHttpClient().interceptors().add(retryingOAuth); + + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + /** + * The function <code> getProgressInterceptor </code> comes from the generated class ApiClient and maybe have to be set on public again. + * <p> + * The OAuth2 token is stored in authentications.get("OAuth") + */ + private void modifiedWithOAuth() { + ConfigHandler configHandler = ConfigHandler.getInstance(); + setBasePath(configHandler.getBasePath()); + retryingOAuthPath = configHandler.getRetryingOAuthPath(); + + final OkHttpClient.Builder builder = new OkHttpClient.Builder(); + // Function have to be set public when client is regenerated + builder.addNetworkInterceptor(getProgressInterceptor()); + setHttpClient(builder.build()); + + setVerifyingSsl(true); + + setJSON(new JSON()); + + // Set default User-Agent. + setUserAgent("OpenAPI-Generator/1.0.0/java"); + + authentications = new HashMap<>(); + authentications + .put("HttpBasicAuth", getHttpBasicAuthFromFile(configHandler.getCredentialPath())); + try { + authentications.put("OAuth", getNewOAuth2Token()); + } catch (OAuthSystemException | OAuthProblemException e) { + throw new ExceptionInInitializerError("Error while getting Token"); + } + } + + /** + * Requests an OAuth2 Token with Username and Password + * + * @return + * @throws OAuthSystemException + * @throws OAuthProblemException + */ + private OAuth getNewOAuth2Token() throws OAuthProblemException, OAuthSystemException { + LOG.debug("Trying to get new access token"); + HttpBasicAuth baseAuth = (HttpBasicAuth) getAuthentications().get("HttpBasicAuth"); + + OAuthClientRequest request = OAuthClientRequest + .tokenLocation(retryingOAuthPath) + .setClientId(baseAuth.getUsername()) + .setClientSecret(baseAuth.getPassword()) + .setGrantType(GrantType.CLIENT_CREDENTIALS) + .buildBodyMessage(); + + request.setHeader("Accept", "application/json"); + + OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient()); + OAuthAccessTokenResponse oAuthResponse = oAuthClient + .accessToken(request, OAuthJSONAccessTokenResponse.class); + + JsonObject jObj = new JsonParser().parse(oAuthResponse.getBody()).getAsJsonObject(); + OAuth oAuth = new OAuth(); + oAuth.setAccessToken(jObj.get("access_token").toString().replaceAll("\"", "")); + setTokenValidation(jObj.get("expires_in").toString()); + LOG.debug("Requesting new OAuth2 token successful"); + return oAuth; + } + + /** + * Sets the time - 10% until the token expires. This ensures that the token is every time valid + * + * @param expires_in is an String with numbers. For example 3600 equals 1 hour. + */ + private void setTokenValidation(String expires_in) { + int seconds = Integer.parseInt(expires_in); + int secureSeconds = (int) (seconds * 0.90); + tokenvalidationDate = LocalDateTime.now().plusSeconds(secureSeconds); + } + + /** + * Checks if the token is still valid. If not request a new one + * + * @return + */ + public boolean validateToken() { + if (LocalDateTime.now().isBefore(tokenvalidationDate)) { + return true; } - line = br.readLine(); - } - HttpBasicAuth basicAuth = new HttpBasicAuth(); - basicAuth.setPassword(client_secret); - basicAuth.setUsername(client_id); - return basicAuth; - } catch (IOException e) { - LOG.error( - "The named file on path " + file.getAbsolutePath() + " could not be accessed"); - throw new IllegalArgumentException( - "The named file on path " + file.getAbsolutePath() + " could not be accessed"); + try { + getNewOAuth2Token(); + } catch (OAuthProblemException | OAuthSystemException e) { + throw new GemClientException("Requesting a new OAuth2 token failed.", e); + } + return LocalDateTime.now().isBefore(tokenvalidationDate); + } + + /** + * Reads the credentialFile and stores the client_id and client_secret + * + * @param arg + * @return + */ + private HttpBasicAuth getHttpBasicAuthFromFile(String arg) { + String client_id = ""; + String client_secret = ""; + File file = new File(arg); + + try (BufferedReader br = new BufferedReader(new FileReader(file))){ + String line = br.readLine(); + while (line != null) { + String[] param = line.split("="); + switch (param[0]) { + case "id": + client_id = param[1]; + break; + case "secret": + client_secret = param[1]; + break; + default: + break; + } + line = br.readLine(); + } + HttpBasicAuth basicAuth = new HttpBasicAuth(); + basicAuth.setPassword(client_secret); + basicAuth.setUsername(client_id); + return basicAuth; + } catch (IOException e) { + LOG.error( + "The named file on path " + file.getAbsolutePath() + " could not be accessed"); + throw new IllegalArgumentException( + "The named file on path " + file.getAbsolutePath() + " could not be accessed"); + } + } + + // <editor-fold desc="Getter & Setter"> + + /** + * Get authentications (key: authentication name, value: authentication). + * + * @return Map of authentication objects + */ + @Override + public Map<String, Authentication> getAuthentications() { + return authentications; } - } - - // <editor-fold desc="Getter & Setter"> - - /** - * Get authentications (key: authentication name, value: authentication). - * - * @return Map of authentication objects - */ - @Override - public Map<String, Authentication> getAuthentications() { - return authentications; - } - - /** - * Get authentication for the given name. - * - * @param authName The authentication name - * @return The authentication, null if not found - */ - @Override - public Authentication getAuthentication(final String authName) { - return authentications.get(authName); - } - - // </editor-fold> + + /** + * Get authentication for the given name. + * + * @param authName The authentication name + * @return The authentication, null if not found + */ + @Override + public Authentication getAuthentication(final String authName) { + return authentications.get(authName); + } + + // </editor-fold> } diff --git a/src/main/resources/jaxb-binding/binding.xml b/src/main/resources/jaxb-binding/binding.xml index f6c7f3e..6b7d707 100644 --- a/src/main/resources/jaxb-binding/binding.xml +++ b/src/main/resources/jaxb-binding/binding.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" - version="2.0" - xmlns:xs="http://www.w3.org/2001/XMLSchema"> + version="2.0" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> <jaxb:bindings schemaLocation="..\xsd\commands.xsd" node="/xs:schema"> <jaxb:globalBindings fixedAttributeAsConstantProperty="true"/> diff --git a/src/main/resources/xsd/commands.xsd b/src/main/resources/xsd/commands.xsd index c45a6aa..fcce750 100644 --- a/src/main/resources/xsd/commands.xsd +++ b/src/main/resources/xsd/commands.xsd @@ -1,58 +1,59 @@ <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" - xmlns:xs="http://www.w3.org/2001/XMLSchema"> + xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <xs:element name="CommandList" type="CommandListType"/> + <xs:element name="CommandList" type="CommandListType"/> - <xs:complexType name="CommandListType"> - <xs:sequence> - <xs:element name="Command" type="CommandType" maxOccurs="unbounded"/> - </xs:sequence> - </xs:complexType> + <xs:complexType name="CommandListType"> + <xs:sequence> + <xs:element name="Command" type="CommandType" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> - <xs:complexType name="CommandType"> - <xs:sequence> - <xs:element name="name" type="xs:string" minOccurs="1" maxOccurs="1"/> - <xs:element name="dn" type="DistinguishedNameType" minOccurs="0" maxOccurs="1"/> - <xs:element name="givenName" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="sn" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="streetAddress" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="postalCode" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="localityName" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="stateOrProvinceName" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="cn" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="displayName" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="title" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="organization" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="otherName" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="specialization" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> - <xs:element name="domainID" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> - <xs:element name="mail" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="UserCertificate" type="UserCertificateType" minOccurs="0" maxOccurs="1"/> - <xs:element name="personalEntry" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="dataFromAuthority" type="xs:string" minOccurs="0" maxOccurs="1"/> - </xs:sequence> - </xs:complexType> + <xs:complexType name="CommandType"> + <xs:sequence> + <xs:element name="commandId" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="name" type="xs:string" minOccurs="1" maxOccurs="1"/> + <xs:element name="dn" type="DistinguishedNameType" minOccurs="0" maxOccurs="1"/> + <xs:element name="givenName" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="sn" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="streetAddress" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="postalCode" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="localityName" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="stateOrProvinceName" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="cn" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="displayName" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="title" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="organization" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="otherName" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="specialization" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="domainID" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="mail" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="UserCertificate" type="UserCertificateType" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="personalEntry" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="dataFromAuthority" type="xs:string" minOccurs="0" maxOccurs="1"/> + </xs:sequence> + </xs:complexType> - <xs:complexType name="DistinguishedNameType"> - <xs:sequence> - <xs:element name="cn" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="uid" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="dc" type="xs:string" minOccurs="0" maxOccurs="2"/> - <xs:element name="ou" type="xs:string" minOccurs="0" maxOccurs="2"/> - </xs:sequence> - </xs:complexType> + <xs:complexType name="DistinguishedNameType"> + <xs:sequence> + <xs:element name="cn" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="uid" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="dc" type="xs:string" minOccurs="0" maxOccurs="2"/> + <xs:element name="ou" type="xs:string" minOccurs="0" maxOccurs="2"/> + </xs:sequence> + </xs:complexType> - <xs:complexType name="UserCertificateType"> - <xs:sequence> - <xs:element name="dn" type="DistinguishedNameType" minOccurs="0" maxOccurs="1"/> - <xs:element name="entryType" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="telematikID" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="professionOID" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="usage" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> - <xs:element name="userCertificate" type="xs:string" minOccurs="0" maxOccurs="1"/> - <xs:element name="description" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:complexType name="UserCertificateType"> + <xs:sequence> + <xs:element name="dn" type="DistinguishedNameType" minOccurs="0" maxOccurs="1"/> + <xs:element name="entryType" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="telematikID" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="professionOID" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="usage" type="xs:string" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="userCertificate" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="description" type="xs:string" minOccurs="0" maxOccurs="1"/> - </xs:sequence> - </xs:complexType> + </xs:sequence> + </xs:complexType> </xs:schema> \ No newline at end of file diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApiTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApiTest.java index 72e78e7..db782e0 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApiTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApiTest.java @@ -1,25 +1,9 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - /* * I_Directory_Administration * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -49,14 +33,11 @@ public class CertificateAdministrationApiTest { private final CertificateAdministrationApi api = new CertificateAdministrationApi(); - + /** * Der Zertifikatseintrag wird im Verzeichnisdienst hinzugefügt und ist logisch über dn.uid mit dem übergeordneten Verzeichniseintrag verknüpft. * - * - * - * @throws ApiException - * if the Api call fails + * @throws ApiException if the Api call fails */ @Test public void addDirectoryEntryCertificateTest() throws ApiException { @@ -66,14 +47,12 @@ public void addDirectoryEntryCertificateTest() throws ApiException { // TODO: test validations } - + /** - * Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. + * Zertifikatseintrag löschen Dem Verzeichniseintrag muss immer mindestens ein Zertifikat zugeordnet sein. Wenn nach dem Löschen kein Zertifikat + * mehr dem Verzeichniseintrag zugeordnet wäre, muss die delete Operation abgelehnt werden. * - * - * - * @throws ApiException - * if the Api call fails + * @throws ApiException if the Api call fails */ @Test public void deleteDirectoryEntryCertificateTest() throws ApiException { @@ -83,14 +62,11 @@ public void deleteDirectoryEntryCertificateTest() throws ApiException { // TODO: test validations } - + /** * Der Zertifikatseintrag wird mit den übergebenen Daten aktualisiert. * - * - * - * @throws ApiException - * if the Api call fails + * @throws ApiException if the Api call fails */ @Test public void modifyDirectoryEntryCertificateTest() throws ApiException { @@ -101,14 +77,13 @@ public void modifyDirectoryEntryCertificateTest() throws ApiException { // TODO: test validations } - + /** * Zertifikat lesen - * + * <p> * Liefert alle Zertifikate, die dem Filter (siehe 'parameters') entsprechen. * - * @throws ApiException - * if the Api call fails + * @throws ApiException if the Api call fails */ @Test public void readDirectoryCertificatesTest() throws ApiException { @@ -122,5 +97,5 @@ public void readDirectoryCertificatesTest() throws ApiException { // TODO: test validations } - + } diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApiTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApiTest.java index 65d65d4..0b9e440 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApiTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApiTest.java @@ -1,25 +1,9 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - /* * I_Directory_Administration * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -51,14 +35,11 @@ public class DirectoryEntryAdministrationApiTest { private final DirectoryEntryAdministrationApi api = new DirectoryEntryAdministrationApi(); - + /** * Einen Eintrag zum Verzeichnisdienst hinzufügen * - * - * - * @throws ApiException - * if the Api call fails + * @throws ApiException if the Api call fails */ @Test public void addDirectoryEntryTest() throws ApiException { @@ -67,14 +48,11 @@ public void addDirectoryEntryTest() throws ApiException { // TODO: test validations } - + /** * Gesamten Verzeichniseintrag löschen * - * - * - * @throws ApiException - * if the Api call fails + * @throws ApiException if the Api call fails */ @Test public void deleteDirectoryEntryTest() throws ApiException { @@ -83,14 +61,11 @@ public void deleteDirectoryEntryTest() throws ApiException { // TODO: test validations } - + /** * Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. * - * - * - * @throws ApiException - * if the Api call fails + * @throws ApiException if the Api call fails */ @Test public void modifyDirectoryEntryTest() throws ApiException { @@ -100,14 +75,13 @@ public void modifyDirectoryEntryTest() throws ApiException { // TODO: test validations } - + /** * Gesamten Verzeichniseintrag lesen - * + * <p> * Liefert alle zum Filter passenden Verzeichniseinträge. Die angegebenen Parameter werden mit logischen UND verknüpft. * - * @throws ApiException - * if the Api call fails + * @throws ApiException if the Api call fails */ @Test public void readDirectoryEntryTest() throws ApiException { @@ -127,9 +101,11 @@ public void readDirectoryEntryTest() throws ApiException { String domainID = null; String personalEntry = null; String dataFromAuthority = null; - List<DirectoryEntry> response = api.readDirectoryEntry(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, stateOrProvienceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority); + List<DirectoryEntry> response = api + .readDirectoryEntry(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, stateOrProvienceName, title, + organization, otherName, specialization, domainID, personalEntry, dataFromAuthority); // TODO: test validations } - + } diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntryTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntryTest.java index 3b79082..c4cef86 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntryTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntryTest.java @@ -1,25 +1,9 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - /* * I_Directory_Administration * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -49,6 +33,7 @@ * Model tests for BaseDirectoryEntry */ public class BaseDirectoryEntryTest { + private final BaseDirectoryEntry model = new BaseDirectoryEntry(); /** diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntryTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntryTest.java index 90efd3b..0febeaf 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntryTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntryTest.java @@ -1,25 +1,9 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - /* * I_Directory_Administration * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -50,6 +34,7 @@ * Model tests for CreateDirectoryEntry */ public class CreateDirectoryEntryTest { + private final CreateDirectoryEntry model = new CreateDirectoryEntry(); /** diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntryTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntryTest.java index b11bc3b..7969706 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntryTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntryTest.java @@ -1,25 +1,9 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - /* * I_Directory_Administration * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -51,6 +35,7 @@ * Model tests for DirectoryEntry */ public class DirectoryEntryTest { + private final DirectoryEntry model = new DirectoryEntry(); /** diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/DistinguishedNameTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/DistinguishedNameTest.java index ee9d68a..c28a772 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/DistinguishedNameTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/DistinguishedNameTest.java @@ -1,25 +1,9 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - /* * I_Directory_Administration * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -48,6 +32,7 @@ * Model tests for DistinguishedName */ public class DistinguishedNameTest { + private final DistinguishedName model = new DistinguishedName(); /** diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/ErrorTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/ErrorTest.java index 91d95f2..eb283bc 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/ErrorTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/ErrorTest.java @@ -1,25 +1,9 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - /* * I_Directory_Administration * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -46,6 +30,7 @@ * Model tests for Error */ public class ErrorTest { + private final Error model = new Error(); /** diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/FAD1Test.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/FAD1Test.java index 1aae66e..1bc5dca 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/FAD1Test.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/FAD1Test.java @@ -1,25 +1,9 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - /* * I_Directory_Administration * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -49,6 +33,7 @@ * Model tests for FAD1 */ public class FAD1Test { + private final FAD1 model = new FAD1(); /** diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/FachdatenTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/FachdatenTest.java index 1aae0e9..966db4d 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/FachdatenTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/FachdatenTest.java @@ -1,25 +1,9 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - /* * I_Directory_Administration * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -50,6 +34,7 @@ * Model tests for Fachdaten */ public class FachdatenTest { + private final Fachdaten model = new Fachdaten(); /** diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/UserCertificateTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/UserCertificateTest.java index 147059f..188abbf 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/UserCertificateTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/UserCertificateTest.java @@ -1,25 +1,9 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - /* * I_Directory_Administration * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. * * The version of the OpenAPI document: 1.0.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -49,6 +33,7 @@ * Model tests for UserCertificate */ public class UserCertificateTest { + private final UserCertificate model = new UserCertificate(); /** diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilderTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilderTest.java index 2df2a07..5098162 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilderTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilderTest.java @@ -1,65 +1,63 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package de.gematik.ti.epa.vzd.gemClient.command; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; +import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; import de.gematik.ti.epa.vzd.gemClient.exceptions.ReadException; import de.gematik.ti.epa.vzd.gemClient.invoker.ConfigHandler; import java.io.File; +import org.apache.logging.log4j.core.tools.picocli.CommandLine.Command; import org.junit.Before; import org.junit.Test; public class CommandsBuilderTest { - private static final String[] ARGS_Wrong_Formatted = new String[]{"-p", - "src" + File.separator + "test" + File.separator + "resources" + File.separator - + "config" + File.separator + "ConfigWrongFormattedCommands.txt", "-c", - "src" + File.separator + "test" + File.separator + "resources" + File.separator - + "config" + File.separator + "bspCredentials.txt"}; - private static final String[] ARGS_MISSING_COMMANDS = new String[]{"-p", - "src" + File.separator + "test" + File.separator + "resources" + File.separator - + "config" + File.separator + "ConfigMissingCommands.txt", "-c", - "src" + File.separator + "test" + File.separator + "resources" + File.separator - + "config" + File.separator + "bspCredentials.txt"}; - - @Before - public void createConfigHandler() { - ConfigHandler.setConfigHandler(null); - } - - @Test - public void checkParsingError() { - ConfigHandler.init(ARGS_Wrong_Formatted); - ReadException exception = assertThrows(ReadException.class, - () -> new CommandsBuilder().buildCommands()); - assertEquals( - "An error have been occurred while reading your command file. Please check if this file is a valid .xml file", - exception.getMessage()); - } - - @Test - public void checkFileNotFoundError() { - ConfigHandler.init(ARGS_MISSING_COMMANDS); - ReadException exception = assertThrows(ReadException.class, - () -> new CommandsBuilder().buildCommands()); - assertEquals( - "A problem with your named file have occurred. Please if check "+ new File("doesNotExist.xml").getAbsolutePath() + " exist", - exception.getMessage()); - } + private static final String[] ARGS_Wrong_Formatted = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "ConfigWrongFormattedCommands.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "bspCredentials.txt"}; + private static final String[] ARGS_MISSING_COMMANDS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "ConfigMissingCommands.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "bspCredentials.txt"}; + private static final String[] DUBLED_COMMAND_ID = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "DoubledCommandId.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "bspCredentials.txt"}; + + @Before + public void createConfigHandler() { + ConfigHandler.setConfigHandler(null); + } + + @Test + public void checkParsingError() { + ConfigHandler.init(ARGS_Wrong_Formatted); + ReadException exception = assertThrows(ReadException.class, + () -> new CommandsBuilder().buildCommands()); + assertEquals( + "An error have been occurred while reading your command file. Please check if this file is a valid .xml file", + exception.getMessage()); + } + + @Test + public void checkFileNotFoundError() { + ConfigHandler.init(ARGS_MISSING_COMMANDS); + ReadException exception = assertThrows(ReadException.class, + () -> new CommandsBuilder().buildCommands()); + assertEquals( + "A problem with your named file have occurred. Please if check " + new File("doesNotExist.xml").getAbsolutePath() + " exist", + exception.getMessage()); + } + + @Test + public void doubledCommandIdDefinedTest(){ + ConfigHandler.init(DUBLED_COMMAND_ID); + CommandException exception = assertThrows(CommandException.class, () -> new CommandsBuilder().buildCommands()); + assertEquals("The predefined ID \"thisShouldOccureAnError\" occurs twice",exception.getMessage()); + } } diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecutionTest.java index cf1d687..fa77cd4 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecutionTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecutionTest.java @@ -1,19 +1,3 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; import static org.junit.Assert.assertFalse; @@ -42,7 +26,7 @@ public void checkValidationMissingTelematikIdAndCertificateTest() { AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(gemApiClient); CommandType command = new CommandType(); UserCertificateType certificate = new UserCertificateType(); - command.setUserCertificate(certificate); + command.getUserCertificate().add(certificate); assertFalse(addDirEntryExecution.checkValidation(command)); } @@ -50,9 +34,10 @@ public void checkValidationMissingTelematikIdAndCertificateTest() { public void checkValidationTelematikIdTest() { AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(gemApiClient); CommandType command = new CommandType(); + command.setCn("Test"); UserCertificateType certificate = new UserCertificateType(); certificate.setUserCertificate("Certificate"); - command.setUserCertificate(certificate); + command.getUserCertificate().add(certificate); assertTrue(addDirEntryExecution.checkValidation(command)); } @@ -60,10 +45,22 @@ public void checkValidationTelematikIdTest() { public void checkValidationCertificateTest() { AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(gemApiClient); CommandType command = new CommandType(); + command.setCn("Test"); UserCertificateType certificate = new UserCertificateType(); certificate.setTelematikID("TelematikId"); - command.setUserCertificate(certificate); + command.getUserCertificate().add(certificate); assertTrue(addDirEntryExecution.checkValidation(command)); } + @Test + public void checkValidationMissingCnTest() { + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + UserCertificateType certificate = new UserCertificateType(); + command.getUserCertificate().add(certificate); + certificate.setTelematikID("TelematikId"); + certificate.setUserCertificate("Certificate"); + assertFalse(addDirEntryExecution.checkValidation(command)); + } + } diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecutionTest.java new file mode 100644 index 0000000..ae8d8f6 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecutionTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; + +import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import generated.CommandType; +import generated.DistinguishedNameType; +import generated.UserCertificateType; +import org.apache.logging.log4j.core.tools.picocli.CommandLine.Command; +import org.junit.Test; + +public class DeleteDirEntryCertExecutionTest { + + private static GemApiClient gemApiClient = mock(GemApiClient.class); + + + @Test + public void checkValidationDifferentUidsTest() { + DeleteDirEntryCertExecution deleteDirEntryCertExecution = new DeleteDirEntryCertExecution(gemApiClient); + CommandType command = new CommandType(); + UserCertificateType certificate1 = new UserCertificateType(); + UserCertificateType certificate2 = new UserCertificateType(); + DistinguishedNameType dn1 = new DistinguishedNameType(); + DistinguishedNameType dn2 = new DistinguishedNameType(); + dn1.setUid("something"); + dn2.setUid("somethingelse"); + certificate1.setDn(dn1); + certificate2.setDn(dn2); + command.getUserCertificate().add(certificate1); + command.getUserCertificate().add(certificate2); + assertFalse(deleteDirEntryCertExecution.checkValidation(command)); + } +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecutionTest.java index 412b043..027d1fa 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecutionTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecutionTest.java @@ -1,19 +1,3 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; import static org.junit.Assert.assertFalse; @@ -44,4 +28,4 @@ public void checkValidationHaveUID() { command.setDn(dn); assertTrue(deleteDirEntryExecution.checkValidation(command)); } -} +} \ No newline at end of file diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBaseTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBaseTest.java new file mode 100644 index 0000000..684def5 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBaseTest.java @@ -0,0 +1,20 @@ +package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class ExecutionBaseTest { + + @Test + public void checkEntryPresentTest() { + //todo + assertTrue(true); + } + + @Test + public void serverNotPresentExecutionTest() { + //todo + assertTrue(true); + } +} \ No newline at end of file diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecutionTest.java index 02a2f0d..6387963 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecutionTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecutionTest.java @@ -1,19 +1,3 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; @@ -27,39 +11,39 @@ public class ModifyDirEntryExecutionTest { - private static GemApiClient gemApiClient = mock(GemApiClient.class); - - @Test - public void preCheckFalseWhenUidIsEmptyString() { - ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(gemApiClient); - CommandType command = new CommandType(); - DistinguishedNameType dn = new DistinguishedNameType(); - dn.setUid(""); - command.setDn(dn); - - assertTrue(!modDirEnt.preCheck(command)); - } - - @Test - public void preCheckIsTrue() { - ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(gemApiClient); - CommandType command = new CommandType(); - DistinguishedNameType dn = new DistinguishedNameType(); - dn.setUid("Test"); - command.setDn(dn); - - assertTrue(modDirEnt.preCheck(command)); - } - - @Test - public void preCheckFalseWhenUidIsMissing() { - ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(gemApiClient); - CommandType command = new CommandType(); - DistinguishedNameType dn = new DistinguishedNameType(); - dn.setCn("Test"); - command.setDn(dn); - - assertTrue(!modDirEnt.preCheck(command)); - } + private static GemApiClient gemApiClient = mock(GemApiClient.class); + + @Test + public void preCheckFalseWhenUidIsEmptyString() { + ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid(""); + command.setDn(dn); + + assertTrue(!modDirEnt.preCheck(command)); + } + + @Test + public void preCheckIsTrue() { + ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid("Test"); + command.setDn(dn); + + assertTrue(modDirEnt.preCheck(command)); + } + + @Test + public void preCheckFalseWhenUidIsMissing() { + ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(gemApiClient); + CommandType command = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setCn("Test"); + command.setDn(dn); + + assertTrue(!modDirEnt.preCheck(command)); + } } diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecutionTest.java index cef3fa2..b15f90f 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecutionTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecutionTest.java @@ -1,19 +1,3 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; import static org.junit.Assert.assertFalse; @@ -133,6 +117,7 @@ public void checkValidationHaveOtherNameTest() { command.setOtherName("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); } + @Test public void checkValidationHaveSpecializationTest() { ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); @@ -140,22 +125,25 @@ public void checkValidationHaveSpecializationTest() { command.getSpecialization().add("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); } - @Test + + @Test public void checkValidationHaveDomainIDTest() { ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); CommandType command = new CommandType(); command.getDomainID().add("TestString"); - assertTrue(readDirEntryExecution.checkValidation(command)); + assertTrue(readDirEntryExecution.checkValidation(command)); } + @Test - public void checkValidationHavePersonalEntryTest(){ + public void checkValidationHavePersonalEntryTest() { ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); CommandType command = new CommandType(); command.setPersonalEntry("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); } + @Test - public void checkValidationHaveDataFromAuthorityTest(){ + public void checkValidationHaveDataFromAuthorityTest() { ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); CommandType command = new CommandType(); command.setDataFromAuthority("TestString"); diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandlerTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandlerTest.java index 190c7bf..ca7e562 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandlerTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandlerTest.java @@ -1,19 +1,3 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package de.gematik.ti.epa.vzd.gemClient.invoker; import static org.junit.Assert.assertEquals; diff --git a/src/test/resources/config/Commands.xml b/src/test/resources/config/Commands.xml index 45e23f7..76ab26b 100644 --- a/src/test/resources/config/Commands.xml +++ b/src/test/resources/config/Commands.xml @@ -1,35 +1,35 @@ <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <CommandList> - <Command> - <name>modifyDirectoryEntries</name> - <dn> - <uid>Blub</uid> - </dn> - <displayName>displayName</displayName> - <otherName>otherName</otherName> - <streetAddress>streetAddress</streetAddress> - <postalCode>postalCode</postalCode> - <localityName>localityName</localityName> - <stateOrProvinceName>stateOrProvinceName</stateOrProvinceName> - <title>title - organization - specialization - domainID - - - modifyDirectoryEntries - - Blub2 - - displayName2 - otherName2 - streetAddress2 - postalCode2 - localityName2 - stateOrProvinceName2 - title2 - organization2 - specialization2 - domainID2 - + + modifyDirectoryEntries + + Blub + + displayName + otherName + streetAddress + postalCode + localityName + stateOrProvinceName + title + organization + specialization + domainID + + + modifyDirectoryEntries + + Blub2 + + displayName2 + otherName2 + streetAddress2 + postalCode2 + localityName2 + stateOrProvinceName2 + title2 + organization2 + specialization2 + domainID2 + \ No newline at end of file diff --git a/src/test/resources/config/CommandsMissingOperationName.xml b/src/test/resources/config/CommandsMissingOperationName.xml index c27c835..b55a5e4 100644 --- a/src/test/resources/config/CommandsMissingOperationName.xml +++ b/src/test/resources/config/CommandsMissingOperationName.xml @@ -1,34 +1,34 @@ - - - Blub - - displayName - otherName - streetAddress - postalCode - localityName - stateOrProvinceName - title - organization - specialization - domainID - - - modifyDirectoryEntries - - Blub2 - - displayName2 - otherName2 - streetAddress2 - postalCode2 - localityName2 - stateOrProvinceName2 - title2 - organization2 - specialization2 - domainID2 - + + + Blub + + displayName + otherName + streetAddress + postalCode + localityName + stateOrProvinceName + title + organization + specialization + domainID + + + modifyDirectoryEntries + + Blub2 + + displayName2 + otherName2 + streetAddress2 + postalCode2 + localityName2 + stateOrProvinceName2 + title2 + organization2 + specialization2 + domainID2 + \ No newline at end of file diff --git a/src/test/resources/config/CommandsWrongFormated.xml b/src/test/resources/config/CommandsWrongFormated.xml index 2694a87..6b832aa 100644 --- a/src/test/resources/config/CommandsWrongFormated.xml +++ b/src/test/resources/config/CommandsWrongFormated.xml @@ -1,35 +1,35 @@ - - modifyDirectoryEntries - - Blub - - displayName - otherName - streetAddress - postalCode - localityName - stateOrProvinceNametitle - organization - specialization - domainID - - - modifyDirectoryEntries - - Blub2 - - displayName2 - otherName2 - streetAddress2 - postalCode2 - localityName2 - stateOrProvinceName2 - title2 - organization2 - specialization2 - domainID2 - + + modifyDirectoryEntries + + Blub + + displayName + otherName + streetAddress + postalCode + localityName + stateOrProvinceNametitle + organization + specialization + domainID + + + modifyDirectoryEntries + + Blub2 + + displayName2 + otherName2 + streetAddress2 + postalCode2 + localityName2 + stateOrProvinceName2 + title2 + organization2 + specialization2 + domainID2 + \ No newline at end of file diff --git a/src/test/resources/config/DoubleIdCommands.xml b/src/test/resources/config/DoubleIdCommands.xml new file mode 100644 index 0000000..e942d5d --- /dev/null +++ b/src/test/resources/config/DoubleIdCommands.xml @@ -0,0 +1,37 @@ + + + + thisShouldOccureAnError + modifyDirectoryEntries + + Blub + + displayName + otherName + streetAddress + postalCode + localityName + stateOrProvinceName + title + organization + specialization + domainID + + + thisShouldOccureAnError + modifyDirectoryEntries + + Blub2 + + displayName2 + otherName2 + streetAddress2 + postalCode2 + localityName2 + stateOrProvinceName2 + title2 + organization2 + specialization2 + domainID2 + + \ No newline at end of file diff --git a/src/test/resources/config/DoubledCommandId.txt b/src/test/resources/config/DoubledCommandId.txt new file mode 100644 index 0000000..bb6ee13 --- /dev/null +++ b/src/test/resources/config/DoubledCommandId.txt @@ -0,0 +1,3 @@ +base=http://[::1]:8080/OAuth2Token +retryingOAuth=https://to.be.defined/oauth/token +commands=src\test\resources\config\DoubleIdCommands.xml \ No newline at end of file diff --git a/version.txt b/version.txt index 0affce7..0565f83 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.5.0 +1.0.1 From a5ebdac127af82bd78ea25ff4788b908d12ffb65 Mon Sep 17 00:00:00 2001 From: Gematik Date: Fri, 25 Sep 2020 09:48:07 +0200 Subject: [PATCH 3/5] =?UTF-8?q?Neue=20Funktionalit=C3=A4ten:=20-=20Proxyfu?= =?UTF-8?q?nktion=20-=20Multithreading=20ready=20-=20SaveModify=20(Modify?= =?UTF-8?q?=20ohne=20=C3=9Cberschreiben)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + README.md | 298 ++++++++++++++++- ReleaseNotes.md | 6 + bspData/Beispiel_Command_Datei.xml | 17 + bspData/Beispiel_Config_Datei.txt | 12 +- bspData/startVzdClient.bat | 2 +- build.gradle | 24 +- doc/images/Workfows_VZD.png | Bin 0 -> 668611 bytes doc/userguide/VZDCL_GettingStarted.adoc | 252 ++++++++++++++- doc/userguide/VZDCL_Introduction.adoc | 3 +- .../api/CertificateAdministrationApi.java | 11 +- .../api/DirectoryEntryAdministrationApi.java | 11 +- .../epa/vzd/client/invoker/ApiCallback.java | 4 +- .../ti/epa/vzd/client/invoker/ApiClient.java | 59 ++-- .../epa/vzd/client/invoker/ApiException.java | 2 +- .../invoker/GzipRequestInterceptor.java | 9 +- .../ti/epa/vzd/client/invoker/JSON.java | 9 +- .../client/invoker/ProgressRequestBody.java | 4 +- .../client/invoker/ProgressResponseBody.java | 4 +- .../vzd/client/invoker/auth/ApiKeyAuth.java | 3 +- .../client/invoker/auth/Authentication.java | 3 +- .../client/invoker/auth/HttpBasicAuth.java | 8 +- .../client/invoker/auth/HttpBearerAuth.java | 3 +- .../ti/epa/vzd/client/invoker/auth/OAuth.java | 3 +- .../invoker/auth/OAuthOkHttpClient.java | 10 +- .../client/invoker/auth/RetryingOAuth.java | 11 +- .../vzd/client/model/BaseDirectoryEntry.java | 10 +- .../client/model/CreateDirectoryEntry.java | 11 +- .../epa/vzd/client/model/DirectoryEntry.java | 12 +- .../vzd/client/model/DistinguishedName.java | 9 +- .../ti/epa/vzd/client/model/Error.java | 9 +- .../gematik/ti/epa/vzd/client/model/FAD1.java | 11 +- .../ti/epa/vzd/client/model/Fachdaten.java | 11 +- .../epa/vzd/client/model/UserCertificate.java | 4 +- .../{gemClient => gem}/CommandNamesEnum.java | 3 +- .../ti/epa/vzd/gem/GemStringUtils.java | 58 ++++ .../ti/epa/vzd/{gemClient => gem}/Main.java | 15 +- .../api/GemCertificateAdministrationApi.java | 4 +- .../GemDirectoryEntryAdministrationApi.java | 5 +- .../command/CommandsBuilder.java | 11 +- .../command}/ExecutionCollection.java | 78 +++-- .../command/ExecutionController.java | 95 +++--- .../command/Transformer.java | 38 ++- .../AddDirEntryCertExecution.java | 138 ++++---- .../AddDirEntryExecution.java | 148 +++++++++ .../DeleteDirEntryCertExecution.java | 174 ++++++++++ .../DeleteDirEntryExecution.java | 72 ++--- .../commandExecutions/ExecutionBase.java | 300 ++++++++++++++++++ .../ModifyDirEntryCertExecution.java | 49 +-- .../ModifyDirEntryExecution.java | 145 +++++++++ .../ReadDirEntryCertExecution.java | 97 +++--- .../ReadDirEntryExecution.java | 85 ++--- .../SaveModifyDirEntryExecution.java | 184 +++++++++++ .../dto/BaseExecutionResult.java | 20 ++ .../dto/ExecutionResult.java | 21 ++ .../exceptions/CommandException.java | 2 +- .../exceptions/ConnectionAlreadyReleased.java | 8 + .../exceptions/GemClientException.java | 2 +- .../exceptions/ReadException.java | 2 +- .../vzd/gem/exceptions/WrongConnection.java | 8 + .../ti/epa/vzd/gem/invoker/AccessHandler.java | 167 ++++++++++ .../invoker/ConfigHandler.java | 124 +++++++- .../epa/vzd/gem/invoker/ConnectionPool.java | 74 +++++ .../ti/epa/vzd/gem/invoker/GemApiClient.java | 161 ++++++++++ .../epa/vzd/gem/invoker/IConnectionPool.java | 17 + .../ti/epa/vzd/gemClient/GemStringUtils.java | 56 ---- .../AddDirEntryExecution.java | 143 --------- .../DeleteDirEntryCertExecution.java | 158 --------- .../commandExecutions/ExecutionBase.java | 207 ------------ .../ModifyDirEntryExecution.java | 137 -------- .../vzd/gemClient/invoker/GemApiClient.java | 248 --------------- .../epa/vzd/oauth2/URLConnectionClient.java | 136 ++++++++ src/main/resources/log4j2.xml | 4 +- .../api/CertificateAdministrationApiTest.java | 9 +- .../DirectoryEntryAdministrationApiTest.java | 111 ------- .../client/model/BaseDirectoryEntryTest.java | 13 - .../model/CreateDirectoryEntryTest.java | 14 - .../vzd/client/model/DirectoryEntryTest.java | 15 - .../client/model/DistinguishedNameTest.java | 12 - .../ti/epa/vzd/client/model/ErrorTest.java | 10 - .../ti/epa/vzd/client/model/FAD1Test.java | 13 - .../epa/vzd/client/model/FachdatenTest.java | 14 - .../vzd/client/model/UserCertificateTest.java | 13 - .../command/CommandsBuilderTest.java | 13 +- .../gem/command/ExecutionCollectionTest.java | 28 ++ ...dDirEntryCertExecutionIntegrationTest.java | 63 ++++ .../AddDirEntryCertExecutionTest.java | 121 +++++++ .../AddDirEntryExecutionIntegrationTest.java | 78 +++++ .../AddDirEntryExecutionTest.java | 27 +- ...eDirEntryCertExecutionIntegrationTest.java | 64 ++++ .../DeleteDirEntryCertExecutionTest.java | 142 +++++++++ ...eleteDirEntryExecutionIntegrationTest.java | 64 ++++ .../DeleteDirEntryExecutionTest.java | 10 +- .../ExecutionBaseIntegrationTest.java | 54 ++++ .../commandExecutions/ExecutionBaseTest.java | 64 ++++ ...odifyDirEntryExecutionIntegrationTest.java | 63 ++++ .../ModifyDirEntryExecutionTest.java | 13 +- ...dDirEntryCertExecutionIntegrationTest.java | 63 ++++ .../ReadDirEntryCertExecutionTest.java | 86 +++++ .../ReadDirEntryExecutionIntegrationTest.java | 64 ++++ .../ReadDirEntryExecutionTest.java | 41 +-- .../SaveModifyDirExecutionTest.java | 48 +++ .../vzd/gem/invoker/ConfigHandlerTest.java | 187 +++++++++++ .../epa/vzd/gem/testSuites/AllTestsuite.java | 12 + .../gem/testSuites/IntegrationTestsuite.java | 82 +++++ .../epa/vzd/gem/testSuites/UnitTestsuite.java | 36 +++ .../DeleteDirEntryCertExecutionTest.java | 50 --- .../commandExecutions/ExecutionBaseTest.java | 20 -- .../gemClient/invoker/ConfigHandlerTest.java | 72 ----- src/test/resources/config/Config.txt | 6 +- .../ConnectionWithLimitConnectionDefined.txt | 7 + .../ConnectionWithMaxConnectionDefined.txt | 7 + .../resources/config/IntegrationConfig.txt | 2 + .../resources/config/commands/addCert.xml | 33 ++ src/test/resources/config/commands/addDir.xml | 55 ++++ .../resources/config/commands/delCert.xml | 25 ++ src/test/resources/config/commands/delDir.xml | 17 + .../config/commands/executionBase.xml | 13 + src/test/resources/config/commands/modDir.xml | 40 +++ .../resources/config/commands/readCert.xml | 26 ++ .../resources/config/commands/readDir.xml | 17 + src/test/resources/exec/Testserver.jar | Bin 0 -> 27638906 bytes version.txt | 2 +- 123 files changed, 4396 insertions(+), 1885 deletions(-) create mode 100644 doc/images/Workfows_VZD.png rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/CommandNamesEnum.java (94%) create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/GemStringUtils.java rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/Main.java (77%) rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/api/GemCertificateAdministrationApi.java (98%) rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/api/GemDirectoryEntryAdministrationApi.java (98%) rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/command/CommandsBuilder.java (93%) rename src/main/java/de/gematik/ti/epa/vzd/{gemClient/command/commandExecutions => gem/command}/ExecutionCollection.java (53%) rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/command/ExecutionController.java (57%) rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/command/Transformer.java (73%) rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/command/commandExecutions/AddDirEntryCertExecution.java (51%) create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryExecution.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryCertExecution.java rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/command/commandExecutions/DeleteDirEntryExecution.java (56%) create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ExecutionBase.java rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/command/commandExecutions/ModifyDirEntryCertExecution.java (76%) create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryExecution.java rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/command/commandExecutions/ReadDirEntryCertExecution.java (54%) rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/command/commandExecutions/ReadDirEntryExecution.java (66%) create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/SaveModifyDirEntryExecution.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/dto/BaseExecutionResult.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/dto/ExecutionResult.java rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/exceptions/CommandException.java (95%) create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/ConnectionAlreadyReleased.java rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/exceptions/GemClientException.java (95%) rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/exceptions/ReadException.java (95%) create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/WrongConnection.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/invoker/AccessHandler.java rename src/main/java/de/gematik/ti/epa/vzd/{gemClient => gem}/invoker/ConfigHandler.java (51%) create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/invoker/ConnectionPool.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/invoker/GemApiClient.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/gem/invoker/IConnectionPool.java delete mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/GemStringUtils.java delete mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecution.java delete mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecution.java delete mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBase.java delete mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecution.java delete mode 100644 src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/GemApiClient.java create mode 100644 src/main/java/de/gematik/ti/epa/vzd/oauth2/URLConnectionClient.java delete mode 100644 src/test/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApiTest.java rename src/test/java/de/gematik/ti/epa/vzd/{gemClient => gem}/command/CommandsBuilderTest.java (86%) create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/ExecutionCollectionTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryCertExecutionIntegrationTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryCertExecutionTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryExecutionIntegrationTest.java rename src/test/java/de/gematik/ti/epa/vzd/{gemClient => gem}/command/commandExecutions/AddDirEntryExecutionTest.java (69%) create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryCertExecutionIntegrationTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryCertExecutionTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryExecutionIntegrationTest.java rename src/test/java/de/gematik/ti/epa/vzd/{gemClient => gem}/command/commandExecutions/DeleteDirEntryExecutionTest.java (63%) create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ExecutionBaseIntegrationTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ExecutionBaseTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryExecutionIntegrationTest.java rename src/test/java/de/gematik/ti/epa/vzd/{gemClient => gem}/command/commandExecutions/ModifyDirEntryExecutionTest.java (79%) create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryCertExecutionIntegrationTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryCertExecutionTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryExecutionIntegrationTest.java rename src/test/java/de/gematik/ti/epa/vzd/{gemClient => gem}/command/commandExecutions/ReadDirEntryExecutionTest.java (86%) create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/SaveModifyDirExecutionTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/invoker/ConfigHandlerTest.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/testSuites/AllTestsuite.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/testSuites/IntegrationTestsuite.java create mode 100644 src/test/java/de/gematik/ti/epa/vzd/gem/testSuites/UnitTestsuite.java delete mode 100644 src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecutionTest.java delete mode 100644 src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBaseTest.java delete mode 100644 src/test/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandlerTest.java create mode 100644 src/test/resources/config/ConnectionWithLimitConnectionDefined.txt create mode 100644 src/test/resources/config/ConnectionWithMaxConnectionDefined.txt create mode 100644 src/test/resources/config/IntegrationConfig.txt create mode 100644 src/test/resources/config/commands/addCert.xml create mode 100644 src/test/resources/config/commands/addDir.xml create mode 100644 src/test/resources/config/commands/delCert.xml create mode 100644 src/test/resources/config/commands/delDir.xml create mode 100644 src/test/resources/config/commands/executionBase.xml create mode 100644 src/test/resources/config/commands/modDir.xml create mode 100644 src/test/resources/config/commands/readCert.xml create mode 100644 src/test/resources/config/commands/readDir.xml create mode 100644 src/test/resources/exec/Testserver.jar diff --git a/.gitignore b/.gitignore index 40bb3a9..55741bc 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ generated/jaxb/* #testData# .bsp*Command.xml .idea/ + +#Incluede TestServer# +!src/test/resources/exec/Testserver.jar diff --git a/README.md b/README.md index 7fb2c2a..9cb334e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ ## Introduction -This part describes the VZD-Client functionalities and structure. +Der VZD-Client dient als Werkzeug um einen Verzeichnisdienst mit Einträgen +so wie Zertifikaten zu füllen, zu bearbeiten, zu löschen oder auszulesen. ## API Documentation @@ -16,6 +17,299 @@ Licensed under the [Apache License, Version 2.0](https://www.apache.org/licenses ## Getting Started +Im Folgenden wird erklärt wie der `VZD-Client` herunterzuladen und zu verwenden ist. + +### VZD-Client herunterladen + +Der `VZD-Client` kann als git repository oder von Maven Central heruntergeladen werden. + +- Git + + + + git clone https://github.com/gematik/app-VZD-Client.git + +- Maven Central + + + + https://search.maven.org/artifact/de.gematik.ti.epa/VZD-CLient + ### Build setup -soon +Zum Compilieren des `VZD-Clients` wird das Verwenden von Java11 und gradle 5.6.2 empfohlen. + +Nach dem Compilieren wird eine .zip-Datei in den build/distributions Ordner abgelegt. +Dieser enthält eine ausführbare .bat-Datei. +Die dazugehörige .jar-Datei befindet sich im Ordner build/libs. + +## Handling + +### Starten des VZD-Clients + +In der erstellten oder heruntergeladenen .zip-Datei befinden sich 2 ausführbare .bat-Dateien. + +- startVzdClient.bat → Startet den `VZD-Client` mit den angegebenen [Parameter](#_parameter). + +- startVzdC\_Log.bat → Diese .bat-Datei erwartet als ersten Parameter den gewünschten OutputPath für das Logfile. + Danach werden die [Parameter](#_parameter) angeführt. + +Beim Verwenden der .jar-Datei kann das Log-Level und der Pfad des Logs wie in [Logging](#Logging) verwendet werden. + +### Parameter + +Zum Bedienen des `VZD-Clients` müssen mindestens 2 Parameter überreicht werden. +Dies geschieht in der Kommandozeile über die Parameter: + +Pflicht: + +- -c <Pfad zur Credentials-Datei> (.txt-Datei) + +- -p <Pfad zu Grundeinstellungen> (.txt-Datei) + +Optional: + +- -b <Pfad zur Command-Datei> (.xml-Datei) ← überschreibt eventuelle Angaben in den Grundeinstellungen + +- -h <ProxyHost IP> ← überschreibt eventuelle Angaben in den Grundeinstellungen + +- -d <Proxy Port> ← überschreibt eventuelle Angaben in den Grundeinstellungen + +### Dateistruktur + +1. **Credentials-Datei:** + Diese Datei beinhaltet die User-ID und das Passwort. + Diese müssen wie folgt in eine .txt-Datei geschrieben werden (die Reihenfolge ist hierbei nicht relevant): + + - id=<UserId> + + - secret=<Passwort> + +2. **Grundeinstellungen:** + Diese Datei beinhaltet einen BasePath (IP-Adresse oder URL zum anzusprechenden Server), einen RetryingOAuthPath (IP Adresse oder URL zum OAuth2 + Token Server) und optional einen CommandsPath (Pfad zur lokalen .xml-Datei) in der die auszuführenden Operationen definiert sind sowie ein Proxy-Host + einen dazugehörigen Proxy-Port. Außerdem können Angaben zum parallelen Ausführen gemacht werden. Eine genauere Beschreibung wie sich die Angaben + auf den Programmablauf auswirken, kann unter [Paralleles Ausführen](#_paralleles_ausführen) nachgelesen werden. + Eine weitergehende Beschreibung der Kommandodatei findet sich unter [Bedienung](#Bedienung). + Die Angaben werden wie folgt in eine .txt-Datei geschrieben (die Reihenfolge ist hierbei nicht relevant): + + - base=<URL or IP zum Server> + + - retryingOAuth=<URL or IP zum Server> + + - proxyHost=<ProxyHost IP> + + - proxyPort=<Proxy Port> + + - commands=<Pfad zur .xml-Datei> + + - maxOperation=<Anzahl an Operationsarten> + + - maxExecutionsPerOperation=<Anzahl an parallelen Operationen> + +### Bedienung + +Die Definition der Kommandos geschieht durch eine .xml-Datei. +Dieser Datei liegt ein generelles Schema zugrunde, welches durch die commands.xsd in der .zip-Datei eingesehen werden kann. +Hierbei ist der Name der Operation verpflichtend. Eine Angabe einer "commandId" ist nicht notwendig, kann jedoch wahlweise gesetzt werden. Sollte +sie nicht gesetzt werden, wird der Client eine ID vergeben, bei der er von 1 an durch iteriert. Dies dient der späteren Zuordnung welche Antwort zu +welchem Kommando gehört. + +Abhängig davon welche Operation durchgeführt werden soll, sind gewisse Angaben verpflichtend und manche optional. +Sollte ein Parameter angegeben werden und für das betreffende Kommando nicht verwendet werden können, so wird dieser ignoriert. +In `gemSpec_VZD` wird in `Kapitel 4.6.1` Operationen der Schnittstelle I\_Directory\_Administration beschrieben, welches Kommando welche Parameter +berücksichtigt und welche verpflichtend benötigt werden. + +### Logging + +Das Logging geschieht auf der Konsole, sowie in einem File. Es wird immer ein File erstellt. Sollte kein spezieller Ort für das Logging angegeben +werden, wird das Log im java.io.temp Ordner abgelegt. Standardmäßig ist das Log-Level in der Konsole "INFO" und im File "DEBUG". + +#### Logging mit .bat-Datei + +Um das Log-Level der Konsole zu beeinflussen kann ein weiterer Parameter beim Starten der .bat-Datei angegeben werden: + +(case sensitive) + +- -info ← Level INFO + +- -trace ← Level TRACE + +- -error ← Level ERROR + +- -debug ← Level DEBUG + +Beispiel Aufruf + + /startVzdC_Log.bat -c /credentials.txt -p /config.txt -debug + +#### Logging mit jar Datei + +Um hier Einfluss auf die Logeigenschaften zu nehmen, muss der Java-VM die entsprechenden Systemvariablen übergeben werden. + +- l4j.lvl=<LogLevel> → für das Log-Level + +- l4j.logDir=<LogDir> → für das Log-Output-Directroy + +Beispiel Aufruf + + java -Dl4j.lvl=DEBUG -Dl4j.logDir= -jar /vzd.jar -c /credentials.txt -p /config.txt + +### Ausführen des VZD-Clients mit Proxy + +Um den VZD-Client über einen Proxy starten, müssen wie unter [Dateistruktur](#Dateistruktur) unter Grundeinstellungen Angaben in der Konfigurationsdatei zu Host +und Port angegeben werden. + +Außerdem ist es möglich den Host über den Parameter "-h" und den Port über "-d" direkt in der Kommandozeile zu übergeben. Sollte dies gemacht werden, +so überschreibt dies die Einstellungen, die eventuell in der Configurationsdatei angegeben wurden. + +### Paralleles Ausführen + +Durch das Konfigurationsfile können Einstellungen getätigt werden, die die Ausführung der Operationen des `VZD-Client` beschleunigen (Wie diese +Angaben korrekt getätigt werden, dann unter [Parameter](#_parameter) nachgelesen werden). + +Der `VZD-Client` kann mehrere Operationsarten (AddDirectory, +ModifyDirectory etc.) gleichzeitig ausführen. Hierzu dient die Angabe maxOperations. Der Maximalwert +dieser Angabe liegt bei 8. Es kann keinen Einfluss auf die Reihenfolge genommen werden. + +Die Einstellung maxExecutionsPerOperation beschreibt wie viele gleichartige Operationen gleichzeitig ausgeführt werden. Diese Zahl ist aufgrund von +Ressourcenplanung auf 20 limitiert. + +## Abarbeiungsflows + +Der Workflow ist in der Abbildung [figure\_title](#Abarbeitungsworkflow) abzulesen. +Alle Operationen müssen über ein Commandfile, wie in 2.3 Bedienung beschrieben, übergeben werden und werden vom `VZD-Client` ausgelesen. +Bevor sie ausgeführt werden, wird abhängig der Operation eine Validitätsprüfung durchgeführt. Genauere Angaben der Validitätsprüfung, können +unter der jeweiligen Operation unter [Abarbeiungsflows](#Abarbeiungsflows) eingesehen werden. + +Sollte diese bei einem Command fehlschlagen, so wird die Durchführung abgebrochen. +Der Workflow unterscheidet zwischen „Modify“, „Add“, „Read“ und „Delete“. In der Abarbeitung gibt es jedoch leichte Unterschiede, die unter +[Abarbeiungsflows](#Abarbeiungsflows) behandelt werden.Vor dem Ausführen des Requests wird überprüft, ob der OAuth2 Token noch Gültigkeit besitzt. +Anschließend wird der Request ausgeführt und das Ergebnis in ein Logfile geschrieben, das ausgewertet werden kann. + +![Abarbeitungsworkflow](de.gematik.ti.openhealthcard.events/doc/images/Workfows_VZD.png) + +### Add Directory Entry / Certificate + +#### Validitätsprüfung + +Erforderliche Einträge für ein Add Directory Entry ist mindestens ein `UserCertificate`. Jedes dieser `UserCertificate` muss mindesten +eine `telematikID` oder ein `userCertificate` beinhalten. Alle weiteren Angaben sind optional. + +Beim Ausführen eines Add Directory Entry Certificate werden für jedes Zertifikat die `uids` mit der im `BaseDirectoryEntry` angegebenen `uid` +verglichen. Sollte die `uid` abweichen so gilt das Kommando als nicht valide. + +#### Ablauf + +Bevor ein Add Directory Entry ausgeführt wird überprüft der Client, ob bereits ein Eintrag existiert. Sollte dies der Fall sein, wird anstelle eines Add- +ein Modify Directory Entry ausgeführt. + +Bei einem Add Directory Entry Certificate werden die angegebenen Parameter an den `VZD` gesendet. + +Bei erfolgreicher Durchführung wird jeweils die Antwort `distinguished name` des Servers in den Log geschrieben. + +### Read Directory Entry / Certificate + +#### Validitätsprüfung + +Es wird nur überprüft, ob mindestens eins der Attribute nach denen gesucht werden kann angegeben wurde. + +Mögliche Parameter für das Lesen eines Entries: + +- uid / telematikID + +- givenName + +- sn + +- cn + +- displayName + +- streetAddress + +- postalCode + +- localityName + +- stateOrProvinceName + +- title + +- organisation + +- otherName + +- specialization + +- domainID + +- personalEntry + +- dataFromAuthority + +Mögliche Parameter für das Lesen eines Zertifikats (hierbei wird nur das Element `UserCertificate` betrachtet): + +- uid + +- entryType + +- telematikId + +- professionOID + +- usage + +#### Ablauf + +Alle in dem Kommando genannten Parameter werden zusammengefügt und als Request an den `VZD` gesendet. Bei erfolgreicher Anfrage wird eine Liste aller +auf die Suchkriterien zutreffende Einträge in das Log geschrieben. + +### Modify Directory Entry / Certificate + +#### Validitätsprüfung + +Bei einem Modify Directory Entry wird die Angabe einer uid vom `VZD-Client` überprüft. **Eine Modify Directory Entry Certificate Operation +ist derzeit nicht vorgesehen**. Sollte versucht werden dieses Kommando auszuführen, wird dies zu einem Fehler führen. + +#### Ablauf + +Bevor ein Modify Directory Entry ausgeführt überprüft der Client, ob bereits ein Eintrag zu der uid existiert. Sollte dies nicht der Fall +sein, so wird anstelle eines Modify- ein Add Directory Entry ausgeführt. +Bei erfolgreicher Durchführung wird der jeweils zurückgelieferte `distinguished name` ins Log geschrieben. + +ACHTUNG: + +Wenn ein Modify Directory Entry durchgeführt wird, werden alle veränderbaren Attribute, die in dem Kommando nicht gesetzt wurden auf null gesetzt. +Sollte nur ein Attribut überschrieben werden, kann die Funktion [Save Modify Directory Entry](#_save_modify_directory_entry) verwendet werden. + +### Delete Directory Entry / Certificate + +#### Validitätsprüfung + +Bei einem Delete Directory Entry Kommando wird die Angabe einer uid vom `VZD-Client` überprüft. Anschließend wird der Löschbefehl gesendet. + +Bei einem Delete Directory Entry Certificate Kommando wird die uid aus dem `UserCertificate` ausgelesen. Dieses muss mit der uid im `BaseDirectorEntry` +überein stimmen, sofern angegeben. Außerdem muss ein `cn` im `UserCertificate` angegeben werden. Sollte eine dieser Voraussetzungen nicht zutreffen, +bricht der VZD die Bearbeitung ab. + +#### Ablauf + +Bevor ein Delete Directory Entry ausgeführt überprüft der Client, ob bereits ein Eintrag zu der `telematikID` existiert. Sollte dies die Löschanfrage +nicht gesendet. + +Sollte der Request erfolgreich sein, so wird die gelöschte uid als Bestätigung in das Log geschrieben. + +### Save Modify Directory Entry + +Dies ist eine ergänzende Funktion die nicht in der Spezifikation festgehalten ist. Sie soll den Umgang erleichtern und die Fehleranfälligkeit +verringern. Sie kann dazu genutzt werden einzelne Attribute eines Eintrages zu überschreiben ohne die restlichen Attribute zu löschen. + +#### Validitätsprüfung + +Die Validitätsprüfung ist deckungsgleich zu der unter [Modify Directory Entry / Certificate](#_modify_directory_entry_certificate) + +#### Ablauf + +Im gegensatz zu dem Modify-Directory-Entry-Befehl wird der Eintrag bei dieser Operation nicht komplett mit den angegebenen Daten ersetzt. Der +`VZD-Client` führt vorher eine Readoperation durch und vergleicht die Daten die unterschiedlich sind und ersetzt die im Command angegebenen Daten. diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 66b0990..497bea0 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,3 +1,9 @@ +# Release 1.1.0 +Neue Funktionalitäten: +- Proxyfunktion +- Multithreading ready +- SaveModify (Modify ohne Überschreiben) + # Release 1.0.1 - Funktionality for modify directory entries - Funktionality for add certificates entries diff --git a/bspData/Beispiel_Command_Datei.xml b/bspData/Beispiel_Command_Datei.xml index 54d7f64..7257c40 100644 --- a/bspData/Beispiel_Command_Datei.xml +++ b/bspData/Beispiel_Command_Datei.xml @@ -58,6 +58,23 @@ specialization domainID + + safeModifyDirectoryEntries + + uid + + cn + displayName + streetAddress + postalCode + localityName + stateOrProvinceName + title + organization + otherName + specialization + domainID + deleteDirectoryEntries diff --git a/bspData/Beispiel_Config_Datei.txt b/bspData/Beispiel_Config_Datei.txt index 16d89ba..4414eeb 100644 --- a/bspData/Beispiel_Config_Datei.txt +++ b/bspData/Beispiel_Config_Datei.txt @@ -1,3 +1,13 @@ base=https://LocationOfVzd:443 retryingOAuth=https://LocationOfOAuth2Server:8443/oauth/token -commands=src\main\resources\bspCommands.xml \ No newline at end of file + +proxyHost=192.168.230.85 +proxyPort=3128 + +// This is the maximum +maxExecutionsPerOperation=20 +// This is the maximum +maxOperations=9 + +commands=src\main\resources\bspCommands.xml + diff --git a/bspData/startVzdClient.bat b/bspData/startVzdClient.bat index 3703c8a..34c7373 100644 --- a/bspData/startVzdClient.bat +++ b/bspData/startVzdClient.bat @@ -21,4 +21,4 @@ for %%z in (%*) do ( if "%%z"=="-warn" set EXTRA_JVM_ARGUMENTS=-Dl4j.lvl=WARN ) -call java -jar %~dp0.\VZD-Client\VZD-Client.jar %* \ No newline at end of file +call java %EXTRA_JVM_ARGUMENTS% -jar %~dp0.\VZD-Client\VZD-Client.jar %* \ No newline at end of file diff --git a/build.gradle b/build.gradle index 67d5e88..701800e 100644 --- a/build.gradle +++ b/build.gradle @@ -55,8 +55,8 @@ dependencies { } jar { - manifest.attributes 'Main-Class': 'de.gematik.ti.epa.vzd.gemClient.Main' - baseName 'VZD-CLient' + manifest.attributes 'Main-Class': 'de.gematik.ti.epa.vzd.gem.Main' + baseName project.name from configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } @@ -84,7 +84,6 @@ compileJava.dependsOn jaxb task sourceJar(type: Jar) { classifier "sources" - baseName "VZD-CLient" from "${project.buildDir}/../src/main/java/" include "**/*.java" } @@ -116,7 +115,7 @@ artifacts { } gematikPublish { - name = "VZD-Client" + name = project.name description = "VZD-Client to add, remove and change data" gitHubProjectName = "app-VZD-Client" } @@ -139,7 +138,24 @@ distributions { include 'Beispiel_Config_Datei.txt' include 'Beispiel_Credential_Datei.txt' include 'Beispiel_Command_Datei.xml' + from 'src/main/resources/xsd/' + include 'commands.xsd' } } } +} + +test { + include '**/UnitTestsuite.class' + include '**/IntegrationTestsuite.class' + finalizedBy jacocoTestReport // report is always generated after tests run +} + + +jacocoTestReport { + reports { + xml.enabled true + xml.destination file("${buildDir}/jacoco/dependency-check-report.xml") + csv.enabled false + } } \ No newline at end of file diff --git a/doc/images/Workfows_VZD.png b/doc/images/Workfows_VZD.png new file mode 100644 index 0000000000000000000000000000000000000000..0259d9df595889ed3df2b3881226274762420e02 GIT binary patch literal 668611 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

8zza%qQr4+2 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Ð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
vC94lC3K6&pg|KS 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 z%0A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

    %RAXCM#m#z{}jEd?6kO`DpA1L)>8ltEGyPND$d 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Ν}jUZ0J~shZ}iwcpCA2)zuYHml7`qIbTU}T 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 Startet den `VZD-Client` mit den angegebenen <>. +* startVzdC_Log.bat -> Diese .bat-Datei erwartet als ersten Parameter den gewünschten OutputPath für das Logfile. +Danach werden die <> angeführt. + +Beim Verwenden der .jar-Datei kann das Log-Level und der Pfad des Logs wie in <> verwendet werden. + +=== Parameter + +Zum Bedienen des `VZD-Clients` müssen mindestens 2 Parameter überreicht werden. +Dies geschieht in der Kommandozeile über die Parameter: + +Pflicht: + +* -c (.txt-Datei) +* -p (.txt-Datei) + +Optional: + +* -b (.xml-Datei) <- überschreibt eventuelle Angaben in den Grundeinstellungen +* -h <- überschreibt eventuelle Angaben in den Grundeinstellungen +* -d <- überschreibt eventuelle Angaben in den Grundeinstellungen + +[[Dateistruktur]] +=== Dateistruktur + +1. *Credentials-Datei:* +Diese Datei beinhaltet die User-ID und das Passwort. +Diese müssen wie folgt in eine .txt-Datei geschrieben werden (die Reihenfolge ist hierbei nicht relevant): +* id= +* secret= +2. *Grundeinstellungen:* +Diese Datei beinhaltet einen BasePath (IP-Adresse oder URL zum anzusprechenden Server), einen RetryingOAuthPath (IP Adresse oder URL zum OAuth2 +Token Server) und optional einen CommandsPath (Pfad zur lokalen .xml-Datei) in der die auszuführenden Operationen definiert sind sowie ein Proxy-Host +einen dazugehörigen Proxy-Port. Außerdem können Angaben zum parallelen Ausführen gemacht werden. Eine genauere Beschreibung wie sich die Angaben +auf den Programmablauf auswirken, kann unter <> nachgelesen werden. +Eine weitergehende Beschreibung der Kommandodatei findet sich unter <>. +Die Angaben werden wie folgt in eine .txt-Datei geschrieben (die Reihenfolge ist hierbei nicht relevant): + +* base= +* retryingOAuth= +* proxyHost= +* proxyPort= +* commands= +* maxOperation= +* maxExecutionsPerOperation= + +[[Bedienung]] +=== Bedienung +Die Definition der Kommandos geschieht durch eine .xml-Datei. +Dieser Datei liegt ein generelles Schema zugrunde, welches durch die commands.xsd in der .zip-Datei eingesehen werden kann. +Hierbei ist der Name der Operation verpflichtend. Eine Angabe einer "commandId" ist nicht notwendig, kann jedoch wahlweise gesetzt werden. Sollte +sie nicht gesetzt werden, wird der Client eine ID vergeben, bei der er von 1 an durch iteriert. Dies dient der späteren Zuordnung welche Antwort zu +welchem Kommando gehört. + +Abhängig davon welche Operation durchgeführt werden soll, sind gewisse Angaben verpflichtend und manche optional. +Sollte ein Parameter angegeben werden und für das betreffende Kommando nicht verwendet werden können, so wird dieser ignoriert. +In `gemSpec_VZD` wird in `Kapitel 4.6.1` Operationen der Schnittstelle I_Directory_Administration beschrieben, welches Kommando welche Parameter +berücksichtigt und welche verpflichtend benötigt werden. + +[[Logging]] +=== Logging + +Das Logging geschieht auf der Konsole, sowie in einem File. Es wird immer ein File erstellt. Sollte kein spezieller Ort für das Logging angegeben +werden, wird das Log im java.io.temp Ordner abgelegt. Standardmäßig ist das Log-Level in der Konsole "INFO" und im File "DEBUG". + +==== Logging mit .bat-Datei +Um das Log-Level der Konsole zu beeinflussen kann ein weiterer Parameter beim Starten der .bat-Datei angegeben werden: + +(case sensitive) + +* -info <- Level INFO +* -trace <- Level TRACE +* -error <- Level ERROR +* -debug <- Level DEBUG + +Beispiel Aufruf +[src,cmd] +----------- +/startVzdC_Log.bat -c /credentials.txt -p /config.txt -debug +----------- + +==== Logging mit jar Datei + +Um hier Einfluss auf die Logeigenschaften zu nehmen, muss der Java-VM die entsprechenden Systemvariablen übergeben werden. + +* l4j.lvl= -> für das Log-Level +* l4j.logDir= -> für das Log-Output-Directroy + +Beispiel Aufruf +[src,cmd] +----------- +java -Dl4j.lvl=DEBUG -Dl4j.logDir= -jar /vzd.jar -c /credentials.txt -p /config.txt +----------- + +=== Ausführen des VZD-Clients mit Proxy + +Um den VZD-Client über einen Proxy starten, müssen wie unter <> unter Grundeinstellungen Angaben in der Konfigurationsdatei zu Host +und Port angegeben werden. + +Außerdem ist es möglich den Host über den Parameter "-h" und den Port über "-d" direkt in der Kommandozeile zu übergeben. Sollte dies gemacht werden, +so überschreibt dies die Einstellungen, die eventuell in der Configurationsdatei angegeben wurden. + +=== Paralleles Ausführen + +Durch das Konfigurationsfile können Einstellungen getätigt werden, die die Ausführung der Operationen des `VZD-Client` beschleunigen (Wie diese +Angaben korrekt getätigt werden, dann unter <> nachgelesen werden). + +Der `VZD-Client` kann mehrere Operationsarten (AddDirectory, +ModifyDirectory etc.) gleichzeitig ausführen. Hierzu dient die Angabe maxOperations. Der Maximalwert +dieser Angabe liegt bei 8. Es kann keinen Einfluss auf die Reihenfolge genommen werden. + +Die Einstellung maxExecutionsPerOperation beschreibt wie viele gleichartige Operationen gleichzeitig ausgeführt werden. Diese Zahl ist aufgrund von +Ressourcenplanung auf 20 limitiert. + +[[Abarbeiungsflows]] +== Abarbeiungsflows + +Der Workflow ist in der Abbildung <> abzulesen. +Alle Operationen müssen über ein Commandfile, wie in 2.3 Bedienung beschrieben, übergeben werden und werden vom `VZD-Client` ausgelesen. +Bevor sie ausgeführt werden, wird abhängig der Operation eine Validitätsprüfung durchgeführt. Genauere Angaben der Validitätsprüfung, können +unter der jeweiligen Operation unter <> eingesehen werden. + +Sollte diese bei einem Command fehlschlagen, so wird die Durchführung abgebrochen. +Der Workflow unterscheidet zwischen „Modify“, „Add“, „Read“ und „Delete“. In der Abarbeitung gibt es jedoch leichte Unterschiede, die unter +<> behandelt werden.Vor dem Ausführen des Requests wird überprüft, ob der OAuth2 Token noch Gültigkeit besitzt. +Anschließend wird der Request ausgeführt und das Ergebnis in ein Logfile geschrieben, das ausgewertet werden kann. + +[[Abarbeitungsworkflow]] +.Abarbeitungsworkflow +image::../images/Workfows_VZD.png[width=800] + +=== Add Directory Entry / Certificate +==== Validitätsprüfung +Erforderliche Einträge für ein Add Directory Entry ist mindestens ein `UserCertificate`. Jedes dieser `UserCertificate` muss mindesten +eine `telematikID` oder ein `userCertificate` beinhalten. Alle weiteren Angaben sind optional. + +Beim Ausführen eines Add Directory Entry Certificate werden für jedes Zertifikat die `uids` mit der im `BaseDirectoryEntry` angegebenen `uid` +verglichen. Sollte die `uid` abweichen so gilt das Kommando als nicht valide. + +==== Ablauf +Bevor ein Add Directory Entry ausgeführt wird überprüft der Client, ob bereits ein Eintrag existiert. Sollte dies der Fall sein, wird anstelle eines Add- +ein Modify Directory Entry ausgeführt. + +Bei einem Add Directory Entry Certificate werden die angegebenen Parameter an den `VZD` gesendet. + +Bei erfolgreicher Durchführung wird jeweils die Antwort `distinguished name` des Servers in den Log geschrieben. + +=== Read Directory Entry / Certificate +==== Validitätsprüfung +Es wird nur überprüft, ob mindestens eins der Attribute nach denen gesucht werden kann angegeben wurde. + +Mögliche Parameter für das Lesen eines Entries: + +- uid / telematikID +- givenName +- sn +- cn +- displayName +- streetAddress +- postalCode +- localityName +- stateOrProvinceName +- title +- organisation +- otherName +- specialization +- domainID +- personalEntry +- dataFromAuthority + +Mögliche Parameter für das Lesen eines Zertifikats (hierbei wird nur das Element `UserCertificate` betrachtet): + +- uid +- entryType +- telematikId +- professionOID +- usage + +==== Ablauf +Alle in dem Kommando genannten Parameter werden zusammengefügt und als Request an den `VZD` gesendet. Bei erfolgreicher Anfrage wird eine Liste aller +auf die Suchkriterien zutreffende Einträge in das Log geschrieben. + +=== Modify Directory Entry / Certificate +==== Validitätsprüfung +Bei einem Modify Directory Entry wird die Angabe einer uid vom `VZD-Client` überprüft. *Eine Modify Directory Entry Certificate Operation +ist derzeit nicht vorgesehen*. Sollte versucht werden dieses Kommando auszuführen, wird dies zu einem Fehler führen. + +==== Ablauf +Bevor ein Modify Directory Entry ausgeführt überprüft der Client, ob bereits ein Eintrag zu der uid existiert. Sollte dies nicht der Fall +sein, so wird anstelle eines Modify- ein Add Directory Entry ausgeführt. +Bei erfolgreicher Durchführung wird der jeweils zurückgelieferte `distinguished name` ins Log geschrieben. + +[red]#ACHTUNG:# + +Wenn ein Modify Directory Entry durchgeführt wird, werden alle veränderbaren Attribute, die in dem Kommando nicht gesetzt wurden auf null gesetzt. +Sollte nur ein Attribut überschrieben werden, kann die Funktion <> verwendet werden. + +=== Delete Directory Entry / Certificate +==== Validitätsprüfung +Bei einem Delete Directory Entry Kommando wird die Angabe einer uid vom `VZD-Client` überprüft. Anschließend wird der Löschbefehl gesendet. + +Bei einem Delete Directory Entry Certificate Kommando wird die uid aus dem `UserCertificate` ausgelesen. Dieses muss mit der uid im `BaseDirectorEntry` +überein stimmen, sofern angegeben. Außerdem muss ein `cn` im `UserCertificate` angegeben werden. Sollte eine dieser Voraussetzungen nicht zutreffen, +bricht der VZD die Bearbeitung ab. + +==== Ablauf +Bevor ein Delete Directory Entry ausgeführt überprüft der Client, ob bereits ein Eintrag zu der `telematikID` existiert. Sollte dies die Löschanfrage +nicht gesendet. + +Sollte der Request erfolgreich sein, so wird die gelöschte uid als Bestätigung in das Log geschrieben. + +=== Save Modify Directory Entry +Dies ist eine ergänzende Funktion die nicht in der Spezifikation festgehalten ist. Sie soll den Umgang erleichtern und die Fehleranfälligkeit +verringern. Sie kann dazu genutzt werden einzelne Attribute eines Eintrages zu überschreiben ohne die restlichen Attribute zu löschen. + +==== Validitätsprüfung +Die Validitätsprüfung ist deckungsgleich zu der unter <> +==== Ablauf +Im gegensatz zu dem Modify-Directory-Entry-Befehl wird der Eintrag bei dieser Operation nicht komplett mit den angegebenen Daten ersetzt. Der +`VZD-Client` führt vorher eine Readoperation durch und vergleicht die Daten die unterschiedlich sind und ersetzt die im Command angegebenen Daten. \ No newline at end of file diff --git a/doc/userguide/VZDCL_Introduction.adoc b/doc/userguide/VZDCL_Introduction.adoc index 0e636c1..3702a2f 100644 --- a/doc/userguide/VZDCL_Introduction.adoc +++ b/doc/userguide/VZDCL_Introduction.adoc @@ -4,4 +4,5 @@ endif::javadoc[] == Introduction -This part describes the VZD-Client functionalities and structure. \ No newline at end of file +Der VZD-Client dient als Werkzeug um einen Verzeichnisdienst mit Einträgen +so wie Zertifikaten zu füllen, zu bearbeiten, zu löschen oder auszulesen. \ No newline at end of file diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApi.java b/src/main/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApi.java index 4533147..9a092fb 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApi.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApi.java @@ -29,24 +29,15 @@ package de.gematik.ti.epa.vzd.client.api; +import com.google.gson.reflect.TypeToken; import de.gematik.ti.epa.vzd.client.invoker.ApiCallback; import de.gematik.ti.epa.vzd.client.invoker.ApiClient; import de.gematik.ti.epa.vzd.client.invoker.ApiException; import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; import de.gematik.ti.epa.vzd.client.invoker.Configuration; import de.gematik.ti.epa.vzd.client.invoker.Pair; -import de.gematik.ti.epa.vzd.client.invoker.ProgressRequestBody; -import de.gematik.ti.epa.vzd.client.invoker.ProgressResponseBody; - -import com.google.gson.reflect.TypeToken; - -import java.io.IOException; - - import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import de.gematik.ti.epa.vzd.client.model.Error; import de.gematik.ti.epa.vzd.client.model.UserCertificate; - import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApi.java b/src/main/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApi.java index e1fded9..10dd6a9 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApi.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApi.java @@ -29,26 +29,17 @@ package de.gematik.ti.epa.vzd.client.api; +import com.google.gson.reflect.TypeToken; import de.gematik.ti.epa.vzd.client.invoker.ApiCallback; import de.gematik.ti.epa.vzd.client.invoker.ApiClient; import de.gematik.ti.epa.vzd.client.invoker.ApiException; import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; import de.gematik.ti.epa.vzd.client.invoker.Configuration; import de.gematik.ti.epa.vzd.client.invoker.Pair; -import de.gematik.ti.epa.vzd.client.invoker.ProgressRequestBody; -import de.gematik.ti.epa.vzd.client.invoker.ProgressResponseBody; - -import com.google.gson.reflect.TypeToken; - -import java.io.IOException; - - import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import de.gematik.ti.epa.vzd.client.model.Error; - import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiCallback.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiCallback.java index e2cd72b..ff13de2 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiCallback.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiCallback.java @@ -29,10 +29,8 @@ package de.gematik.ti.epa.vzd.client.invoker; -import java.io.IOException; - -import java.util.Map; import java.util.List; +import java.util.Map; /** * Callback for asynchronous API call. diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiClient.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiClient.java index 1a36bd7..b15443b 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiClient.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiClient.java @@ -29,17 +29,12 @@ package de.gematik.ti.epa.vzd.client.invoker; -import okhttp3.*; -import okhttp3.internal.http.HttpMethod; -import okhttp3.internal.tls.OkHostnameVerifier; -import okhttp3.logging.HttpLoggingInterceptor; -import okhttp3.logging.HttpLoggingInterceptor.Level; -import okio.BufferedSink; -import okio.Okio; -import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder; -import org.apache.oltu.oauth2.common.message.types.GrantType; - -import javax.net.ssl.*; +import de.gematik.ti.epa.vzd.client.invoker.auth.ApiKeyAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.Authentication; +import de.gematik.ti.epa.vzd.client.invoker.auth.HttpBasicAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.OAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.OAuthFlow; +import de.gematik.ti.epa.vzd.client.invoker.auth.RetryingOAuth; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -53,24 +48,46 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; import java.text.DateFormat; import java.time.LocalDate; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; - -import de.gematik.ti.epa.vzd.client.invoker.auth.Authentication; -import de.gematik.ti.epa.vzd.client.invoker.auth.HttpBasicAuth; -import de.gematik.ti.epa.vzd.client.invoker.auth.HttpBearerAuth; -import de.gematik.ti.epa.vzd.client.invoker.auth.ApiKeyAuth; -import de.gematik.ti.epa.vzd.client.invoker.auth.OAuth; -import de.gematik.ti.epa.vzd.client.invoker.auth.RetryingOAuth; -import de.gematik.ti.epa.vzd.client.invoker.auth.OAuthFlow; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Headers; +import okhttp3.Interceptor; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.internal.http.HttpMethod; +import okhttp3.internal.tls.OkHostnameVerifier; +import okhttp3.logging.HttpLoggingInterceptor; +import okhttp3.logging.HttpLoggingInterceptor.Level; +import okio.BufferedSink; +import okio.Okio; +import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder; public class ApiClient { diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiException.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiException.java index 238fc69..a6e1124 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiException.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ApiException.java @@ -29,8 +29,8 @@ package de.gematik.ti.epa.vzd.client.invoker; -import java.util.Map; import java.util.List; +import java.util.Map; public class ApiException extends Exception { diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/GzipRequestInterceptor.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/GzipRequestInterceptor.java index 346c211..ad7ee42 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/GzipRequestInterceptor.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/GzipRequestInterceptor.java @@ -29,14 +29,17 @@ package de.gematik.ti.epa.vzd.client.invoker; -import okhttp3.*; +import java.io.IOException; +import okhttp3.Interceptor; +import okhttp3.MediaType; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; import okio.Buffer; import okio.BufferedSink; import okio.GzipSink; import okio.Okio; -import java.io.IOException; - /** * Encodes request bodies using gzip. *

    diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/JSON.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/JSON.java index 8e393f8..eb88c7b 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/JSON.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/JSON.java @@ -31,18 +31,13 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import com.google.gson.TypeAdapter; import com.google.gson.internal.bind.util.ISO8601Utils; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; -import com.google.gson.JsonElement; import io.gsonfire.GsonFireBuilder; -import io.gsonfire.TypeSelector; - -import de.gematik.ti.epa.vzd.client.model.*; -import okio.ByteString; - import java.io.IOException; import java.io.StringReader; import java.lang.reflect.Type; @@ -55,7 +50,7 @@ import java.util.Date; import java.util.Locale; import java.util.Map; -import java.util.HashMap; +import okio.ByteString; public class JSON { diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressRequestBody.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressRequestBody.java index 88096f7..dafa1e7 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressRequestBody.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressRequestBody.java @@ -29,11 +29,9 @@ package de.gematik.ti.epa.vzd.client.invoker; +import java.io.IOException; import okhttp3.MediaType; import okhttp3.RequestBody; - -import java.io.IOException; - import okio.Buffer; import okio.BufferedSink; import okio.ForwardingSink; diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressResponseBody.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressResponseBody.java index 5ca684c..1769d7a 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressResponseBody.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/ProgressResponseBody.java @@ -29,11 +29,9 @@ package de.gematik.ti.epa.vzd.client.invoker; +import java.io.IOException; import okhttp3.MediaType; import okhttp3.ResponseBody; - -import java.io.IOException; - import okio.Buffer; import okio.BufferedSource; import okio.ForwardingSource; diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/ApiKeyAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/ApiKeyAuth.java index afecfb6..e612615 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/ApiKeyAuth.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/ApiKeyAuth.java @@ -30,9 +30,8 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; import de.gematik.ti.epa.vzd.client.invoker.Pair; - -import java.util.Map; import java.util.List; +import java.util.Map; public class ApiKeyAuth implements Authentication { diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/Authentication.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/Authentication.java index 226e311..bfac6ec 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/Authentication.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/Authentication.java @@ -30,9 +30,8 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; import de.gematik.ti.epa.vzd.client.invoker.Pair; - -import java.util.Map; import java.util.List; +import java.util.Map; public interface Authentication { diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBasicAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBasicAuth.java index 652c31c..9d0d59e 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBasicAuth.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBasicAuth.java @@ -30,13 +30,9 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; import de.gematik.ti.epa.vzd.client.invoker.Pair; - -import okhttp3.Credentials; - -import java.util.Map; import java.util.List; - -import java.io.UnsupportedEncodingException; +import java.util.Map; +import okhttp3.Credentials; public class HttpBasicAuth implements Authentication { diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBearerAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBearerAuth.java index 682e788..af1dfd6 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBearerAuth.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/HttpBearerAuth.java @@ -30,9 +30,8 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; import de.gematik.ti.epa.vzd.client.invoker.Pair; - -import java.util.Map; import java.util.List; +import java.util.Map; public class HttpBearerAuth implements Authentication { diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuth.java index 4eca415..8647004 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuth.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuth.java @@ -30,9 +30,8 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; import de.gematik.ti.epa.vzd.client.invoker.Pair; - -import java.util.Map; import java.util.List; +import java.util.Map; public class OAuth implements Authentication { diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthOkHttpClient.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthOkHttpClient.java index f28399e..077bcd3 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthOkHttpClient.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/OAuthOkHttpClient.java @@ -16,12 +16,14 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; -import okhttp3.OkHttpClient; +import java.io.IOException; +import java.util.Map; +import java.util.Map.Entry; import okhttp3.MediaType; +import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; - import org.apache.oltu.oauth2.client.HttpClient; import org.apache.oltu.oauth2.client.request.OAuthClientRequest; import org.apache.oltu.oauth2.client.response.OAuthClientResponse; @@ -29,10 +31,6 @@ import org.apache.oltu.oauth2.common.exception.OAuthProblemException; import org.apache.oltu.oauth2.common.exception.OAuthSystemException; -import java.io.IOException; -import java.util.Map; -import java.util.Map.Entry; - public class OAuthOkHttpClient implements HttpClient { private OkHttpClient client; diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/RetryingOAuth.java b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/RetryingOAuth.java index fa3acea..14af54f 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/RetryingOAuth.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/invoker/auth/RetryingOAuth.java @@ -17,12 +17,14 @@ package de.gematik.ti.epa.vzd.client.invoker.auth; import de.gematik.ti.epa.vzd.client.invoker.Pair; - +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.List; +import java.util.Map; import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; - import org.apache.oltu.oauth2.client.OAuthClient; import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest; import org.apache.oltu.oauth2.client.request.OAuthClientRequest; @@ -32,11 +34,6 @@ import org.apache.oltu.oauth2.common.exception.OAuthSystemException; import org.apache.oltu.oauth2.common.message.types.GrantType; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.util.Map; -import java.util.List; - public class RetryingOAuth extends OAuth implements Interceptor { private OAuthClient oAuthClient; diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntry.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntry.java index 492fc16..b6f22fa 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntry.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntry.java @@ -29,19 +29,11 @@ package de.gematik.ti.epa.vzd.client.model; -import java.util.Objects; -import java.util.Arrays; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * BaseDirectoryEntry diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntry.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntry.java index ac69ca4..35cb1df 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntry.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntry.java @@ -29,20 +29,11 @@ package de.gematik.ti.epa.vzd.client.model; -import java.util.Objects; -import java.util.Arrays; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; -import de.gematik.ti.epa.vzd.client.model.UserCertificate; -import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * CreateDirectoryEntry diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntry.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntry.java index f94e47f..2392c6b 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntry.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntry.java @@ -29,21 +29,11 @@ package de.gematik.ti.epa.vzd.client.model; -import java.util.Objects; -import java.util.Arrays; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; -import de.gematik.ti.epa.vzd.client.model.Fachdaten; -import de.gematik.ti.epa.vzd.client.model.UserCertificate; -import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * DirectoryEntry diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/DistinguishedName.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/DistinguishedName.java index 04cdc29..d5feb1f 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/DistinguishedName.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/DistinguishedName.java @@ -29,18 +29,11 @@ package de.gematik.ti.epa.vzd.client.model; -import java.util.Objects; -import java.util.Arrays; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * DistinguishedName diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/Error.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/Error.java index fb7925c..600d23d 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/Error.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/Error.java @@ -29,16 +29,9 @@ package de.gematik.ti.epa.vzd.client.model; -import java.util.Objects; -import java.util.Arrays; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; +import java.util.Objects; /** * Error diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/FAD1.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/FAD1.java index d54c1a6..0a72dd3 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/FAD1.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/FAD1.java @@ -29,19 +29,10 @@ package de.gematik.ti.epa.vzd.client.model; -import java.util.Objects; -import java.util.Arrays; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; -import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * FAD1 diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/Fachdaten.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/Fachdaten.java index 7edcbc8..0620740 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/Fachdaten.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/Fachdaten.java @@ -29,20 +29,11 @@ package de.gematik.ti.epa.vzd.client.model; -import java.util.Objects; -import java.util.Arrays; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import de.gematik.ti.epa.vzd.client.model.FAD1; -import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Fachdaten diff --git a/src/main/java/de/gematik/ti/epa/vzd/client/model/UserCertificate.java b/src/main/java/de/gematik/ti/epa/vzd/client/model/UserCertificate.java index 6a091b8..c2cc51b 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/client/model/UserCertificate.java +++ b/src/main/java/de/gematik/ti/epa/vzd/client/model/UserCertificate.java @@ -29,19 +29,17 @@ package de.gematik.ti.epa.vzd.client.model; -import java.util.Objects; -import java.util.Arrays; import com.google.gson.TypeAdapter; import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; -import de.gematik.ti.epa.vzd.client.model.DistinguishedName; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Jeder Verzeichniseintrag muss mindestens ein Zertifikat enthalten. diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/CommandNamesEnum.java b/src/main/java/de/gematik/ti/epa/vzd/gem/CommandNamesEnum.java similarity index 94% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/CommandNamesEnum.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/CommandNamesEnum.java index 02af16b..efd548b 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/CommandNamesEnum.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/CommandNamesEnum.java @@ -14,13 +14,14 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient; +package de.gematik.ti.epa.vzd.gem; public enum CommandNamesEnum { ADD_DIR_ENTRY("addDirectoryEntries"), READ_DIR_ENTRY("readDirectoryEntries"), MOD_DIR_ENTRY("modifyDirectoryEntries"), + SMOD_DIR_ENTRY("safeModifyDirectoryEntries"), DEL_DIR_ENTRY("deleteDirectoryEntries"), ADD_DIR_CERT("addDirectoryEntryCertificate"), READ_DIR_CERT("readDirectoryEntryCertificate"), diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/GemStringUtils.java b/src/main/java/de/gematik/ti/epa/vzd/gem/GemStringUtils.java new file mode 100644 index 0000000..a353496 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/GemStringUtils.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gem; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +public class GemStringUtils { + + private static final String GOTHINC = "____ ______________________ _________ .__ .__ __ \n\\ \\ / /\\____ /\\______ \\ \\_ ___ \\| | |__| ____ _____/ |_ \n \\ Y / / / | | \\ ______ / \\ \\/| | | |/ __ \\ / \\ __\\ \n \\ / / /_ | ` \\ /_____/ \\ \\___| |_| \\ ___/| | \\ |\n \\___/ /_______ \\/_______ / \\______ /____/__|\\___ >___| /__|\n \\/ \\/ \\/ \\/ \\/ \n"; + private static final String GOOFY = " __ __ __ ___________ __ _____ __ __ ___ __ __\n| | | | (___ ) | \\ / __) \\ | (_ _) \\ ___) | \\ | | (__ __) \n| | | | / / | | ___ | / | | | | | (__ | | \\ | | | \n| | | | / / | | (___) | | | | | | | __) | | \\ \\| | | | \n \\ \\/ / / /__ | | | \\__ | |__ _| |_ | (___ | | \\ | | | \n__\\ /___( )_| /_________\\ )_/ )_( )_/ )_ | |___\\ |____| |____\n"; + private static final String FUZZY = ".-..-..----..---. .--. .-. _ .-. \n: :: :`--. :: . : : .--': : :_; .' `.\n: :: : ,',': :: : _____ : : : : .-. .--. ,-.,-.`. .'\n: `' ;.'.'_ : :; ::_____:: :__ : :_ : :' '_.': ,. : : : \n `.,' :____;:___.' `.__.'`.__;:_;`.__.':_;:_; :_;\n"; + private static final String FOURTOPS = "| |~~/|~~\\ /~~|' | \n \\ / / | |---| ||/~/|/~\\~|~\n \\/ /__|__/ \\__||\\/_| || \n"; + private static final String FENDER = "\\\\ // |'''''/ '||'''|. .|'''', '||` || \n \\\\ // // || || || || '' || \n \\\\ // // || || --- || || || .|''|, `||''|, ''||'' \n \\\\// // || || || || || ||..|| || || || \n \\/ /.....| .||...|' `|....' .||. .||. `|... .|| ||. `|..' \n"; + private static final String UNIVERS = "8b d8 888888888888 88888888ba, ,ad8888ba, 88 88 \n`8b d8' ,88 88 `\"8b d8\"' `\"8b 88 \"\" ,d \n `8b d8' ,88\" 88 `8b d8' 88 88 \n `8b d8' ,88\" 88 88 88 88 88 ,adPPYba, 8b,dPPYba, MM88MMM \n `8b d8' ,88\" 88 88 aaaaaaaa 88 88 88 a8P_____88 88P' `\"8a 88 \n `8b d8' ,88\" 88 8P \"\"\"\"\"\"\"\" Y8, 88 88 8PP\"\"\"\"\"\"\" 88 88 88 \n `888' 88\" 88 .a8P Y8a. .a8P 88 88 \"8b, ,aa 88 88 88, \n `8' 888888888888 88888888Y\"' `\"Y8888Y\"' 88 88 `\\\"Ybbd8\\\"'88 88 \\\"Y888 \"\n"; + private static final String SLANT = " _ _______ ____ ________ ___________ ________\n| | / /__ / / __ \\ / ____/ / / _/ ____/ | / /_ __/\n| | / / / / / / / /_____/ / / / / // __/ / |/ / / / \n| |/ / / /__/ /_/ /_____/ /___/ /____/ // /___/ /| / / / \n|___/ /____/_____/ \\____/_____/___/_____/_/ |_/ /_/ \n"; + private static final String MERLIN = " ___ ___ ________ ________- ______ ___ __ _______ _____ ___ ___________ \n|\" \\ /\" |(\" \"\\ |\" \"\\ /\" _ \"\\ |\" | |\" \\ /\" \"|(\\\" \\|\" \\(\" _ \") \n \\ \\ // / \\___/ :)(. ___ :)(: ( \\___)|| | || | (: ______)|.\\\\ \\ |)__/ \\\\__/ \n \\\\ \\/. ./ / ___/ |: \\ ) || \\/ \\ |: | |: | \\/ | |: \\. \\\\ | \\\\_ / \n \\. // // \\__ (| (___\\ || // \\ _ \\ |___ |. | // ___)_ |. \\ \\. | |. | \n \\\\ / (: / \"\\ |: :)(: _) \\ ( \\_|: \\ /\\ |\\(: \"|| \\ \\ | \\: | \n \\__/ \\_______)(________/ \\_______) \\_______)(__\\_|_)\\_______) \\___|\\____\\) \\__| \n"; + private static final String DIMENSION3 = "** ** ******** ******* ****** ** ** ******** **** ** **********\n/** /**//////** /**////** **////**/** /**/**///// /**/** /**/////**/// \n/** /** ** /** /** ** // /** /**/** /**//** /** /** \n//** ** ** /** /** *****/** /** /**/******* /** //** /** /** \n //** ** ** /** /**///// /** /** /**/**//// /** //**/** /** \n //**** ** /** ** //** **/** /**/** /** //**** /** \n //** ********/******* //****** /********/**/********/** //*** /** \n // //////// /////// ////// //////// // //////// // /// // "; + private static final String ISOMETRIC = " ___ ___ ___ ___ ___ ___ ___ ___ \n /\\__\\ /\\ \\ /\\ \\ /\\ \\ /\\__\\ ___ /\\ \\ /\\__\\ /\\ \\ \n /:/ / \\:\\ \\ /::\\ \\ /::\\ \\ /:/ / /\\ \\ /::\\ \\ /::| | \\:\\ \\ \n /:/ / \\:\\ \\ /:/\\:\\ \\ /:/\\:\\ \\ /:/ / \\:\\ \\ /:/\\:\\ \\ /:|:| | \\:\\ \\ \n /:/__/ ___ \\:\\ \\ /:/ \\:\\__\\ /:/ \\:\\ \\ /:/ / /::\\__\\ /::\\~\\:\\ \\ /:/|:| |__ /::\\ \\ \n |:| | /\\__\\ _______\\:\\__\\ /:/__/ \\:|__| /:/__/ \\:\\__\\ /:/__/ __/:/\\/__/ /:/\\:\\ \\:\\__\\ /:/ |:| /\\__\\ /:/\\:\\__\\\n |:| |/:/ / \\::::::::/__/ \\:\\ \\ /:/ / \\:\\ \\ \\/__/ \\:\\ \\ /\\/:/ / \\:\\~\\:\\ \\/__/ \\/__|:|/:/ / /:/ \\/__/\n |:|__/:/ / \\:\\~~\\~~ \\:\\ /:/ / \\:\\ \\ \\:\\ \\ \\::/__/ \\:\\ \\:\\__\\ |:/:/ / /:/ / \n \\::::/__/ \\:\\ \\ \\:\\/:/ / \\:\\ \\ \\:\\ \\ \\:\\__\\ \\:\\ \\/__/ |::/ / \\/__/ \n ~~~~ \\:\\__\\ \\::/__/ \\:\\__\\ \\:\\__\\ \\/__/ \\:\\__\\ /:/ / \n \\/__/ ~~ \\/__/ \\/__/ \\/__/ \\/__/ "; + private static final List PICS = Arrays + .asList(GOTHINC, GOOFY, FUZZY, FOURTOPS, FENDER, UNIVERS, SLANT, MERLIN, DIMENSION3, ISOMETRIC); + public static final String LINE = "\n=====================================================================\n\t\t\t=========================================================\n=====================================================================\n"; + + public static String listToString(List list) { + StringBuilder sb = new StringBuilder(); + for (String s : list) { + sb.append(s + ","); + } + if (sb.length() > 0) { + sb.setLength(sb.length() - 1); + } + return sb.toString(); + } + + public static String getPic() { + StringBuffer asciPic = new StringBuffer(); + asciPic.append(LINE); + asciPic.append(PICS.get(new Random().nextInt(GemStringUtils.PICS.size()))); + asciPic.append(LINE); + return asciPic.toString(); + } + +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/Main.java b/src/main/java/de/gematik/ti/epa/vzd/gem/Main.java similarity index 77% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/Main.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/Main.java index 882dfa0..c88fc3f 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/Main.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/Main.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient; +package de.gematik.ti.epa.vzd.gem; -import de.gematik.ti.epa.vzd.gemClient.command.CommandsBuilder; -import de.gematik.ti.epa.vzd.gemClient.command.ExecutionController; -import de.gematik.ti.epa.vzd.gemClient.command.commandExecutions.ExecutionCollection; -import de.gematik.ti.epa.vzd.gemClient.invoker.ConfigHandler; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.command.CommandsBuilder; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollection; +import de.gematik.ti.epa.vzd.gem.command.ExecutionController; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandler; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; import generated.CommandType; import java.util.List; import org.slf4j.Logger; @@ -38,15 +38,16 @@ public static void main(final String[] args) { } private static void start() { - ExecutionCollection.init(new GemApiClient()); List commands = new CommandsBuilder().buildCommands(); ConfigHandler configHandler = ConfigHandler.getInstance(); + configHandler.adjustConnectionCount(commands); LOG.debug("============ Execution parameter ============"); LOG.debug("Server: " + configHandler.getBasePath()); LOG.debug("OAuth Server: " + configHandler.getRetryingOAuthPath()); LOG.debug("Command data: " + configHandler.getCommandsPath()); LOG.debug("Commands in progress: " + commands.size()); LOG.debug("============================================="); + ExecutionCollection.init(ConnectionPool.createConnectionPool(ConfigHandler.getInstance().getConnectionCount())); new ExecutionController().execute(commands); } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemCertificateAdministrationApi.java b/src/main/java/de/gematik/ti/epa/vzd/gem/api/GemCertificateAdministrationApi.java similarity index 98% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemCertificateAdministrationApi.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/api/GemCertificateAdministrationApi.java index 0d12288..5ebe760 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemCertificateAdministrationApi.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/api/GemCertificateAdministrationApi.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.api; +package de.gematik.ti.epa.vzd.gem.api; import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; import de.gematik.ti.epa.vzd.client.invoker.ApiCallback; @@ -22,7 +22,7 @@ import de.gematik.ti.epa.vzd.client.invoker.Pair; import de.gematik.ti.epa.vzd.client.invoker.auth.OAuth; import de.gematik.ti.epa.vzd.client.model.UserCertificate; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.GemApiClient; import java.util.ArrayList; import java.util.HashMap; import java.util.List; diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemDirectoryEntryAdministrationApi.java b/src/main/java/de/gematik/ti/epa/vzd/gem/api/GemDirectoryEntryAdministrationApi.java similarity index 98% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemDirectoryEntryAdministrationApi.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/api/GemDirectoryEntryAdministrationApi.java index 8e467b9..c9a278d 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/api/GemDirectoryEntryAdministrationApi.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/api/GemDirectoryEntryAdministrationApi.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.api; +package de.gematik.ti.epa.vzd.gem.api; import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; import de.gematik.ti.epa.vzd.client.invoker.ApiCallback; @@ -23,13 +23,12 @@ import de.gematik.ti.epa.vzd.client.invoker.auth.OAuth; import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.GemApiClient; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import okhttp3.Call; -import okhttp3.Request; /** * Overrides all functions of DirectoryEntryAdministration api that build calls for the different commands to add the OAuth2 Token to the header diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilder.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/CommandsBuilder.java similarity index 93% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilder.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/command/CommandsBuilder.java index b292f77..9afcf0f 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilder.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/CommandsBuilder.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.command; +package de.gematik.ti.epa.vzd.gem.command; -import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; -import de.gematik.ti.epa.vzd.gemClient.invoker.ConfigHandler; -import de.gematik.ti.epa.vzd.gemClient.exceptions.ReadException; +import de.gematik.ti.epa.vzd.gem.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gem.exceptions.ReadException; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandler; import generated.CommandListType; import generated.CommandType; import generated.ObjectFactory; @@ -77,7 +77,7 @@ public List buildCommands() { Object commands = ((JAXBElement) obj).getValue(); if (commands instanceof CommandListType) { commandList = (CommandListType) commands; - if(addId(commandList)){ + if (addId(commandList)) { writeCommandDataWithIds(commandList); } LOG.debug("Commands have been build"); @@ -99,6 +99,7 @@ private boolean addId(CommandListType commandList) { Set givenIds = new HashSet<>(); for (CommandType command : commandList.getCommand()) { if (!StringUtils.isBlank(command.getCommandId()) && !givenIds.add(command.getCommandId())) { + LOG.error("The predefined ID \"" + command.getCommandId() + "\" occurs twice"); throw new CommandException("The predefined ID \"" + command.getCommandId() + "\" occurs twice"); } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionCollection.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/ExecutionCollection.java similarity index 53% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionCollection.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/command/ExecutionCollection.java index 0de8259..e2ba7b5 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionCollection.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/ExecutionCollection.java @@ -14,9 +14,19 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; - -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +package de.gematik.ti.epa.vzd.gem.command; + +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.AddDirEntryCertExecution; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.AddDirEntryExecution; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.DeleteDirEntryCertExecution; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.DeleteDirEntryExecution; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ExecutionBase; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ModifyDirEntryCertExecution; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ModifyDirEntryExecution; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ReadDirEntryCertExecution; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ReadDirEntryExecution; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.SaveModifyDirEntryExecution; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; @@ -27,6 +37,8 @@ */ public class ExecutionCollection { + private static ExecutionCollection executionCollection; + private final ReadDirEntryExecution readDirEntryExecution; private final ReadDirEntryCertExecution readDirEntryCertExecution; private final AddDirEntryExecution addDirEntryExecution; @@ -36,9 +48,8 @@ public class ExecutionCollection { private static Logger LOG = LoggerFactory.getLogger(ExecutionCollection.class); - private ArrayList specificExecutors = new ArrayList<>(); + private ArrayList executors = new ArrayList<>(); - private static ExecutionCollection executions; /** * Gives the instance as long as it exists. @@ -46,51 +57,56 @@ public class ExecutionCollection { * @return */ public static ExecutionCollection getInstance() { - if (executions == null) { - throw new InstantiationError("Please instance a executor first. It needs an ApiClient"); + if (executionCollection == null) { + throw new InstantiationError("Please instance a executor first. It needs an ConnectionPool"); } - return executions; + return executionCollection; } /** * Instances the executions * - * @param apiClient + * @param connectionPool * @return */ - public static ExecutionCollection init(GemApiClient apiClient) { - if (executions != null) { + public static ExecutionCollection init(IConnectionPool connectionPool) { + if (executionCollection != null) { LOG.error("Error occurred while initializing executions. Executor is already instanced"); throw new InstantiationError("Executor is already instanced"); } - executions = new ExecutionCollection(apiClient); - LOG.debug("Executions have been initialized correctly"); + executionCollection = new ExecutionCollection(connectionPool); + LOG.debug("Executiors have been initialized correctly"); - return executions; + return executionCollection; } - private ExecutionCollection(GemApiClient apiClient) { - this.readDirEntryExecution = new ReadDirEntryExecution(apiClient); - this.readDirEntryCertExecution = new ReadDirEntryCertExecution(apiClient); - this.addDirEntryExecution = new AddDirEntryExecution(apiClient); - this.addDirEntryCertExecution = new AddDirEntryCertExecution(apiClient); - this.modifyDirEntryExecution = new ModifyDirEntryExecution(apiClient); - this.modifyDirEntryCertExecution = new ModifyDirEntryCertExecution(apiClient); - - specificExecutors.add(readDirEntryExecution); - specificExecutors.add(addDirEntryExecution); - specificExecutors.add(modifyDirEntryExecution); - specificExecutors.add(new DeleteDirEntryExecution(apiClient)); - specificExecutors.add(readDirEntryCertExecution); - specificExecutors.add(addDirEntryCertExecution); - specificExecutors.add(modifyDirEntryCertExecution); - specificExecutors.add(new DeleteDirEntryCertExecution(apiClient)); + private ExecutionCollection(IConnectionPool connectionPool) { + this.readDirEntryExecution = new ReadDirEntryExecution(connectionPool); + this.readDirEntryCertExecution = new ReadDirEntryCertExecution(connectionPool); + this.addDirEntryExecution = new AddDirEntryExecution(connectionPool); + this.addDirEntryCertExecution = new AddDirEntryCertExecution(connectionPool); + this.modifyDirEntryExecution = new ModifyDirEntryExecution(connectionPool); + this.modifyDirEntryCertExecution = new ModifyDirEntryCertExecution(connectionPool); + + executors.add(readDirEntryExecution); + executors.add(addDirEntryExecution); + executors.add(modifyDirEntryExecution); + executors.add(new DeleteDirEntryExecution(connectionPool)); + executors.add(readDirEntryCertExecution); + executors.add(addDirEntryCertExecution); + executors.add(modifyDirEntryCertExecution); + executors.add(new DeleteDirEntryCertExecution(connectionPool)); + executors.add(new SaveModifyDirEntryExecution(connectionPool)); } // + public void setExecutionCollection(ExecutionCollection executionCollection) { + this.executionCollection = executionCollection; + } + public List getExecutors() { - return this.specificExecutors; + return this.executors; } public ReadDirEntryExecution getReadDirEntryExecution() { diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/ExecutionController.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/ExecutionController.java similarity index 57% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/command/ExecutionController.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/command/ExecutionController.java index 0417f0e..9d63786 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/ExecutionController.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/ExecutionController.java @@ -14,18 +14,20 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.command; +package de.gematik.ti.epa.vzd.gem.command; -import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; -import de.gematik.ti.epa.vzd.gemClient.command.commandExecutions.ExecutionBase; -import de.gematik.ti.epa.vzd.gemClient.command.commandExecutions.ExecutionCollection; -import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gem.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ExecutionBase; +import de.gematik.ti.epa.vzd.gem.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandler; import generated.CommandType; - import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,37 +49,43 @@ public void execute(List commandList) { Map report = new HashMap<>(); loadCommands(commandList); LOG.debug("Execution -> Run executions"); - boolean correctExecution = true; - for (ExecutionBase executor : ExecutionCollection.getInstance().getExecutors()) { - String executorName = extractExecutorName(executor); - if (!executor.executeCommands()) { - correctExecution = false; - report.put(executorName, false); - LOG.error("Error while execute commands in " + executorName); - } else { - report.put(executorName, true); - LOG.debug("All commands of " + executorName + " operated correctly"); - } - if (!executor.postCheck()) { - throw new CommandException("Command executed, but postcheck failed!"); - } + Optional correctExecution; + + ExecutorService executorService = Executors.newFixedThreadPool(ConfigHandler.getInstance().getMaxParallelExecutor()); + + try { + correctExecution = executorService + .invokeAll(ExecutionCollection.getInstance().getExecutors()) + .parallelStream() + .map( + executor -> { + try { + report.put(executor.get().getName(), executor.get().getResult()); + if (!executor.get().getResult()) { + LOG.error("Error while execute commands in " + executor.get().getName()); + } else { + LOG.debug( + "All commands of " + executor.get().getName() + " operated correctly"); + } + return executor.get().getResult(); + } catch (Exception e) { + LOG.error("Something went wrong. " + e.getMessage()); + } + return false; + }) + .filter(b -> !b) + .findAny(); + } catch (InterruptedException e) { + correctExecution = Optional.of(false); + Thread.currentThread().interrupt(); + } finally { + executorService.shutdown(); } logReport(report, correctExecution); - return; - } - /** - * Gets the name of the specific executor for logging - * - * @param executor - * @return - */ - private String extractExecutorName(ExecutionBase executor) { - String[] splitClass = executor.getClass().getName().split("\\."); - return splitClass[splitClass.length - 1]; } - private void logReport(Map report, boolean correctExecution) { + private void logReport(Map report, Optional correctExecution) { Iterator keys = report.keySet().iterator(); while (keys.hasNext()) { String key = keys.next(); @@ -86,7 +94,7 @@ private void logReport(Map report, boolean correctExecution) { String path = System.getProperties().getProperty("l4j.logDir") == null ? System.getProperties().getProperty("java.io.tmpdir") + "logs" : System.getProperties().getProperty("l4j.logDir"); - LOG.info("Execution -> All executions done" + (correctExecution ? " correctly" + LOG.info("Execution -> All executions done" + (correctExecution.isEmpty() ? " correctly" : ". Some commands failed. Please look at the logfile at " + path)); } @@ -95,10 +103,8 @@ private void loadCommands(List commandList) { boolean commandError = false; for (CommandType command : commandList) { boolean unknownCommand = true; - for (ExecutionBase specificExecutor : ExecutionCollection.getInstance() - .getExecutors()) { - if (specificExecutor - .canHandleCommand(CommandNamesEnum.getEntry(command.getName()))) { + for (ExecutionBase specificExecutor : ExecutionCollection.getInstance().getExecutors()) { + if (specificExecutor.canHandleCommand(CommandNamesEnum.getEntry(command.getName()))) { unknownCommand = false; if (!specificExecutor.preCheck(command)) { commandError = true; @@ -116,20 +122,5 @@ private void loadCommands(List commandList) { } LOG.debug("Execution -> Precheck successful"); } - - /** - * If a command was identified with the wrong name, the name can be changed and reordered by this function. Only call this in pre-check phase! - * - * @param command with another name - * @return - */ - public boolean reorder(CommandType command) { - for (ExecutionBase specificExecutor : ExecutionCollection.getInstance().getExecutors()) { - if (specificExecutor.canHandleCommand(CommandNamesEnum.getEntry(command.getName()))) { - return specificExecutor.preCheck(command); - } - } - return false; - } } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/Transformer.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/Transformer.java similarity index 73% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/command/Transformer.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/command/Transformer.java index 969fa10..7728afe 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/Transformer.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/Transformer.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.command; +package de.gematik.ti.epa.vzd.gem.command; import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; import de.gematik.ti.epa.vzd.client.model.DistinguishedName; import de.gematik.ti.epa.vzd.client.model.UserCertificate; import generated.CommandType; import generated.DistinguishedNameType; import generated.UserCertificateType; import java.util.ArrayList; -import java.util.List; import org.apache.commons.lang3.StringUtils; /** @@ -87,7 +87,7 @@ public static CreateDirectoryEntry getCreateDirectoryEntry(CommandType command) createDirectoryEntry.setDirectoryEntryBase(getBaseDirectoryEntryFromCommandType(command)); if (!command.getUserCertificate().isEmpty()) { createDirectoryEntry.setUserCertificates(new ArrayList<>()); - for (UserCertificateType cert:command.getUserCertificate()){ + for (UserCertificateType cert : command.getUserCertificate()) { createDirectoryEntry.getUserCertificates().add(getUserCertificate(cert)); } } @@ -114,8 +114,38 @@ private static UserCertificate getUserCertificate( userCertificate.setUserCertificate(cert); } - return userCertificate; } + public static CommandType getCommandTypeFromDirectoryEntry(DirectoryEntry entry) { + BaseDirectoryEntry baseEntry = entry.getDirectoryEntryBase(); + CommandType commandType = new CommandType(); + + commandType.setDn(getDnFromDnBase(baseEntry.getDn())); + commandType.setGivenName(baseEntry.getGivenName()); + commandType.setSn(baseEntry.getSn()); + commandType.setCn(baseEntry.getCn()); + commandType.setDisplayName(baseEntry.getDisplayName()); + commandType.setStreetAddress(baseEntry.getStreetAddress()); + commandType.setPostalCode(baseEntry.getPostalCode()); + commandType.setLocalityName(baseEntry.getLocalityName()); + commandType.setStateOrProvinceName(baseEntry.getStateOrProvinceName()); + commandType.setTitle(baseEntry.getTitle()); + commandType.setOrganization(baseEntry.getOrganization()); + commandType.setOtherName(baseEntry.getOtherName()); + commandType.getSpecialization().addAll(baseEntry.getSpecialization()); + commandType.getDomainID().addAll(baseEntry.getDomainID()); + commandType.setPersonalEntry(baseEntry.getPersonalEntry().toString()); + commandType.setDataFromAuthority(baseEntry.getDataFromAuthority().toString()); + + return commandType; + } + + private static DistinguishedNameType getDnFromDnBase(DistinguishedName dn) { + DistinguishedNameType dnType = new DistinguishedNameType(); + dnType.setUid(dn.getUid()); + dnType.setCn(dn.getCn()); + return dnType; + } + } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryCertExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryCertExecution.java similarity index 51% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryCertExecution.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryCertExecution.java index e267244..7d2bb5c 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryCertExecution.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryCertExecution.java @@ -14,24 +14,24 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; -import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; import de.gematik.ti.epa.vzd.client.invoker.ApiException; import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; import de.gematik.ti.epa.vzd.client.model.DistinguishedName; import de.gematik.ti.epa.vzd.client.model.UserCertificate; -import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; -import de.gematik.ti.epa.vzd.gemClient.api.GemCertificateAdministrationApi; -import de.gematik.ti.epa.vzd.gemClient.command.Transformer; -import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gem.api.GemCertificateAdministrationApi; +import de.gematik.ti.epa.vzd.gem.command.Transformer; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.dto.ExecutionResult; +import de.gematik.ti.epa.vzd.gem.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; import generated.CommandType; import generated.DistinguishedNameType; import generated.UserCertificateType; -import java.security.cert.CertificateRevokedException; +import java.util.concurrent.Callable; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpStatus; import org.slf4j.Logger; @@ -42,107 +42,103 @@ */ public class AddDirEntryCertExecution extends ExecutionBase { - private CertificateAdministrationApi certificateAdministrationApi; - private Logger LOG = LoggerFactory.getLogger(CertificateAdministrationApi.class); + private Logger LOG = LoggerFactory.getLogger(AddDirEntryCertExecution.class); - public AddDirEntryCertExecution(GemApiClient api) { - super(api, CommandNamesEnum.ADD_DIR_CERT); - certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); + public AddDirEntryCertExecution(IConnectionPool connectionPool) { + super(connectionPool, CommandNamesEnum.ADD_DIR_CERT); } @Override public boolean checkValidation(CommandType command) { - if (command.getUserCertificate() == null) { + if (command.getUserCertificate().isEmpty()) { LOG.error("No certificate element found"); return false; } - if (command.getUserCertificate().isEmpty()) { - LOG.error("No certificate delivered"); - return false; + String uid = null; + if (command.getDn() != null) { + uid = command.getDn().getUid(); } - String uid= null; for (UserCertificateType cert : command.getUserCertificate()) { - String uidDn = null; - if (command.getDn() != null) { - uidDn = command.getDn().getUid(); + if (StringUtils.isBlank(cert.getUserCertificate())) { + LOG.error("No user certificate for element found"); + return false; } - if (StringUtils.isBlank(uidDn)) { + if (cert.getDn() != null) { DistinguishedNameType certDn = cert.getDn(); - if (certDn != null) { + if (uid == null) { uid = certDn.getUid(); } - } - if(StringUtils.isBlank(uid)){ - uid = uidDn; - } - - if (StringUtils.isBlank(uid)) { - LOG.error("No uid delivered"); - return false; - } - if(!StringUtils.isNotBlank(uid) && !StringUtils.isNotBlank(uidDn) && !uid.equals(uidDn)) { - LOG.error("Mismatching uid delivered"); - return false; + if (StringUtils.isNotBlank(certDn.getUid())) { + if (!uid.equals(certDn.getUid())) { + LOG.error("Mismatching uid delivered"); + return false; + } } } + } + if (StringUtils.isBlank(uid)) { + LOG.error("No or mismatching uid delivered"); + return false; + } return true; } @Override - public boolean executeCommands() { - boolean runSuccessful = true; - for (CommandType command : commands) { - LOG.debug("--- Command " + command.getCommandId() + " ---"); - if (isEntryPresent(command)) { - try { - executeCommand(command); - } catch (Exception ex) { - LOG.error("An error have occured: " + ex.getMessage()); - runSuccessful = false; + protected Callable createCallable(CommandType command) { + return new Callable() { + + @Override + public Boolean call() throws Exception { + StringBuffer sb = new StringBuffer(); + sb.append("\n--- Command " + command.getCommandId() + " ---"); + try (GemApiClient apiClient = connectionPool.getConnection()) { + apiClient.validateToken(); + ExecutionResult result = executeCommand(command, apiClient); + sb.append(result.getMessage()); + sb.append("--- Command " + command.getCommandId() + " end ---"); + LOG.debug(sb.toString()); + return result.getResult(); + } catch (ApiException ex) { + sb.append(ex.getMessage()); + sb.append("\n--- Command " + command.getCommandId() + " end ---"); + LOG.error(sb.toString()); + return false; } - } else { - LOG.error("Entry for this Certificate could not be found: " - + Transformer.getCreateDirectoryEntry(command)); - runSuccessful = false; } - LOG.debug("--- Command " + command.getCommandId() + " end ---"); - } - return runSuccessful; + }; } - private ApiResponse executeCommand(CommandType command) { - apiClient.validateToken(); - + protected ExecutionResult executeCommand(CommandType command, GemApiClient apiClient) throws ApiException { + StringBuffer sb = new StringBuffer(); boolean runSuccessful = true; + int errorCode = 0; + CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); - ApiResponse response = null; + ApiResponse response; for (UserCertificate userCertificate : createDirectoryEntry.getUserCertificates()) { try { String uid = getUid(createDirectoryEntry.getDirectoryEntryBase(), userCertificate); - response = addSingleCertificate(uid, userCertificate); + response = addSingleCertificate(uid, userCertificate, apiClient); if (response.getStatusCode() == HttpStatus.SC_CREATED) { - LOG.debug( - "Certificate successful added: \n" + userCertificate); + sb.append("\nCertificate successful added: \n" + response.getData()); } - } catch (ApiException e) { + } catch (ApiException ex) { runSuccessful = false; - LOG.error( - "Something went wrong will adding certificate. Responsecode: " + e.getCode() - + " certificate: " + userCertificate.getUserCertificate()); + errorCode = ex.getCode(); + sb.append("\nSomething went wrong will adding certificate. Responsecode: " + ex.getCode() + + " certificate: " + userCertificate.getUserCertificate()); } } if (!runSuccessful) { - throw new CommandException( + throw new ApiException(errorCode, sb.toString() + "\n" + "At least one certificate could not be added in:" + "\n" + Transformer - .getCreateDirectoryEntry(command)); + .getCreateDirectoryEntry(command)); } - - return response; + return new ExecutionResult(sb.toString(), true); } private String getUid(BaseDirectoryEntry directoryEntryBase, UserCertificate userCertificate) { - // uid present because checked in validation String uidCert = null; String uidEntry = null; @@ -159,9 +155,9 @@ private String getUid(BaseDirectoryEntry directoryEntryBase, UserCertificate use return uidCert == null ? uidEntry : uidCert; } - private ApiResponse addSingleCertificate(String uid, - UserCertificate userCertificate) throws ApiException { - return certificateAdministrationApi + private ApiResponse addSingleCertificate(String uid, UserCertificate userCertificate, GemApiClient apiClient) + throws ApiException { + return new GemCertificateAdministrationApi(apiClient) .addDirectoryEntryCertificateWithHttpInfo(uid, userCertificate); } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryExecution.java new file mode 100644 index 0000000..14b5e10 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryExecution.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.gem.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gem.api.GemDirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollection; +import de.gematik.ti.epa.vzd.gem.command.Transformer; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.dto.ExecutionResult; +import de.gematik.ti.epa.vzd.gem.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gem.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; +import generated.CommandType; +import generated.UserCertificateType; +import java.util.concurrent.Callable; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific execution for Command "AddDirectoryEntry" + */ +public class AddDirEntryExecution extends ExecutionBase { + + private Logger LOG = LoggerFactory.getLogger(AddDirEntryExecution.class); + + public AddDirEntryExecution(IConnectionPool connectionPool) { + super(connectionPool, CommandNamesEnum.ADD_DIR_ENTRY); + } + + @Override + public boolean checkValidation(CommandType command) { + boolean check = true; + if (command.getUserCertificate().isEmpty()) { + LOG.error("Missing certificate"); + check = false; + } else { + for (UserCertificateType userCertificateType : command.getUserCertificate()) { + String telematikId = userCertificateType.getTelematikID(); + String userCertificate = userCertificateType.getUserCertificate(); + if ((StringUtils.isBlank(telematikId) && StringUtils + .isBlank(userCertificate))) { + check = false; + } + } + } + if (!check) { + LOG.error("Missing argument -> telematikId or userCertificate in every entry have to be set." + + command.getName() + "\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command)); + } + return check; + } + + @Override + protected Callable createCallable(CommandType command) { + return new Callable() { + + @Override + public Boolean call() throws Exception { + StringBuffer sb = new StringBuffer(); + sb.append("\n--- Command " + command.getCommandId() + " ---"); + try (GemApiClient apiClient = connectionPool.getConnection()) { + apiClient.validateToken(); + if (isEntryPresent(command, apiClient)) { + sb.append("\nEntry is already present in VZD. Will Proceed with modify directory entry command\n"); + ExecutionResult modifyExecutionResult = doModify(command, apiClient); + sb.append(modifyExecutionResult.getMessage()); + sb.append("\n--- Command " + command.getCommandId() + " end (proceeded as modify) ---"); + LOG.debug(sb.toString()); + return modifyExecutionResult.getResult(); + } else { + sb.append(executeCommand(command, apiClient).getMessage()); + sb.append("\n--- Command " + command.getCommandId() + " end ---"); + LOG.debug(sb.toString()); + return true; + } + } catch (ApiException ex) { + sb.append("\nAdd directory entry execution failed\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command) + "\n" + ex.getMessage()); + sb.append("\n--- Command " + command.getCommandId() + " end ---"); + LOG.error(sb.toString()); + return false; + } + } + }; + } + + /** + * Function that execute one command and logs the result + * + * @param command + * @return + * @throws ApiException + */ + protected ExecutionResult executeCommand(CommandType command, GemApiClient apiClient) throws ApiException { + + CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); + ApiResponse response = new GemDirectoryEntryAdministrationApi(apiClient) + .addDirectoryEntryWithHttpInfo(createDirectoryEntry); + if (response.getStatusCode() == HttpStatus.SC_CREATED) { + return new ExecutionResult("\nAdd directory entry execution successful operated\n" + response.getData(), true); + } else { + throw new CommandException( + "Add directory entry execution failed. Response-Status was: " + response + .getStatusCode() + "\n" + Transformer.getCreateDirectoryEntry(command)); + } + } + + private ExecutionResult doModify(CommandType command, GemApiClient apiClient) { + try { + return ExecutionCollection.getInstance().getModifyDirEntry().executeCommand(command, apiClient); + } catch (Exception ex) { + return new ExecutionResult(ex.getMessage(), false); + } + } + + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + } + + return false; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryCertExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryCertExecution.java new file mode 100644 index 0000000..697ca41 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryCertExecution.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import de.gematik.ti.epa.vzd.gem.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gem.api.GemCertificateAdministrationApi; +import de.gematik.ti.epa.vzd.gem.command.Transformer; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.dto.ExecutionResult; +import de.gematik.ti.epa.vzd.gem.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; +import generated.CommandType; +import generated.UserCertificateType; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific execution for Command "DeleteDirectoryEntryCertificate" + */ +public class DeleteDirEntryCertExecution extends ExecutionBase { + + private Logger LOG = LoggerFactory.getLogger(DeleteDirEntryCertExecution.class); + + + public DeleteDirEntryCertExecution(IConnectionPool connectionPool) { + super(connectionPool, CommandNamesEnum.DEL_DIR_CERT); + } + + @Override + public boolean checkValidation(CommandType command) { + String uid = null; + String cn; + if (command.getDn() != null) { + uid = command.getDn().getUid(); + } + List listCn = new ArrayList<>(); + for (UserCertificateType userCertificateType : command.getUserCertificate()) { + if (userCertificateType.getDn() != null) { + if (uid == null) { + uid = userCertificateType.getDn().getUid(); + } + if (!userCertificateType.getDn().getUid().equals(uid)) { + LOG.error("Different Uid delivered (" + userCertificateType.getDn().getUid() + " / " + uid + " at command " + command + .getCommandId()); + return false; + } + if (StringUtils.isBlank(userCertificateType.getDn().getCn())) { + LOG.error("Missing cn"); + return false; + } + if (listCn.contains(userCertificateType.getDn().getCn())) { + LOG.error("Some cn is named twice"); + return false; + } else { + listCn.add(userCertificateType.getDn().getCn()); + } + + } + } + if (StringUtils.isBlank(uid)) { + LOG.error("Missing UId or Cn"); + return false; + } + return true; + } + + @Override + protected Callable createCallable(CommandType command) { + return new Callable() { + @Override + public Boolean call() throws Exception { + StringBuffer sb = new StringBuffer(); + sb.append("\n--- Command " + command.getCommandId() + " ---"); + try (GemApiClient apiClient = connectionPool.getConnection()) { + apiClient.validateToken(); + sb.append(executeCommand(command, apiClient).getMessage()); + sb.append("--- Command " + command.getCommandId() + " end ---"); + LOG.debug(sb.toString()); + return true; + } catch (ApiException ex) { + sb.append(ex.getMessage()); + sb.append("\n--- Command " + command.getCommandId() + " end ---"); + LOG.error(sb.toString()); + return false; + } + } + }; + } + + protected ExecutionResult executeCommand(CommandType command, GemApiClient apiClient) throws ApiException { + StringBuffer sb = new StringBuffer(); + boolean runSuccessfull = true; + int errorCode = 0; + + CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); + + for (UserCertificate userCertificate : createDirectoryEntry.getUserCertificates()) { + try { + String uid = getUid(createDirectoryEntry.getDirectoryEntryBase(), userCertificate); + String cn = userCertificate.getDn().getCn(); + ApiResponse response = deleteSingleCertificate(uid, cn, apiClient); + if (response.getStatusCode() == HttpStatus.SC_OK) { + sb.append("\nCertificate successful deleted: \n" + userCertificate); + } + } catch (ApiException ex) { + runSuccessfull = false; + errorCode = ex.getCode(); + sb.append("\nSomething went wrong will deleting certificate. Responsecode: " + ex.getCode() + + " certificate: " + userCertificate.getUserCertificate()); + } + } + if (!runSuccessfull) { + throw new ApiException(errorCode, sb.toString() + "\n" + + "At least one certificate could not be deleted in:" + "\n" + Transformer + .getCreateDirectoryEntry(command)); + } + return new ExecutionResult(sb.toString(), true); + } + + private String getUid(BaseDirectoryEntry directoryEntryBase, UserCertificate userCertificate) { + String uidCert = userCertificate.getDn().getUid(); + String uidEntry = null; + + if (directoryEntryBase != null) { + DistinguishedName dn = directoryEntryBase.getDn(); + if (dn != null) { + uidEntry = dn.getUid(); + } + } + return uidCert == null ? uidEntry : uidCert; + } + + private ApiResponse deleteSingleCertificate(String uid, String certificateEntryId, GemApiClient apiClient) + throws ApiException { + return new GemCertificateAdministrationApi(apiClient) + .deleteDirectoryEntryCertificateWithHttpInfo(uid, certificateEntryId); + } + + @Override + public boolean postCheck() { + try { + super.postCheck(); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + } + + return false; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryExecution.java similarity index 56% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecution.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryExecution.java index 8e045db..0b0d384 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecution.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryExecution.java @@ -14,17 +14,18 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; -import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; import de.gematik.ti.epa.vzd.client.invoker.ApiException; import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; -import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; -import de.gematik.ti.epa.vzd.gemClient.api.GemDirectoryEntryAdministrationApi; -import de.gematik.ti.epa.vzd.gemClient.command.Transformer; -import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gem.api.GemDirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.gem.command.Transformer; +import de.gematik.ti.epa.vzd.gem.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gem.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; import generated.CommandType; +import java.util.concurrent.Callable; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpStatus; import org.slf4j.Logger; @@ -37,11 +38,8 @@ public class DeleteDirEntryExecution extends ExecutionBase { private Logger LOG = LoggerFactory.getLogger(DeleteDirEntryExecution.class); - private final DirectoryEntryAdministrationApi directoryEntryAdministrationApi; - - public DeleteDirEntryExecution(GemApiClient api) { - super(api, CommandNamesEnum.DEL_DIR_ENTRY); - directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); + public DeleteDirEntryExecution(IConnectionPool connectionPool) { + super(connectionPool, CommandNamesEnum.DEL_DIR_ENTRY); } @Override @@ -63,40 +61,37 @@ public boolean checkValidation(CommandType command) { } @Override - public boolean executeCommands() { - boolean runSuccessful = true; - if (commands.isEmpty()) { - return true; - } - for (CommandType command : commands) { - LOG.debug("--- Command " + command.getCommandId() + " ---"); - if (isEntryPresent(command)) { - try { - executeCommand(command); - } catch (Exception ex) { - LOG.error("An error have occured: " + ex.getMessage()); - runSuccessful = false; + protected Callable createCallable(CommandType command) { + return new Callable() { + @Override + public Boolean call() throws Exception { + StringBuffer sb = new StringBuffer(); + sb.append("\n--- Command " + command.getCommandId() + " ---\n"); + try (GemApiClient apiClient = connectionPool.getConnection()) { + apiClient.validateToken(); + sb.append(executeCommand(command, apiClient)); + sb.append("\n--- Command " + command.getCommandId() + " end ---"); + LOG.debug(sb.toString()); + } catch (ApiException ex) { + sb.append("Delete directory entry execution failed\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command) + "\n" + ex.getMessage()); + sb.append("\n--- Command " + command.getCommandId() + " end ---"); + LOG.error(sb.toString()); + return false; } - } else { - LOG.debug(command.getDn().getUid() + " could not be found"); - runSuccessful = false; + return true; } - LOG.debug("--- Command " + command.getCommandId() + " end ---"); - } - return runSuccessful; + }; } - private void executeCommand(CommandType command) throws ApiException { - apiClient.validateToken(); + private String executeCommand(CommandType command, GemApiClient apiClient) throws ApiException { - ApiResponse response = directoryEntryAdministrationApi + ApiResponse response = new GemDirectoryEntryAdministrationApi(apiClient) .deleteDirectoryEntryWithHttpInfo(command.getDn().getUid()); if (response.getStatusCode() == HttpStatus.SC_OK) { - LOG.debug("Delete directory entry execution successful operated for " + command.getDn() - .getUid()); + return "Delete directory entry execution successful operated for " + command.getDn().getUid(); } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) { - LOG.debug(command.getDn().getUid() + " could not be found"); - throw new CommandException(command.getDn().getUid() + " could not be found"); + return (command.getDn().getUid() + " could not be found"); } else { throw new CommandException( "Delete directory entry execution failed. Response-Status was: " @@ -116,4 +111,5 @@ public boolean postCheck() { return false; } + } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ExecutionBase.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ExecutionBase.java new file mode 100644 index 0000000..a9dac17 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ExecutionBase.java @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.UserCertificate; +import de.gematik.ti.epa.vzd.gem.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gem.api.GemCertificateAdministrationApi; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollection; +import de.gematik.ti.epa.vzd.gem.command.Transformer; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.dto.BaseExecutionResult; +import de.gematik.ti.epa.vzd.gem.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gem.exceptions.GemClientException; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandler; +import de.gematik.ti.epa.vzd.gem.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; +import generated.CommandType; +import generated.DistinguishedNameType; +import generated.UserCertificateType; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Represents the base for every specific execution + */ +public abstract class ExecutionBase implements Callable { + + private Logger LOG = LoggerFactory.getLogger(ExecutionBase.class); + protected static final int FIRST_INDEX = 0; + + protected final IConnectionPool connectionPool; + protected CommandNamesEnum execCommand; + protected List commands; + + public ExecutionBase(IConnectionPool connectionPool, CommandNamesEnum cmd) { + this.connectionPool = connectionPool; + this.execCommand = cmd; + this.commands = new ArrayList<>(); + } + + /** + * Every single executor validates their commands and log the missing or wrong values + * + * @param command + * @return + */ + public abstract boolean checkValidation(CommandType command); + + /** + * Creates Callable to execute one command + * + * @param command + * @return + */ + protected abstract Callable createCallable(CommandType command); + + /** + * Checks if the Executor can progress the named Command + * + * @param cmd + * @return + */ + public boolean canHandleCommand(CommandNamesEnum cmd) { + return this.execCommand.equals(cmd); + } + + /** + * Checks the given command for validation and adds it to the queue of commands to execute + * + * @param command + * @return + */ + public boolean preCheck(CommandType command) { + try { + if (!checkValidation(command)) { + throw new CommandException( + "Command invalid. Please check " + command.getName()); + } + commands.add(command); + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + return false; + } + } + + /** + * Gets the name of the specific executor for logging + * + * @param executor + * @return + */ + protected String extractExecutorName(ExecutionBase executor) { + String[] splitClass = executor.getClass().getName().split("\\."); + return splitClass[splitClass.length - 1]; + } + + /** + * Checks if the execution was successful and logs the result + * + * @return + */ + public boolean postCheck() { + try { + return true; + } catch (Exception ex) { + LOG.error(ex.getMessage()); + } + return false; + } + + /** + * This function proceed a read command, to check if a entry without cert is already present + * + * @param command + * @return + */ + protected boolean isEntryPresent(CommandType command, GemApiClient apiClient) throws ApiException { + if (command.getDn() != null) { + CommandType searchCommand = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid(getUid(command, apiClient)); + searchCommand.setDn(dn); + try { + ApiResponse> response = ExecutionCollection.getInstance().getReadDirEntryExecution() + .executeCommand(searchCommand, apiClient); + return response.getStatusCode() == HttpStatus.SC_OK ? true : false; + } catch (ApiException ex) { + if (ex.getCode() == 0) { + throw new GemClientException( + "The server you address is probably not reachable at the moment"); + } + LOG.error(ex.getMessage()); + return false; + } + } else { + return serachByTelematikId(command, apiClient); + } + } + + /** + * This function proceed a read command, to check if a entry without cert is already present + * + * @param command + * @return + */ + private boolean serachByTelematikId(CommandType command, GemApiClient apiClient) { + UserCertificateType userCertificate = command.getUserCertificate().get(FIRST_INDEX); + if (userCertificate != null) { + try { + CommandType searchCommand = new CommandType(); + searchCommand.getUserCertificate().add(new UserCertificateType()); + searchCommand.getUserCertificate().get(FIRST_INDEX).setTelematikID(userCertificate.getTelematikID()); + ApiResponse> response = ExecutionCollection + .getInstance().getReadDirEntryCertExecution().executeCommand(searchCommand, apiClient); + return response.getStatusCode() == HttpStatus.SC_OK ? true : false; + } catch (ApiException ex) { + if (ex.getCode() == 0) { + throw new GemClientException( + "The server you address is probably not reachable at the moment"); + } + return false; + } + } + throw new GemClientException("No valid parameter found for present check" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + } + + public boolean searchByUserCertificate(CommandType command, GemApiClient apiClient) { + ApiResponse> response; + try { + response = ExecutionCollection.getInstance().getReadDirEntryCertExecution() + .executeCommand(command, apiClient); + } catch (ApiException ex) { + if (ex.getCode() == 0) { + throw new GemClientException( + "The server you address is probably not reachable at the moment"); + } + LOG.error(ex.getMessage()); + return false; + } + if (response.getData().size() == command.getUserCertificate().size()) { + return true; + } + return false; + } + + public String getUidByTelematikId(String telematikId, GemApiClient apiClient) throws ApiException { + ApiResponse> response = null; + try { + response = new GemCertificateAdministrationApi(apiClient) + .readDirectoryCertificatesWithHttpInfo(null, null, null, + telematikId, null, null); + if (!response.getData().isEmpty()) { + return response.getData().get(FIRST_INDEX).getDn().getUid(); + } + } catch (ApiException ex) { + if (ex.getCode() == 0) { + throw new GemClientException( + "The server you address is probably not reachable at the moment"); + } + if (ex.getCode() == 404) { + return null; + } + } + throw new ApiException("Problem occurred while finding uid via telematikId"); + } + + + @Override + public BaseExecutionResult call() { + LOG.debug("CALL " + extractExecutorName(this)); + + if (commands.size() == 0) { + LOG.debug("CALL END " + extractExecutorName(this)); + return new BaseExecutionResult(extractExecutorName(this), true); + } + List> callables = new ArrayList<>(); + ExecutorService executorService = Executors.newFixedThreadPool(ConfigHandler.getInstance().getMaxParaExecutionsPerExecutor()); + for (CommandType command : commands) { + callables.add(createCallable(command)); + } + try { + List> futures = executorService.invokeAll(callables); + Optional> first = + futures.stream() + .filter( + booleanFuture -> { + try { + return !booleanFuture.get(); + } catch (InterruptedException | ExecutionException e) { + Thread.currentThread().interrupt(); + return false; + } + }) + .findFirst(); + if (first.isPresent()) { + return new BaseExecutionResult(extractExecutorName(this), false); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return new BaseExecutionResult(extractExecutorName(this), false); + } finally { + LOG.debug("CALL END " + extractExecutorName(this)); + executorService.shutdown(); + } + + return new BaseExecutionResult(extractExecutorName(this), true); + } + + + protected String getUid(CommandType command, GemApiClient apiClient) throws ApiException { + String uidFromTelematikID = null; + String uidFromCommand = null; + + if (command.getDn() != null) { + uidFromCommand = command.getDn().getUid(); + } + if (!command.getUserCertificate().isEmpty()) { + if (command.getUserCertificate().get(FIRST_INDEX).getDn() != null && StringUtils.isBlank(uidFromCommand)) { + uidFromCommand = command.getUserCertificate().get(FIRST_INDEX).getDn().getUid(); + } + if (StringUtils.isNotBlank(command.getUserCertificate().get(FIRST_INDEX).getTelematikID())) { + uidFromTelematikID = getUidByTelematikId(command.getUserCertificate().get(FIRST_INDEX).getTelematikID(), apiClient); + } + } + if (StringUtils.isNotBlank(uidFromCommand) && StringUtils.isNotBlank(uidFromTelematikID)) { + if (!uidFromCommand.equals(uidFromTelematikID)) { + throw new ApiException("UID delivered by TelematikId does not match the UID in command file"); + } + } + return StringUtils.isBlank(uidFromCommand) ? uidFromTelematikID : uidFromCommand; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryCertExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryCertExecution.java similarity index 76% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryCertExecution.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryCertExecution.java index dfa7503..f6e3684 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryCertExecution.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryCertExecution.java @@ -14,21 +14,22 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; -import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; import de.gematik.ti.epa.vzd.client.invoker.ApiException; import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; import de.gematik.ti.epa.vzd.client.model.UserCertificate; -import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; -import de.gematik.ti.epa.vzd.gemClient.api.GemCertificateAdministrationApi; -import de.gematik.ti.epa.vzd.gemClient.command.Transformer; -import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gem.api.GemCertificateAdministrationApi; +import de.gematik.ti.epa.vzd.gem.command.Transformer; +import de.gematik.ti.epa.vzd.gem.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gem.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; import generated.CommandType; import generated.DistinguishedNameType; import generated.UserCertificateType; +import java.util.concurrent.Callable; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpStatus; import org.slf4j.Logger; @@ -40,11 +41,9 @@ public class ModifyDirEntryCertExecution extends ExecutionBase { private Logger LOG = LoggerFactory.getLogger(ModifyDirEntryCertExecution.class); - private CertificateAdministrationApi certificateAdministrationApi; - public ModifyDirEntryCertExecution(GemApiClient api) { - super(api, CommandNamesEnum.MOD_DIR_CERT); - certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); + public ModifyDirEntryCertExecution(IConnectionPool connectionPool) { + super(connectionPool, CommandNamesEnum.MOD_DIR_CERT); } @Override @@ -59,6 +58,17 @@ public boolean checkValidation(CommandType command) { return true; } + @Override + protected Callable createCallable(CommandType command) { + LOG.info("The execution for ModifyCertificate is exposed"); + return new Callable() { + @Override + public Boolean call() throws Exception { + return true; + } + }; + } + private boolean checkSingleUserCertificate(UserCertificateType cert) { if (cert.getDn() != null) { DistinguishedNameType dn = cert.getDn(); @@ -69,24 +79,14 @@ private boolean checkSingleUserCertificate(UserCertificateType cert) { return true; } - @Override - public boolean executeCommands() { - if (commands.size() == 0) { - return true; - } - LOG.info("The execution for ModifyCertificate is exposed"); - return true; - } - - protected ApiResponse executeCommand(CommandType command) { - apiClient.validateToken(); + protected ApiResponse executeCommand(CommandType command, GemApiClient apiClient) throws ApiException { CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); ApiResponse response = null; for (UserCertificate userCertificate : createDirectoryEntry.getUserCertificates()) { try { - response = certificateAdministrationApi + response = new GemCertificateAdministrationApi(apiClient) .modifyDirectoryEntryCertificateWithHttpInfo(userCertificate.getDn().getUid(), userCertificate.getDn().getCn(), userCertificate); if (response.getStatusCode() == HttpStatus.SC_OK) { @@ -99,7 +99,7 @@ protected ApiResponse executeCommand(CommandType command) { .getBaseDirectoryEntryFromCommandType(command)); } } catch (ApiException e) { - e.printStackTrace(); + LOG.error("ModifyCertExecution could not be proceed. " + e.getMessage()); } } return response; @@ -116,4 +116,5 @@ public boolean postCheck() { return false; } + } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryExecution.java new file mode 100644 index 0000000..4fabbcf --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryExecution.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.gem.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gem.api.GemDirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollection; +import de.gematik.ti.epa.vzd.gem.command.Transformer; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.dto.ExecutionResult; +import de.gematik.ti.epa.vzd.gem.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gem.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; +import generated.CommandType; +import generated.UserCertificateType; +import java.util.concurrent.Callable; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Specific execution for Command "ModifyDirectoryEntry" + */ +public class ModifyDirEntryExecution extends ExecutionBase { + + private Logger LOG = LoggerFactory.getLogger(ModifyDirEntryExecution.class); + + public ModifyDirEntryExecution(IConnectionPool connectionPool) { + super(connectionPool, CommandNamesEnum.MOD_DIR_ENTRY); + } + + @Override + public boolean checkValidation(CommandType command) { + if (command.getDn() != null) { + if (StringUtils.isBlank(command.getDn().getUid())) { + LOG.error( + "Missing argument -> uid for command " + command.getName() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + return false; + } + return true; + } + if (!command.getUserCertificate().isEmpty()) { + String telematikId = command.getUserCertificate().get(0).getTelematikID(); + for (UserCertificateType userCertificateType : command.getUserCertificate()) { + if (!telematikId.equals(userCertificateType.getTelematikID())) { + return false; + } + } + return true; + } + LOG.error("Missing element \"dn\" " + command.getName() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + return false; + } + + @Override + protected Callable createCallable(CommandType command) { + return new Callable() { + + @Override + public Boolean call() throws Exception { + StringBuffer sb = new StringBuffer(); + sb.append("\n--- Command " + command.getCommandId() + " ---\n"); + + try (GemApiClient apiClient = connectionPool.getConnection()) { + apiClient.validateToken(); + if (isEntryPresent(command, apiClient)) { + ExecutionResult modifyExecutionResult = executeCommand(command, apiClient); + sb.append(modifyExecutionResult.getMessage()); + sb.append("--- Command " + command.getCommandId() + " end ---"); + LOG.debug(sb.toString()); + return modifyExecutionResult.getResult(); + } else { + sb.append("\nEntry is not present in VZD. Will Proceed with add directory entry command\n"); + ExecutionResult addExecutionResult = doAdd(command, apiClient); + sb.append(addExecutionResult.getMessage()); + sb.append("\n--- Command " + command.getCommandId() + " end (proceeded as add) ---"); + return addExecutionResult.getResult(); + } + } catch (ApiException ex) { + sb.append("Modify directory entry execution failed\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command)+ "\n" + ex.getMessage()); + sb.append("\n--- Command " + command.getCommandId() + " end ---"); + LOG.error(sb.toString()); + return false; + } + } + }; + } + + /** + * Function that execute one command and logs the result + * + * @param command + * @return + * @throws ApiException + */ + protected ExecutionResult executeCommand(CommandType command, GemApiClient apiClient) + throws ApiException { + + BaseDirectoryEntry baseDirectoryEntry = Transformer + .getBaseDirectoryEntryFromCommandType(command); + if (baseDirectoryEntry.getDn() == null) { + baseDirectoryEntry.setDn(new DistinguishedName()); + } + if (StringUtils.isBlank(baseDirectoryEntry.getDn().getUid())) { + baseDirectoryEntry.getDn().setUid(getUid(command, apiClient)); + } + ApiResponse response = new GemDirectoryEntryAdministrationApi(apiClient) + .modifyDirectoryEntryWithHttpInfo(baseDirectoryEntry.getDn().getUid(), baseDirectoryEntry); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return (new ExecutionResult( + "Modify directory entry execution successful operated\n" + response.getData(), true)); + } else { + throw new CommandException( + "Modify directory entry execution failed. Response status was: " + + response.getStatusCode() + "\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command)); + } + } + + private ExecutionResult doAdd(CommandType command, GemApiClient apiClient) throws ApiException { + LOG.debug("Entry not present at VZD. Will proceed with add directory entry command"); + return ExecutionCollection.getInstance().getAddDirEntryExecution().executeCommand(command, apiClient); + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryCertExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryCertExecution.java similarity index 54% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryCertExecution.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryCertExecution.java index 18f86da..cc64554 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryCertExecution.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryCertExecution.java @@ -14,21 +14,22 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; -import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; import de.gematik.ti.epa.vzd.client.invoker.ApiException; import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; import de.gematik.ti.epa.vzd.client.model.UserCertificate; -import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; -import de.gematik.ti.epa.vzd.gemClient.GemStringUtils; -import de.gematik.ti.epa.vzd.gemClient.api.GemCertificateAdministrationApi; -import de.gematik.ti.epa.vzd.gemClient.command.Transformer; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gem.GemStringUtils; +import de.gematik.ti.epa.vzd.gem.api.GemCertificateAdministrationApi; +import de.gematik.ti.epa.vzd.gem.command.Transformer; +import de.gematik.ti.epa.vzd.gem.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; import generated.CommandType; import generated.UserCertificateType; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Callable; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,16 +39,18 @@ */ public class ReadDirEntryCertExecution extends ExecutionBase { - private CertificateAdministrationApi certificateAdministrationApi; private Logger LOG = LoggerFactory.getLogger(ReadDirEntryCertExecution.class); - public ReadDirEntryCertExecution(GemApiClient api) { - super(api, CommandNamesEnum.READ_DIR_CERT); - certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); + public ReadDirEntryCertExecution(IConnectionPool connectionPool) { + super(connectionPool, CommandNamesEnum.READ_DIR_CERT); } @Override public boolean checkValidation(CommandType command) { + if (command.getUserCertificate().isEmpty()) { + LOG.error("No certificate delivered for command " + Transformer.getCreateDirectoryEntry(command)); + return false; + } for (UserCertificateType cert : command.getUserCertificate()) { if (checkSingleCertificate(cert) == false) { return false; @@ -57,51 +60,51 @@ public boolean checkValidation(CommandType command) { } private boolean checkSingleCertificate(UserCertificateType cert) { - if (cert != null) { - List params = new ArrayList<>(); - if (cert.getDn() != null) { - params.add(cert.getDn().getUid()); - } - params.add(cert.getEntryType()); - params.add(cert.getTelematikID()); - params.add(cert.getProfessionOID()); - if (cert.getUsage().isEmpty()) { - params.add("usage Vorhanden"); - } - for (String param : params) { - if (!StringUtils.isBlank(param)) { - return true; - } + List params = new ArrayList<>(); + if (cert.getDn() != null) { + params.add(cert.getDn().getUid()); + } + params.add(cert.getEntryType()); + params.add(cert.getTelematikID()); + params.add(cert.getProfessionOID()); + if (!cert.getUsage().isEmpty()) { + params.add("usage Vorhanden"); + } + for (String param : params) { + if (!StringUtils.isBlank(param)) { + return true; } } return false; } @Override - public boolean executeCommands() { - boolean runSuccessful = true; - if (commands.isEmpty()) { - return true; - } - for (CommandType command : commands) { - LOG.debug("--- Command " + command.getCommandId() + " ---"); - try { - for (UserCertificate userCertificate : executeCommand(command).getData()) { - LOG.debug("Entry found: " + userCertificate); + protected Callable createCallable(CommandType command) { + return new Callable() { + + @Override + public Boolean call() throws Exception { + StringBuffer sb = new StringBuffer(); + sb.append("\n--- Command " + command.getCommandId() + " ---\n"); + try (GemApiClient apiClient = connectionPool.getConnection()) { + apiClient.validateToken(); + executeCommand(command, apiClient).getData().forEach(directoryEntry -> sb.append("Entry found: " + directoryEntry)); + sb.append("--- Command " + command.getCommandId() + " end ---"); + LOG.debug(sb.toString()); + } catch (ApiException ex) { + sb.append("Read directory entry cert execution failed\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command) + "\n" + ex.getMessage()); + sb.append("\n--- Command " + command.getCommandId() + " end ---"); + LOG.error(sb.toString()); + return false; } - } catch (Exception ex) { - LOG.error("Read directory entry execution failed\n" + Transformer - .getCreateDirectoryEntry(command)); - runSuccessful = false; + return true; } - LOG.debug("--- Command " + command.getCommandId() + " end ---"); - } - return runSuccessful; + }; } - protected ApiResponse> executeCommand(CommandType command) + protected ApiResponse> executeCommand(CommandType command, GemApiClient apiClient) throws ApiException { - apiClient.validateToken(); ApiResponse> result = null; for (UserCertificateType cert : command.getUserCertificate()) { @@ -119,7 +122,7 @@ protected ApiResponse> executeCommand(CommandType command) if (cert.getUsage().isEmpty()) { usage = GemStringUtils.listToString(cert.getUsage()); } - ApiResponse> tempResult = certificateAdministrationApi + ApiResponse> tempResult = new GemCertificateAdministrationApi(apiClient) .readDirectoryCertificatesWithHttpInfo(uid, cn, entryType, telematikID, professionOID, usage); if (result == null) { @@ -128,7 +131,6 @@ protected ApiResponse> executeCommand(CommandType command) result.getData().addAll(tempResult.getData()); } } - return result; } @@ -142,4 +144,5 @@ public boolean postCheck() { } return false; } + } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryExecution.java similarity index 66% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecution.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryExecution.java index e9a80de..dbdc4d0 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecution.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryExecution.java @@ -14,23 +14,23 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; -import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; import de.gematik.ti.epa.vzd.client.invoker.ApiException; import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; -import de.gematik.ti.epa.vzd.client.model.UserCertificate; -import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; -import de.gematik.ti.epa.vzd.gemClient.GemStringUtils; -import de.gematik.ti.epa.vzd.gemClient.api.GemDirectoryEntryAdministrationApi; -import de.gematik.ti.epa.vzd.gemClient.command.Transformer; -import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gem.GemStringUtils; +import de.gematik.ti.epa.vzd.gem.api.GemDirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.gem.command.Transformer; +import de.gematik.ti.epa.vzd.gem.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gem.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; import generated.CommandType; import generated.UserCertificateType; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Callable; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpStatus; import org.slf4j.Logger; @@ -42,11 +42,9 @@ public class ReadDirEntryExecution extends ExecutionBase { private Logger LOG = LoggerFactory.getLogger(ReadDirEntryExecution.class); - private DirectoryEntryAdministrationApi directoryEntryAdministrationApi; - public ReadDirEntryExecution(GemApiClient api) { - super(api, CommandNamesEnum.READ_DIR_ENTRY); - directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); + public ReadDirEntryExecution(IConnectionPool connectionPool) { + super(connectionPool, CommandNamesEnum.READ_DIR_ENTRY); } @Override @@ -101,29 +99,33 @@ private boolean certificateCheck(List userCertificate) { } @Override - public boolean executeCommands() { - boolean runSuccessful = true; - for (CommandType command : commands) { - LOG.debug("--- Command " + command.getCommandId() + " ---"); - try { - for (DirectoryEntry directoryEntry : executeCommand(command).getData()) { - LOG.debug("Entry found: " + directoryEntry); + protected Callable createCallable(CommandType command) { + return new Callable() { + + @Override + public Boolean call() throws Exception { + StringBuffer sb = new StringBuffer(); + sb.append("\n--- Command " + command.getCommandId() + " ---\n"); + try (GemApiClient apiClient = connectionPool.getConnection()) { + apiClient.validateToken(); + executeCommand(command, apiClient).getData().forEach(directoryEntry -> sb.append("Entry found: " + directoryEntry)); + sb.append("\n--- Command " + command.getCommandId() + " end ---"); + LOG.debug(sb.toString()); + return true; + } catch (ApiException ex) { + sb.append("\nRead directory entry execution failed\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command) + "\n" + ex.getMessage()); + sb.append("\n--- Command " + command.getCommandId() + " end ---"); + LOG.error(sb.toString()); + return false; } - } catch (Exception ex) { - LOG.error("Read directory entry execution failed\n" - + Transformer.getBaseDirectoryEntryFromCommandType(command)); - runSuccessful = false; } - LOG.debug("--- Command " + command.getCommandId() + " end ---"); - } - return runSuccessful; + }; } - public ApiResponse> executeCommand(CommandType command) - throws ApiException { - apiClient.validateToken(); + public ApiResponse> executeCommand(CommandType command, GemApiClient apiClient) throws ApiException { - String uid = getUid(command); + String uid = getUid(command, apiClient); String givenName = command.getGivenName(); String sn = command.getSn(); String cn = command.getCn(); @@ -141,7 +143,7 @@ public ApiResponse> executeCommand(CommandType command) String dataFromAuthority = command.getDataFromAuthority(); ApiResponse> response = - directoryEntryAdministrationApi.readDirectoryEntryWithHttpInfo(uid, givenName, sn, cn, + new GemDirectoryEntryAdministrationApi(apiClient).readDirectoryEntryWithHttpInfo(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, stateOrProvinceName, title, organization, otherName, specialization, domainID, personalEntry, dataFromAuthority); @@ -149,29 +151,12 @@ public ApiResponse> executeCommand(CommandType command) return response; } else { throw new CommandException( - "Modify directory entry execution failed. Response status was: " + "Read directory entry execution failed. Response status was: " + response.getStatusCode() + "\n" + Transformer.getBaseDirectoryEntryFromCommandType(command)); } } - private String getUid(CommandType command) throws ApiException { - String cUid = null; - String tUid = null; - if (command.getDn() != null) { - cUid = command.getDn().getUid(); - } - if (!command.getUserCertificate().isEmpty()) { - tUid = getUidByTelematikId(command.getUserCertificate().get(FIRST_INDEX).getTelematikID()); - } - if (!StringUtils.isBlank(tUid) && !StringUtils.isBlank(cUid)) { - if (!tUid.equals(cUid)) { - throw new ApiException("UID delivered by TelematikId does not match the UID in commandfile"); - } - } - return StringUtils.isBlank(tUid) ? cUid : tUid; - } - @Override public boolean postCheck() { try { diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/SaveModifyDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/SaveModifyDirEntryExecution.java new file mode 100644 index 0000000..5517a0d --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/SaveModifyDirEntryExecution.java @@ -0,0 +1,184 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; +import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; +import de.gematik.ti.epa.vzd.client.model.DistinguishedName; +import de.gematik.ti.epa.vzd.gem.CommandNamesEnum; +import de.gematik.ti.epa.vzd.gem.api.GemDirectoryEntryAdministrationApi; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollection; +import de.gematik.ti.epa.vzd.gem.command.Transformer; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.dto.ExecutionResult; +import de.gematik.ti.epa.vzd.gem.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gem.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; +import generated.CommandType; +import generated.UserCertificateType; +import java.util.List; +import java.util.concurrent.Callable; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SaveModifyDirEntryExecution extends ExecutionBase { + + private Logger LOG = LoggerFactory.getLogger(SaveModifyDirEntryExecution.class); + private static final int FIRST_INDEX = 0; + + public SaveModifyDirEntryExecution(IConnectionPool connectionPool) { + super(connectionPool, CommandNamesEnum.SMOD_DIR_ENTRY); + } + + @Override + public boolean checkValidation(CommandType command) { + if (command.getDn() != null) { + if (StringUtils.isBlank(command.getDn().getUid())) { + LOG.error( + "Missing argument -> uid for command " + command.getName() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + return false; + } + return true; + } + if (!command.getUserCertificate().isEmpty()) { + String telematikId = command.getUserCertificate().get(FIRST_INDEX).getTelematikID(); + for (UserCertificateType userCertificateType : command.getUserCertificate()) { + if (!telematikId.equals(userCertificateType.getTelematikID())) { + return false; + } + } + return true; + } + LOG.error("Missing element \"dn\" " + command.getName() + "\n" + Transformer + .getBaseDirectoryEntryFromCommandType(command)); + return false; + } + + @Override + protected Callable createCallable(CommandType command) { + + return new Callable() { + @Override + public Boolean call() throws Exception { + StringBuffer sb = new StringBuffer(); + sb.append("\n--- Command " + command.getCommandId() + " ---\n"); + + try (GemApiClient apiClient = connectionPool.getConnection()) { + apiClient.validateToken(); + if (isEntryPresent(command, apiClient)) { + CommandType safeCommand = getSaveCommand(command, apiClient); + ExecutionResult modifyExecutionResult = executeCommand(safeCommand, apiClient); + sb.append(modifyExecutionResult.getMessage()); + sb.append("--- Command " + safeCommand.getCommandId() + " end ---"); + LOG.debug(sb.toString()); + return modifyExecutionResult.getResult(); + } else { + sb.append("\nEntry is not present in VZD. Will Proceed with add directory entry command\n"); + ExecutionResult addExecutionResult = doAdd(command, apiClient); + sb.append(addExecutionResult.getMessage()); + sb.append("\n--- Command " + command.getCommandId() + " end (proceeded as add) ---"); + return addExecutionResult.getResult(); + } + } catch (ApiException ex) { + sb.append("Save modify directory entry execution failed\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command)); + sb.append("\n--- Command " + command.getCommandId() + " end ---"); + LOG.error(sb.toString()); + return false; + } + } + }; + } + + private CommandType getSaveCommand(CommandType command, GemApiClient apiClient) throws ApiException { + ApiResponse> response = new GemDirectoryEntryAdministrationApi(apiClient) + .readDirectoryEntryWithHttpInfo(command.getDn().getUid(), null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null); + CommandType saveCommand = Transformer.getCommandTypeFromDirectoryEntry(response.getData().get(0)); + + saveCommand.setCommandId(command.getCommandId()); + saveCommand.setName(command.getName()); + + if (command.getDn() != null) { + saveCommand.setDn(command.getDn()); + } + if (StringUtils.isNotBlank(command.getGivenName())) { + saveCommand.setGivenName(command.getGivenName()); + } + if (StringUtils.isNotBlank(command.getSn())) { + saveCommand.setSn(command.getSn()); + } + if (StringUtils.isNotBlank(command.getStreetAddress())) { + saveCommand.setStreetAddress(command.getStreetAddress()); + } + if (StringUtils.isNotBlank(command.getPostalCode())) { + saveCommand.setPostalCode(command.getPostalCode()); + } + if (StringUtils.isNotBlank(command.getLocalityName())) { + saveCommand.setLocalityName(command.getLocalityName()); + } + if (StringUtils.isNotBlank(command.getStateOrProvinceName())) { + saveCommand.setStateOrProvinceName(command.getStateOrProvinceName()); + } + if (StringUtils.isNotBlank(command.getCn())) { + saveCommand.setCn(command.getCn()); + } + if (StringUtils.isNotBlank(command.getDisplayName())) { + saveCommand.setDisplayName(command.getDisplayName()); + } + if (StringUtils.isNotBlank(command.getTitle())) { + saveCommand.setTitle(command.getTitle()); + } + if (StringUtils.isNotBlank(command.getOrganization())) { + saveCommand.setOtherName(command.getOrganization()); + } + if (StringUtils.isNotBlank(command.getOtherName())) { + saveCommand.setOtherName(command.getOtherName()); + } + if (!command.getSpecialization().isEmpty()) { + while (!saveCommand.getSpecialization().isEmpty()) { + saveCommand.getSpecialization().remove(FIRST_INDEX); + } + saveCommand.getSpecialization().addAll(command.getSpecialization()); + } + if (!command.getDomainID().isEmpty()) { + while (!saveCommand.getDomainID().isEmpty()) { + saveCommand.getDomainID().remove(FIRST_INDEX); + } + saveCommand.getDomainID().addAll(command.getDomainID()); + } + + return saveCommand; + } + + protected ExecutionResult executeCommand(CommandType command, GemApiClient apiClient) + throws ApiException { + + BaseDirectoryEntry baseDirectoryEntry = Transformer + .getBaseDirectoryEntryFromCommandType(command); + if (baseDirectoryEntry.getDn() == null) { + baseDirectoryEntry.setDn(new DistinguishedName()); + } + if (StringUtils.isBlank(baseDirectoryEntry.getDn().getUid())) { + baseDirectoryEntry.getDn().setUid(getUid(command, apiClient)); + } + ApiResponse response = new GemDirectoryEntryAdministrationApi(apiClient) + .modifyDirectoryEntryWithHttpInfo(baseDirectoryEntry.getDn().getUid(), baseDirectoryEntry); + if (response.getStatusCode() == HttpStatus.SC_OK) { + return (new ExecutionResult( + "Modify directory entry execution successful operated\n" + response.getData(), true)); + } else { + throw new CommandException( + "Modify directory entry execution failed. Response status was: " + + response.getStatusCode() + "\n" + + Transformer.getBaseDirectoryEntryFromCommandType(command)); + } + } + + private ExecutionResult doAdd(CommandType command, GemApiClient apiClient) throws ApiException { + LOG.debug("Entry not present at VZD. Will proceed with add directory entry command"); + return ExecutionCollection.getInstance().getAddDirEntryExecution().executeCommand(command, apiClient); + } +} \ No newline at end of file diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/dto/BaseExecutionResult.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/dto/BaseExecutionResult.java new file mode 100644 index 0000000..b7d356e --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/dto/BaseExecutionResult.java @@ -0,0 +1,20 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions.dto; + +public class BaseExecutionResult { + + private final String name; + private final Boolean result; + + public BaseExecutionResult(String name, Boolean result) { + this.name = name; + this.result = result; + } + + public String getName() { + return name; + } + + public Boolean getResult() { + return result; + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/dto/ExecutionResult.java b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/dto/ExecutionResult.java new file mode 100644 index 0000000..bcf8885 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/dto/ExecutionResult.java @@ -0,0 +1,21 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions.dto; + +public class ExecutionResult { + + private final String message; + private final Boolean result; + + public ExecutionResult(String message, Boolean result) { + this.message = message; + this.result = result; + } + + public String getMessage() { + return message; + } + + public Boolean getResult() { + return result; + } + +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/CommandException.java b/src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/CommandException.java similarity index 95% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/CommandException.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/CommandException.java index 17cb5ff..7f8cc6b 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/CommandException.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/CommandException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.exceptions; +package de.gematik.ti.epa.vzd.gem.exceptions; /** * Exception that should be thrown if something went wrong during executing commands diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/ConnectionAlreadyReleased.java b/src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/ConnectionAlreadyReleased.java new file mode 100644 index 0000000..97fea9b --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/ConnectionAlreadyReleased.java @@ -0,0 +1,8 @@ +package de.gematik.ti.epa.vzd.gem.exceptions; + +public class ConnectionAlreadyReleased extends RuntimeException { + + public ConnectionAlreadyReleased() { + super("The Connection is already released!"); + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/GemClientException.java b/src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/GemClientException.java similarity index 95% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/GemClientException.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/GemClientException.java index 2a78b67..f663647 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/GemClientException.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/GemClientException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.exceptions; +package de.gematik.ti.epa.vzd.gem.exceptions; public class GemClientException extends RuntimeException { diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/ReadException.java b/src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/ReadException.java similarity index 95% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/ReadException.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/ReadException.java index ea130e4..6bf858a 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/exceptions/ReadException.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/ReadException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.exceptions; +package de.gematik.ti.epa.vzd.gem.exceptions; /** * Exception that should be thrown when something while reading files went wrong diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/WrongConnection.java b/src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/WrongConnection.java new file mode 100644 index 0000000..73fd0f1 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/exceptions/WrongConnection.java @@ -0,0 +1,8 @@ +package de.gematik.ti.epa.vzd.gem.exceptions; + +public class WrongConnection extends RuntimeException { + + public WrongConnection() { + super("Connection is not associated with this ConnectionPool"); + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/AccessHandler.java b/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/AccessHandler.java new file mode 100644 index 0000000..359b760 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/AccessHandler.java @@ -0,0 +1,167 @@ +package de.gematik.ti.epa.vzd.gem.invoker; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import de.gematik.ti.epa.vzd.client.invoker.auth.HttpBasicAuth; +import de.gematik.ti.epa.vzd.client.invoker.auth.OAuth; +import de.gematik.ti.epa.vzd.gem.exceptions.GemClientException; +import de.gematik.ti.epa.vzd.oauth2.URLConnectionClient; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.time.LocalDateTime; +import org.apache.oltu.oauth2.client.OAuthClient; +import org.apache.oltu.oauth2.client.request.OAuthClientRequest; +import org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse; +import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse; +import org.apache.oltu.oauth2.common.exception.OAuthProblemException; +import org.apache.oltu.oauth2.common.exception.OAuthSystemException; +import org.apache.oltu.oauth2.common.message.types.GrantType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AccessHandler { + + private static final Logger LOG = LoggerFactory.getLogger(AccessHandler.class); + private static AccessHandler tokenCreator; + private OAuth oAuth2Token; + private LocalDateTime tokenvalidationDate; + private HttpBasicAuth baseAuth; + + private AccessHandler() { + baseAuth = getHttpBasicAuthFromFile(); + } + + public static AccessHandler getInstance() { + if (tokenCreator == null) { + tokenCreator = new AccessHandler(); + } + return tokenCreator; + } + + public OAuth getOAuth2Token() throws OAuthSystemException, OAuthProblemException { + if (oAuth2Token == null) { + oAuth2Token = getNewOAuth2Token(); + } + validateToken(); + return oAuth2Token; + } + + /** + * Requests an OAuth2 Token with Username and Password + * + * @return + * @throws OAuthSystemException + * @throws OAuthProblemException + */ + public OAuth getNewOAuth2Token() throws OAuthProblemException, OAuthSystemException { + LOG.debug("Trying to get new access token"); + + OAuthClientRequest request = OAuthClientRequest + .tokenLocation(ConfigHandler.getInstance().getRetryingOAuthPath()) + .setClientId(baseAuth.getUsername()) + .setClientSecret(baseAuth.getPassword()) + .setGrantType(GrantType.CLIENT_CREDENTIALS) + .buildBodyMessage(); + + request.setHeader("Accept", "application/json"); + + OAuthClient oAuthClient; + + ConfigHandler configHandler = ConfigHandler.getInstance(); + if (configHandler.isProxySet()) { + Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(configHandler.getProxyHost(), configHandler.getProxyPort())); + oAuthClient = new OAuthClient(new URLConnectionClient(proxy)); + } else { + oAuthClient = new OAuthClient(new URLConnectionClient()); + } + + OAuthAccessTokenResponse oAuthResponse = oAuthClient + .accessToken(request, OAuthJSONAccessTokenResponse.class); + + JsonObject jObj = new JsonParser().parse(oAuthResponse.getBody()).getAsJsonObject(); + OAuth oAuth = new OAuth(); + oAuth.setAccessToken(jObj.get("access_token").toString().replaceAll("\"", "")); + setTokenValidation(jObj.get("expires_in").toString()); + LOG.debug("Requesting new OAuth2 token successful"); + return oAuth; + } + + /** + * Sets the time - 10% until the token expires. This ensures that the token is every time valid + * + * @param expires_in is an String with numbers. For example 3600 equals 1 hour. + */ + private void setTokenValidation(String expires_in) { + int seconds = Integer.parseInt(expires_in); + int secureSeconds = (int) (seconds * 0.90); + tokenvalidationDate = LocalDateTime.now().plusSeconds(secureSeconds); + } + + /** + * Checks if the token is still valid. If not request a new one + * + * @return + */ + private boolean validateToken() { + if (LocalDateTime.now().isBefore(tokenvalidationDate)) { + return true; + } + try { + getNewOAuth2Token(); + } catch (OAuthProblemException | OAuthSystemException e) { + throw new GemClientException("Requesting a new OAuth2 token failed.", e); + } + return LocalDateTime.now().isBefore(tokenvalidationDate); + } + + public HttpBasicAuth getBaseAuth() { + if (baseAuth == null) { + baseAuth = getHttpBasicAuthFromFile(); + } + return baseAuth; + } + + /** + * Reads the credentialFile and stores the client_id and client_secret + * + * @return + */ + private HttpBasicAuth getHttpBasicAuthFromFile() { + String client_id = ""; + String client_secret = ""; + File file = new File(ConfigHandler.getInstance().getCredentialPath()); + + try (BufferedReader br = new BufferedReader(new FileReader(file))) { + String line = br.readLine(); + while (line != null) { + String[] param = line.split("="); + switch (param[0]) { + case "id": + client_id = param[1]; + break; + case "secret": + client_secret = param[1]; + break; + default: + break; + } + line = br.readLine(); + } + HttpBasicAuth basicAuth = new HttpBasicAuth(); + basicAuth.setPassword(client_secret); + basicAuth.setUsername(client_id); + return basicAuth; + } catch (IOException e) { + LOG.error( + "The named file on path " + file.getAbsolutePath() + " could not be accessed"); + throw new IllegalArgumentException( + "The named file on path " + file.getAbsolutePath() + " could not be accessed"); + } + } + + +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandler.java b/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/ConfigHandler.java similarity index 51% rename from src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandler.java rename to src/main/java/de/gematik/ti/epa/vzd/gem/invoker/ConfigHandler.java index a057e83..8047966 100644 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandler.java +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/ConfigHandler.java @@ -14,13 +14,17 @@ * limitations under the License. */ -package de.gematik.ti.epa.vzd.gemClient.invoker; +package de.gematik.ti.epa.vzd.gem.invoker; -import de.gematik.ti.epa.vzd.gemClient.exceptions.GemClientException; +import de.gematik.ti.epa.vzd.gem.exceptions.GemClientException; +import generated.CommandType; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,6 +40,12 @@ public final class ConfigHandler { private static final String BASE_PATH = "base"; private static final String RETRY_OAUTH = "retryingOAuth"; private static final String COMMANDS = "commands"; + private static final String PARA_EXECUTIONS = "maxExecutionsPerOperation"; + private static final String PARA_EXECUTORS = "maxOperations"; + private static final String PROXY_HOST = "proxyHost"; + private static final String PROXY_PORT = "proxyPort"; + private static final int MAX_EXECUTORS = 9; + private static final int MAX_EXECUTIONS_PER_EXECUTOR = 20; private static ConfigHandler configHandler = null; private String configPath; @@ -43,6 +53,12 @@ public final class ConfigHandler { private String credentialPath; private String commandsPath; private String retryingOAuthPath; + private String proxyHost; + private int maxParallelExecutor = 1; + private int maxParaExecutionsPerExecutor = 1; + private int connectionCount; + private int proxyPort = -1; + private boolean proxySet; private ConfigHandler() { } @@ -70,14 +86,25 @@ public static ConfigHandler init(String[] args) { for (int iIndex = 0; iIndex < args.length; iIndex++) { switch (args[iIndex]) { case "-p": - configHandler.configPath = new File((args[iIndex + 1])).getAbsolutePath(); + configHandler.configPath = new File(args[iIndex + 1]).getAbsolutePath(); configHandler.setParams(configHandler.configPath); break; case "-c": - configHandler.credentialPath = new File((args[iIndex + 1])).getAbsolutePath(); + configHandler.credentialPath = new File(args[iIndex + 1]).getAbsolutePath(); break; case "-b": - configHandler.commandsPath = new File((args[iIndex + 1])).getAbsolutePath(); + configHandler.commandsPath = new File(args[iIndex + 1]).getAbsolutePath(); + break; + case "-h": + configHandler.proxyHost = args[iIndex + 1]; + break; + case "-d": + try { + configHandler.proxyPort = Integer.parseInt(args[iIndex + 1]); + } catch (NumberFormatException ex) { + LOG.error("Your named proxy port is not valid." + args[iIndex + 1]); + throw new IllegalArgumentException("Your named proxy port is not valid." + args[iIndex + 1]); + } break; default: break; @@ -96,10 +123,9 @@ public static ConfigHandler init(String[] args) { return configHandler; } - private void setParams(String arg) { File file = new File(arg); - try (BufferedReader br = new BufferedReader(new FileReader(file))){ + try (BufferedReader br = new BufferedReader(new FileReader(file))) { String line = br.readLine(); while (line != null) { if (StringUtils.isNotBlank(line) && line.contains("=")) { @@ -118,16 +144,61 @@ private void setParams(String arg) { configHandler.commandsPath = new File(value).getAbsolutePath(); } break; + case PARA_EXECUTORS: + try { + maxParallelExecutor = Integer.parseInt(value.trim()); + if (maxParallelExecutor > MAX_EXECUTORS) { + maxParallelExecutor = MAX_EXECUTORS; + } else if (maxParallelExecutor <= 0) { + maxParallelExecutor = 1; + } + } catch (NumberFormatException ex) { + LOG.error("maxExecutors have to be a number"); + } + break; + case PARA_EXECUTIONS: + try { + maxParaExecutionsPerExecutor = Integer.parseInt(value.trim()); + if (maxParaExecutionsPerExecutor > MAX_EXECUTIONS_PER_EXECUTOR) { + maxParaExecutionsPerExecutor = MAX_EXECUTIONS_PER_EXECUTOR; + } else if (maxParaExecutionsPerExecutor <= 0) { + maxParaExecutionsPerExecutor = 1; + } + } catch (NumberFormatException ex) { + LOG.error("maxExecutionsPerExecutor have to be a number"); + } + break; + case PROXY_HOST: + if (StringUtils.isBlank(configHandler.proxyHost)) { + configHandler.proxyHost = value; + } + break; + case PROXY_PORT: + if (configHandler.proxyPort == -1) { + try { + configHandler.proxyPort = Integer.parseInt(value.trim()); + } catch (NumberFormatException ex) { + LOG.error("Your named proxy port is not valid." + value); + throw new IllegalArgumentException("Your named proxy port is not valid." + value); + } + } + break; default: break; } } line = br.readLine(); } + connectionCount = maxParaExecutionsPerExecutor * maxParallelExecutor; } catch (IOException e) { LOG.error("File not found at " + file.getAbsolutePath()); throw new IllegalArgumentException("No access to given file " + file.getAbsolutePath()); } + + if (StringUtils.isNotBlank(proxyHost) && (proxyPort != -1)) { + proxySet = true; + } + if (StringUtils.isBlank(configHandler.retryingOAuthPath)) { LOG.error("No authorization server named"); throw new GemClientException("No authorization server named"); @@ -138,7 +209,21 @@ private void setParams(String arg) { } } + public void adjustConnectionCount(List commands) { + Map> commandMap = commands.stream().collect(Collectors.groupingBy(CommandType::getName)); + int count = 0; + for (var entry : commandMap.entrySet()) { + if (entry.getValue().size() >= MAX_EXECUTIONS_PER_EXECUTOR) { + count += MAX_EXECUTIONS_PER_EXECUTOR; + } else { + count += entry.getValue().size(); + } + } + this.connectionCount = count < this.connectionCount ? count : this.connectionCount; + } + // + public static void setConfigHandler(ConfigHandler setConfigHandler) { configHandler = setConfigHandler; } @@ -162,5 +247,30 @@ public String getBasePath() { public String getCommandsPath() { return commandsPath; } + + public String getProxyHost() { + return proxyHost; + } + + public int getProxyPort() { + return proxyPort; + } + + public boolean isProxySet() { + return proxySet; + } + + public int getMaxParallelExecutor() { + return maxParallelExecutor; + } + + public int getMaxParaExecutionsPerExecutor() { + return maxParaExecutionsPerExecutor; + } + + public int getConnectionCount() { + return connectionCount; + } + // } diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/ConnectionPool.java b/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/ConnectionPool.java new file mode 100644 index 0000000..1ac9e33 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/ConnectionPool.java @@ -0,0 +1,74 @@ +package de.gematik.ti.epa.vzd.gem.invoker; + +import de.gematik.ti.epa.vzd.gem.exceptions.ConnectionAlreadyReleased; +import de.gematik.ti.epa.vzd.gem.exceptions.WrongConnection; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConnectionPool implements IConnectionPool { + + private static final Logger LOG = LoggerFactory.getLogger(ConnectionPool.class); + + private final LinkedBlockingQueue connections; + private final List usedConnections; + + private ConnectionPool(int size) { + connections = new LinkedBlockingQueue<>(size); + usedConnections = new ArrayList<>(); + for (int i = 0; i < size; i++) { + connections.add(new GemApiClient(this)); + } + } + + public static IConnectionPool createConnectionPool(final int size) { + return new ConnectionPool(size); + } + + @Override + public synchronized GemApiClient getConnection() throws InterruptedException { + LOG.trace("Try to get Connection (" + getUsedConnectionSize() + " / " + getConnectionSize() + ")"); + GemApiClient connection = connections.take(); + usedConnections.add(connection); + LOG.trace("Connection found and return (" + getUsedConnectionSize() + " / " + getConnectionSize() + ")"); + return connection; + } + + @Override + public synchronized void releaseConnection(GemApiClient gemApiClient) { + LOG.trace("Try to releaseConnection (" + getUsedConnectionSize() + " / " + getConnectionSize() + ")"); + if (usedConnections.contains(gemApiClient)) { + usedConnections.remove(gemApiClient); + connections.add(gemApiClient); + LOG.trace("Connection released (" + getUsedConnectionSize() + " / " + getConnectionSize() + ")"); + } else if (!connections.contains(gemApiClient)) { + throw new WrongConnection(); + } else { + throw new ConnectionAlreadyReleased(); + } + } + + @Override + public void reset() { + connections.addAll(usedConnections); + usedConnections.clear(); + LOG.trace("Connection reset done (" + getUsedConnectionSize() + " / " + getConnectionSize() + ")"); + } + + @Override + public int getConnectionSize() { + return connections.size() + connections.remainingCapacity(); + } + + @Override + public int getAvailableConnectionSize() { + return connections.size(); + } + + @Override + public int getUsedConnectionSize() { + return usedConnections.size(); + } +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/GemApiClient.java b/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/GemApiClient.java new file mode 100644 index 0000000..5e16168 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/GemApiClient.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gem.invoker; + +import de.gematik.ti.epa.vzd.client.invoker.ApiClient; +import de.gematik.ti.epa.vzd.client.invoker.JSON; +import de.gematik.ti.epa.vzd.client.invoker.auth.Authentication; +import de.gematik.ti.epa.vzd.client.invoker.auth.OAuthFlow; +import de.gematik.ti.epa.vzd.client.invoker.auth.RetryingOAuth; +import de.gematik.ti.epa.vzd.gem.exceptions.GemClientException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import okhttp3.OkHttpClient; +import org.apache.oltu.oauth2.common.exception.OAuthProblemException; +import org.apache.oltu.oauth2.common.exception.OAuthSystemException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GemApiClient extends ApiClient implements AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(GemApiClient.class); + + private String retryingOAuthPath = ConfigHandler.getInstance().getRetryingOAuthPath(); + private Map authentications; + private IConnectionPool connectionPool; + + + public GemApiClient(IConnectionPool connectionPool) { + + this.connectionPool = connectionPool; + modifiedWithOAuth(); + authentications = Collections.unmodifiableMap(authentications); + } + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID + */ + public GemApiClient(final String clientId) { + this(clientId, null, null); + } + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID and additional parameters + */ + public GemApiClient(final String clientId, final Map parameters) { + this(clientId, null, parameters); + } + + /* + * Constructor for ApiClient to support access token retry on 401/403 configured with client ID, secret, and additional parameters + */ + public GemApiClient(final String clientId, final String clientSecret, + final Map parameters) { + modifiedWithOAuth(); + + final RetryingOAuth retryingOAuth = new RetryingOAuth(retryingOAuthPath, clientId, + OAuthFlow.application, clientSecret, parameters); + authentications.put("OAuth2", retryingOAuth); + getHttpClient().interceptors().add(retryingOAuth); + + // Prevent the authentications from being modified. + authentications = Collections.unmodifiableMap(authentications); + } + + /** + * The function getProgressInterceptor comes from the generated class ApiClient and maybe have to be set on public again. + *

    + * The OAuth2 token is stored in authentications.get("OAuth") + */ + private void modifiedWithOAuth() { + ConfigHandler configHandler = ConfigHandler.getInstance(); + setBasePath(configHandler.getBasePath()); + + OkHttpClient.Builder builder; + if (configHandler.isProxySet()) { + Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(configHandler.getProxyHost(), configHandler.getProxyPort())); + builder = new OkHttpClient.Builder().proxy(proxy); + } else { + builder = new OkHttpClient.Builder(); + } + + // Function have to be set public when client is regenerated + builder.addNetworkInterceptor(getProgressInterceptor()); + setHttpClient(builder.build()); + + setVerifyingSsl(true); + + setJSON(new JSON()); + + // Set default User-Agent. + setUserAgent("OpenAPI-Generator/1.0.0/java"); + + authentications = new HashMap<>(); + authentications + .put("HttpBasicAuth", AccessHandler.getInstance().getBaseAuth()); + try { + authentications.put("OAuth", AccessHandler.getInstance().getOAuth2Token()); + } catch (OAuthSystemException | OAuthProblemException e) { + LOG.error("Error while getting Token"); + throw new ExceptionInInitializerError("Error while getting Token"); + } + } + + public void validateToken() { + try { + AccessHandler.getInstance().getOAuth2Token(); + } catch (OAuthSystemException | OAuthProblemException e) { + LOG.error("Error while refreshing OAuthToken"); + throw new GemClientException("Error while refreshing OAuthToken"); + } + } + + // + + /** + * Get authentications (key: authentication name, value: authentication). + * + * @return Map of authentication objects + */ + @Override + public Map getAuthentications() { + return authentications; + } + + /** + * Get authentication for the given name. + * + * @param authName The authentication name + * @return The authentication, null if not found + */ + @Override + public Authentication getAuthentication(final String authName) { + return authentications.get(authName); + } + + @Override + public void close() throws Exception { + if (connectionPool != null) { + connectionPool.releaseConnection(this); + } + } + + // +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/IConnectionPool.java b/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/IConnectionPool.java new file mode 100644 index 0000000..3791fd8 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/gem/invoker/IConnectionPool.java @@ -0,0 +1,17 @@ +package de.gematik.ti.epa.vzd.gem.invoker; + +public interface IConnectionPool { + + GemApiClient getConnection() throws InterruptedException; + + void releaseConnection(GemApiClient gemApiClient); + + void reset(); + + int getConnectionSize(); + + int getAvailableConnectionSize(); + + int getUsedConnectionSize(); + +} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/GemStringUtils.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/GemStringUtils.java deleted file mode 100644 index d05f943..0000000 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/GemStringUtils.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.gematik.ti.epa.vzd.gemClient; - -import java.util.Arrays; -import java.util.List; -import java.util.Random; - -public class GemStringUtils { - - private static final String gothic = "____ ______________________ _________ .__ .__ __ \n\\ \\ / /\\____ /\\______ \\ \\_ ___ \\| | |__| ____ _____/ |_ \n \\ Y / / / | | \\ ______ / \\ \\/| | | |/ __ \\ / \\ __\\ \n \\ / / /_ | ` \\ /_____/ \\ \\___| |_| \\ ___/| | \\ |\n \\___/ /_______ \\/_______ / \\______ /____/__|\\___ >___| /__|\n \\/ \\/ \\/ \\/ \\/ \n"; - private static final String goofy = " __ __ __ ___________ __ _____ __ __ ___ __ __\n| | | | (___ ) | \\ / __) \\ | (_ _) \\ ___) | \\ | | (__ __) \n| | | | / / | | ___ | / | | | | | (__ | | \\ | | | \n| | | | / / | | (___) | | | | | | | __) | | \\ \\| | | | \n \\ \\/ / / /__ | | | \\__ | |__ _| |_ | (___ | | \\ | | | \n__\\ /___( )_| /_________\\ )_/ )_( )_/ )_ | |___\\ |____| |____\n"; - private static final String fuzzy = ".-..-..----..---. .--. .-. _ .-. \n: :: :`--. :: . : : .--': : :_; .' `.\n: :: : ,',': :: : _____ : : : : .-. .--. ,-.,-.`. .'\n: `' ;.'.'_ : :; ::_____:: :__ : :_ : :' '_.': ,. : : : \n `.,' :____;:___.' `.__.'`.__;:_;`.__.':_;:_; :_;\n"; - private static final String fourtops = "| |~~/|~~\\ /~~|' | \n \\ / / | |---| ||/~/|/~\\~|~\n \\/ /__|__/ \\__||\\/_| || \n"; - private static final String fender = "\\\\ // |'''''/ '||'''|. .|'''', '||` || \n \\\\ // // || || || || '' || \n \\\\ // // || || --- || || || .|''|, `||''|, ''||'' \n \\\\// // || || || || || ||..|| || || || \n \\/ /.....| .||...|' `|....' .||. .||. `|... .|| ||. `|..' \n"; - private static final String univers = "8b d8 888888888888 88888888ba, ,ad8888ba, 88 88 \n`8b d8' ,88 88 `\"8b d8\"' `\"8b 88 \"\" ,d \n `8b d8' ,88\" 88 `8b d8' 88 88 \n `8b d8' ,88\" 88 88 88 88 88 ,adPPYba, 8b,dPPYba, MM88MMM \n `8b d8' ,88\" 88 88 aaaaaaaa 88 88 88 a8P_____88 88P' `\"8a 88 \n `8b d8' ,88\" 88 8P \"\"\"\"\"\"\"\" Y8, 88 88 8PP\"\"\"\"\"\"\" 88 88 88 \n `888' 88\" 88 .a8P Y8a. .a8P 88 88 \"8b, ,aa 88 88 88, \n `8' 888888888888 88888888Y\"' `\"Y8888Y\"' 88 88 `\\\"Ybbd8\\\"'88 88 \\\"Y888 \"\n"; - private static final List pics = Arrays - .asList(gothic, goofy, fuzzy, fourtops, fender, univers); - - public static String listToString(List list) { - StringBuilder sb = new StringBuilder(); - for (String s : list) { - sb.append(s + ","); - } - if (sb.length() > 0) { - sb.setLength(sb.length() - 1); - } - return sb.toString(); - } - - public static String getPic() { - StringBuffer asciPic = new StringBuffer(); - asciPic.append("\n=====================================================================\n"); - asciPic.append("\t\t\t=========================================================\n"); - asciPic.append("=====================================================================\n"); - asciPic.append(pics.get(new Random().nextInt(GemStringUtils.pics.size()))); - asciPic.append("=====================================================================\n"); - asciPic.append("\t\t\t=========================================================\n"); - asciPic.append("=====================================================================\n"); - return asciPic.toString(); - } -} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecution.java deleted file mode 100644 index a310d60..0000000 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecution.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; - -import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; -import de.gematik.ti.epa.vzd.client.invoker.ApiException; -import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; -import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; -import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; -import de.gematik.ti.epa.vzd.gemClient.api.GemDirectoryEntryAdministrationApi; -import de.gematik.ti.epa.vzd.gemClient.command.Transformer; -import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; -import generated.CommandType; -import generated.UserCertificateType; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Specific execution for Command "AddDirectoryEntry" - */ -public class AddDirEntryExecution extends ExecutionBase { - - private final DirectoryEntryAdministrationApi directoryEntryAdministrationApi; - private Logger LOG = LoggerFactory.getLogger(AddDirEntryExecution.class); - - public AddDirEntryExecution(GemApiClient api) { - super(api, CommandNamesEnum.ADD_DIR_ENTRY); - directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); - } - - @Override - public boolean checkValidation(CommandType command) { - boolean check = true; - if (command.getUserCertificate().isEmpty()) { - check = false; - } else { - for (UserCertificateType userCertificateType : command.getUserCertificate()) { - String telematikId = userCertificateType.getTelematikID(); - String userCertificate = userCertificateType.getUserCertificate(); - if ((StringUtils.isBlank(telematikId) && StringUtils - .isBlank(userCertificate))) { - check = false; - } - } - } - if (StringUtils.isBlank(command.getCn())) { - check = false; - } - if (check == false) { - LOG.error("Missing argument -> cn and telematikId or userCertificate have to be set " - + command.getName() + "\n" - + Transformer.getBaseDirectoryEntryFromCommandType(command)); - } - return check; - } - - @Override - public boolean executeCommands() { - boolean runSuccessful = true; - for (CommandType command : commands) { - LOG.debug("--- Command " + command.getCommandId() + " ---"); - if (!isEntryPresent(command)) { - try { - executeCommand(command); - } catch (Exception ex) { - LOG.error("An error have occured: " + ex.getMessage() + "\n" - + Transformer.getCreateDirectoryEntry(command)); - runSuccessful = false; - } - } else { - runSuccessful = doModify(command); - } - LOG.debug("--- Command " + command.getCommandId() + " end ---"); - } - return runSuccessful; - } - - /** - * Function that execute one command and logs the result - * - * @param command - * @return - * @throws ApiException - */ - protected ApiResponse executeCommand(CommandType command) throws - ApiException { - apiClient.validateToken(); - - CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); - ApiResponse response = directoryEntryAdministrationApi - .addDirectoryEntryWithHttpInfo(createDirectoryEntry); - if (response.getStatusCode() == HttpStatus.SC_CREATED) { - LOG.debug("Add directory entry execution successful operated\n" + response.getData()); - } else { - throw new CommandException( - "Add directory entry execution failed. Response-Status was: " + response - .getStatusCode() + "\n" + Transformer.getCreateDirectoryEntry(command)); - } - return response; - } - - private boolean doModify(CommandType command) { - LOG.debug( - "Entry is already present in VZD. Will Proceed with modify directory entry command"); - try { - ExecutionCollection.getInstance().getModifyDirEntry().executeCommand(command); - return true; - } catch (Exception ex) { - LOG.error(ex.getMessage()); - return false; - } - } - - @Override - public boolean postCheck() { - try { - super.postCheck(); - return true; - } catch (Exception ex) { - LOG.error(ex.getMessage()); - } - - return false; - } -} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecution.java deleted file mode 100644 index 9b5060f..0000000 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecution.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; - -import de.gematik.ti.epa.vzd.client.api.CertificateAdministrationApi; -import de.gematik.ti.epa.vzd.client.invoker.ApiException; -import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; -import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; -import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; -import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import de.gematik.ti.epa.vzd.client.model.UserCertificate; -import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; -import de.gematik.ti.epa.vzd.gemClient.api.GemCertificateAdministrationApi; -import de.gematik.ti.epa.vzd.gemClient.command.Transformer; -import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; -import generated.CommandType; -import generated.UserCertificateType; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Specific execution for Command "DeleteDirectoryEntryCertificate" - */ -public class DeleteDirEntryCertExecution extends ExecutionBase { - - private CertificateAdministrationApi certificateAdministrationApi; - private Logger LOG = LoggerFactory.getLogger(DeleteDirEntryCertExecution.class); - - - public DeleteDirEntryCertExecution(GemApiClient api) { - super(api, CommandNamesEnum.DEL_DIR_CERT); - certificateAdministrationApi = new GemCertificateAdministrationApi(apiClient); - } - - @Override - public boolean checkValidation(CommandType command) { - String uid = null; - String cn = null; - boolean check = true; - String totalUid = null; - for (UserCertificateType userCertificateType : command.getUserCertificate()) { - if (userCertificateType.getDn() != null) { - if (totalUid == null) { - totalUid = userCertificateType.getDn().getUid(); - } - uid = userCertificateType.getDn().getUid(); - cn = userCertificateType.getDn().getCn(); - } - if (StringUtils.isBlank(uid)) { - uid = command.getDn().getUid(); - } - if (StringUtils.isBlank(uid) || StringUtils.isBlank(cn) || !totalUid.equals(uid)) { - check = false; - } - } - return check; - } - - @Override - public boolean executeCommands() { - boolean runSuccessful = true; - if (commands.isEmpty()) { - return true; - } - for (CommandType command : commands) { - LOG.debug("--- Command " + command.getCommandId() + " ---"); - if (searchByUserCertificate(command)) { - try { - executeCommand(command); - } catch (Exception ex) { - LOG.error("An error have occured: " + ex.getMessage()); - runSuccessful = false; - } - } - LOG.debug("--- Command " + command.getCommandId() + " end ---"); - } - return runSuccessful; - } - - private void executeCommand(CommandType command) { - apiClient.validateToken(); - - boolean runSucsessfull = true; - CreateDirectoryEntry createDirectoryEntry = Transformer.getCreateDirectoryEntry(command); - - ApiResponse response; - for (UserCertificate userCertificate : createDirectoryEntry.getUserCertificates()) { - try { - String uid = getUid(createDirectoryEntry.getDirectoryEntryBase(), userCertificate); - String cn = userCertificate.getDn().getCn(); - response = deleteSingleCertificate(uid, cn); - if (response.getStatusCode() == HttpStatus.SC_OK) { - LOG.debug( - "Certificate successful deleted: \n" + userCertificate); - } - } catch (ApiException e) { - runSucsessfull = false; - LOG.error( - "Something went wrong will deleting certificate. Responsecode: " + e.getCode() - + " certificate: " + userCertificate - .getUserCertificate()); - } - } - if (!runSucsessfull) { - throw new CommandException( - "At least one certificate could not be added in:" + "\n" + Transformer - .getCreateDirectoryEntry(command)); - } - } - - private String getUid(BaseDirectoryEntry directoryEntryBase, UserCertificate userCertificate) { - String uidCert = userCertificate.getDn().getUid(); - String uidEntry = null; - - if (directoryEntryBase != null) { - DistinguishedName dn = directoryEntryBase.getDn(); - if (dn != null) { - uidEntry = dn.getUid(); - } - } - return uidCert == null ? uidEntry : uidCert; - } - - private ApiResponse deleteSingleCertificate(String uid, String certificateEntryId) - throws ApiException { - return certificateAdministrationApi - .deleteDirectoryEntryCertificateWithHttpInfo(uid, certificateEntryId); - } - - @Override - public boolean postCheck() { - try { - super.postCheck(); - return true; - } catch (Exception ex) { - LOG.error(ex.getMessage()); - } - - return false; - } -} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBase.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBase.java deleted file mode 100644 index 0a31197..0000000 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBase.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; - -import de.gematik.ti.epa.vzd.client.invoker.ApiException; -import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; -import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; -import de.gematik.ti.epa.vzd.client.model.UserCertificate; -import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; - -import de.gematik.ti.epa.vzd.gemClient.command.Transformer; -import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; -import de.gematik.ti.epa.vzd.gemClient.exceptions.GemClientException; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; -import generated.CommandType; -import generated.DistinguishedNameType; -import generated.UserCertificateType; -import java.util.ArrayList; -import java.util.List; -import org.apache.http.HttpStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Represents the base for every specific execution - */ -public abstract class ExecutionBase { - - protected final int FIRST_INDEX = 0; - - private Logger LOG = LoggerFactory.getLogger(ExecutionBase.class); - - protected GemApiClient apiClient; - protected CommandNamesEnum execCommand; - protected List commands; - - public ExecutionBase(GemApiClient api, CommandNamesEnum cmd) { - this.apiClient = api; - this.execCommand = cmd; - this.commands = new ArrayList<>(); - } - - public boolean canHandleCommand(CommandNamesEnum cmd) { - return this.execCommand.equals(cmd); - } - - /** - * Checks the given command for validation and adds it to the queue of commands to execute - * - * @param command - * @return - */ - public boolean preCheck(CommandType command) { - try { - if (!checkValidation(command)) { - throw new CommandException( - "Command invalid. Please check " + command.getName()); - } - commands.add(command); - return true; - } catch (Exception ex) { - LOG.error(ex.getMessage()); - return false; - } - } - - /** - * Every single executor validates their commands and log the missing or wrong values - * - * @param command - * @return - */ - public abstract boolean checkValidation(CommandType command); - - /** - * Executes all commands in the queue - * - * @return - */ - public abstract boolean executeCommands(); - - /** - * Checks if the execution was successful and logs the result - * - * @return - */ - public boolean postCheck() { - try { - return true; - } catch (Exception ex) { - LOG.error(ex.getMessage()); - } - return false; - } - - /** - * This function proceed a read command, to check if a entry without cert is already present - * - * @param command - * @return - */ - protected boolean isEntryPresent(CommandType command) { - if (command.getDn() != null) { - CommandType searchCommand = new CommandType(); - DistinguishedNameType dn = new DistinguishedNameType(); - dn.setUid(command.getDn().getUid()); - searchCommand.setDn(dn); - try { - ApiResponse> response = ExecutionCollection - .getInstance().getReadDirEntryExecution().executeCommand(searchCommand); - return response.getStatusCode() == HttpStatus.SC_OK ? true : false; - } catch (ApiException ex) { - if (ex.getCode() == 0) { - throw new GemClientException( - "The server you address is probably not reachable at the moment"); - } - LOG.error(ex.getMessage()); - return false; - } - } else { - return serachByTelematikId(command); - } - } - - /** - * This function proceed a read command, to check if a entry without cert is already present - * - * @param command - * @return - */ - private boolean serachByTelematikId(CommandType command) { - UserCertificateType userCertificate = command.getUserCertificate().get(FIRST_INDEX); - if (userCertificate != null) { - try { - CommandType searchCommand = new CommandType(); - searchCommand.getUserCertificate().add(new UserCertificateType()); - searchCommand.getUserCertificate().get(FIRST_INDEX).setTelematikID(userCertificate.getTelematikID()); - ApiResponse> response = ExecutionCollection - .getInstance().getReadDirEntryCertExecution().executeCommand(searchCommand); - return response.getStatusCode() == HttpStatus.SC_OK ? true : false; - } catch (ApiException ex) { - if (ex.getCode() == 0) { - throw new GemClientException( - "The server you address is probably not reachable at the moment"); - } - return false; - } - } - throw new GemClientException("No valid parameter found for present check" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - } - - public boolean searchByUserCertificate(CommandType command) { - ApiResponse> response; - try { - response = ExecutionCollection.getInstance().getReadDirEntryCertExecution() - .executeCommand(command); - } catch (ApiException ex) { - if (ex.getCode() == 0) { - throw new GemClientException( - "The server you address is probably not reachable at the moment"); - } - LOG.error(ex.getMessage()); - return false; - } - if (response.getData().size() == command.getUserCertificate().size()) { - return true; - } - return false; - } - - public String getUidByTelematikId(String telematikId) { - ApiResponse> response; - CommandType command = new CommandType(); - command.getUserCertificate().add(new UserCertificateType()); - command.getUserCertificate().get(FIRST_INDEX).setTelematikID(telematikId); - try { - response = ExecutionCollection.getInstance().getReadDirEntryCertExecution() - .executeCommand(command); - } catch (ApiException ex) { - if (ex.getCode() == 0) { - throw new GemClientException( - "The server you address is probably not reachable at the moment"); - } - LOG.error(ex.getMessage()); - return null; - } - if (!response.getData().isEmpty()) { - return response.getData().get(FIRST_INDEX).getDn().getUid(); - } - return null; - } -} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecution.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecution.java deleted file mode 100644 index 4779372..0000000 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecution.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; - -import de.gematik.ti.epa.vzd.client.api.DirectoryEntryAdministrationApi; -import de.gematik.ti.epa.vzd.client.invoker.ApiException; -import de.gematik.ti.epa.vzd.client.invoker.ApiResponse; -import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; -import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import de.gematik.ti.epa.vzd.gemClient.CommandNamesEnum; -import de.gematik.ti.epa.vzd.gemClient.api.GemDirectoryEntryAdministrationApi; -import de.gematik.ti.epa.vzd.gemClient.command.Transformer; -import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; -import generated.CommandType; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.HttpStatus; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Specific execution for Command "ModifyDirectoryEntry" - */ -public class ModifyDirEntryExecution extends ExecutionBase { - - private Logger LOG = LoggerFactory.getLogger(ModifyDirEntryExecution.class); - private final DirectoryEntryAdministrationApi directoryEntryAdministrationApi; - - public ModifyDirEntryExecution(GemApiClient api) { - super(api, CommandNamesEnum.MOD_DIR_ENTRY); - directoryEntryAdministrationApi = new GemDirectoryEntryAdministrationApi(apiClient); - } - - @Override - public boolean checkValidation(CommandType command) { - boolean check = true; - if (command.getDn() != null) { - if (StringUtils.isBlank(command.getDn().getUid())) { - LOG.error( - "Missing argument -> uid for command " + command.getName() + "\n" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - check = false; - } - } else { - LOG.error("Missing element \"dn\" " + command.getName() + "\n" + Transformer - .getBaseDirectoryEntryFromCommandType(command)); - check = false; - } - return check; - } - - @Override - public boolean executeCommands() { - boolean runSuccessful = true; - for (CommandType command : commands) { - LOG.debug("--- Command " + command.getCommandId() + " ---"); - if (isEntryPresent(command)) { - try { - executeCommand(command); - } catch (Exception ex) { - LOG.error("An error have occured: " + ex.getMessage()); - runSuccessful = false; - } - } else { - runSuccessful = doAdd(command); - } - LOG.debug("--- Command " + command.getCommandId() + " end ---"); - } - return runSuccessful; - } - - /** - * Function that execute one command and logs the result - * - * @param command - * @return - * @throws ApiException - */ - protected ApiResponse executeCommand(CommandType command) - throws ApiException { - apiClient.validateToken(); - - BaseDirectoryEntry baseDirectoryEntry = Transformer - .getBaseDirectoryEntryFromCommandType(command); - ApiResponse response = directoryEntryAdministrationApi - .modifyDirectoryEntryWithHttpInfo(command.getDn().getUid(), baseDirectoryEntry); - if (response.getStatusCode() == HttpStatus.SC_OK) { - LOG.debug( - "Modify directory entry execution successful operated\n" + response.getData()); - } else { - throw new CommandException( - "Modify directory entry execution failed. Response status was: " - + response.getStatusCode() + "\n" - + Transformer.getBaseDirectoryEntryFromCommandType(command)); - } - return response; - } - - private boolean doAdd(CommandType command) { - LOG.debug("Entry not present at VZD. Will proceed with add directory entry command"); - try { - ExecutionCollection.getInstance().getAddDirEntryExecution().executeCommand(command); - return true; - } catch (ApiException ex) { - LOG.error("Add directory entry execution failed\n" + Transformer - .getCreateDirectoryEntry(command)); - return false; - } - - } - - @Override - public boolean postCheck() { - try { - super.postCheck(); - return true; - } catch (Exception ex) { - LOG.error(ex.getMessage()); - } - - return false; - } -} diff --git a/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/GemApiClient.java b/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/GemApiClient.java deleted file mode 100644 index cc28486..0000000 --- a/src/main/java/de/gematik/ti/epa/vzd/gemClient/invoker/GemApiClient.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.gematik.ti.epa.vzd.gemClient.invoker; - -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import de.gematik.ti.epa.vzd.client.invoker.ApiClient; -import de.gematik.ti.epa.vzd.client.invoker.JSON; -import de.gematik.ti.epa.vzd.client.invoker.auth.Authentication; -import de.gematik.ti.epa.vzd.client.invoker.auth.HttpBasicAuth; -import de.gematik.ti.epa.vzd.client.invoker.auth.OAuth; -import de.gematik.ti.epa.vzd.client.invoker.auth.OAuthFlow; -import de.gematik.ti.epa.vzd.client.invoker.auth.RetryingOAuth; -import de.gematik.ti.epa.vzd.gemClient.exceptions.GemClientException; -import java.time.LocalDateTime; -import okhttp3.OkHttpClient; -import org.apache.oltu.oauth2.client.OAuthClient; -import org.apache.oltu.oauth2.client.URLConnectionClient; -import org.apache.oltu.oauth2.client.request.OAuthClientRequest; -import org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse; -import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse; -import org.apache.oltu.oauth2.common.exception.OAuthProblemException; -import org.apache.oltu.oauth2.common.exception.OAuthSystemException; -import org.apache.oltu.oauth2.common.message.types.GrantType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public class GemApiClient extends ApiClient { - - private static final Logger LOG = LoggerFactory.getLogger(GemApiClient.class); - - private String retryingOAuthPath; - private Map authentications; - private LocalDateTime tokenvalidationDate; - - public GemApiClient() { - modifiedWithOAuth(); - authentications = Collections.unmodifiableMap(authentications); - } - - /* - * Constructor for ApiClient to support access token retry on 401/403 configured with client ID - */ - public GemApiClient(final String clientId) { - this(clientId, null, null); - } - - /* - * Constructor for ApiClient to support access token retry on 401/403 configured with client ID and additional parameters - */ - public GemApiClient(final String clientId, final Map parameters) { - this(clientId, null, parameters); - } - - /* - * Constructor for ApiClient to support access token retry on 401/403 configured with client ID, secret, and additional parameters - */ - public GemApiClient(final String clientId, final String clientSecret, - final Map parameters) { - modifiedWithOAuth(); - - final RetryingOAuth retryingOAuth = new RetryingOAuth(retryingOAuthPath, clientId, - OAuthFlow.application, clientSecret, parameters); - authentications.put("OAuth2", retryingOAuth); - getHttpClient().interceptors().add(retryingOAuth); - - // Prevent the authentications from being modified. - authentications = Collections.unmodifiableMap(authentications); - } - - /** - * The function getProgressInterceptor comes from the generated class ApiClient and maybe have to be set on public again. - *

    - * The OAuth2 token is stored in authentications.get("OAuth") - */ - private void modifiedWithOAuth() { - ConfigHandler configHandler = ConfigHandler.getInstance(); - setBasePath(configHandler.getBasePath()); - retryingOAuthPath = configHandler.getRetryingOAuthPath(); - - final OkHttpClient.Builder builder = new OkHttpClient.Builder(); - // Function have to be set public when client is regenerated - builder.addNetworkInterceptor(getProgressInterceptor()); - setHttpClient(builder.build()); - - setVerifyingSsl(true); - - setJSON(new JSON()); - - // Set default User-Agent. - setUserAgent("OpenAPI-Generator/1.0.0/java"); - - authentications = new HashMap<>(); - authentications - .put("HttpBasicAuth", getHttpBasicAuthFromFile(configHandler.getCredentialPath())); - try { - authentications.put("OAuth", getNewOAuth2Token()); - } catch (OAuthSystemException | OAuthProblemException e) { - throw new ExceptionInInitializerError("Error while getting Token"); - } - } - - /** - * Requests an OAuth2 Token with Username and Password - * - * @return - * @throws OAuthSystemException - * @throws OAuthProblemException - */ - private OAuth getNewOAuth2Token() throws OAuthProblemException, OAuthSystemException { - LOG.debug("Trying to get new access token"); - HttpBasicAuth baseAuth = (HttpBasicAuth) getAuthentications().get("HttpBasicAuth"); - - OAuthClientRequest request = OAuthClientRequest - .tokenLocation(retryingOAuthPath) - .setClientId(baseAuth.getUsername()) - .setClientSecret(baseAuth.getPassword()) - .setGrantType(GrantType.CLIENT_CREDENTIALS) - .buildBodyMessage(); - - request.setHeader("Accept", "application/json"); - - OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient()); - OAuthAccessTokenResponse oAuthResponse = oAuthClient - .accessToken(request, OAuthJSONAccessTokenResponse.class); - - JsonObject jObj = new JsonParser().parse(oAuthResponse.getBody()).getAsJsonObject(); - OAuth oAuth = new OAuth(); - oAuth.setAccessToken(jObj.get("access_token").toString().replaceAll("\"", "")); - setTokenValidation(jObj.get("expires_in").toString()); - LOG.debug("Requesting new OAuth2 token successful"); - return oAuth; - } - - /** - * Sets the time - 10% until the token expires. This ensures that the token is every time valid - * - * @param expires_in is an String with numbers. For example 3600 equals 1 hour. - */ - private void setTokenValidation(String expires_in) { - int seconds = Integer.parseInt(expires_in); - int secureSeconds = (int) (seconds * 0.90); - tokenvalidationDate = LocalDateTime.now().plusSeconds(secureSeconds); - } - - /** - * Checks if the token is still valid. If not request a new one - * - * @return - */ - public boolean validateToken() { - if (LocalDateTime.now().isBefore(tokenvalidationDate)) { - return true; - } - try { - getNewOAuth2Token(); - } catch (OAuthProblemException | OAuthSystemException e) { - throw new GemClientException("Requesting a new OAuth2 token failed.", e); - } - return LocalDateTime.now().isBefore(tokenvalidationDate); - } - - /** - * Reads the credentialFile and stores the client_id and client_secret - * - * @param arg - * @return - */ - private HttpBasicAuth getHttpBasicAuthFromFile(String arg) { - String client_id = ""; - String client_secret = ""; - File file = new File(arg); - - try (BufferedReader br = new BufferedReader(new FileReader(file))){ - String line = br.readLine(); - while (line != null) { - String[] param = line.split("="); - switch (param[0]) { - case "id": - client_id = param[1]; - break; - case "secret": - client_secret = param[1]; - break; - default: - break; - } - line = br.readLine(); - } - HttpBasicAuth basicAuth = new HttpBasicAuth(); - basicAuth.setPassword(client_secret); - basicAuth.setUsername(client_id); - return basicAuth; - } catch (IOException e) { - LOG.error( - "The named file on path " + file.getAbsolutePath() + " could not be accessed"); - throw new IllegalArgumentException( - "The named file on path " + file.getAbsolutePath() + " could not be accessed"); - } - } - - // - - /** - * Get authentications (key: authentication name, value: authentication). - * - * @return Map of authentication objects - */ - @Override - public Map getAuthentications() { - return authentications; - } - - /** - * Get authentication for the given name. - * - * @param authName The authentication name - * @return The authentication, null if not found - */ - @Override - public Authentication getAuthentication(final String authName) { - return authentications.get(authName); - } - - // -} diff --git a/src/main/java/de/gematik/ti/epa/vzd/oauth2/URLConnectionClient.java b/src/main/java/de/gematik/ti/epa/vzd/oauth2/URLConnectionClient.java new file mode 100644 index 0000000..8541657 --- /dev/null +++ b/src/main/java/de/gematik/ti/epa/vzd/oauth2/URLConnectionClient.java @@ -0,0 +1,136 @@ +/** + * Copyright 2010 Newcastle University + *

    + * http://research.ncl.ac.uk/smart/ + *

    + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work + * for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy of the License at + *

    + * http://www.apache.org/licenses/LICENSE-2.0 + *

    + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations + * under the License. + */ + +package de.gematik.ti.epa.vzd.oauth2; + +import static org.apache.http.HttpStatus.SC_BAD_REQUEST; +import static org.apache.http.HttpStatus.SC_UNAUTHORIZED; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.oltu.oauth2.client.HttpClient; +import org.apache.oltu.oauth2.client.request.OAuthClientRequest; +import org.apache.oltu.oauth2.client.response.OAuthClientResponse; +import org.apache.oltu.oauth2.client.response.OAuthClientResponseFactory; +import org.apache.oltu.oauth2.common.OAuth; +import org.apache.oltu.oauth2.common.exception.OAuthProblemException; +import org.apache.oltu.oauth2.common.exception.OAuthSystemException; +import org.apache.oltu.oauth2.common.utils.OAuthUtils; + + +/** + * Implementation of the Oltu OAuth HttpClient using URL Connection + */ +public class URLConnectionClient implements HttpClient { + + private Proxy proxy = Proxy.NO_PROXY; + + public URLConnectionClient() { + } + + public URLConnectionClient(final Proxy proxy) { + this.proxy = proxy; + } + + @Override + public T execute(final OAuthClientRequest request, final Map headers, + final String requestMethod, final Class responseClass) + throws OAuthSystemException, OAuthProblemException { + + InputStream responseBody = null; + URLConnection c; + Map> responseHeaders = new HashMap>(); + int responseCode; + try { + URL url = new URL(request.getLocationUri()); + + c = url.openConnection(proxy); + responseCode = -1; + if (c instanceof HttpURLConnection) { + HttpURLConnection httpURLConnection = (HttpURLConnection) c; + + if (headers != null && !headers.isEmpty()) { + for (Map.Entry header : headers.entrySet()) { + httpURLConnection.addRequestProperty(header.getKey(), header.getValue()); + } + } + + if (request.getHeaders() != null) { + for (Map.Entry header : request.getHeaders().entrySet()) { + httpURLConnection.addRequestProperty(header.getKey(), header.getValue()); + } + } + + if (OAuthUtils.isEmpty(requestMethod)) { + httpURLConnection.setRequestMethod(OAuth.HttpMethod.GET); + } else { + httpURLConnection.setRequestMethod(requestMethod); + setRequestBody(request, requestMethod, httpURLConnection); + } + + httpURLConnection.connect(); + + InputStream inputStream; + responseCode = httpURLConnection.getResponseCode(); + if (responseCode == SC_BAD_REQUEST || responseCode == SC_UNAUTHORIZED) { + inputStream = httpURLConnection.getErrorStream(); + } else { + inputStream = httpURLConnection.getInputStream(); + } + + responseHeaders = httpURLConnection.getHeaderFields(); + responseBody = inputStream; + } + } catch (IOException e) { + throw new OAuthSystemException(e); + } + + return OAuthClientResponseFactory + .createCustomResponse(responseBody, c.getContentType(), responseCode, responseHeaders, responseClass); + } + + private void setRequestBody(OAuthClientRequest request, String requestMethod, HttpURLConnection httpURLConnection) + throws IOException { + String requestBody = request.getBody(); + if (OAuthUtils.isEmpty(requestBody)) { + return; + } + + if (OAuth.HttpMethod.POST.equals(requestMethod) || OAuth.HttpMethod.PUT.equals(requestMethod)) { + httpURLConnection.setDoOutput(true); + OutputStream ost = httpURLConnection.getOutputStream(); + PrintWriter pw = new PrintWriter(ost); + pw.print(requestBody); + pw.flush(); + pw.close(); + } + } + + @Override + public void shutdown() { + // Nothing to do here + } + +} \ No newline at end of file diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index d94bd51..3aa1690 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -15,11 +15,11 @@ - + - + diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApiTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApiTest.java index db782e0..afa65a0 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApiTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/api/CertificateAdministrationApiTest.java @@ -15,15 +15,10 @@ import de.gematik.ti.epa.vzd.client.invoker.ApiException; import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import de.gematik.ti.epa.vzd.client.model.Error; import de.gematik.ti.epa.vzd.client.model.UserCertificate; -import org.junit.Test; -import org.junit.Ignore; - -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import org.junit.Ignore; +import org.junit.Test; /** * API tests for CertificateAdministrationApi diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApiTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApiTest.java deleted file mode 100644 index 0b9e440..0000000 --- a/src/test/java/de/gematik/ti/epa/vzd/client/api/DirectoryEntryAdministrationApiTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * I_Directory_Administration - * REST Schnittstelle zur Pflege der Verzeichniseinträge. Über diese Schnittstelle können Verzeichniseinträge inklusive Zertifikaten erzeugt, aktualisiert und gelöscht werden. Die Administration von Fachdaten erfolgt über Schnittstelle I_Directory_Application_Maintenance und wird durch die Fachanwendungen durchgeführt. Lesender Zugriff auf die Fachdaten ist mit Operation getDirectoryEntries in vorliegender Schnittstelle möglich. - * - * The version of the OpenAPI document: 1.0.0 - * - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - - -package de.gematik.ti.epa.vzd.client.api; - -import de.gematik.ti.epa.vzd.client.invoker.ApiException; -import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; -import de.gematik.ti.epa.vzd.client.model.CreateDirectoryEntry; -import de.gematik.ti.epa.vzd.client.model.DirectoryEntry; -import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import de.gematik.ti.epa.vzd.client.model.Error; -import org.junit.Test; -import org.junit.Ignore; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * API tests for DirectoryEntryAdministrationApi - */ -@Ignore -public class DirectoryEntryAdministrationApiTest { - - private final DirectoryEntryAdministrationApi api = new DirectoryEntryAdministrationApi(); - - - /** - * Einen Eintrag zum Verzeichnisdienst hinzufügen - * - * @throws ApiException if the Api call fails - */ - @Test - public void addDirectoryEntryTest() throws ApiException { - CreateDirectoryEntry createDirectoryEntry = null; - DistinguishedName response = api.addDirectoryEntry(createDirectoryEntry); - - // TODO: test validations - } - - /** - * Gesamten Verzeichniseintrag löschen - * - * @throws ApiException if the Api call fails - */ - @Test - public void deleteDirectoryEntryTest() throws ApiException { - String uid = null; - api.deleteDirectoryEntry(uid); - - // TODO: test validations - } - - /** - * Der Verzeichniseintrag (ohne Zertifikate und Fachdaten) wird mit den übergebenen Daten aktualisiert. - * - * @throws ApiException if the Api call fails - */ - @Test - public void modifyDirectoryEntryTest() throws ApiException { - String uid = null; - BaseDirectoryEntry baseDirectoryEntry = null; - DistinguishedName response = api.modifyDirectoryEntry(uid, baseDirectoryEntry); - - // TODO: test validations - } - - /** - * Gesamten Verzeichniseintrag lesen - *

    - * Liefert alle zum Filter passenden Verzeichniseinträge. Die angegebenen Parameter werden mit logischen UND verknüpft. - * - * @throws ApiException if the Api call fails - */ - @Test - public void readDirectoryEntryTest() throws ApiException { - String uid = null; - String givenName = null; - String sn = null; - String cn = null; - String displayName = null; - String streetAddress = null; - String postalCode = null; - String localityName = null; - String stateOrProvienceName = null; - String title = null; - String organization = null; - String otherName = null; - String specialization = null; - String domainID = null; - String personalEntry = null; - String dataFromAuthority = null; - List response = api - .readDirectoryEntry(uid, givenName, sn, cn, displayName, streetAddress, postalCode, localityName, stateOrProvienceName, title, - organization, otherName, specialization, domainID, personalEntry, dataFromAuthority); - - // TODO: test validations - } - -} diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntryTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntryTest.java index c4cef86..04959c0 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntryTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/BaseDirectoryEntryTest.java @@ -13,19 +13,6 @@ package de.gematik.ti.epa.vzd.client.model; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntryTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntryTest.java index 0febeaf..ace3793 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntryTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/CreateDirectoryEntryTest.java @@ -13,20 +13,6 @@ package de.gematik.ti.epa.vzd.client.model; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; -import de.gematik.ti.epa.vzd.client.model.UserCertificate; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntryTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntryTest.java index 7969706..0643483 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntryTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/DirectoryEntryTest.java @@ -13,21 +13,6 @@ package de.gematik.ti.epa.vzd.client.model; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import de.gematik.ti.epa.vzd.client.model.BaseDirectoryEntry; -import de.gematik.ti.epa.vzd.client.model.Fachdaten; -import de.gematik.ti.epa.vzd.client.model.UserCertificate; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/DistinguishedNameTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/DistinguishedNameTest.java index c28a772..7e0d7a9 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/DistinguishedNameTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/DistinguishedNameTest.java @@ -13,18 +13,6 @@ package de.gematik.ti.epa.vzd.client.model; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/ErrorTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/ErrorTest.java index eb283bc..0009326 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/ErrorTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/ErrorTest.java @@ -13,16 +13,6 @@ package de.gematik.ti.epa.vzd.client.model; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; -import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/FAD1Test.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/FAD1Test.java index 1bc5dca..3eb0129 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/FAD1Test.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/FAD1Test.java @@ -13,19 +13,6 @@ package de.gematik.ti.epa.vzd.client.model; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/FachdatenTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/FachdatenTest.java index 966db4d..0fc5e1e 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/FachdatenTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/FachdatenTest.java @@ -13,20 +13,6 @@ package de.gematik.ti.epa.vzd.client.model; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import de.gematik.ti.epa.vzd.client.model.FAD1; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; diff --git a/src/test/java/de/gematik/ti/epa/vzd/client/model/UserCertificateTest.java b/src/test/java/de/gematik/ti/epa/vzd/client/model/UserCertificateTest.java index 188abbf..959fd9f 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/client/model/UserCertificateTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/client/model/UserCertificateTest.java @@ -13,19 +13,6 @@ package de.gematik.ti.epa.vzd.client.model; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import de.gematik.ti.epa.vzd.client.model.DistinguishedName; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilderTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/CommandsBuilderTest.java similarity index 86% rename from src/test/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilderTest.java rename to src/test/java/de/gematik/ti/epa/vzd/gem/command/CommandsBuilderTest.java index 5098162..879cc34 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/CommandsBuilderTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/CommandsBuilderTest.java @@ -1,13 +1,12 @@ -package de.gematik.ti.epa.vzd.gemClient.command; +package de.gematik.ti.epa.vzd.gem.command; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; -import de.gematik.ti.epa.vzd.gemClient.exceptions.CommandException; -import de.gematik.ti.epa.vzd.gemClient.exceptions.ReadException; -import de.gematik.ti.epa.vzd.gemClient.invoker.ConfigHandler; +import de.gematik.ti.epa.vzd.gem.exceptions.CommandException; +import de.gematik.ti.epa.vzd.gem.exceptions.ReadException; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandler; import java.io.File; -import org.apache.logging.log4j.core.tools.picocli.CommandLine.Command; import org.junit.Before; import org.junit.Test; @@ -55,9 +54,9 @@ public void checkFileNotFoundError() { } @Test - public void doubledCommandIdDefinedTest(){ + public void doubledCommandIdDefinedTest() { ConfigHandler.init(DUBLED_COMMAND_ID); CommandException exception = assertThrows(CommandException.class, () -> new CommandsBuilder().buildCommands()); - assertEquals("The predefined ID \"thisShouldOccureAnError\" occurs twice",exception.getMessage()); + assertEquals("The predefined ID \"thisShouldOccureAnError\" occurs twice", exception.getMessage()); } } diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/ExecutionCollectionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/ExecutionCollectionTest.java new file mode 100644 index 0000000..95bd9f1 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/ExecutionCollectionTest.java @@ -0,0 +1,28 @@ +package de.gematik.ti.epa.vzd.gem.command; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.mock; + +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import org.junit.Test; + +public class ExecutionCollectionTest { + + private static ConnectionPool connectionPool = mock(ConnectionPool.class); + + @Test + public void getInstanceWithoutInit() { + InstantiationError ex = assertThrows(InstantiationError.class, () -> ExecutionCollection.getInstance()); + assertEquals("Please instance a executor first. It needs an ConnectionPool", ex.getMessage()); + } + + @Test + public void initWithoutConnectionPool() { + ExecutionCollection.init(connectionPool); + InstantiationError ex = assertThrows(InstantiationError.class, () -> ExecutionCollection.init(connectionPool)); + + assertEquals("Executor is already instanced", ex.getMessage()); + } +} \ No newline at end of file diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryCertExecutionIntegrationTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryCertExecutionIntegrationTest.java new file mode 100644 index 0000000..d495153 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryCertExecutionIntegrationTest.java @@ -0,0 +1,63 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.gem.command.CommandsBuilder; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollection; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandler; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import generated.CommandType; +import java.io.File; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class AddDirEntryCertExecutionIntegrationTest { + + private static final String[] TEST_ARGS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "IntegrationConfig.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt", "-b", "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "commands" + File.separator + "addCert.xml"}; + public static List commands; + + @Before + public void initConfigHandler() { + ConfigHandler.setConfigHandler(null); + ConfigHandler.init(TEST_ARGS); + ExecutionCollection.init(ConnectionPool.createConnectionPool(1)); + commands = new CommandsBuilder().buildCommands(); + } + + @After + public void unsetConfigHandler() { + ConfigHandler.setConfigHandler(null); + ExecutionCollection.getInstance().setExecutionCollection(null); + } + + @Test + public void addCommandSuccessTest() throws Exception { + AddDirEntryCertExecution addDirEntryCertExecution = new AddDirEntryCertExecution(ConnectionPool.createConnectionPool(1)); + assertTrue(addDirEntryCertExecution.createCallable(commands.get(0)).call()); + } + + @Test + public void addCommandFailTest() throws Exception { + AddDirEntryCertExecution addDirEntryCertExecution = new AddDirEntryCertExecution(ConnectionPool.createConnectionPool(1)); + assertFalse(addDirEntryCertExecution.createCallable(commands.get(1)).call()); + } + + @Test + public void executeCommandExceptionTest() { + AddDirEntryCertExecution addDirEntryCertExecution = new AddDirEntryCertExecution(ConnectionPool.createConnectionPool(1)); + ApiException exception = assertThrows(ApiException.class, + () -> addDirEntryCertExecution.executeCommand(commands.get(1), addDirEntryCertExecution.connectionPool.getConnection())); + assertEquals(422, exception.getCode()); + } +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryCertExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryCertExecutionTest.java new file mode 100644 index 0000000..b619b07 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryCertExecutionTest.java @@ -0,0 +1,121 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; +import generated.CommandType; +import generated.DistinguishedNameType; +import generated.UserCertificateType; +import org.junit.Test; + +public class AddDirEntryCertExecutionTest { + + private static IConnectionPool connectionPool = mock(ConnectionPool.class); + + @Test + public void missingUserCertificateElementTest() { + AddDirEntryCertExecution addDirEntryCertExecution = new AddDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + assertFalse(addDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void missingUidTest() { + AddDirEntryCertExecution addDirEntryCertExecution = new AddDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + UserCertificateType userCertificateType = new UserCertificateType(); + userCertificateType.setUserCertificate("SomeUserCertificate"); + command.getUserCertificate().add(userCertificateType); + assertFalse(addDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void missingUserCertificateTest() { + AddDirEntryCertExecution addDirEntryCertExecution = new AddDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid("SomeUid"); + UserCertificateType userCertificateType = new UserCertificateType(); + + command.getUserCertificate().add(userCertificateType); + command.setDn(dn); + + assertFalse(addDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void uidInDnTest() { + AddDirEntryCertExecution addDirEntryCertExecution = new AddDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid("SomeUid"); + UserCertificateType userCertificateType = new UserCertificateType(); + userCertificateType.setUserCertificate("SomeUserCertificate"); + + command.getUserCertificate().add(userCertificateType); + command.setDn(dn); + + assertTrue(addDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void uidInCertTest() { + AddDirEntryCertExecution addDirEntryCertExecution = new AddDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid("SomeUid"); + UserCertificateType userCertificateType = new UserCertificateType(); + userCertificateType.setUserCertificate("SomeUserCertificate"); + + userCertificateType.setDn(dn); + command.getUserCertificate().add(userCertificateType); + + assertTrue(addDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void uidInCertAndDnTest() { + AddDirEntryCertExecution addDirEntryCertExecution = new AddDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid("SomeUid"); + DistinguishedNameType dn2 = new DistinguishedNameType(); + dn2.setUid("SomeUid"); + UserCertificateType userCertificateType = new UserCertificateType(); + userCertificateType.setUserCertificate("SomeUserCertificate"); + + userCertificateType.setDn(dn); + command.getUserCertificate().add(userCertificateType); + command.setDn(dn2); + + assertTrue(addDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void mismatchingUidTest() { + AddDirEntryCertExecution addDirEntryCertExecution = new AddDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid("SomeUid"); + DistinguishedNameType dn2 = new DistinguishedNameType(); + dn2.setUid("SomeOtherUid"); + UserCertificateType userCertificateType = new UserCertificateType(); + userCertificateType.setUserCertificate("SomeUserCertificate"); + + userCertificateType.setDn(dn); + command.getUserCertificate().add(userCertificateType); + command.setDn(dn2); + + assertFalse(addDirEntryCertExecution.checkValidation(command)); + } + + +} \ No newline at end of file diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryExecutionIntegrationTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryExecutionIntegrationTest.java new file mode 100644 index 0000000..60d28d0 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryExecutionIntegrationTest.java @@ -0,0 +1,78 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.gem.command.CommandsBuilder; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollection; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandler; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import generated.CommandType; +import java.io.File; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class AddDirEntryExecutionIntegrationTest { + + private static final String[] TEST_ARGS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "IntegrationConfig.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt", "-b", "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "commands" + File.separator + "addDir.xml"}; + public static List commands; + + @Before + public void initConfigHandler() { + ConfigHandler.setConfigHandler(null); + ConfigHandler.init(TEST_ARGS); + ExecutionCollection.init(ConnectionPool.createConnectionPool(1)); + commands = new CommandsBuilder().buildCommands(); + } + + @After + public void unsetConfigHandler() { + ConfigHandler.setConfigHandler(null); + ExecutionCollection.getInstance().setExecutionCollection(null); + } + + @Test + public void addCommandSuccessTest() throws Exception { + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(ConnectionPool.createConnectionPool(1)); + assertTrue(addDirEntryExecution.createCallable(commands.get(0)).call()); + } + + @Test + public void addCommandFailTest() throws Exception { + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(ConnectionPool.createConnectionPool(1)); + assertFalse(addDirEntryExecution.createCallable(commands.get(1)).call()); + } + + @Test + public void executeCommandExceptionTest() { + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(ConnectionPool.createConnectionPool(1)); + ApiException exception = assertThrows(ApiException.class, + () -> addDirEntryExecution.executeCommand(commands.get(1), addDirEntryExecution.connectionPool.getConnection())); + assertEquals(405, exception.getCode()); + } + + @Test + public void addCommandDifferentTelematikIdTest() throws Exception { + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(ConnectionPool.createConnectionPool(1)); + assertFalse(addDirEntryExecution.createCallable(commands.get(2)).call()); + } + + @Test + public void executeCommandDifferentTelematikIdTest() { + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(ConnectionPool.createConnectionPool(1)); + ApiException exception = assertThrows(ApiException.class, + () -> addDirEntryExecution.executeCommand(commands.get(2), addDirEntryExecution.connectionPool.getConnection())); + assertEquals(405, exception.getCode()); + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryExecutionTest.java similarity index 69% rename from src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecutionTest.java rename to src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryExecutionTest.java index fa77cd4..2d73e7e 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/AddDirEntryExecutionTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/AddDirEntryExecutionTest.java @@ -1,29 +1,29 @@ -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; import generated.CommandType; import generated.UserCertificateType; import org.junit.Test; public class AddDirEntryExecutionTest { - private static GemApiClient gemApiClient = mock(GemApiClient.class); - + private static IConnectionPool connectionPool = mock(ConnectionPool.class); @Test public void checkValidationMissingCertificateObjectTest() { - AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(gemApiClient); + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(connectionPool); CommandType command = new CommandType(); assertFalse(addDirEntryExecution.checkValidation(command)); } @Test public void checkValidationMissingTelematikIdAndCertificateTest() { - AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(gemApiClient); + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(connectionPool); CommandType command = new CommandType(); UserCertificateType certificate = new UserCertificateType(); command.getUserCertificate().add(certificate); @@ -32,7 +32,7 @@ public void checkValidationMissingTelematikIdAndCertificateTest() { @Test public void checkValidationTelematikIdTest() { - AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(gemApiClient); + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setCn("Test"); UserCertificateType certificate = new UserCertificateType(); @@ -43,7 +43,7 @@ public void checkValidationTelematikIdTest() { @Test public void checkValidationCertificateTest() { - AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(gemApiClient); + AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setCn("Test"); UserCertificateType certificate = new UserCertificateType(); @@ -52,15 +52,4 @@ public void checkValidationCertificateTest() { assertTrue(addDirEntryExecution.checkValidation(command)); } - @Test - public void checkValidationMissingCnTest() { - AddDirEntryExecution addDirEntryExecution = new AddDirEntryExecution(gemApiClient); - CommandType command = new CommandType(); - UserCertificateType certificate = new UserCertificateType(); - command.getUserCertificate().add(certificate); - certificate.setTelematikID("TelematikId"); - certificate.setUserCertificate("Certificate"); - assertFalse(addDirEntryExecution.checkValidation(command)); - } - } diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryCertExecutionIntegrationTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryCertExecutionIntegrationTest.java new file mode 100644 index 0000000..9b527eb --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryCertExecutionIntegrationTest.java @@ -0,0 +1,64 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.gem.command.CommandsBuilder; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollection; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandler; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import generated.CommandType; +import java.io.File; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class DeleteDirEntryCertExecutionIntegrationTest { + + private static final String[] TEST_ARGS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "IntegrationConfig.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt", "-b", "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "commands" + File.separator + "delCert.xml"}; + public static List commands; + + @Before + public void initConfigHandler() { + ConfigHandler.setConfigHandler(null); + ConfigHandler.init(TEST_ARGS); + ExecutionCollection.init(ConnectionPool.createConnectionPool(1)); + commands = new CommandsBuilder().buildCommands(); + } + + @After + public void unsetConfigHandler() { + ConfigHandler.setConfigHandler(null); + ExecutionCollection.getInstance().setExecutionCollection(null); + } + + @Test + public void delCertCommandSuccessTest() throws Exception { + DeleteDirEntryCertExecution deleteDirEntryCertExecution = new DeleteDirEntryCertExecution(ConnectionPool.createConnectionPool(1)); + assertTrue(deleteDirEntryCertExecution.createCallable(commands.get(0)).call()); + } + + @Test + public void delCertCommandFailTest() throws Exception { + DeleteDirEntryCertExecution deleteDirEntryCertExecution = new DeleteDirEntryCertExecution(ConnectionPool.createConnectionPool(1)); + assertFalse(deleteDirEntryCertExecution.createCallable(commands.get(1)).call()); + } + + @Test + public void executeCommandExceptionTest() { + DeleteDirEntryCertExecution deleteDirEntryCertExecution = new DeleteDirEntryCertExecution(ConnectionPool.createConnectionPool(1)); + ApiException exception = assertThrows(ApiException.class, + () -> deleteDirEntryCertExecution.executeCommand(commands.get(1), deleteDirEntryCertExecution.connectionPool.getConnection())); + assertEquals(400, exception.getCode()); + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryCertExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryCertExecutionTest.java new file mode 100644 index 0000000..9dfd9e1 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryCertExecutionTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2020 gematik GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; +import generated.CommandType; +import generated.DistinguishedNameType; +import generated.UserCertificateType; +import org.junit.Test; + +public class DeleteDirEntryCertExecutionTest { + + private static IConnectionPool connectionPool = mock(ConnectionPool.class); + + + @Test + public void checkValidationDifferentUidsTest() { + DeleteDirEntryCertExecution deleteDirEntryCertExecution = new DeleteDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + UserCertificateType certificate1 = new UserCertificateType(); + UserCertificateType certificate2 = new UserCertificateType(); + DistinguishedNameType dn1 = new DistinguishedNameType(); + DistinguishedNameType dn2 = new DistinguishedNameType(); + dn1.setUid("something"); + dn2.setUid("somethingelse"); + certificate1.setDn(dn1); + certificate2.setDn(dn2); + command.getUserCertificate().add(certificate1); + command.getUserCertificate().add(certificate2); + command.setCn("SomeCn"); + assertFalse(deleteDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void checkValidationMissingCnTest() { + DeleteDirEntryCertExecution deleteDirEntryCertExecution = new DeleteDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + UserCertificateType certificate1 = new UserCertificateType(); + DistinguishedNameType dn1 = new DistinguishedNameType(); + dn1.setUid("SomeUid"); + certificate1.setDn(dn1); + command.getUserCertificate().add(certificate1); + assertFalse(deleteDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void checkValidationMissingUidTest() { + DeleteDirEntryCertExecution deleteDirEntryCertExecution = new DeleteDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + command.setCn("SomeCn"); + assertFalse(deleteDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void checkValidationSameUidsTest() { + DeleteDirEntryCertExecution deleteDirEntryCertExecution = new DeleteDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + UserCertificateType certificate1 = new UserCertificateType(); + UserCertificateType certificate2 = new UserCertificateType(); + DistinguishedNameType dn1 = new DistinguishedNameType(); + DistinguishedNameType dn2 = new DistinguishedNameType(); + DistinguishedNameType dn3 = new DistinguishedNameType(); + dn1.setUid("SomeUid"); + dn1.setCn("SomeCn"); + dn2.setUid("SomeUid"); + dn2.setCn("SomeOtherCn"); + dn3.setUid("SomeUid"); + certificate1.setDn(dn1); + certificate2.setDn(dn2); + command.setDn(dn3); + command.getUserCertificate().add(certificate1); + command.getUserCertificate().add(certificate2); + assertTrue(deleteDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void doubledCnTest(){ + DeleteDirEntryCertExecution deleteDirEntryCertExecution = new DeleteDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + UserCertificateType certificate1 = new UserCertificateType(); + UserCertificateType certificate2 = new UserCertificateType(); + DistinguishedNameType dn1 = new DistinguishedNameType(); + DistinguishedNameType dn2 = new DistinguishedNameType(); + DistinguishedNameType dn3 = new DistinguishedNameType(); + dn1.setUid("SomeUid"); + dn1.setCn("SameCn"); + dn2.setUid("SomeUid"); + dn2.setCn("SameCn"); + dn3.setUid("SomeUid"); + certificate1.setDn(dn1); + certificate2.setDn(dn2); + command.setDn(dn3); + command.getUserCertificate().add(certificate1); + command.getUserCertificate().add(certificate2); + assertFalse(deleteDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void checkValidationUidInCertTest() { + DeleteDirEntryCertExecution deleteDirEntryCertExecution = new DeleteDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + UserCertificateType certificate1 = new UserCertificateType(); + DistinguishedNameType dn1 = new DistinguishedNameType(); + dn1.setUid("SomeUid"); + dn1.setCn("SomeCn"); + certificate1.setDn(dn1); + command.setCn("SomeUid"); + dn1.setCn("SomeOtherCn"); + command.getUserCertificate().add(certificate1); + assertTrue(deleteDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void checkValidationUidInDnTest() { + DeleteDirEntryCertExecution deleteDirEntryCertExecution = new DeleteDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + DistinguishedNameType dn1 = new DistinguishedNameType(); + dn1.setUid("SomeUid"); + command.setDn(dn1); + command.setCn("SomeUid"); + assertTrue(deleteDirEntryCertExecution.checkValidation(command)); + } +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryExecutionIntegrationTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryExecutionIntegrationTest.java new file mode 100644 index 0000000..fe56e85 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryExecutionIntegrationTest.java @@ -0,0 +1,64 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.gem.command.CommandsBuilder; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollection; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandler; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import generated.CommandType; +import java.io.File; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class DeleteDirEntryExecutionIntegrationTest { + + private static final String[] TEST_ARGS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "IntegrationConfig.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt", "-b", "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "commands" + File.separator + "delDir.xml"}; + public static List commands; + + @Before + public void initConfigHandler() { + ConfigHandler.setConfigHandler(null); + ConfigHandler.init(TEST_ARGS); + ExecutionCollection.init(ConnectionPool.createConnectionPool(1)); + commands = new CommandsBuilder().buildCommands(); + } + + @After + public void unsetConfigHandler() { + ConfigHandler.setConfigHandler(null); + ExecutionCollection.getInstance().setExecutionCollection(null); + } + + @Test + public void delCommandSuccessTest() throws Exception { + DeleteDirEntryExecution deleteDirEntryExecution = new DeleteDirEntryExecution(ConnectionPool.createConnectionPool(1)); + assertTrue(deleteDirEntryExecution.createCallable(commands.get(0)).call()); + } + + @Test + public void delCommandFailTest() throws Exception { + DeleteDirEntryExecution deleteDirEntryExecution = new DeleteDirEntryExecution(ConnectionPool.createConnectionPool(1)); + assertFalse(deleteDirEntryExecution.createCallable(commands.get(1)).call()); + } + + @Test + public void executeCommandEntryNotPresentTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(ConnectionPool.createConnectionPool(1)); + ApiException exception = assertThrows(ApiException.class, + () -> readDirEntryExecution.executeCommand(commands.get(1), readDirEntryExecution.connectionPool.getConnection())); + assertEquals(404, exception.getCode()); + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryExecutionTest.java similarity index 63% rename from src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecutionTest.java rename to src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryExecutionTest.java index 027d1fa..c1fb39e 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryExecutionTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/DeleteDirEntryExecutionTest.java @@ -1,27 +1,29 @@ -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; import generated.CommandType; import generated.DistinguishedNameType; import org.junit.Test; public class DeleteDirEntryExecutionTest { - private static GemApiClient gemApiClient = mock(GemApiClient.class); - private static DeleteDirEntryExecution deleteDirEntryExecution = new DeleteDirEntryExecution(gemApiClient); + private static IConnectionPool connectionPool = mock(ConnectionPool.class); @Test public void checkValidationMissingArgumentTest() { + DeleteDirEntryExecution deleteDirEntryExecution = new DeleteDirEntryExecution(connectionPool); CommandType command = new CommandType(); assertFalse(deleteDirEntryExecution.checkValidation(command)); } @Test public void checkValidationHaveUID() { + DeleteDirEntryExecution deleteDirEntryExecution = new DeleteDirEntryExecution(connectionPool); CommandType command = new CommandType(); DistinguishedNameType dn = new DistinguishedNameType(); dn.setUid("cbca60fe-8ca7-4960-990d-ec526a200582"); diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ExecutionBaseIntegrationTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ExecutionBaseIntegrationTest.java new file mode 100644 index 0000000..8818dea --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ExecutionBaseIntegrationTest.java @@ -0,0 +1,54 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.gem.command.CommandsBuilder; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollection; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandler; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import generated.CommandType; +import java.io.File; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ExecutionBaseIntegrationTest { + + private static final String[] TEST_ARGS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "IntegrationConfig.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt", "-b", "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "commands" + File.separator + "executionBase.xml"}; + public static List commands; + + @Before + public void initConfigHandler() { + ConfigHandler.setConfigHandler(null); + ConfigHandler.init(TEST_ARGS); + ExecutionCollection.init(ConnectionPool.createConnectionPool(1)); + commands = new CommandsBuilder().buildCommands(); + } + + @After + public void unsetConfigHandler() { + ConfigHandler.setConfigHandler(null); + ExecutionCollection.getInstance().setExecutionCollection(null); + } + + @Test + public void getUidTestMissMatchUid() throws InterruptedException, ApiException { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(ConnectionPool.createConnectionPool(1)); + ReadDirEntryCertExecution readDirEntryCertExecution = new ReadDirEntryCertExecution(ConnectionPool.createConnectionPool(1)); + + readDirEntryCertExecution.executeCommand(commands.get(0),readDirEntryCertExecution.connectionPool.getConnection()); + + ApiException exception = assertThrows(ApiException.class, + () -> readDirEntryExecution.executeCommand(commands.get(0), readDirEntryExecution.connectionPool.getConnection())); + assertEquals("UID delivered by TelematikId does not match the UID in command file", exception.getMessage()); + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ExecutionBaseTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ExecutionBaseTest.java new file mode 100644 index 0000000..e233ef5 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ExecutionBaseTest.java @@ -0,0 +1,64 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.client.invoker.auth.OAuth; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import de.gematik.ti.epa.vzd.gem.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; +import generated.CommandType; +import generated.DistinguishedNameType; +import generated.UserCertificateType; +import org.junit.Test; + +public class ExecutionBaseTest { + + private static IConnectionPool connectionPool = mock(ConnectionPool.class); + private static GemApiClient apiClient = mock(GemApiClient.class); + private static OAuth token = mock(OAuth.class); + + @Test + public void getUidTestUidInDn() throws ApiException { + + ExecutionBase readDirEntryExecution = new ReadDirEntryExecution(connectionPool); + CommandType command = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid("SomeUid"); + command.setDn(dn); + + String result = readDirEntryExecution.getUid(command, apiClient); + + assertEquals("SomeUid", result); + } + + @Test + public void getUidTestUidCert() throws ApiException { + + ExecutionBase readDirEntryExecution = new ReadDirEntryExecution(connectionPool); + CommandType command = new CommandType(); + UserCertificateType cert = new UserCertificateType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid("SomeUid"); + cert.setDn(dn); + command.getUserCertificate().add(cert); + + String result = readDirEntryExecution.getUid(command, apiClient); + + assertEquals("SomeUid", result); + } + + @Test + public void getUidTestNoUid() throws ApiException { + + ExecutionBase readDirEntryExecution = new ReadDirEntryExecution(connectionPool); + CommandType command = new CommandType(); + + String result = readDirEntryExecution.getUid(command, apiClient); + + assertEquals(null, result); + } + +} \ No newline at end of file diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryExecutionIntegrationTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryExecutionIntegrationTest.java new file mode 100644 index 0000000..a99f9f0 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryExecutionIntegrationTest.java @@ -0,0 +1,63 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.gem.command.CommandsBuilder; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollection; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandler; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import generated.CommandType; +import java.io.File; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ModifyDirEntryExecutionIntegrationTest { + + private static final String[] TEST_ARGS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "IntegrationConfig.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt", "-b", "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "commands" + File.separator + "modDir.xml"}; + public static List commands; + + @Before + public void initConfigHandler() { + ConfigHandler.setConfigHandler(null); + ConfigHandler.init(TEST_ARGS); + ExecutionCollection.init(ConnectionPool.createConnectionPool(1)); + commands = new CommandsBuilder().buildCommands(); + } + + @After + public void unsetConfigHandler() { + ConfigHandler.setConfigHandler(null); + ExecutionCollection.getInstance().setExecutionCollection(null); + } + + @Test + public void modCommandSuccessTest() throws Exception { + ModifyDirEntryExecution modifyDirEntryExecution = new ModifyDirEntryExecution(ConnectionPool.createConnectionPool(1)); + assertTrue(modifyDirEntryExecution.createCallable(commands.get(0)).call()); + } + + @Test + public void modCommandDoAddTest() throws Exception { + ModifyDirEntryExecution modifyDirEntryExecution = new ModifyDirEntryExecution(ConnectionPool.createConnectionPool(1)); + assertTrue(modifyDirEntryExecution.createCallable(commands.get(1)).call()); + } + + @Test + public void executeCommandNotFoundTest() { + ModifyDirEntryExecution modifyDirEntryExecution = new ModifyDirEntryExecution(ConnectionPool.createConnectionPool(1)); + ApiException exception = assertThrows(ApiException.class, + () -> modifyDirEntryExecution.executeCommand(commands.get(1), modifyDirEntryExecution.connectionPool.getConnection())); + assertEquals(400, exception.getCode()); + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryExecutionTest.java similarity index 79% rename from src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecutionTest.java rename to src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryExecutionTest.java index 6387963..e943493 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ModifyDirEntryExecutionTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ModifyDirEntryExecutionTest.java @@ -1,21 +1,22 @@ -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; import generated.CommandType; import generated.DistinguishedNameType; import org.junit.Test; public class ModifyDirEntryExecutionTest { - private static GemApiClient gemApiClient = mock(GemApiClient.class); + private static IConnectionPool connectionPool = mock(ConnectionPool.class); @Test public void preCheckFalseWhenUidIsEmptyString() { - ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(gemApiClient); + ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(connectionPool); CommandType command = new CommandType(); DistinguishedNameType dn = new DistinguishedNameType(); dn.setUid(""); @@ -26,7 +27,7 @@ public void preCheckFalseWhenUidIsEmptyString() { @Test public void preCheckIsTrue() { - ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(gemApiClient); + ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(connectionPool); CommandType command = new CommandType(); DistinguishedNameType dn = new DistinguishedNameType(); dn.setUid("Test"); @@ -37,7 +38,7 @@ public void preCheckIsTrue() { @Test public void preCheckFalseWhenUidIsMissing() { - ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(gemApiClient); + ModifyDirEntryExecution modDirEnt = new ModifyDirEntryExecution(connectionPool); CommandType command = new CommandType(); DistinguishedNameType dn = new DistinguishedNameType(); dn.setCn("Test"); diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryCertExecutionIntegrationTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryCertExecutionIntegrationTest.java new file mode 100644 index 0000000..c485c8f --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryCertExecutionIntegrationTest.java @@ -0,0 +1,63 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.gem.command.CommandsBuilder; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollection; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandler; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import generated.CommandType; +import java.io.File; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ReadDirEntryCertExecutionIntegrationTest { + + private static final String[] TEST_ARGS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "IntegrationConfig.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt", "-b", "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "commands" + File.separator + "readCert.xml"}; + public static List commands; + + @Before + public void initConfigHandler() { + ConfigHandler.setConfigHandler(null); + ConfigHandler.init(TEST_ARGS); + ExecutionCollection.init(ConnectionPool.createConnectionPool(1)); + commands = new CommandsBuilder().buildCommands(); + } + + @After + public void unsetConfigHandler() { + ConfigHandler.setConfigHandler(null); + ExecutionCollection.getInstance().setExecutionCollection(null); + } + + @Test + public void readCommandSuccessTest() throws Exception { + ReadDirEntryCertExecution readDirEntryCertExecution = new ReadDirEntryCertExecution(ConnectionPool.createConnectionPool(1)); + assertTrue(readDirEntryCertExecution.createCallable(commands.get(0)).call()); + } + + @Test + public void readCommandFailTest() throws Exception { + ReadDirEntryCertExecution readDirEntryCertExecution = new ReadDirEntryCertExecution(ConnectionPool.createConnectionPool(1)); + assertFalse(readDirEntryCertExecution.createCallable(commands.get(1)).call()); + } + + @Test + public void executeCommandIdNotPresentTest() { + ReadDirEntryCertExecution readDirEntryCertExecution = new ReadDirEntryCertExecution(ConnectionPool.createConnectionPool(1)); + ApiException exception = assertThrows(ApiException.class, + () -> readDirEntryCertExecution.executeCommand(commands.get(1), readDirEntryCertExecution.connectionPool.getConnection())); + assertEquals(404, exception.getCode()); + } +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryCertExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryCertExecutionTest.java new file mode 100644 index 0000000..2557ad5 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryCertExecutionTest.java @@ -0,0 +1,86 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; +import generated.CommandType; +import generated.DistinguishedNameType; +import generated.UserCertificateType; +import org.junit.Test; + +public class ReadDirEntryCertExecutionTest { + + private static IConnectionPool connectionPool = mock(ConnectionPool.class); + + @Test + public void noCertificatePresentTest() { + ReadDirEntryCertExecution readDirEntryCertExecution = new ReadDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + assertFalse(readDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void emptyCertificateTest() { + ReadDirEntryCertExecution readDirEntryCertExecution = new ReadDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + UserCertificateType userCertificateType = new UserCertificateType(); + command.getUserCertificate().add(userCertificateType); + assertFalse(readDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void uidPresentTest() { + ReadDirEntryCertExecution readDirEntryCertExecution = new ReadDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + UserCertificateType userCertificateType = new UserCertificateType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid("SomeUid"); + userCertificateType.setDn(dn); + command.getUserCertificate().add(userCertificateType); + assertTrue(readDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void entryTypePresentTest() { + ReadDirEntryCertExecution readDirEntryCertExecution = new ReadDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + UserCertificateType userCertificateType = new UserCertificateType(); + userCertificateType.setEntryType("1"); + command.getUserCertificate().add(userCertificateType); + assertTrue(readDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void telematikIDPresentTest() { + ReadDirEntryCertExecution readDirEntryCertExecution = new ReadDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + UserCertificateType userCertificateType = new UserCertificateType(); + userCertificateType.setTelematikID("SomeTelematikId"); + command.getUserCertificate().add(userCertificateType); + assertTrue(readDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void professionOIDPresentTest() { + ReadDirEntryCertExecution readDirEntryCertExecution = new ReadDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + UserCertificateType userCertificateType = new UserCertificateType(); + userCertificateType.setProfessionOID("SomeProfessionOID"); + command.getUserCertificate().add(userCertificateType); + assertTrue(readDirEntryCertExecution.checkValidation(command)); + } + + @Test + public void usagePresentTest() { + ReadDirEntryCertExecution readDirEntryCertExecution = new ReadDirEntryCertExecution(connectionPool); + CommandType command = new CommandType(); + UserCertificateType userCertificateType = new UserCertificateType(); + userCertificateType.getUsage().add("SomeUsage"); + command.getUserCertificate().add(userCertificateType); + assertTrue(readDirEntryCertExecution.checkValidation(command)); + } + +} \ No newline at end of file diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryExecutionIntegrationTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryExecutionIntegrationTest.java new file mode 100644 index 0000000..3b47c25 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryExecutionIntegrationTest.java @@ -0,0 +1,64 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import de.gematik.ti.epa.vzd.client.invoker.ApiException; +import de.gematik.ti.epa.vzd.gem.command.CommandsBuilder; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollection; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandler; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import generated.CommandType; +import java.io.File; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ReadDirEntryExecutionIntegrationTest { + + private static final String[] TEST_ARGS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "IntegrationConfig.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt", "-b", "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "commands" + File.separator + "readDir.xml"}; + public static List commands; + + @Before + public void initConfigHandler() { + ConfigHandler.setConfigHandler(null); + ConfigHandler.init(TEST_ARGS); + ExecutionCollection.init(ConnectionPool.createConnectionPool(1)); + commands = new CommandsBuilder().buildCommands(); + } + + @After + public void unsetConfigHandler() { + ConfigHandler.setConfigHandler(null); + ExecutionCollection.getInstance().setExecutionCollection(null); + } + + @Test + public void readCommandSuccessTest() throws Exception { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(ConnectionPool.createConnectionPool(1)); + assertTrue(readDirEntryExecution.createCallable(commands.get(0)).call()); + } + + @Test + public void readCommandFailTest() throws Exception { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(ConnectionPool.createConnectionPool(1)); + assertFalse(readDirEntryExecution.createCallable(commands.get(1)).call()); + } + + @Test + public void executeCommandIdNotPresentTest() { + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(ConnectionPool.createConnectionPool(1)); + ApiException exception = assertThrows(ApiException.class, + () -> readDirEntryExecution.executeCommand(commands.get(1), readDirEntryExecution.connectionPool.getConnection())); + assertEquals(404, exception.getCode()); + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryExecutionTest.java similarity index 86% rename from src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecutionTest.java rename to src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryExecutionTest.java index b15f90f..e86d74a 100644 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ReadDirEntryExecutionTest.java +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/ReadDirEntryExecutionTest.java @@ -1,28 +1,29 @@ -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; import generated.CommandType; import generated.DistinguishedNameType; import org.junit.Test; public class ReadDirEntryExecutionTest { - private static GemApiClient gemApiClient = mock(GemApiClient.class); + private static IConnectionPool connectionPool = mock(ConnectionPool.class); @Test public void checkValidationMissingArgumentTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); assertFalse(readDirEntryExecution.checkValidation(command)); } @Test public void checkValidationHaveDnTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); DistinguishedNameType dn = new DistinguishedNameType(); dn.setUid("cbca60fe-8ca7-4960-990d-ec526a200582"); @@ -32,7 +33,7 @@ public void checkValidationHaveDnTest() { @Test public void checkValidationHaveGivenNameTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setGivenName("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -40,7 +41,7 @@ public void checkValidationHaveGivenNameTest() { @Test public void checkValidationHaveSnTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setSn("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -48,7 +49,7 @@ public void checkValidationHaveSnTest() { @Test public void checkValidationHaveCnTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setCn("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -56,7 +57,7 @@ public void checkValidationHaveCnTest() { @Test public void checkValidationHaveDisplayNameTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setDisplayName("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -64,7 +65,7 @@ public void checkValidationHaveDisplayNameTest() { @Test public void checkValidationHaveStreetAddressTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setStreetAddress("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -72,7 +73,7 @@ public void checkValidationHaveStreetAddressTest() { @Test public void checkValidationHavePostalCodeTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setPostalCode("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -80,7 +81,7 @@ public void checkValidationHavePostalCodeTest() { @Test public void checkValidationHaveLocalityNameTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setLocalityName("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -88,7 +89,7 @@ public void checkValidationHaveLocalityNameTest() { @Test public void checkValidationHaveStateOrProvinceNameTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setGivenName("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -96,7 +97,7 @@ public void checkValidationHaveStateOrProvinceNameTest() { @Test public void checkValidationHaveTitleTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setTitle("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -104,7 +105,7 @@ public void checkValidationHaveTitleTest() { @Test public void checkValidationHaveOrganizationTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setOrganization("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -112,7 +113,7 @@ public void checkValidationHaveOrganizationTest() { @Test public void checkValidationHaveOtherNameTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setOtherName("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -120,7 +121,7 @@ public void checkValidationHaveOtherNameTest() { @Test public void checkValidationHaveSpecializationTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.getSpecialization().add("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -128,7 +129,7 @@ public void checkValidationHaveSpecializationTest() { @Test public void checkValidationHaveDomainIDTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.getDomainID().add("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -136,7 +137,7 @@ public void checkValidationHaveDomainIDTest() { @Test public void checkValidationHavePersonalEntryTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setPersonalEntry("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); @@ -144,7 +145,7 @@ public void checkValidationHavePersonalEntryTest() { @Test public void checkValidationHaveDataFromAuthorityTest() { - ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(gemApiClient); + ReadDirEntryExecution readDirEntryExecution = new ReadDirEntryExecution(connectionPool); CommandType command = new CommandType(); command.setDataFromAuthority("TestString"); assertTrue(readDirEntryExecution.checkValidation(command)); diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/SaveModifyDirExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/SaveModifyDirExecutionTest.java new file mode 100644 index 0000000..329f5bc --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/command/commandExecutions/SaveModifyDirExecutionTest.java @@ -0,0 +1,48 @@ +package de.gematik.ti.epa.vzd.gem.command.commandExecutions; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import de.gematik.ti.epa.vzd.gem.invoker.ConnectionPool; +import de.gematik.ti.epa.vzd.gem.invoker.IConnectionPool; +import generated.CommandType; +import generated.DistinguishedNameType; +import org.junit.Test; + +public class SaveModifyDirExecutionTest { + + private static IConnectionPool connectionPool = mock(ConnectionPool.class); + + @Test + public void preCheckFalseWhenUidIsEmptyString() { + SaveModifyDirEntryExecution sModDirEnt = new SaveModifyDirEntryExecution(connectionPool); + CommandType command = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid(""); + command.setDn(dn); + + assertTrue(!sModDirEnt.preCheck(command)); + } + + @Test + public void preCheckIsTrue() { + SaveModifyDirEntryExecution sModDirEnt = new SaveModifyDirEntryExecution(connectionPool); + CommandType command = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setUid("Test"); + command.setDn(dn); + + assertTrue(sModDirEnt.preCheck(command)); + } + + @Test + public void preCheckFalseWhenUidIsMissing() { + SaveModifyDirEntryExecution sModDirEnt = new SaveModifyDirEntryExecution(connectionPool); + CommandType command = new CommandType(); + DistinguishedNameType dn = new DistinguishedNameType(); + dn.setCn("Test"); + command.setDn(dn); + + assertTrue(!sModDirEnt.preCheck(command)); + } +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/invoker/ConfigHandlerTest.java b/src/test/java/de/gematik/ti/epa/vzd/gem/invoker/ConfigHandlerTest.java new file mode 100644 index 0000000..106b24b --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/invoker/ConfigHandlerTest.java @@ -0,0 +1,187 @@ +package de.gematik.ti.epa.vzd.gem.invoker; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import de.gematik.ti.epa.vzd.gem.exceptions.GemClientException; +import generated.CommandType; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import org.junit.Before; +import org.junit.Test; + +public class ConfigHandlerTest { + + private static final String[] TEST_ARGS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Config.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt", "-b", "rightPath"}; + private static final String[] TEST_ARGS_MAX_CONNECTIONS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "ConnectionWithMaxConnectionDefined.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt", "-b", "rightPath"}; + private static final String[] TEST_ARGS_LIMIT_CONNECTIONS = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "ConnectionWithLimitConnectionDefined.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt", "-b", "rightPath"}; + private static final String[] TEST_ARGS_WITH_COMMANDPATH_AND_PROXY_IN_FILE = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Config.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt"}; + private static final String[] TEST_ARGS_WITH_COMMANDPATH_AND_PROXY_IN_CLI = new String[]{"-p", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Config.txt", "-c", + "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "config" + File.separator + "Credentials.txt", "-d", "4321", "-h", "anotherHost.com"}; + + @Before + public void resetConfigHandler() { + ConfigHandler.setConfigHandler(null); + } + + @Test + public void testGetInstanceBeforeInit() { + GemClientException exception = assertThrows(GemClientException.class, + ConfigHandler::getInstance); + assertEquals("A ConfigHandler have to be initialized first", exception.getMessage()); + } + + @Test + public void doubleInitConfigHandlerTest() { + ConfigHandler.init(TEST_ARGS); + GemClientException exception = assertThrows(GemClientException.class, () -> + ConfigHandler.init(TEST_ARGS)); + assertEquals("Configurations are only allowed to set once", exception.getMessage()); + } + + @Test + public void checkRightCommandsWithCliPath() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS); + + assertEquals(new File("rightPath").getAbsolutePath(), configHandler.getCommandsPath()); + } + + @Test + public void checkRightCommandsWithFilePath() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS_WITH_COMMANDPATH_AND_PROXY_IN_FILE); + + assertEquals(new File("src\\test\\resources\\config\\Commands.xml").getAbsolutePath(), configHandler.getCommandsPath()); + } + + @Test + public void checkRightRetryOAuthPath() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS); + + assertEquals("https://to.be.defined/oauth/token", configHandler.getRetryingOAuthPath()); + } + + @Test + public void checkRightBasePath() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS); + + assertEquals("http://[::1]:8080/OAuth2Token", configHandler.getBasePath()); + } + + @Test + public void testGetProxyFromFile() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS_WITH_COMMANDPATH_AND_PROXY_IN_FILE); + + assertEquals("Wrong proxy", 1234, configHandler.getProxyPort()); + assertEquals("Wrong host", "testHost.de", configHandler.getProxyHost()); + } + + @Test + public void testGetProxyOverrideFile() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS_WITH_COMMANDPATH_AND_PROXY_IN_CLI); + + assertEquals("Wrong proxy", 4321, configHandler.getProxyPort()); + assertEquals("Wrong host", "anotherHost.com", configHandler.getProxyHost()); + } + + @Test + public void testMoreConnectionNeededThanSpecified() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS); + List list = new ArrayList<>(); + CommandType command = new CommandType(); + command.setName("CommandType1"); + list.add(command); + list.add(command); + list.add(command); + CommandType command2 = new CommandType(); + command2.setName("CommandType2"); + list.add(command2); + list.add(command2); + list.add(command2); + configHandler.adjustConnectionCount(list); + assertEquals(4, configHandler.getConnectionCount()); + } + + @Test + public void testLesConnectionNeededThanSpecified() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS); + List list = new ArrayList<>(); + CommandType command = new CommandType(); + command.setName("CommandType1"); + list.add(command); + CommandType command2 = new CommandType(); + command2.setName("CommandType2"); + list.add(command2); + configHandler.adjustConnectionCount(list); + assertEquals(2, configHandler.getConnectionCount()); + } + + @Test + public void testMoreThan20Commands() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS_MAX_CONNECTIONS); + List list = new ArrayList<>(); + CommandType command = new CommandType(); + command.setName("CommandType1"); + for (int i = 0; i < 30; i++) { + list.add(command); + } + CommandType command2 = new CommandType(); + command2.setName("CommandType2"); + for (int i = 0; i < 25; i++) { + list.add(command2); + } + CommandType command3 = new CommandType(); + command3.setName("CommandType3"); + list.add(command3); + CommandType command4 = new CommandType(); + command4.setName("CommandType4"); + list.add(command4); + list.add(command4); + list.add(command4); + CommandType command5 = new CommandType(); + command5.setName("CommandType5"); + list.add(command5); + list.add(command5); + list.add(command5); + configHandler.adjustConnectionCount(list); + assertEquals(47, configHandler.getConnectionCount()); + } + + @Test + public void testConnectionLimitByCommands() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS_LIMIT_CONNECTIONS); + List commands = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + CommandType command = new CommandType(); + command.setName("CommandType"); + commands.add(command); + } + configHandler.adjustConnectionCount(commands); + assertEquals(2, configHandler.getInstance().getConnectionCount()); + } + + @Test + public void testMoreThanPossibleConnectionDefined() { + ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS_MAX_CONNECTIONS); + assertEquals(180, configHandler.getConnectionCount()); + } +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/testSuites/AllTestsuite.java b/src/test/java/de/gematik/ti/epa/vzd/gem/testSuites/AllTestsuite.java new file mode 100644 index 0000000..5584243 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/testSuites/AllTestsuite.java @@ -0,0 +1,12 @@ +package de.gematik.ti.epa.vzd.gem.testSuites; + + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Suite.class) +@SuiteClasses({IntegrationTestsuite.class, UnitTestsuite.class}) +public class AllTestsuite { + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/testSuites/IntegrationTestsuite.java b/src/test/java/de/gematik/ti/epa/vzd/gem/testSuites/IntegrationTestsuite.java new file mode 100644 index 0000000..7d9abe0 --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/testSuites/IntegrationTestsuite.java @@ -0,0 +1,82 @@ +package de.gematik.ti.epa.vzd.gem.testSuites; + +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.AddDirEntryCertExecutionIntegrationTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.AddDirEntryExecutionIntegrationTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.DeleteDirEntryCertExecutionIntegrationTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.DeleteDirEntryExecutionIntegrationTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ExecutionBaseIntegrationTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ModifyDirEntryExecutionIntegrationTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ReadDirEntryCertExecutionIntegrationTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ReadDirEntryExecutionIntegrationTest; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@RunWith(Suite.class) +@SuiteClasses({ + AddDirEntryCertExecutionIntegrationTest.class, + AddDirEntryExecutionIntegrationTest.class, + DeleteDirEntryCertExecutionIntegrationTest.class, + DeleteDirEntryExecutionIntegrationTest.class, + ModifyDirEntryExecutionIntegrationTest.class, + ReadDirEntryCertExecutionIntegrationTest.class, + ReadDirEntryExecutionIntegrationTest.class, + ExecutionBaseIntegrationTest.class}) +public class IntegrationTestsuite { + + private static final Logger LOG = LoggerFactory.getLogger(IntegrationTestsuite.class); + public static Process serverProcess; + + @BeforeClass + public static void startServer() throws InterruptedException { + ExecutorService service = Executors.newFixedThreadPool(2); + service.execute(new StartServer()); + Thread.currentThread().sleep(10000); + service.shutdown(); + } + + @AfterClass + public static void tearDownServer() { + serverProcess.destroy(); + } + + static class StartServer implements Runnable { + + @Override + public void run() { + File f = new File("src/test/resources/exec/Testserver.jar"); + ProcessBuilder pb = new ProcessBuilder( + "java", + "-jar", + f.getAbsolutePath() + ); + try { + System.out.println("startServer"); + serverProcess = pb.start(); + System.out.println("Server started by " + Thread.currentThread().getName()); + BufferedReader input = new BufferedReader(new InputStreamReader(serverProcess.getInputStream())); + String line; + try { + while ((line = input.readLine()) != null) { + System.out.println(line); + } + } catch (IOException e) { + e.printStackTrace(); + } + } catch (IOException ioException) { + LOG.error("Server could not be started"); + } + } + } + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gem/testSuites/UnitTestsuite.java b/src/test/java/de/gematik/ti/epa/vzd/gem/testSuites/UnitTestsuite.java new file mode 100644 index 0000000..425b2fd --- /dev/null +++ b/src/test/java/de/gematik/ti/epa/vzd/gem/testSuites/UnitTestsuite.java @@ -0,0 +1,36 @@ +package de.gematik.ti.epa.vzd.gem.testSuites; + +import de.gematik.ti.epa.vzd.gem.command.CommandsBuilderTest; +import de.gematik.ti.epa.vzd.gem.command.ExecutionCollectionTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.AddDirEntryCertExecutionTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.AddDirEntryExecutionTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.DeleteDirEntryCertExecutionTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.DeleteDirEntryExecutionTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ExecutionBaseTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ModifyDirEntryExecutionTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ReadDirEntryCertExecutionTest; +import de.gematik.ti.epa.vzd.gem.command.commandExecutions.ReadDirEntryExecutionTest; +import de.gematik.ti.epa.vzd.gem.invoker.ConfigHandlerTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Suite.class) +@SuiteClasses({ + // CommandExecutions + AddDirEntryCertExecutionTest.class, + AddDirEntryExecutionTest.class, + DeleteDirEntryCertExecutionTest.class, + DeleteDirEntryExecutionTest.class, + ModifyDirEntryExecutionTest.class, + ReadDirEntryCertExecutionTest.class, + ReadDirEntryExecutionTest.class, + ExecutionBaseTest.class, + // Command + CommandsBuilderTest.class, + ExecutionCollectionTest.class, + // Invoker + ConfigHandlerTest.class}) +public class UnitTestsuite { + +} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecutionTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecutionTest.java deleted file mode 100644 index ae8d8f6..0000000 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/DeleteDirEntryCertExecutionTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2020 gematik GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; - -import de.gematik.ti.epa.vzd.gemClient.invoker.GemApiClient; -import generated.CommandType; -import generated.DistinguishedNameType; -import generated.UserCertificateType; -import org.apache.logging.log4j.core.tools.picocli.CommandLine.Command; -import org.junit.Test; - -public class DeleteDirEntryCertExecutionTest { - - private static GemApiClient gemApiClient = mock(GemApiClient.class); - - - @Test - public void checkValidationDifferentUidsTest() { - DeleteDirEntryCertExecution deleteDirEntryCertExecution = new DeleteDirEntryCertExecution(gemApiClient); - CommandType command = new CommandType(); - UserCertificateType certificate1 = new UserCertificateType(); - UserCertificateType certificate2 = new UserCertificateType(); - DistinguishedNameType dn1 = new DistinguishedNameType(); - DistinguishedNameType dn2 = new DistinguishedNameType(); - dn1.setUid("something"); - dn2.setUid("somethingelse"); - certificate1.setDn(dn1); - certificate2.setDn(dn2); - command.getUserCertificate().add(certificate1); - command.getUserCertificate().add(certificate2); - assertFalse(deleteDirEntryCertExecution.checkValidation(command)); - } -} diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBaseTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBaseTest.java deleted file mode 100644 index 684def5..0000000 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/command/commandExecutions/ExecutionBaseTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.gematik.ti.epa.vzd.gemClient.command.commandExecutions; - -import static org.junit.Assert.*; - -import org.junit.Test; - -public class ExecutionBaseTest { - - @Test - public void checkEntryPresentTest() { - //todo - assertTrue(true); - } - - @Test - public void serverNotPresentExecutionTest() { - //todo - assertTrue(true); - } -} \ No newline at end of file diff --git a/src/test/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandlerTest.java b/src/test/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandlerTest.java deleted file mode 100644 index ca7e562..0000000 --- a/src/test/java/de/gematik/ti/epa/vzd/gemClient/invoker/ConfigHandlerTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package de.gematik.ti.epa.vzd.gemClient.invoker; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThrows; - -import de.gematik.ti.epa.vzd.gemClient.exceptions.GemClientException; -import de.gematik.ti.epa.vzd.gemClient.invoker.ConfigHandler; -import java.io.File; -import org.junit.Before; -import org.junit.Test; - -public class ConfigHandlerTest { - - private static final String[] TEST_ARGS = new String[]{"-p", - "src" + File.separator + "test" + File.separator + "resources" + File.separator - + "config" + File.separator + "Config.txt", "-c", - "src" + File.separator + "test" + File.separator + "resources" + File.separator - + "config" + File.separator + "Credentials.txt", "-b", "rightPath"}; - private static final String[] TEST_ARGS_WITH_COMMANDPATH_IN_FILE = new String[]{"-p", - "src" + File.separator + "test" + File.separator + "resources" + File.separator - + "config" + File.separator + "Config.txt", "-c", - "src" + File.separator + "test" + File.separator + "resources" + File.separator - + "config" + File.separator + "Credentials.txt"}; - - @Before - public void resetConfigHandler() { - ConfigHandler.setConfigHandler(null); - } - - @Test - public void testGetInstanceBeforeInit() { - GemClientException exception = assertThrows(GemClientException.class, - ConfigHandler::getInstance); - assertEquals("A ConfigHandler have to be initialized first", exception.getMessage()); - } - - @Test - public void doubleInitConfigHandlerTest() { - ConfigHandler.init(TEST_ARGS); - GemClientException exception = assertThrows(GemClientException.class, () -> - ConfigHandler.init(TEST_ARGS)); - assertEquals("Configurations are only allowed to set once", exception.getMessage()); - } - - @Test - public void checkRightCommandsWithCliPath() { - ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS); - - assertEquals(new File("rightPath").getAbsolutePath(), configHandler.getCommandsPath()); - } - - @Test - public void checkRightCommandsWithFilePath() { - ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS_WITH_COMMANDPATH_IN_FILE); - - assertEquals(new File("src\\test\\resources\\config\\Commands.xml").getAbsolutePath(), configHandler.getCommandsPath()); - } - - @Test - public void checkRightRetryOAuthPath() { - ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS); - - assertEquals("https://to.be.defined/oauth/token", configHandler.getRetryingOAuthPath()); - } - - @Test - public void checkRightBasePath() { - ConfigHandler configHandler = ConfigHandler.init(TEST_ARGS); - - assertEquals("http://[::1]:8080/OAuth2Token", configHandler.getBasePath()); - } -} diff --git a/src/test/resources/config/Config.txt b/src/test/resources/config/Config.txt index 6254beb..eb11a0f 100644 --- a/src/test/resources/config/Config.txt +++ b/src/test/resources/config/Config.txt @@ -1,3 +1,7 @@ base=http://[::1]:8080/OAuth2Token retryingOAuth=https://to.be.defined/oauth/token -commands=src\test\resources\config\Commands.xml \ No newline at end of file +commands=src\test\resources\config\Commands.xml +proxyPort=1234 +proxyHost=testHost.de +maxExecutionsPerOperation=2 +maxOperations=2 \ No newline at end of file diff --git a/src/test/resources/config/ConnectionWithLimitConnectionDefined.txt b/src/test/resources/config/ConnectionWithLimitConnectionDefined.txt new file mode 100644 index 0000000..98e326b --- /dev/null +++ b/src/test/resources/config/ConnectionWithLimitConnectionDefined.txt @@ -0,0 +1,7 @@ +base=http://[::1]:8080/OAuth2Token +retryingOAuth=https://to.be.defined/oauth/token +commands=src\test\resources\config\Commands.xml +proxyPort=1234 +proxyHost=testHost.de +maxExecutionsPerOperation=5 +maxOperations=9 \ No newline at end of file diff --git a/src/test/resources/config/ConnectionWithMaxConnectionDefined.txt b/src/test/resources/config/ConnectionWithMaxConnectionDefined.txt new file mode 100644 index 0000000..9016259 --- /dev/null +++ b/src/test/resources/config/ConnectionWithMaxConnectionDefined.txt @@ -0,0 +1,7 @@ +base=http://[::1]:8080/OAuth2Token +retryingOAuth=https://to.be.defined/oauth/token +commands=src\test\resources\config\Commands.xml +proxyPort=1234 +proxyHost=testHost.de +maxExecutionsPerOperation=999 +maxOperations=99 \ No newline at end of file diff --git a/src/test/resources/config/IntegrationConfig.txt b/src/test/resources/config/IntegrationConfig.txt new file mode 100644 index 0000000..303edad --- /dev/null +++ b/src/test/resources/config/IntegrationConfig.txt @@ -0,0 +1,2 @@ +base=http://[::1]:4041 +retryingOAuth=http://[::1]:4041/oauth/token \ No newline at end of file diff --git a/src/test/resources/config/commands/addCert.xml b/src/test/resources/config/commands/addCert.xml new file mode 100644 index 0000000..6a45712 --- /dev/null +++ b/src/test/resources/config/commands/addCert.xml @@ -0,0 +1,33 @@ + + + + TestSuccess + addDirectoryEntryCertificate + + SuccessUId + + + SuccessTelematikId + ePA + + MIIFHjCCBAagAwIBAgIHA+5BCCCEfTANBgkqhkiG9w0BAQsFADCBmjELMAkGA1UEBhMCREUxHzAdBgNVBAoMFmdlbWF0aWsgR21iSCBOT1QtVkFMSUQxRjBEBgNVBAsMPUhCQS1xQ0EgZGVyIFRlbGVtYXRpa2luZnJhc3RydWt0dXIgbWl0IEFuYmlldGVyYWtrcmVkaXRpZXJ1bmcxIjAgBgNVBAMMGUdFTS5IQkEtcUNBNTpQTiBURVNULU9OTFkwHhcNMjAwNTEzMDAwMDAwWhcNMjUwNTEyMjM1OTU5WjBtMQswCQYDVQQGEwJERTFeMA0GA1UEKgwGTWFydGluMA8GA1UEBAwIR2VpYsO2bGwwGwYDVQQFExQ4MDI3Njg4MzExMDAwMDIxODU3MjAfBgNVBAMMGE1hcnRpbiBHZWliw7ZsbFRFU1QtT05MWTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMPTJsw7AHirJcBnS8Jbuf6pjBasQTH7NTcNUDyyOwh35QUdCWgO83kbipxv8yJLrF6Rgb5qw2hbNEsUiNsrwuwn/CF65HiyXMajbNo1etAb23sWHeQt2XLuInJGuT9oM8VWh5rfVTbQL+Bp2VbeoEoqyMLufojL3S/Hal2HDIEd5OWiiTr3i5ECLZX9iHOe8+9c0Q8X1AjeISQLCyuJ3iR67UCpSQBUaMiJaaiVzKaf6kNeRBsAvX78pThkv1oTT7apoQ9l3+x1yjhfF5BQXrigD78W7SYLdRYP/40xj5PUAMD73Cfwij/c4VcqowDRBctRZtP1OyU9FZ6+lgaogoMCAwEAAaOCAZMwggGPMB0GA1UdDgQWBBQTf/qksNlSX3Q1RAcuoXKWLlsZRjA5BgNVHSAEMjAwMAkGByqCFABMBEgwCQYHBACL7EABAjAKBggqghQATASBETAMBgorBgEEAYLNMwEBMCIGCCsGAQUFBwEDBBYwFDAIBgYEAI5GAQEwCAYGBACORgEEMA4GA1UdDwEB/wQEAwIGQDA4BggrBgEFBQcBAQQsMCowKAYIKwYBBQUHMAGGHGh0dHA6Ly9laGNhLmdlbWF0aWsuZGUvb2NzcC8wDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBShOjUzorMhCb0AWpKm55d2xFD7tzAbBgkrBgEEAcBtAwUEDjAMBgorBgEEAcBtAwUBMHkGBSskCAMDBHAwbqQoMCYxCzAJBgNVBAYTAkRFMRcwFQYDVQQKDA5nZW1hdGlrIEJlcmxpbjBCMEAwPjA8MA4MDMOEcnp0aW4vQXJ6dDAJBgcqghQATAQeEx8xLUhCQS1UZXN0a2FydGUtODgzMTEwMDAwMjE4NTcyMA0GCSqGSIb3DQEBCwUAA4IBAQA6wy8SHuB3O2m9xT5WZU7krCBbTjAqVpK8Prmq/eqCXp0c7JHp8tZMiflEzckO01divk9FvtsDEHjwnTEFKYrquPbU9BBsfcLM2u/NugWLqhc6DlH/cxfjhL/e5wgFcBKhJ0UC/3mLCO9EImsuS8qMMBvh4giY/BdHLM7xHZWHxAnknieqSFoutLmFbSSV8ggW1sxPKVGKfh1QYOL1cDz8MbOUKnMTKQGFTSwiJ4lfPq7TkevCBlGYvZQPlodL8xTVWcJLhTjuoLuWcrmkMe/aF4WKd4MwMsmHhgGN4yV2YY56D3L6OV/Np0UJ7A918E9E7iLqLkyVKJcCRTltk+35 + + crazyStuff + + + + TestFail + addDirectoryEntryCertificate + + SuccessUId + + + TelematikId + ePA + + does not fit to TelematikId + + crazyStuff + + + diff --git a/src/test/resources/config/commands/addDir.xml b/src/test/resources/config/commands/addDir.xml new file mode 100644 index 0000000..1503763 --- /dev/null +++ b/src/test/resources/config/commands/addDir.xml @@ -0,0 +1,55 @@ + + + + TestSuccess + addDirectoryEntries + GivenName + SN + streetAddress + postalCode + localityName + stateOrProvinceName + CN + displayName + title + organization + otherName + specialization + domainID + asdf@gmx.net + + SuccessTelematikId + + + + TestFail + addDirectoryEntries + GivenName + SN + streetAddress + postalCode + localityName + stateOrProvinceName + CN + displayName + title + organization + otherName + specialization + domainID + asdf@gmx.net + + FailTelematikId + + + + TestTelematikIdFail + addDirectoryEntries + + FailTelematikId + + + OtherFailTelematikId + + + \ No newline at end of file diff --git a/src/test/resources/config/commands/delCert.xml b/src/test/resources/config/commands/delCert.xml new file mode 100644 index 0000000..faeabda --- /dev/null +++ b/src/test/resources/config/commands/delCert.xml @@ -0,0 +1,25 @@ + + + + TestSuccess + deleteDirectoryEntryCertificate + + + e6483730-6788-4e4b-8ac2-19e858dbae64 + fdb30aad-5f37-4af6-970a-1d4c643ce36a + + 1-HBA-Testkarte-883110000218572 + + + + TestFail + deleteDirectoryEntryCertificate + + + noValidCn + fdb30aad-5f37-4af6-970a-1d4c643ce36a + + 1-HBA-Testkarte-883110000218572 + + + \ No newline at end of file diff --git a/src/test/resources/config/commands/delDir.xml b/src/test/resources/config/commands/delDir.xml new file mode 100644 index 0000000..a8d1b13 --- /dev/null +++ b/src/test/resources/config/commands/delDir.xml @@ -0,0 +1,17 @@ + + + + TestSuccess + deleteDirectoryEntries + + d0752991-d822-48a5-aba9-9af73cbe8f1c + + + + TestFail + deleteDirectoryEntries + + failUid + + + diff --git a/src/test/resources/config/commands/executionBase.xml b/src/test/resources/config/commands/executionBase.xml new file mode 100644 index 0000000..2fd8239 --- /dev/null +++ b/src/test/resources/config/commands/executionBase.xml @@ -0,0 +1,13 @@ + + + + RealTelematik + readDirectoryEntries + + SomeUid + + + Some-TelematikID-that-does-not-fit-the-uid + + + \ No newline at end of file diff --git a/src/test/resources/config/commands/modDir.xml b/src/test/resources/config/commands/modDir.xml new file mode 100644 index 0000000..3ee4cde --- /dev/null +++ b/src/test/resources/config/commands/modDir.xml @@ -0,0 +1,40 @@ + + + + TestSuccess + modifyDirectoryEntries + + 469fd210-b2f8-4d8e-b7ef-c0434303c26d + Hallo hier ist ein Test + + Hero + Whut? + Nirvana + 12345 + Luluhausen + Vereiniges Spaßhausen + Thut + Holla die Waldfee + Denk dir was eigenes lustiges aus + Gagfabrik + Hups I did it again + Spaßmachen + Malen + Lachen + Vereiern + Immer + noch + keinen + Schimmer + was + das + ist + + + ModNotExistentDoModifyTest + modifyDirectoryEntries + + FailModifyUId + + + \ No newline at end of file diff --git a/src/test/resources/config/commands/readCert.xml b/src/test/resources/config/commands/readCert.xml new file mode 100644 index 0000000..a8c8d58 --- /dev/null +++ b/src/test/resources/config/commands/readCert.xml @@ -0,0 +1,26 @@ + + + + TestSuccess + readDirectoryEntryCertificate + + UIdSuccess + + + + UIdSuccess + + 1-HBA-Testkarte-883110000218572 + + + + TestFail + readDirectoryEntryCertificate + + + UIdFail + + SomeTelematikid + + + diff --git a/src/test/resources/config/commands/readDir.xml b/src/test/resources/config/commands/readDir.xml new file mode 100644 index 0000000..0fc64b0 --- /dev/null +++ b/src/test/resources/config/commands/readDir.xml @@ -0,0 +1,17 @@ + + + + 1 + readDirectoryEntries + + 469fd210-b2f8-4d8e-b7ef-c0434303c26d + + + + 2 + readDirectoryEntries + + failUid + + + \ No newline at end of file diff --git a/src/test/resources/exec/Testserver.jar b/src/test/resources/exec/Testserver.jar new file mode 100644 index 0000000000000000000000000000000000000000..da161b7e07ebffeba7525a17c2518db74e850c17 GIT binary patch literal 27638906 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

    sEkStCSj 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

    ?c_Jz1?kiG5T5HM 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�BzyPiR&iX?vCjkw?zut)C~s7gn|xv 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�(mokz{e7E5IEKH{#!|?lb5XKznCZ?MIn~OH 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

    Q`>n^vmhrTdfl z!)&HRwO2LBQYoSgUNSN^Gdq#QN)7?@P6h$IAPm#YPL4hzeUeF%gT)K2M`Nl)ZBGE$FKR4L0rnvDm?f)WPVeksV8STnEdb^L8z5 z=FU`eaYQjS^&1*VRUZXTDxF=K{v~o%W%}sbpc!Mjvby+LmXTp)GOPFbY~CE?oM~}_w&z0YeYz9#SpAO4#DeX|7F;2gq&#S7K;@=Qe8?SRv!d> zY15nt68G&tqu0NS|KAm}K%|i}4;bh>Km!38{r454;9&W`>4fONVwj%3rO^*dGd%|h zqyH*>|53{}Do;DC3u1U#kkTt36p7`KQ1e0u>T;SXl2L;;2Pqab%m`X*65tJ=>KPMQXpF%m{R8Rd#t@dC6>7-R*Dd z_JsE#y60UdS=m;WsSc4IEn?Zsx9-l+Ma%Wc;^@yzT5{d_-d$PMzC_gfBq|i$x zIZncTwyxWNT|CW4@+_#~yKB3OoO$#>fs{HN7hGw|kIC18ekopo>w~psg)p+#uG9+T z&@Nm!P1t0BYoaB;t#&CAmv>|TcKLZ}g(&&@9Y4X*MYQ{n=bA|Ut`RJv?YbO9fF$jg zNmb8F`R?(@{AjpQ9?mPCMzrrDglY4S-CQ{}_qw&&x@*<}o>@KLaeWdIFLA~wFwFvF zHm`vcXZrM*0$CFta=%J-m<%8WqFE#j#jE7;`P3v>-_hvOuJT%L4tI~2^ljqv8gXI? zE30P%!7*mkCCl!@C9IC5ptPd+I-(s&8Y`ML&|?PdXVfq1lVd2YK;9QLgJEuxeFq}8<4q0*7bFx z(hWg&5mwhpE=Mm0%ii?_wJ`c|mCg~uh&;?_w~NVRZG=UiSqiW-!b=IS_gEaT<}jZ- z@ZKB%&7$7H>Mx0Xp)yS4o6sFos4N?w|HR+lX8FidIaV(g=!&Cc$yDf?(O9~Z^tvON zyqwV}bgaBV)i8eKgNkl_qwoVX^A#zFQus;VaIe1d27LTNZg6UAhE>f1xtEz`;8hj@ z%~CP})dHjar8i41sev1)Tw4%Lub7t2T^|YXrrZk?^UTx4>TF0dMVa35{P0Uc=@8&v zI2Y{@u|m-=l7N)Lu~2kW0w>AJ4z8EV5-le#!s{wgL-ALC%xOzcP@Mm+l)_itQ|mF`9909RnG?5d3!`m@~`MLMSLi^EGomYvH{$ ztBB^vzBOx4e5bHJ#X&D$u_%@Gh%RS@>9ZPB{ysrzsLqbKcs5jXPRPXrdBIwwvMX`6 z!UX+*<{aPG*Q&5qOe-uNCE@-g$ETPs6z4=NWpBD)x;u{Emj(K5xj>SB8y0tKAW1I& zE906jC*%hHAC>u6E&1;%<7!YaGz|>|WQFtpaWn3JJ}3Q8D)aA3(y0mQp|XhjY0oq& zO-64B9S{d(2q~8U`G-oemxvGsDVv5&uux-<#FOM_VmikiSeR(#Liu1*lj@%awW=ni zs->_55nJ6h0c-X4<;yoCbZhoTjn~{KGvf59`02->*0a{9&m;b$9B(|HyWHU%Acv50 zS0h@xDe<3n@kZS!&EK#EIo!6eoDW7b=iiSolxFECILnQb0Wu#JeD@u@6F-?Y{8ZGZ zkrhF5z5wqc?_3xDvK>EIcn-xtc}oul_tXB&>*oE#_dRvsIWj|1{5|_mBhfqvkO>k zM7R`><`=i;)4&kT<$?TOJpH0L+;9+-@(zH5zOD(wH^pcR`vw-34Y+=6yHpsdT(fjp z7LB@wBi30)gmf5msXk^9HY}jUS>3rp1$j``_3PuhO!ki9KW0~_hH1^1BhF%~O$<_S zMTE45dBB32@A%A{MZ!I1>&xz|?~}1F9Y${%L4TCyXFFzi`yg3JeBlXNyMM&$be$!L zWfbNv^>v;pQz{9FpVF(X^Vlgw}~M)$81N@VLL-CAjf*l6n3Qr#8}At z3+K@f*nRynSC3`1coh~LqsJ!CaOedlwkU?O+HmZFX4+GwIA`$XUCZ-3{^{3e5ti{WnZi2lEc@5l=4cC* zWC;@c<`)rOT!$SY*G$Nk4pMi#ohC~}PANAx;HoFUk z$K+7r#a1?|O+%d;j2$n6{WICyt-}nD*%GFN0JTs~77NjP7DSTR8&VY5CkH-zhMm{H zvkdq{`TG?Oe>S8`{Pp_rS>MA$PHUGcpT-LmGU(dqQ;WE?uFY}J9CKn|E#@5g=w^0A znXjT^x(%pqc zB2ro2#fKe{N>RjB(DaP5aD*AL1w`|hWrA#&q*szwI&%>PJZ98>IS_w?W`TEQ#L1MsyZY(8@nw-d)YH>!(%wy z>7fDQ8^WA+>$i#i9VE64!g%E&?r*D7Z-F7NQ0-~It_8xjhU!f*%u7}eT3b?NShZYV zT~XE9Sl{?(aZPYw6lcDL$4WzN3hE}NT}OK^AcKIeHDF)uv)7LrkwTWY^iP>>P)TQn zv9WSVXy~NuL(bHvgGEVC^Mpp$CH1(R6MHbxD6nUKVj07<+{HqB@q2FzOKS{P*5j-C zY@;?$j%!B&x%+;{Xrg!5(?t$6J6q%U<3W1&51dRaEw1l1A~X+fc3pmLQDZ%I5q(VE z)XHfj*u;QyMXeDOJEtxY-rKpv;V!6I>7OlhJBU!3}UuQ;SGFW`7k9XQ-rNd7g1HQn5U` zEQx`ZefJ^A@QP@UM@VBy;udnz^pptGlMZgH*PpHzf9_p~xmncdrt3FT1{3iSrqw;| zg@eQxJ})Q2*LDZMCD*l?92nJBJ=d%w=0T-Y@_?;-4WPlzq;jBZ&C0D8C)}zsAnC2i z{b?;k(NPR~6|24&e-|4=cS`A8am1#CvnKm~69ws{RCo@xjtpa= z+$?DFG$$Q3jd#WdY~6joDxsbYXP$ZK(iFhv82m?BG{W#+kV zF@CSj)I`|B5CbzO`*;p@%!83LIAh6+W0K9ci{Vt_=#p@Xk@xc=!&n-)10^ zbj1&F9`8wgnm7&1EeVvMKP^Zt(Hgfi9h(dEm0k>D|BL0j4sy4exRn-toJOI z$CJ%6@%;Zn5C=~J+`%vXZuS9-+8jO*sG~trG~{P{gdaRE*sYr} zPB0qaDJ$NjEY-DHlS?ZBmHB-wAxGL+x7zfF1{E!>&u;Fnu(EBXrZADiwI)SD94UB$ z02Sg=9*9t_bma_ZlzNW3a|o9*j$T1=Tgxd3xt(8U5E13pQYY-H1*LdRSpBrSw2iy8 zXamre6IeoO&DhoT2N%`v z63!cwwtAAW+*jqEMi1d+Nk}CcQw>0cFi31OX`5Tt>r~tW3okJw=Mu2fO=neqXQ?X! z2rCKeLgHco!KNpUt>IYtSZ)>r#Vmoya(DGe=n?bz*rspqXpop*?<5{jKTsrcJIG zEDxp{2vcf=EV4qDT1+UXHkRUy_l424x}jbm0)4Q9X6+7a4bO{l(EeM!?h7LKR8Xpn zQB5|Y{NkOM=7Ula?LLy(a%iSE^a1mud5-c)8;JVc7pZm(xpqvs<*FzafX&gr z?GT4{(3e(I)={umVXbj7*4J|RRPv5lW5GJR zigup#s#{~bSL@9WCMgE`}2#en=+PP(GC~(i6Z65FU7Au=EL@;_iFniFm>2Kk;A@WRsUNs3}Rj zs;^z1pM27E%+CS%;6G&r{Z3KDSqGq80p5-@GFS=8bDQsm*7uCG&-6AizQ!}(_)Ih( z-eM#5EQ{+eEFn3`Z@+Y#-r;ztFUU}Zt(-UjZmnHV!)G{itoGs z=_3BMSNN|k;-6{jY$PN(`?vGUAtVrx(EoZ?VP)##;z=*{&+|X$>q{$7MWW0WmKLip#p=w&{RedB25&nu72nJEy-Vun-3nfD z%c`UMpZCv{?`%L)b-iDz<2xaMh;uR9r- zpn$z9RUtjpr+0^Wnzt^NtMEF?a#O>#J~sB?q_sm<)=iXygzs*hL)Og zk)@+Aj^??^;Fyjpr$vwW!67|WS4$GzH?p0Y$7R9Un6V6q0YjZvJ{UArJU(yuuQ5n` zHUUqWDB-fNcX&m=Qm~0~{YSz+z(itSB?~u2o{#n<6z1oP3;3*+K4DrHj6EoVN`j>qeDA|j|fa_ECxdKb8e zbLr^TijEwLlCR%+G%Y%6uIq!#FrBUDH7C)xU}f@W3p3B3orkD$m5@}eeP3k(9KOjJ z=9Ep7O0*C<jOLRPwDreVwNpL2l3+zgsPS}yW()`-*&!n-&z-UxpvIezX zDjs7vHdDGOCydr1%7(9@)E(`ZUDTd(SSpoQM7OpoL1FxPu*LcXI(c<>axNk$w3oB^ z!Y)0*Q=5O@IAyw~hUVh(wPdhm@az_~O;7NV&RXkRESE zs?P={vSgC}6;hgxeTPc%|CRD~zYxfWwbob&I;7%iO0~!$H63SHKFrnv0tnD zmMWG1TbVL+{*IcI{3nU!I|=xEz)hlV=%lJ7^F30vGd2Dvp5@=?9#zWUjzEInt{SwJ zG}5g}(u3{!DOGiw_NDY9i5YIlPDp{{u>&QexVe3 zT}L)n?NBwY>vk+u|Bg?vlq>3NaO$Y9)K z$gsfx5nS=kU@(HF{=$3-BE$)gk8;FCUJujB{?Vh}j2Yt0onTgyimBubAjbfU^dmsa zXsnBq?$9a9jyF2KJ!MMmirJe?(z?Nh5sqzH@9i@{9Z7Xo-L!xh)1V2d+hsYOgQt=w ztqFfnsOCIiP?>6@;0_Gqk@uJ?S2+Fc9Ls(xgjOn@tNxKHB=;dJU`&!(Hx{?a`J>F? zsFnJZtHf{{=YfrWLF$;+X|g=H&@4)WqC9?{89oWcefiLnH-}0Wu^eF`v%Iiyn>8eS z-6eAfKD+woq#9J_9ICr-H1^iwQ+*c(fEV?hzZ-!@SsJsNgT)k`e6OQJKn z{!ZB4%ZcZa_97lI=aBtX{#cm{XC<+~GZl+{38^AdTR;99Mx;> z6M-_CL{j2$8@u~FehV^n(w^5VXELzmld&E^paO69_IT%i2_)eYYgI%?TgHmK(992x z-B*N{*(nsx)3*QaVvBt#Vot`Cy$*NCZI{w^JmE)0=D;(Qo0-5Glvu9E2ztT(=L7w( z_5I%~pR$BD-r>9I*T0tuCjXa}|Igyu#`N0*<2$g-!{uN80spy8_^0|w{^iAg>=N|V zzpX>RWRWnC&>#p@_vIIru#p@n7gqy`(!dJsN=Z>rwNK5Md#qMA>e~^IrS)HNz2xxU z`s$A;Rv&TRHm_!{X2>tHC)r5yc^k(1r`B8l`Gmijn;*s=b@Tmnz!Ss4ZHW^=!k0cZ z`jNY>%qc^{5H6k2mPOE;gi}qz5RFOMvKm3dCruDajZ=@~jI)f~R8IbKcelOc;<43NuTbX`_s{SOqmhecQPLgHx%;$;%2Zw!p z%_K}?oeClxYNllb5V~9^?bINXOEtsx^3-&fZG}r`OveaW;#!6o>o7$OuDkRM40ewt zC|AOkfmrczkMO7@ud9Y~HONI?MdcXeEr9E3}%h*7V zD`T5ux;%0v4`Vpa@Qj(pL~6Aa=9p_8cFgusVjFX1Fu|I5Ws&*vUe{xtSqA4B1vu_w z%6arn+rIVX(^wXZgP@uY$G1ioshK#?5}`2HqFT8LY1dL$6Pn3t7ESCVc*z}#2(rge z9`{m$W*_frC|rZuQ9L6>;(&rmYp7hKrl7)%^B(0nt*badH=|Z=TgTX=RPJiSWdrQ_ z$D!Q-Mm8#ENzS^NNDW>MzP${7wO7<#nrg7#^M7a!UruFa?{qh=S)FA~++orybJX3y zgrE5QE&Cgr%lQRxo0srmnB6xU9$|v!ZXd_B&$39cfgrOZw#-y?Qg>g?eQ8)LJ3As} zo+_7p*n(HqYCCVbIlNn#g8{l&HO;GgovUl33)WeqF?v34or3(o?J)WZf_$Q4Zt-LH zOL6_6ldlS4*v3vcDijk{3WFNs+wrD*TkWEB0wvsAgyl)+=m{`UPBZTg9G~@alW@u{xMmcvxEC_V z_Rh&8NXGJr-5ch41r9w5@I-T%g>^;fX-S$Y^-9730h8*AFi5!Y1lrv$VtRO`I;ecS zqr77JMy%jvuw=}|B5VlX;c>Lu5{iY4=r$Y(JMBM(=JBU0WDyQp{4ztx8q&l%l39&#Y^G*Gomb{n5Lqes&79=vd5X;CnY zdcqg+fq7Ey_WrdAZMN0kUl7jA73AvB-pW^?1+b1v^6cs(%-3X++%l19@Vr; z?qz}*?0=`k#UH9!cF`es5l*tZc1_B@_#{@g>mJ#U-mTeD?bDrX5Kd;#tsjuV`-`J( zRo(I0Q5|IJ_|))pQ&0NN;fx%;y#S|wbuVdB z#-gqnZe1YX=hPD;lVF~eRWFycQw5NnmCwndQ^|LgD=jirfbsHGR%pFAM#y9lO@ipk zE-td9v+1fcueLA z2pXJQNS2SRpsQ*UT?^+rq`!+n*-*LGHnh8lct83%$QFY_t8u)2SbGie2HZ;hr9Yli z<|oz%t?j0SIK=pwlHRl~Doo?TYsQby$J!dVRC=Vp`t8fj8*7_-?Hq`=S)M>D>xhrE z&>@dr!jsfKLO9nqXav`6tS_s?f_Lt`exbg=`iT$i-y7Lu=nLRQ+#QM9MgN#q2VpI# zz6-+IcH({8hYQ#cw`DtcFk&Hvsv6h!n%2L|HuEh^xGISweQ~tZ(ta|kmcZ`VtVaBRZ6Kh zd@8sF{46@%y@7~HD*|OVd&KNV=({g^vqt3j3+rwN3K6bF*t?tt`AuI?R`Pug@Z0_&Q`7WCH+SqKhR7)MUc~}I575n+#26kvBrs-P6Wisbf z(hYB87MT<|6$iEOwl1y5NEXCJzj z9!Wprreg#}Q4?t)DCC1lNB0qB1YxIH1{6mU43xzyYG~`=LI(DsI7y9$@=4hSDZP-! zi;_2ILJ)k487>jAm#Pvl&@+Lpq7mW-BNY$Uz$1{#gNoh?5m5XDokvJ28IsXEIaeA+ zsKqohbRaJ5V^M{j>r0iG9a!L{T4At}ZG;QC*qCLqw%a01F6Vw1M&*zF-9g`NfJ01N zGelkdJV>#_{mF)lS>|reV33lRPNW~WR>M#t1qGIuyyFHQwrTZO>JTImXt0U;7p85p zxud;KR-IzY=NF@&DXve?=jJ|xHW*(@h*0m0mVG3>B@>8Jyj(3N5d6>$Jzfchym~pA+Toy~zKQOb zSY=Z_KR2T!UJ$UU@D;DQ+#s5#_4Y;I^OUk5+YozBP=AV&a#OY`D&!wiM+SsisW#4~ zYH~+2%hzxj^I~7dW#B?h(O_yW@Y-xWS%{91xG| zxJDcxP6|YlL3Spj^FeN=Qm?r~SiIE~`ks+Q{=AF4x1*CQ`Hs$#&wnNot$yyI>%zc$ zXg9Na4?j^g59jtZP@aM0Xar{%7-fo*R1{}#BGA;i{dr)SJp`T5obuG%mEr`gD-P_K zFvvsUl!gr*mgfX*(97gA&Y5xCmaKPayh5=omS9E&Vb9R?PS?bay@(9P0=B#c^#KT8 z&26QF4LZ`m{O))wT3|m$eJv2QpUH*wQqJfenRY+nwd6COkD_D*Xdi_b;N+Jz3vep`IIpH`a9mL z(^!BX-O@Tmr@n+ev3Hu>S?YsV|3#NV-=#zp__kk}pXoKt$%j^_&9&L4(KR05^tM#g zA9cNVv5zH%ZsqRfMSPfth}VK%W{=`(I@X2Ug^ISe0fm}y!;We@(+oeNP!*zK_B6As znU^HM=R`g10~txhXa1*EY4gHbo`TOi?gM*qzGz%nmTt3lu6^InyM=bq(<_hn0*Q}# zg)gPArA2?3hn!b>WxzlImVYhT-0J2z{K=`Wux}op$e%b52x1UnadA(!Q>w(d(?f%K zH+zf)pF%#i1;)H(2p7vrO5J%FTJiBgx@{BEf}4{r+dwK>dG%DxjOtbx+h2z<@wT~3 z80$S1u(bLptgLI!nI5-N*j#h;Og)akiR8KgnL8d5rSvQf0a}VQHKh1qgk(eFXM)O+ z>@yfQVm?za0SY!2q}MpMaS{f#Yy&e=zt^&KHx+Q}ezT!y7yW63m0teN)zj~sAS9-V zzzXRrc)~80)Xd=>9nCUb4^b<={4=0xOkc@}zO({uogm)^W7{&}-oJ|m_M^A6%dB`E zcN6Gb5*rZB(xB+TVB5eZwmb%FTgf&x!v~0FZ-89yE4+Ye#$FY%O{iAIST%7iz-j2M zg{e~dn4IC!N(QJ}vts`AznZ5Pu0VB)P;zDtE3X7*Q?WX9&IM%W%Q7~Ut+E77@5*C$ zT-VVU0|9GG17vn#DFIA2!lpE-;$S1W!()Gr-c z*U4UtDPyG#*mkq;nUOxP?Ufr-y-hn=HMuLiXy}--s?{EYmeaj6Q@#(@qT$?c~um-Gup9;p@eJMtf5C zN7LpDZv@+Pp=h_yrJ!jl*iAh(Nss;zY;NlFwmKkX`DI(~!!nY1RcbqVcp_Cpji2ZZ z`!^Gp21iqIYuQ?z1OvJ^h}ug&X&TjcJ|t_~ehtwCD?wr<j_& zEO6BMd_8SYr5O10dXA0p$V|CNyGODOm&xOSyGb*{9lZs8> zsgnQ=J$=aB(X4xxi`=L=#^iHFwWm-fwupC#$}Q1?PcqJ&K(PjB5+4 zvi=8#6IVlVs0LU)@J8(c@tN$Agtdx8&+P!TB-*_SS!!{#@y8L7^Q}ZTh-EL7#37!& zEk(<){jHzX<1D3R(SX9*euQq-sgE_8ny4gP@Q zd6gI-^f;RgvoMxlbVsk5CW1PFo@}IH?7UlkRZ!@1X9#R8A<(0b`ixKVc7F#I^AHMG z?On1ZfD>q{c5Hcx=%deJOgg=D0N)PY&1TUtU+Fy5z`LX4>V=&)8Zr*dGu)+X#}v6*o2^+`Z{ z<`U0dz^)}^St>uMZ4JoT3ty8zNMb-F^v9kUMEhSqb1vTXNJ@RD@;hsV6~r9p!$B5i z@4}l*oj^AOoOPo!qrVI1f|}}MAW=GCf4U}C^!0Yp>mm{(*rKK#t)&}Hhojl#7*C5+ z8xIa`LqGla>#Ap&7tQ_q>41l0V%X}7%XRmM&m6=nT(c>{(K#Ms3@j-3dswP>IU*-} z(h=mI`i-;z`hQo014o?VKg?#+2IUN6RO`~g5`FBedZc#U9AcD{!SvptG`sk-JoM%A zi<)Uy4nyOrt`uHuyE;4VeK`SL{LNiF!4g1yFp`Ee_kRdcAZq>sO9r#Q5OZVuAlmz`#kk%Gk0y}N*MBLHPB+8j zmVFQj-Ua0~`kq9SOs1iHeQ;Dq4_Fwv!&QD#4u8jA1>BnE$3zA2NCd&z66NDz`D_Yh?J2j!#Xu|qqEcTWt7VVAOYewINi>okQki<`qNvcxK z6@DeBW@e&~`4R7J2!eEmBzma!chtHel^;j@<|3_{b7}8-^7)|>)7XSoAx}YC8arHF zU)+YKW|D6_-juX*G7@wY-Bc(hRAd_~NhdpHkm&bu^)2B~B&#i$uM89|DMR$6LzNGV zKT5A5*aM3!t6^w$HjQo5-=-pX?n%7cdIz@qHdahn3nx^%4ak+PZQDLla z053mF=9{kpRmwN06+*@~n-xQ%rK624?jw6!0?xfJ_BZX(y?+LX`46y*FE0s9Yu@y; zv|V|!r05lI#pQ3!T}cezp}rC`euP7OsNY<%dTpF{3;MI=+OJM`lT5r(e|C!6XMVUJ z+k43MwY#5OS$qZP%N7E}SpapJDyHz3neCKEgkx1I@QOJsUOqFfJs@}T4gHkx7k`t4 zwJiudEg3HIm{ukCC`G>du={^VeHDLgQ$lR@O9`%k1w&1TClj|>3ivbT2)FiVfJ6!Z za^zr;%X5~EDne7iF1Y`Mc4X*`^`qMh6&(U+!+aAAA)70`k2)uF_djWYxIm)WQd>(=hr3Be+&h2 zEL9GmpL2Od+SCQT(UomK`6hf_U3fpc)9vzV=2Oce?FzwvX$QCdaP#Md%`=Y6rE_u| z@JdTZ9XQQ@)7--uvB&V8N2vvqS$3v75=>!rAw4?}1cvF!m`!p|l5%yU-;TAU*4sa` z!69v)0>z(ML0VJMa4Fg^&#lyiWN&euksFp3iX!blVfcEvJLDm7YhmOU5Mu`%{XMWaV+t_m%-sSaGfyOzHy zE~}^j+*>MU&`o11W@zFTX{P*e9&@q*Rg;)j*)DT!HEoTZKbNDT8~2Se!_VhB=g(&; zIa`rlbgYskiC?f~xUla9bVp+>h(;d6ediC%VYNl_NxILgwGHoEa!F{56zTK@NsMeB zIy6rn%6Xx=4>m-A-yzJ&$ik3Z5SPzkj!|6uY(EDxOt(?~0`N?r^}xFIq=C*(^9uDJ zAQsJYm6gsBe+04AWFFQCx%UccVPK2-$HRClX>tQ8U5a;j0pDR}lNL-ve+D3BfO=;7q#f)w-DtoGQylT`IISG=M}GOhi)7p5V9`x;&ZTGep6fXE7&GpL=Gh;v zgI|8{7*u2%{Y1qp9BpRWg7PCvZI=ER<`p&Sb(vu^6*0z7`a+_~`{c=mZ|;IrqEMY6 zUJsW3Jr#3KW(c=cH)!S#QrT`fZASvQpN@Ic`WIu?urUpIT*L`#jsZ9!$7GA~hyf>; z{DNn~6e^c|$l_h0@vkJF@`=lRL{mmz0b4hZa+BDq*j@oL6Wd^z3{u1A{03uJRZ~~? zMuGG4NS!bkZu-cCEw9#!#Bwi-Co_?10bM!xMxx+mNtd28gQV4QCy`on_3$)dM}VAY zCD8&_YuF#qQq~9s^Qc~D9IGg*1q^0I(+TnDKR;B5uuN8A^XSML(!<5Iyine7+h6V_Yu5Fh4b zP*!5{%)4bV{}OqxU2zUxN6^4P8jGLsYVI#VZT`$g0E@uoij>W$WB`JnSVkLwg2^#5 z%0N|y9>|**6pgbQa8Bcb0GP(+x0q`o?)ZZA!z0I`a*jmU07(8gQB5~dv)ACFho+qK zO1!Cg<_K*c=fC5v()&5f@my+p@PYLXBVEHZ-s}gIag`~^6vHTqmm#n<>l|?ku(J=v zVu2luL=E>&x5BE5BkZ&(bSGz_7fn|-K^L{*XsnuZmjT62;}+hvkH8-Be-Y>c-Hnxb zMd+Be>Z;5LqubGqAK1OV6Shgrk_mJ?Bj_fmc*?j_#Y&&uk1>&SjGyY5R#WJ1N(%44 zfaV1hwb3Bx4(Essv=}*S#ym3UdWLnC*4|2QxTq#oU*-oG9WL9YFdemt!wdKY0Yboc zMmURqsm&8S$Ab|b%@_oCD~5R^BOq84CTN})yV@wEK_BbT6;dmpt^eA8N2LR-#XVgP z-T}cEfyb}q>qx~C+K0zY2iH!CLU2SM0mLAK%3eBvcKlF`PK1J!aY#~~JrFk!iImzr z8R>g52F^;q96bULkhk`VI$*3YWS*{_?pgE%)xvFs_PoXzla~cw#LIvpv?KiSXB`^2 zYIKV4PKFt{=?}@AD;;4Xp{sbnq~<%7m+mM~2_>h668J*6*vTF9Y4nqGvmmiOSSRGp z@93_H+150icyTU5D12KJi^E-|YP%58R)2YXCd*M)R7y1>CsXP9Htv(TC{*JW)@+z} zGl$y8z;ie0QUwHy#|41sgs^DFOmLPDqEdcdSaE;X@l+1R5WYD?CH+5u}ev#BKe@Jel!QoWcZ*;P=rcJ6B{zPh3InV~~; z6-d=>WX~l|>0*4!t-nkffe`Y=QkNlEfH|X97Gb$52`3`pmk>Zr2`hw`oV~!k++Fl2 zglV363U|0I$dt2lseA#?Ar`ErOFe7|v94d{P;6KyeCkGiW58V$xTRx}j)+BX-p9Z+ zJ`S#pG(M$VPlge8OHysv>_A@w5_H?NxZA+^gm%<4F^<3E{L@2h5#7QR-=mGdVy|0VR# z=+wlY5tg?BYu$=XO*LEEWkzP(`f2knRlW5Hc3lYcEJ*chi3Nx*fxENGAa%_T2uQr} zmj@N9z%H4`FX8~;7!a@(B#9j+@y~&tA;x}?aCC4b1O4QX8aA=YaA~-A(L82{%ChReg ze;zGjm`~%1GLzWqr^&2ybn*68z3}owlJbAq_ax%;KsLRR*9x;;zj2sT&;rc$$J$f?q3^p!G zAR+Ocj;fuz*KP3gk0q!YcI7>KQxvt%1hvjD9h*PAhmbVv%mO;0#PouC^yKLooQG3H z*v4~L|2ne7ubV56`f|gzPUP<0&9{Emm#KZ>Z+p`REsg_M3(s>GgV1e5(~{<=)IJ8Z z^Ty6QXE*fUVemG$Q9TG)L^~7Qci6n--_OfYRnvJ2w$Y276wxtF-}t!&gQ;g`KSvrl zZy!DO(6_~G5I1O(-V+Uo?7}17#t^aki6p->-o7(tfTEpV>*TR$0h}zV#)`yv*s0My z7lEvX>P0adq*1xVrIKB+{B!7qrt{_6MDSk;ZCP<^tjo*8p=jM`}b(5bT+4$w? zk6`CjEnqdwm&w5X*)NrnR2E)jP<{0O2dVm3Y~a5G1x!cQY=+-3A=kIV#Q&aow);2c zS?+&ko|A09(ZTO9!8J6vF~Wmca214#l-t_8(r|<*HCY*`41w*3O;=m^m<=;~=;ywH z8WSQ6-yQI4VN7#)6ck|*z4eL9^>zEhZfEzcF8>!O12}l@9m9e2SspG2;#kJ@b0Ng$ z+m>i~a4k5&{^2GR}o;Em};(Mf{tqqQ`Z14lYd|yFNJnH3dLiDEer` zq>lb73g_*n0*%Z|?-04T?)`Yd)1pnBj;Bu|f9x4k_8v(h=O=t4oTQ1f z1t)F~*Q+ZSY16qk1d>pX8J$}s2ssXqjL8vQ_C^QdDx>Kq)ge0@<0ZSA^B%{K|0{mU zyo&KS+vTT1pX*j?xgleXckK;tZRSWii8G{n%c;}vdS4A+WVdMnmH!GzfZu$y9BJ!8X%!=WqPff-WTQF zUpb#ZJzzA823=s#tQsf`Iq_S4QB92nE86b#2Kk#EeQ;8?2i0Rj`u)cswx5Writ~SW9VnTO-W&Yp;YH)_lsQ>~fj=b=EISZPu8YX9u*R z39E>dqEb%q(4&b6n*`8p(4U=5*0N7J9&LA|K3{8iV=BG9S^n)KTm|o^C3073D^zGm zweLK<{dhR3wO?Pvy829t-3JAdRI=+ z9Pe{AzH;NxXfh=hNS+wFH{8(A9{>roSLyW{&59o&_cu>L%U!F}(h#UqiKI#o{$vjOhMX%{_V>hKI!&7p zO*-#(?0IFb10q2BrIg8bCp~5mUz6O8jHqN$Z(AlC8*Z{DGP-aWSu&-b0%sM*3zrvt z2*{-TVYsBy4T!h`2+}E#6b%*#_Q^O@B#DFyT*e)2V_Kp_?{AeULd41oduv~{(z+t# zT9TQBltX>w(-K{dqtr|ONYk<|0XP!eB36ZZgW%tzm~AxB2Y60EX^p{s8B-id8iJVc z7|wMqS^!dQb?i$-PSH-roJ>THs=KR2t#Eio(^#>GF71}onQV30xC=Yly}saZ)yy5Q z{@#WjZ3;Fd_vtge+Ow8L$4Z1%QGRU0p~CSZFtMG7TkRGLH41kRGwwEXrZgUO7_0>t zOC1&ANjqN!JEVsVHH!nA$_1N4aObUomrX|M5*FbOnAH2~lwPqgga*rBz>k+W(#aDj ze@{`ylX3nJW$zdyTe~fd_G;VqYIm=;ZQHhO+qP|UwQbwBZTstY?-OzMx%WnV=l-aQ zs;GZ6Ydmv~962)m$nP$&LAu{y25HQ0Vd{)1068stMPS~EU@E5#ZILW^!!0?2H3CyG zK%9e}EMptJfbJ%^Uv=a@Jo7RoMgtUQ(}*WfFsw_|T)zHg?estXXc9ockO1J|-~j)e zQ@BcfpG0T?0M9=;)BpP$?|+?B|Btijzcl;m|MDu}J~lGmGQ7jrUmLc^l5s;Vl#v9U3u zF=De)=+x)Rqn;=YnBtTBZmaWo)8k6*+u;f)+x2}%kAPhxLr!%4L<$f0`!+5j`urg| zvZHDq0?+M)m0eQX7|%iz9|!HIPD33x%LxyrWMBJfZ!+j^khChFfY~+GF=V`8MP`67 zqBMTolQUdod|yUzw06iOQK~ zkwasOg{R_RW1nbN%+~b0p|BT%%rS;`Ho0adaaO(2OD2K!jzh!?C-?Sn6sJT9p%7;V zO>7)V7R`ekq0YfR8)tLu@$CnJb`O-7DHS4pMCV53{h)T2V3XPHtfuwjEuuC>jL{uB zm1KtmYnP(4MS^lWC-wcEJxxvQ(V@0BIeqnqa^X$2gM04wS~kM?#Z^bK>-F`M1%q ze|nKE!${fBCaoD3%oaoTw6tzj>$JIr^>H4lZll#CiSt}3jf4%|I!Ke_m6)hhGR`O8 zvK5KKg=%GG2=oT!AB`!tP#_kkMAgF{ z<6@a;U{M89!M?&Bm`$15=_>wG%^@%-a6v?z-gW7iJjeA)rp<~@RNB;+O{r%+Tab0C zc^R*(Nv#Y4ZPA69iEkN=CZ>r#IxaU?sP!eUCWJSSk|EXjSDFW-fEKMHh;9xz8XuO$ zma9)4q@%%U5O5}T7M55p81o z?S(l#TI}5_eu7#^b%N0E{za*7261T8(5piDB)d;+P%oQubxk9W(9@o&(Qn^9CCR&wn4g=|ast3+&oWukdW9sJ8S;}WwKl)PaMXJzh*n@Iw| z@QK4t-Q=DBygi?Ma#;=`m=K1e*wkT$mo^+PZ&F zm7)#e6aq9Q&Kaz3s9!9FxbiX1RffLpRQ^zNSF`z!U0B$`(_5)%azc?JF@<=q%#{pR z%W|ksPR`lNTX*DG!5_FksqM%p%grz(X>9+R5Wed3mygnS?KkN}yC8e1O+ea&X)|&M zRRpR0=&Le)Vkm32hH`5;#W2#EyuN+j3=WzIStKMqje=D(G+%m;@2i8yPX5@pIwue0%} zUF^~%-=u^)ebx2q?7qW%QTRw5gF(Biqk191dB?h7b%fx}z3K%#+t*CJLvKxk#oh$2 zM?KQjOOy{1pC$P*1!N;HCLM`gfqtrTGeMhu7RqaR4bel@ts3?j(UC*=m$}NSail&a z=2j7@M!{ivhIUuqvp{oCh@_5%+&Ifl*so$ospA+U|BbD)f6Cn0G6Uh*|7*Z%O`JrR zMTE>40E@Hwj1XX*rvc-gsLk|3=dF2cgZV+_4ny88P3gF>V`G5m6VhFAj|A!ylOxE_ z^z#0J3@ZrAGuiI+<=4w|fQRt`jbo7h4(o5|$B=Fu=`+DI`>Gb;GsZLj$`+7wY#BHFtDB&~)MIa~)DbJ8f#Q^pdo*)z412Kp&i_AVLljj!!~ z`%R++>j3n3C7Z6jOZ~e6$ED@!|yiF5W;Oz|f!<%&hrmh*vC(nIH6{ zP1)R(u#sK>t0?LU=#3fmd<|Uy>c)CuL;!PUW@yd=6bBJyk~`~Uw$WaoVrFO;-gfNR zs<`bNLiruvu|4tO>6D&EyU7R^LSSi?^*&pl4)kDhxC+rx8nFY=*Rf>G6~npF3Z{0% zCaAEd7k0M!$ErYv6k+YMpXt_4x0i7#pCs88%K`GL8oXc@w{6}h#5m~@?fC*r^wN>A zzMkV@ym{0H>w&x_<{BSZ^no=6d->FUHI>_*f>g4?fW9IT*#IaT)QAc=gYjP*R{3&` zgb4_Dd85VAshLk~Zqk!*!dltBGymm+T9`Km7_qf^spGKp`8Vw4H zj1Wj&uC#2`lT4;T+5oHUR^ah=H}^()d5xnBY-~xf)%kndV|ogQzA79`sjqW%7cxw= z+K@-KU;8a9n?Q}(F5=e7EY{(d93|KQPhshE&-7B!*;}`hlKUJwRyjRN$8}ek@fMCb zw3D5`CnCr&ZsbrnvGc3Eriw11j&dF%%a1r__9}5MBDr*!LsQWVpf>>Gh28 zd5%UdYYQSrY0$VnX=bNRNQFHWIE@ycGnQz;4s(c}X~G0KXKC!90|UFl3Mel&*1|I- z45l`z_=zov6~bOfdIVR_zz(xZQO;v4$m#4Pp5`R-C|QvQs7CV_GX?uLh9pf#DY7fQL;>V1kZx*>9mY)Xr{jp~9P@)4bT5z~V zzH~jUh{}0SKgROHpsD(aNX`rCGpBZ`kHKITI^kwY9|<=7ZrC zGb*L@+CFgdlzjN^v^}LuIn%%ofl@QjVua^URl3=6!^+CL@?xCXCk3%`*TaMEFzE&K zli!pioa!K_co=B7p|i>|)pdg6+cFx?v!0|jO|$48=nV77+xsqXQBq=WDm|ZgMr%6S zoP+bE7R#aEpgtF8%3p~EDt4Ehs+MNX4s5x|CFl_~V&*!!gf=Of)yW;K(u4A=EoGL^ zdKDx%yQE_eM>Vst0iRF`qYOvMpC&jlEdB=NXSqDhZrPtSDgkv*F%--;iH?1d)1-Yd z!QUX!gLqTilePtd@cqq88~I`_tNxx;EUA_X@sbgvBhw4X7C|z@Y7IRND4bVql_KeT zM{Pr~>DE-Kc>cNP9eH9~MREIm;W(CHc1K(dc~ol->~euRIM0yM9V?t@iGr9SoGvm| zdlgYbtlV zG(CC#hb?}ATw6DMDR+5^PX@0hhlOGq+|>&II6mRdkmy1@*hF8i3kk-_-_@;L(V3GV zEQflE!@Jg|;Nm4@D)&y^_@jXf6k_y(wWzM4Rmz+wr^+li(=+5m8{%0DWM4XC#?mZjxZfO8Ln5N=0#j}) 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�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