diff --git a/adobe-esign-connector-test/cms/Files/samplePdf.pdf b/adobe-esign-connector-test/cms/Files/samplePdf.pdf new file mode 100644 index 0000000..c0e31a0 Binary files /dev/null and b/adobe-esign-connector-test/cms/Files/samplePdf.pdf differ diff --git a/adobe-esign-connector-test/pom.xml b/adobe-esign-connector-test/pom.xml index cfed024..d11dc75 100644 --- a/adobe-esign-connector-test/pom.xml +++ b/adobe-esign-connector-test/pom.xml @@ -4,15 +4,21 @@ 4.0.0 com.axonivy.connector.adobe.esign adobe-esign-connector-test - 1.0.0-SNAPSHOT + 10.0.11-SNAPSHOT iar com.axonivy.connector.adobe.esign - adobe-esign-connector-demo + adobe-esign-connector 10.0.11-SNAPSHOT iar + + com.axonivy.ivy.test + unit-tester + 10.0.0 + test + src_test diff --git a/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/AdobeSignAgreementsServiceMock.java b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/AdobeSignAgreementsServiceMock.java new file mode 100644 index 0000000..9dccee9 --- /dev/null +++ b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/AdobeSignAgreementsServiceMock.java @@ -0,0 +1,68 @@ +package com.axonivy.connector.adobe.esign.connector.test; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import javax.annotation.security.PermitAll; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.commons.io.IOUtils; + +import ch.ivyteam.api.API; +import io.swagger.v3.oas.annotations.Hidden; + +@Hidden +@PermitAll +@Path("adobeSignMock") +public class AdobeSignAgreementsServiceMock { + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("agreements") + public Response createAgreement(String payload) { + API.checkParameterNotNull(payload, "payload"); + return Response.status(201).entity(load("json/createAgreement.json")).build(); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("agreements/{agreementId}/documents") + public Response getDocuments(@PathParam(value = "agreementId") String agreementId) { + API.checkParameterNotNull(agreementId, "agreementId"); + return Response.status(201).entity(load("json/getDocuments.json")).build(); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("agreements/{agreementId}/documents/{documentId}") + public Response downloadDocument(@PathParam(value = "agreementId") String agreementId, + @PathParam(value = "documentId") String documentId) throws IOException { + API.checkParameterNotNull(agreementId, "agreementId"); + return Response.status(200).entity(TestService.getSamplePdf()).build(); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("agreements/{agreementId}/signingUrls") + public Response getSigningUrls(@PathParam(value = "agreementId") String agreementId) { + API.checkParameterNotNull(agreementId, "agreementId"); + return Response.status(201).entity(load("json/getSigningUrls.json")).build(); + } + + private static String load(String path) { + try (InputStream is = AdobeSignAgreementsServiceMock.class.getResourceAsStream(path)) { + return IOUtils.toString(is, StandardCharsets.UTF_8); + } catch (IOException ex) { + throw new RuntimeException("Failed to read resource: " + path); + } + } +} diff --git a/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/AdobeSignTransientDocumentsServiceMock.java b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/AdobeSignTransientDocumentsServiceMock.java new file mode 100644 index 0000000..9fcb013 --- /dev/null +++ b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/AdobeSignTransientDocumentsServiceMock.java @@ -0,0 +1,43 @@ +package com.axonivy.connector.adobe.esign.connector.test; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import javax.annotation.security.PermitAll; +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.commons.io.IOUtils; + +import ch.ivyteam.api.API; +import io.swagger.v3.oas.annotations.Hidden; + +@Hidden +@PermitAll +@Path("adobeSignMock") +public class AdobeSignTransientDocumentsServiceMock { + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @Path("transientDocuments") + public Response transientDocuemts(String payload) { + API.checkParameterNotNull(payload, "payload"); + return Response.status(201) + .entity(load("json/uploadDocument.json")) + .build(); + } + + private static String load(String path) { + try (InputStream is = AdobeSignTransientDocumentsServiceMock.class.getResourceAsStream(path)) { + return IOUtils.toString(is, StandardCharsets.UTF_8); + } catch (IOException ex) { + throw new RuntimeException("Failed to read resource: " + path); + } + } +} diff --git a/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/TestService.java b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/TestService.java new file mode 100644 index 0000000..c5576e4 --- /dev/null +++ b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/TestService.java @@ -0,0 +1,27 @@ +package com.axonivy.connector.adobe.esign.connector.test; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Locale; + +import org.apache.commons.compress.utils.IOUtils; + +import ch.ivyteam.ivy.cm.ContentObjectValue; +import ch.ivyteam.ivy.environment.Ivy; +import ch.ivyteam.ivy.scripting.objects.File; + +public class TestService { + private static final String SAMPLE_PDF_CMS_PATH = "/Files/samplePdf"; + + public static java.io.File getSamplePdf() throws IOException { + ContentObjectValue cov = Ivy.cm().findValue(SAMPLE_PDF_CMS_PATH).resolve(Locale.ENGLISH).orElse(null); + if (cov == null) { + return null; + } + java.io.File sampleFile = new File("/samplePdf.pdf", true).getJavaFile(); + FileOutputStream fos = new FileOutputStream(sampleFile); + IOUtils.copy(cov.read().inputStream(), fos); + return sampleFile; + + } +} diff --git a/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/json/createAgreement.json b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/json/createAgreement.json new file mode 100644 index 0000000..31c3d25 --- /dev/null +++ b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/json/createAgreement.json @@ -0,0 +1,3 @@ +{ + "id": "CBJCHBCAABAAMaMFFCyiTyCblablablablabla" +} diff --git a/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/json/getDocuments.json b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/json/getDocuments.json new file mode 100644 index 0000000..9cecbad --- /dev/null +++ b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/json/getDocuments.json @@ -0,0 +1,17 @@ +{ + "documents": [ + { + "createdDate": [ + 2023, + 9, + 18 + ], + "id": "3AAABLblqZhDM2f8YEhDF1N6Oz-8ZY5jVZVgpRh2b_69hblablablablabla", + "label": null, + "mimeType": "application/pdf", + "name": "sample.pdf", + "numPages": 2 + } + ], + "supportingDocuments": null +} diff --git a/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/json/getSigningUrls.json b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/json/getSigningUrls.json new file mode 100644 index 0000000..3dfc4d3 --- /dev/null +++ b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/json/getSigningUrls.json @@ -0,0 +1,13 @@ +{ + "signingUrlSetInfos": [ + { + "signingUrlSetName": null, + "signingUrls": [ + { + "email": "stma@mailinator.com", + "esignUrl": "https://secure.eu2.adobesign.com/public/apiesign?pid=CBFCIBAA3AAABLblqZhBfFw-mWHXrt1tSN_AfnQQld3Ea9pq61iY57nLp7Duyp5I8XoIyLkT1TcjoW7lshFZXF4zoa5UPIOBRKUPhP8YuenqrWRNjVAXoTO8P7eMr3qIr8-hOhCOIfKUXiB0UOmQ%2A&client_id=CBJCHblablablablabla" + } + ] + } + ] +} diff --git a/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/json/uploadDocument.json b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/json/uploadDocument.json new file mode 100644 index 0000000..a8c68b8 --- /dev/null +++ b/adobe-esign-connector-test/src/com/axonivy/connector/adobe/esign/connector/test/json/uploadDocument.json @@ -0,0 +1,3 @@ +{ + "transientDocumentId": "3AAABLblqZhDVSvtTTuHIxFoTcvrHMhZwdRBEz62SCvv1G8yTaPzxcu8NGkq9IIILoVyC4UHJ8vgl_Mw5CY9lBSMEbMriLGzZyy6KCWpF8Ac2TpP-3Bpfrr8LHyv92ukAsKnyJBDP9XPpSyVOm1CtLUbNtX7M6cfu_LKRSTl-nGfMK6eRalZ6AsxLzDTW8lALl20Md5x5EyOkBLW1ZvvyF2OITH-cPVgBAoh7ejVkDiHwskPq9NIUoTZ4jN3bvq0ma5Dz7gP1F5RGuLlzGDlje3_MIof7-EgdIQt92grPt7fe0-Dfgjblablabla" +} diff --git a/adobe-esign-connector-test/src_test/com/axonivy/connector/adobe/esign/connector/test/TestAdobeSignConnector.java b/adobe-esign-connector-test/src_test/com/axonivy/connector/adobe/esign/connector/test/TestAdobeSignConnector.java new file mode 100644 index 0000000..711d10d --- /dev/null +++ b/adobe-esign-connector-test/src_test/com/axonivy/connector/adobe/esign/connector/test/TestAdobeSignConnector.java @@ -0,0 +1,34 @@ +package com.axonivy.connector.adobe.esign.connector.test; + +import ch.ivyteam.ivy.application.IApplication; +import ch.ivyteam.ivy.bpm.exec.client.IvyProcessTest; +import ch.ivyteam.ivy.environment.AppFixture; +import ch.ivyteam.ivy.rest.client.RestClient; +import ch.ivyteam.ivy.rest.client.RestClient.Builder; +import ch.ivyteam.ivy.rest.client.RestClients; + +@IvyProcessTest +public class TestAdobeSignConnector { + protected static final String TRANSIENT_DOCUMENTS = "TransientDocuments"; + protected static final String AGREEMENTS = "Agreements"; + + protected void prepareRestClient(IApplication app, AppFixture fixture, String clientName) { + fixture.var("adobe-sign-connector.host", "TESTHOST"); + fixture.var("adobe-sign-connector.integrationKey", "TESTUSER"); + RestClient restClient = RestClients.of(app).find(clientName); + // change created client: use test url and a slightly different version of + // the + // DocuWare Auth feature + Builder builder = RestClient.create(restClient.name()).uuid(restClient.uniqueId()) + .uri("http://{ivy.engine.host}:{ivy.engine.http.port}/{ivy.request.application}/api/adobeSignMock") + .description(restClient.description()).properties(restClient.properties()); + + for (String feature : restClient.features()) { + builder.feature(feature); + } + + builder.feature("ch.ivyteam.ivy.rest.client.security.CsrfHeaderFeature"); + restClient = builder.toRestClient(); + RestClients.of(app).set(restClient); + } +} diff --git a/adobe-esign-connector-test/src_test/com/axonivy/connector/adobe/esign/connector/test/TestAgreementsService.java b/adobe-esign-connector-test/src_test/com/axonivy/connector/adobe/esign/connector/test/TestAgreementsService.java new file mode 100644 index 0000000..157a4e9 --- /dev/null +++ b/adobe-esign-connector-test/src_test/com/axonivy/connector/adobe/esign/connector/test/TestAgreementsService.java @@ -0,0 +1,143 @@ +package com.axonivy.connector.adobe.esign.connector.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import com.axonivy.connector.adobe.esign.connector.AgreementsData; +import com.axonivy.connector.adobe.esign.connector.rest.DownloadResult; +import com.axonivy.connector.adobe.esign.connector.service.AdobeSignService; + +import api.rest.v6.client.AgreementCreationInfo; +import api.rest.v6.client.AgreementCreationInfo.SignatureTypeEnum; +import api.rest.v6.client.AgreementCreationInfo.StateEnum; +import api.rest.v6.client.AgreementCreationInfoParticipantSetsInfo.RoleEnum; +import api.rest.v6.client.AgreementCreationResponse; +import api.rest.v6.client.AgreementDocuments; +import api.rest.v6.client.SigningUrlResponseSigningUrlSetInfos; +import ch.ivyteam.ivy.application.IApplication; +import ch.ivyteam.ivy.bpm.engine.client.BpmClient; +import ch.ivyteam.ivy.bpm.engine.client.ExecutionResult; +import ch.ivyteam.ivy.bpm.engine.client.element.BpmElement; +import ch.ivyteam.ivy.bpm.engine.client.element.BpmProcess; +import ch.ivyteam.ivy.bpm.exec.client.IvyProcessTest; +import ch.ivyteam.ivy.environment.AppFixture; +import ch.ivyteam.ivy.environment.Ivy; +import ch.ivyteam.ivy.security.ISession; + +@IvyProcessTest +public class TestAgreementsService extends TestAdobeSignConnector { + + private static final BpmElement testeeCreateAgreement = BpmProcess.path("connector/Agreements") + .elementName("createAgreement(AgreementCreationInfo)"); + + private static final BpmElement testeeGetDocuments = BpmProcess.path("connector/Agreements") + .elementName("getDocuments(String)"); + + private static final BpmElement testeeDownloadDocument = BpmProcess.path("connector/Agreements") + .elementName("dowloadDocument(String, String, String, Boolean)"); + + private static final BpmElement testeeGetSigningUrls = BpmProcess.path("connector/Agreements") + .elementName("getSigningURLs(String,String)"); + + @Test + public void createAgreement(BpmClient bpmClient, ISession session, AppFixture fixture, IApplication app) + throws IOException { + + prepareRestClient(app, fixture, AGREEMENTS); + + AgreementCreationInfo agreement = createTestAgreement(); + + ExecutionResult result = bpmClient.start().subProcess(testeeCreateAgreement).withParam("agreement", agreement) + .execute(); + AgreementsData data = result.data().last(); + AgreementCreationResponse response = data.getAgreementCreationResponse(); + assertThat(response).isNotNull(); + assertThat(response.getId()).isNotEmpty(); + } + + @Test + public void getDocuments(BpmClient bpmClient, ISession session, AppFixture fixture, IApplication app) + throws IOException { + + prepareRestClient(app, fixture, AGREEMENTS); + + String agreementId = "test-agreement-id"; + + ExecutionResult result = bpmClient.start().subProcess(testeeGetDocuments).withParam("agreementId", agreementId) + .execute(); + AgreementsData data = result.data().last(); + AgreementDocuments response = data.getDocuments(); + assertThat(response).isNotNull(); + assertThat(response.getDocuments()).isNotEmpty(); + } + + @Test + public void downloadDocument(BpmClient bpmClient, ISession session, AppFixture fixture, IApplication app) + throws IOException { + + prepareRestClient(app, fixture, AGREEMENTS); + + String agreementId = "test-agreement-id"; + String documentId = "test-document-id"; + String filename = "sample.pdf"; + Boolean asFile = Boolean.TRUE; + + ExecutionResult result = bpmClient.start().subProcess(testeeDownloadDocument) + .withParam("agreementId", agreementId).withParam("documentId", documentId) + .withParam("filename", filename).withParam("asFile", asFile).execute(); + AgreementsData data = result.data().last(); + DownloadResult downloadResult = data.getDownload(); + assertThat(downloadResult).isNotNull(); + if (downloadResult.getError() != null) { + Ivy.log().error(downloadResult.getError()); + } + assertThat(downloadResult.getFile()).isNotNull(); + } + + @Test + public void getSigningUrls(BpmClient bpmClient, ISession session, AppFixture fixture, IApplication app) + throws IOException { + + prepareRestClient(app, fixture, AGREEMENTS); + + String agreementId = "test-agreement-id"; + String frameParent = "test"; + + ExecutionResult result = bpmClient.start().subProcess(testeeGetSigningUrls) + .withParam("agreementId", agreementId).withParam("frameParent", frameParent).execute(); + AgreementsData data = result.data().last(); + + List signingUrls = data.getSigningUrls(); + + assertThat(signingUrls).isNotNull(); + assertThat(signingUrls).isNotEmpty(); + } + + private AgreementCreationInfo createTestAgreement() { + AgreementCreationInfo agreement = new AgreementCreationInfo(); + + agreement.setName("test name"); + agreement.setMessage("Please sign this document!"); + agreement.setSignatureType(SignatureTypeEnum.ESIGN); + agreement.setState(StateEnum.IN_PROCESS); + + // add signers + AdobeSignService.getInstance().createParticipantInfoForEmail(Arrays.asList("testEmail@test.test"), + RoleEnum.SIGNER); + agreement.setParticipantSetsInfo(AdobeSignService.getInstance() + .createParticipantInfoForEmail(Arrays.asList("testEmail@test.test"), RoleEnum.SIGNER)); + + // add documentIds - need to be already transferred with the upload document + // service + agreement.setFileInfos( + AdobeSignService.getInstance().createFileInfosForDocumentIds(Arrays.asList("test-document-id"))); + + agreement.setEmailOption(AdobeSignService.getInstance().createAllDisabledSendOptions()); + return agreement; + } +} diff --git a/adobe-esign-connector-test/src_test/com/axonivy/connector/adobe/esign/connector/test/TestTransientDocumentsService.java b/adobe-esign-connector-test/src_test/com/axonivy/connector/adobe/esign/connector/test/TestTransientDocumentsService.java new file mode 100644 index 0000000..a85fa13 --- /dev/null +++ b/adobe-esign-connector-test/src_test/com/axonivy/connector/adobe/esign/connector/test/TestTransientDocumentsService.java @@ -0,0 +1,39 @@ +package com.axonivy.connector.adobe.esign.connector.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import com.axonivy.connector.adobe.esign.connector.TransientDocumentsData; + +import ch.ivyteam.ivy.application.IApplication; +import ch.ivyteam.ivy.bpm.engine.client.BpmClient; +import ch.ivyteam.ivy.bpm.engine.client.ExecutionResult; +import ch.ivyteam.ivy.bpm.engine.client.element.BpmElement; +import ch.ivyteam.ivy.bpm.engine.client.element.BpmProcess; +import ch.ivyteam.ivy.bpm.exec.client.IvyProcessTest; +import ch.ivyteam.ivy.environment.AppFixture; +import ch.ivyteam.ivy.security.ISession; + +@IvyProcessTest +public class TestTransientDocumentsService extends TestAdobeSignConnector { + + + private static final BpmElement testeeUploadDocument = BpmProcess.path("connector/TransientDocuments") + .elementName("uploadDocument(file)"); + + @Test + public void uploadFile(BpmClient bpmClient, ISession session, AppFixture fixture, IApplication app) + throws IOException { + prepareRestClient(app, fixture, TRANSIENT_DOCUMENTS); + + java.io.File pdf = TestService.getSamplePdf(); + assertThat(pdf).isNotNull(); + + ExecutionResult result = bpmClient.start().subProcess(testeeUploadDocument).withParam("file", pdf).execute(); + TransientDocumentsData data = result.data().last(); + assertThat(data.getId()).isNotEmpty(); + } +} diff --git a/adobe-esign-connector/config/rest-clients.yaml b/adobe-esign-connector/config/rest-clients.yaml index 54682e3..b1c4238 100644 --- a/adobe-esign-connector/config/rest-clients.yaml +++ b/adobe-esign-connector/config/rest-clients.yaml @@ -37,7 +37,7 @@ RestClients: OpenAPI: SpecUrl: file:///C:/Users/jpl/Downloads/json/REST-SDK-V6-master/json/baseUrisV3.json Namespace: api.rest.v6.client - Agreements (secure.na1.adobesign.com): + Agreements: UUID: 8e96fab1-4701-47cb-ae35-d821bb12305b Url: https://{host}/api/rest/v6 Features: @@ -57,13 +57,13 @@ RestClients: SpecUrl: file:///C:/Users/jpl/OneDrive%20-%20Docuware%20GmbH/data/Projekte/Ricoh/HR%20Transformation/Adobe%20Sign/agreementsV2.json Namespace: api.rest.v6.client ResolveFully: true - TransientDocuments (secure.eu2.echosign.com): + TransientDocuments: UUID: 6a243983-5f6d-4278-89ec-aab09fea161c Url: https://{host}/api/rest/v6 Features: - - ch.ivyteam.ivy.rest.client.mapper.JsonFeature - org.glassfish.jersey.media.multipart.MultiPartFeature - com.axonivy.connector.adobe.esign.connector.auth.OAuth2Feature + - ch.ivyteam.ivy.rest.client.mapper.JsonFeature Properties: AUTH.scope: ${ivy.var.adobe-sign-connector.permissions} AUTH.baseUri: ${ivy.var.adobe-sign-connector.baseUri} diff --git a/adobe-esign-connector/config/variables.yaml b/adobe-esign-connector/config/variables.yaml index f25f10b..199b449 100644 --- a/adobe-esign-connector/config/variables.yaml +++ b/adobe-esign-connector/config/variables.yaml @@ -14,7 +14,7 @@ Variables: returnPage: '' # Integration key from Adobe Sign configuration - integrationKey: + integrationKey: '' # Base URI for getting the access and refresh access tokens (without the `/token` or `/refresh` part) baseUri: 'https://api.eu2.adobesign.com/oauth/v2'