diff --git a/client-lib/pom.xml b/client-lib/pom.xml index 0550326..9117fcf 100644 --- a/client-lib/pom.xml +++ b/client-lib/pom.xml @@ -5,14 +5,14 @@ com.signnow api-client-lib - 1.0.0-SNAPSHOT + 1.1.0-SNAPSHOT jar UTF-8 1.8 - 1.8 - 1.8 + 11 + 11 diff --git a/client-lib/src/main/java/com/signnow/library/SNClient.java b/client-lib/src/main/java/com/signnow/library/SNClient.java index ebbc099..de5fe7b 100644 --- a/client-lib/src/main/java/com/signnow/library/SNClient.java +++ b/client-lib/src/main/java/com/signnow/library/SNClient.java @@ -110,10 +110,16 @@ public T delete(String path, Map parameters, Class ret return response.readEntity(returnType); } + public int delete(String path, Map parameters) throws SNException { + Response response = buildRequest(path, parameters).delete(); + checkAPIException(response); + return response.getStatus(); + } + private Invocation.Builder buildRequest(String path, Map parameters) { WebTarget target = apiWebTarget.path(path); if (parameters != null) { - for (String key : parameters.keySet()){ + for (String key : parameters.keySet()) { WebTarget targetUpd = target.resolveTemplate(key, parameters.get(key)); if (!targetUpd.toString().equals(target.toString())) { target = targetUpd; diff --git a/client-lib/src/main/java/com/signnow/library/dto/Document.java b/client-lib/src/main/java/com/signnow/library/dto/Document.java index b442028..38d1f12 100644 --- a/client-lib/src/main/java/com/signnow/library/dto/Document.java +++ b/client-lib/src/main/java/com/signnow/library/dto/Document.java @@ -17,6 +17,15 @@ public class Document extends GenericId { public String updated; @JsonProperty("original_filename") public String originalFilename; + @JsonProperty("origin_document_id") + public String originDocumentId; + public String owner; + @JsonProperty(value = "template", defaultValue = "false") + public boolean template; + public Thumbnail thumbnail; + public List signatures; + public List tags; + public List fields; @JsonProperty("version_time") public String versionTime; /** @@ -28,6 +37,7 @@ public class Document extends GenericId { * Free form invites info */ public List requests; + public List roles; public static class SigningLinkRequest { @JsonProperty("document_id") @@ -38,6 +48,62 @@ public SigningLinkRequest(String documentId) { } } + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Thumbnail { + public String small; + public String medium; + public String large; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Signature extends GenericId { + @JsonProperty("user_id") + public String userId; + public String email; + @JsonProperty("page_number") + public String pageNumber; + public String width; + public String height; + public String x; + public String y; + public String created; + public String data; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class DocumentField extends GenericId { + public FieldType type; + @JsonProperty("role_id") + public String roleId; + public String role; + public String originator; + public String fulfiller; + @JsonProperty("field_request_id") + public String fieldRequestId; + @JsonProperty("element_id") + public String elementId; + @JsonProperty("field_request_canceled") + public String fieldRequestCanceled; + @JsonProperty("template_field_id") + public String fieldTemplateId; + @JsonProperty("field_id") + public String fieldId; + } + + public static class Role { + @JsonProperty("unique_id") + public String uniqueId; + @JsonProperty("signing_order") + public String signingOrder; + public String name; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Tag { + public String type; + public String name; + } + @JsonIgnoreProperties(ignoreUnknown = true) public static class SigningLinkResponce { public String url; @@ -110,7 +176,6 @@ public enum FieldType { this.name = name; } - @JsonSetter public static FieldType typeOf(String name) { for (FieldType type : values()) { if (type.name.equalsIgnoreCase(name)) { @@ -120,6 +185,11 @@ public static FieldType typeOf(String name) { throw new IllegalArgumentException(name + " field not supported."); } + @JsonValue + public String getType() { + return name; + } + @JsonCreator @Override public String toString() { @@ -175,4 +245,91 @@ public static class DocumentDownloadLink { public String link; } + @JsonIgnoreProperties(ignoreUnknown = true) + public static class EmbeddedInviteResponse { + public List data = new ArrayList<>(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class EmbeddedInviteResult { + public String id; + public String email; + public String role_id; + public int order; + public String status; + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class EmbeddedInviteRequest { + public List invites = new ArrayList<>(); + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class EmbeddedInvite { + public String email; + public String role_id; + public int order; + public String auth_method; + + public EmbeddedInvite() { + } + + public EmbeddedInvite(String email, String role_id, int order, String auth_method) { + this.email = email; + this.role_id = role_id; + this.order = order; + this.auth_method = auth_method; + } + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class GenerateEmbeddedSigningLinkRequest { + public String auth_method; + public int link_expiration; + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class GenerateEmbeddedSigningLinkResponse { + public LinkData data; + } + + public static class LinkData { + public String link; + } + + public enum AuthMethod { + PASSWORD("password"), + EMAIL("email"), + MFA("mfa"), + SOCIAL("social"), + BIOMETRIC("biometric"), + OTHER("other"), + NONE("none"); + + private final String method; + + AuthMethod(String method) { + this.method = method; + } + + public static AuthMethod getAuthMethod(final String authMethod) { + for (AuthMethod value : values()) { + if (value.method.equalsIgnoreCase(authMethod)) { + return value; + } + } + throw new EnumConstantNotPresentException(AuthMethod.class, authMethod); + } + + @JsonValue + public String getAuthMethod() { + return method; + } + + @JsonCreator + @Override + public String toString() { + return method; + } + } } diff --git a/client-lib/src/main/java/com/signnow/library/facades/Documents.java b/client-lib/src/main/java/com/signnow/library/facades/Documents.java index a1a6539..a3dd91b 100644 --- a/client-lib/src/main/java/com/signnow/library/facades/Documents.java +++ b/client-lib/src/main/java/com/signnow/library/facades/Documents.java @@ -29,4 +29,20 @@ public interface Documents { void deleteDocument(String documentId) throws SNException; String getDownloadLink(String documentId) throws SNException; + + Document.EmbeddedInviteResponse createEmbeddedInvites(final String documentId, + final Document.EmbeddedInviteRequest request) throws SNException; + + Document.GenerateEmbeddedSigningLinkResponse generateEmbeddedInviteLink(final String documentId, + final String fieldId, + final Document.GenerateEmbeddedSigningLinkRequest request) throws SNException; + + /** + * All embedded invites created for the document will be deleted. + * + * @param documentId the documentId of the document containing embedded invites. + * @return the 204 status code will be returned if the embedded invites have been deleted successfully. + * @throws SNException + */ + int deleteEmbeddedInvite(final String documentId) throws SNException; } diff --git a/client-lib/src/main/java/com/signnow/library/services/DocumentsService.java b/client-lib/src/main/java/com/signnow/library/services/DocumentsService.java index 551f64f..bdb6033 100644 --- a/client-lib/src/main/java/com/signnow/library/services/DocumentsService.java +++ b/client-lib/src/main/java/com/signnow/library/services/DocumentsService.java @@ -15,6 +15,7 @@ import java.io.InputStream; import java.util.Collections; import java.util.List; +import java.util.Map; public class DocumentsService extends ApiService implements Documents { public DocumentsService(SNClient client) { @@ -109,4 +110,32 @@ public String getDownloadLink(String documentId) throws SNException { Document.DocumentDownloadLink.class ).link; } + + @Override + public Document.EmbeddedInviteResponse createEmbeddedInvites(String documentId, Document.EmbeddedInviteRequest request) throws SNException { + return client.post( + "/v2/documents/{documentUniqueId}/embedded-invites", + Collections.singletonMap("documentUniqueId", documentId), + request, + Document.EmbeddedInviteResponse.class + ); + } + + @Override + public Document.GenerateEmbeddedSigningLinkResponse generateEmbeddedInviteLink(String documentId, String fieldId, Document.GenerateEmbeddedSigningLinkRequest request) throws SNException { + return client.post( + "/v2/documents/{document_id}/embedded-invites/{fieldInviteUniqueId}/link", + Map.of("document_id", documentId, "fieldInviteUniqueId", fieldId), + request, + Document.GenerateEmbeddedSigningLinkResponse.class + ); + } + + @Override + public int deleteEmbeddedInvite(String documentId) throws SNException { + return client.delete( + "/v2/documents/{document_id}/embedded-invites", + Collections.singletonMap("document_id", documentId) + ); + } } diff --git a/client-lib/src/test/java/com/signnow/library/services/DocumentsServiceTest.java b/client-lib/src/test/java/com/signnow/library/services/DocumentsServiceTest.java index 0ba6b08..35a8932 100644 --- a/client-lib/src/test/java/com/signnow/library/services/DocumentsServiceTest.java +++ b/client-lib/src/test/java/com/signnow/library/services/DocumentsServiceTest.java @@ -7,7 +7,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; -import org.mockito.Matchers; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.GenericType; @@ -19,7 +18,7 @@ import java.util.List; import java.util.Random; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; class DocumentsServiceTest extends CommonServiceTestCase { @@ -162,6 +161,16 @@ void getDownloadLink() throws SNException { , eq(Document.DocumentDownloadLink.class)); } + @Test + void createEmbeddedInvites() throws SNException { + Document.EmbeddedInviteResponse response = mock(Document.EmbeddedInviteResponse.class); + Document.EmbeddedInviteRequest request = mock(Document.EmbeddedInviteRequest.class); + when(clientMock.post(anyString(), anyMap(), eq(request), eq(Document.EmbeddedInviteResponse.class))).thenReturn(response); + service.createEmbeddedInvites("1", request); + verify(clientMock, times(1)) + .post(anyString(), anyMap(), eq(request), eq(Document.EmbeddedInviteResponse.class)); + } + private InputStream getSomeInputStream() { byte[] bytes = new byte[25]; Random rnd = new Random(); @@ -204,4 +213,4 @@ private void verify_uploadDocument() { verify(builderMock, times(1)).header(anyString(), any()); verify(builderMock, times(1)).post(any()); } -} \ No newline at end of file +} diff --git a/example-app/pom.xml b/example-app/pom.xml index 246c483..65cf2d5 100644 --- a/example-app/pom.xml +++ b/example-app/pom.xml @@ -5,7 +5,7 @@ com.signnow api-example-app - 1.0.0-SNAPSHOT + 1.1.0-SNAPSHOT jar @@ -17,7 +17,7 @@ UTF-8 - 1.8 + 11 @@ -40,6 +40,17 @@ ${project.version} + + + org.webjars + highlightjs + 10.1.2 + + + org.webjars + webjars-locator-core + + diff --git a/example-app/src/main/java/com/signnow/examples/controller/Examples.java b/example-app/src/main/java/com/signnow/examples/controller/Examples.java index 0faee03..2b80ebe 100644 --- a/example-app/src/main/java/com/signnow/examples/controller/Examples.java +++ b/example-app/src/main/java/com/signnow/examples/controller/Examples.java @@ -1,5 +1,7 @@ package com.signnow.examples.controller; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.signnow.examples.model.*; import com.signnow.examples.session.UserData; import com.signnow.library.SNClient; @@ -10,19 +12,25 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; +import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import javax.inject.Provider; +import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; @RequestMapping("/examples") @Controller public class Examples { + @Autowired + private ObjectMapper mapper; + @Autowired private Provider provider; @@ -40,20 +48,21 @@ public String uploadDocument(RedirectAttributes redirectAttributes) { if (isUserNotAuthorized()) { return getLoginRedirectPage(redirectAttributes, "/examples/uploadDocument"); } - - return "upload_document"; + return "uploadDocument"; } @PostMapping("/uploadDocument") public String handleFileUpload(@RequestParam("file") MultipartFile file, Model model, - RedirectAttributes redirectAttributes + RedirectAttributes redirectAttributes, + HttpServletRequest request ) { + String documentId = ""; try { if (isUserNotAuthorized()) { return getLoginRedirectPage(redirectAttributes, "/examples/uploadDocument"); } - String documentId = provider.get().getSnClient() + documentId = provider.get().getSnClient() .documentsService() .uploadDocument(file.getInputStream(), file.getOriginalFilename()); updateDocumentList(); @@ -62,7 +71,14 @@ public String handleFileUpload(@RequestParam("file") MultipartFile file, e.printStackTrace(); redirectAttributes.addFlashAttribute("exception", e.getMessage()); } - return "upload_document"; + + final String templateNameFromReferer = getTemplateNameFromReferer(request); + String action = templateNameFromReferer; + if (!templateNameFromReferer.equals("uploadDocument")) { + redirectAttributes.addFlashAttribute("message", "File uploaded successfully. FileId: " + documentId); + action = "redirect:/examples/" + templateNameFromReferer; + } + return action; } @GetMapping("/signingLink") @@ -83,7 +99,7 @@ public String singingLink(@ModelAttribute DocumentInfo signingLinkModel, Model m try { String documentId = signingLinkModel.getDocumentId(); //Create signature field in document - Document.Field sign = createExampleField(20, 20, "Signer role"); + Document.Field sign = createExampleField(20, 20, "Signer role", Document.FieldType.SIGNATURE, true, "Sign It"); //update document with signeture field Documents documentsService = provider.get().getSnClient().documentsService(); @@ -187,9 +203,9 @@ public String oneTemplateMultipleSigners(@ModelAttribute OneTemplateMultipleSign model.addAttribute("documentList", provider.get().getUserDocuments()); //Create signature field in document - Document.Field sign = createExampleField(25, 25, first.getRole()); + Document.Field sign = createExampleField(25, 25, first.getRole(), Document.FieldType.SIGNATURE, true, "Sign It"); //Signature field for second signer - Document.Field sign2 = createExampleField(10, 60, second.getRole()); + Document.Field sign2 = createExampleField(10, 60, second.getRole(), Document.FieldType.SIGNATURE, true, "Sign It"); //update document with signature field final Documents documentsService = provider.get().getSnClient().documentsService(); @@ -259,8 +275,8 @@ public String multipleTemplatesAndSigners(@ModelAttribute MultipleTemplatesAndSi final String documentId2 = multipleTemplatesAndSigners.getDocumentIds().get(1); //Create signature field in document - Document.Field sign = createExampleField(10, 10, first.getRole()); - Document.Field sign2 = createExampleField(10, 60, second.getRole()); + Document.Field sign = createExampleField(10, 10, first.getRole(), Document.FieldType.SIGNATURE, true, "Sign It"); + Document.Field sign2 = createExampleField(10, 60, second.getRole(), Document.FieldType.SIGNATURE, true, "Sign It"); //update document with signature field final SNClient snClient = provider.get().getSnClient(); @@ -432,12 +448,17 @@ public String addFillableFields(RedirectAttributes redirectAttributes, Model mod return getLoginRedirectPage(redirectAttributes, "/examples/addFillableFields"); } model.addAttribute("documentList", provider.get().getUserDocuments()); + model.addAttribute("deleteInviteForm", new DocumentInfo()); model.addAttribute("fillableFields", new FillableFields()); - return "add_fillable_fields"; + return "addFillableFields"; } @PostMapping("/addFillableFields") - public String addFillableFields(@ModelAttribute FillableFields fillableFields, Model model, RedirectAttributes redirectAttributes) { + public String addFillableFields(@ModelAttribute FillableFields fillableFields, + Model model, + RedirectAttributes redirectAttributes, + HttpServletRequest request + ) { if (isUserNotAuthorized()) { return getLoginRedirectPage(redirectAttributes, "/examples/addFillableFields"); } @@ -447,27 +468,29 @@ public String addFillableFields(@ModelAttribute FillableFields fillableFields, M try { model.addAttribute("fillableFields", new FillableFields()); model.addAttribute("documentList", provider.get().getUserDocuments()); - //Signature field - Document.Field sign = createExampleField(10, 10, fillableFields.getSignatureFieldRole()); - - //Text field - Document.Field text = new Document.Field(); - text.x = 10; - text.y = 60; - text.height = 30; - text.width = 150; - text.type = Document.FieldType.TEXT; - text.required = false; - text.pageNumber = 0; - text.role = fillableFields.getTextFieldRole(); - text.label = "Fill in value here"; + model.addAttribute("deleteInviteForm", new DocumentInfo()); + model.addAttribute("selectedDocId", documentId); + Document.Field sign = createExampleField(10, + 10, + fillableFields.getSignatureFieldRole(), + Document.FieldType.SIGNATURE, + true, + "Your signature here" + ); + Document.Field text = createExampleField(10, + 60, + fillableFields.getTextFieldRole(), + Document.FieldType.TEXT, + false, + "Fill in value here" + ); //update document fields provider.get().getSnClient() .documentsService() .updateDocumentFields(documentId, Arrays.asList(sign, text)); - model.addAttribute("message", String.format("Fillable fields added to the document %s", documentId)); + model.addAttribute("message", "The fields was successfully added to FileId: " + documentId); } catch (SNException e) { String message = String.format("Couldn't adding fillable fields to document %s. %s %s" , documentId, e.getMessage(), e.getCause() @@ -475,7 +498,208 @@ public String addFillableFields(@ModelAttribute FillableFields fillableFields, M model.addAttribute("message", message); } - return "add_fillable_fields"; + final String templateNameFromReferer = getTemplateNameFromReferer(request); + String action = templateNameFromReferer; + if (!templateNameFromReferer.equals("addFillableFields")) { + redirectAttributes.addFlashAttribute("message", "The fields was successfully added to FileId: " + documentId); + redirectAttributes.addFlashAttribute("selectedDocId", documentId); + action = "redirect:/examples/" + templateNameFromReferer; + } + return action; + } + + @GetMapping("/embeddedSigning") + public String embeddedSigning(@ModelAttribute("selectedDocId") String selectedDocId, RedirectAttributes redirectAttributes, Model model) { + if (isUserNotAuthorized()) { + return getLoginRedirectPage(redirectAttributes, "/examples/embeddedSigning"); + } + if (!StringUtils.isEmpty(selectedDocId)) { + try { + setAttributeForCreateEmbeddedInvite(selectedDocId, model, null); + } catch (SNException e) { + e.printStackTrace(); + model.addAttribute("message", "Couldn't get information about document by id: " + selectedDocId); + } + } + model.addAttribute("documentList", provider.get().getUserDocuments()); + model.addAttribute("fillableFields", new FillableFields()); + model.addAttribute("deleteInviteForm", new DocumentInfo()); + return "embeddedSigning"; + } + + private DocumentInfo setAttributeForCreateEmbeddedInvite(String selectedDocId, Model model, RedirectAttributes redirectAttributes) throws SNException { + final Document document = provider.get().getSnClient().documentsService().getDocument(selectedDocId); + DocumentInfo info = null; + if (document != null) { + info = new DocumentInfo(document.id, document.document_name); + final EmbeddedSigningModel embeddedSigningModel = new EmbeddedSigningModel(); + embeddedSigningModel.documentInfo = info; + + for (int i = 0; i < 2; i++) { + final EmbeddedSigningModel.InviteInfo inviteInfo = new EmbeddedSigningModel.InviteInfo(); + inviteInfo.availableAuthMethods = Arrays.asList(Document.AuthMethod.values()); + inviteInfo.availableRoles = document.roles; + embeddedSigningModel.invites.add(inviteInfo); + } + if (redirectAttributes != null) { + redirectAttributes.addFlashAttribute("form", embeddedSigningModel); + } else { + model.addAttribute("form", embeddedSigningModel); + } + } else { + if (redirectAttributes != null) { + redirectAttributes.addFlashAttribute("selectedDocId", null); + } else { + model.addAttribute("selectedDocId", null); + } + } + return info; + } + + @PostMapping("/embeddedSigning") + public String embeddedSigning(@ModelAttribute EmbeddedSigningModel form, + RedirectAttributes redirectAttributes, + Model model + ) { + if (isUserNotAuthorized()) { + return getLoginRedirectPage(redirectAttributes, "/examples/embeddedSigning"); + } + final String documentId = form.documentInfo.getDocumentId(); + try { + Document.EmbeddedInviteRequest request = new Document.EmbeddedInviteRequest(); + for (EmbeddedSigningModel.InviteInfo invite : form.invites) { + request.invites.add( + new Document.EmbeddedInvite(invite.email, + invite.roleId, + invite.order, + invite.authMethod) + ); + } + final Document.EmbeddedInviteResponse response = provider.get().getSnClient() + .documentsService() + .createEmbeddedInvites(documentId, request); + + final String embeddedInvites = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(response); + final GenerateEmbeddedInviteLinkForm inviteLinkForm = new GenerateEmbeddedInviteLinkForm(); + inviteLinkForm.fields = response.data.stream().map(data -> data.id) + .collect(Collectors.toSet()); + inviteLinkForm.documentInfo = form.documentInfo; + inviteLinkForm.embeddedInvites = embeddedInvites; + + model.addAttribute("embeddedInvites", embeddedInvites); + model.addAttribute("generateLinkForm", inviteLinkForm); + } catch (SNException e) { + e.printStackTrace(); + String message = String.format("Couldn't create embedded invites for document %s. %s %s" + , documentId, e.getMessage(), e.getCause() + ); + model.addAttribute("embeddedInvites", ""); + model.addAttribute("generateLinkForm", new GenerateEmbeddedInviteLinkForm()); + model.addAttribute("message", message); + } catch (JsonProcessingException ex) { + ex.printStackTrace(); + String message = String.format("Couldn't parse embedded invites response for document %s. %s %s" + , documentId, ex.getMessage(), ex.getCause() + ); + model.addAttribute("embeddedInvites", ""); + model.addAttribute("generateLinkForm", new GenerateEmbeddedInviteLinkForm()); + model.addAttribute("message", message); + } finally { + model.addAttribute("documentInfo", form.documentInfo); + + model.addAttribute("message", "The embedded invites have been successfully created."); + model.addAttribute("documentList", provider.get().getUserDocuments()); + model.addAttribute("deleteInviteForm", new DocumentInfo()); + model.addAttribute("fillableFields", new FillableFields()); + + } + + return "embeddedSigning"; + } + + @PostMapping("/generateEmbeddedLink") + public String generateEmbeddedInviteLink(@ModelAttribute GenerateEmbeddedInviteLinkForm generateLinkForm, + RedirectAttributes redirectAttributes, + Model model + ) { + if (isUserNotAuthorized()) { + return getLoginRedirectPage(redirectAttributes, "/examples/generateEmbeddedLink"); + } + String documentId = generateLinkForm.documentInfo.getDocumentId(); + try { + Document.GenerateEmbeddedSigningLinkRequest request = new Document.GenerateEmbeddedSigningLinkRequest(); + request.auth_method = generateLinkForm.auth_method; + request.link_expiration = generateLinkForm.link_expiration; + ; + final Document.GenerateEmbeddedSigningLinkResponse response = provider.get().getSnClient() + .documentsService() + .generateEmbeddedInviteLink(documentId, + generateLinkForm.fieldId, + request + ); + model.addAttribute("linkResponse", mapper.writerWithDefaultPrettyPrinter().writeValueAsString(response)); + model.addAttribute("documentInfo", generateLinkForm.documentInfo); + model.addAttribute("documentList", provider.get().getUserDocuments()); + model.addAttribute("deleteInviteForm", new DocumentInfo()); + model.addAttribute("fillableFields", new FillableFields()); + model.addAttribute("message", "The embedded invites have been successfully created."); + model.addAttribute("embeddedInvites", generateLinkForm.embeddedInvites); + model.addAttribute("generateLinkForm", generateLinkForm); + } catch (SNException e) { + e.printStackTrace(); + String message = String.format("Couldn't generate embedded invite link for document %s. %s %s" + , documentId, e.getMessage(), e.getCause() + ); + model.addAttribute("message", message); + } catch (JsonProcessingException e) { + e.printStackTrace(); + String message = String.format("Couldn't parse embedded invites response for document %s. %s %s" + , documentId, e.getMessage(), e.getCause() + ); + model.addAttribute("message", message); + } + return "embeddedSigning"; + } + + @PostMapping("/deleteEmbeddedInvite") + public String deleteEmbeddedInvite(@ModelAttribute DocumentInfo deleteInviteForm, + RedirectAttributes redirectAttributes, + Model model + ) { + if (isUserNotAuthorized()) { + return getLoginRedirectPage(redirectAttributes, "/examples/deleteEmbeddedInvite"); + } + String documentId = deleteInviteForm.getDocumentId(); + try { + int httpStatus = provider.get().getSnClient() + .documentsService() + .deleteEmbeddedInvite(documentId); + + if (httpStatus == 204) { + redirectAttributes.addFlashAttribute("message", "The embedded invites have been deleted successfully."); + } else { + redirectAttributes.addFlashAttribute("message", "The embedded invites haven't been deleted. Status code:" + httpStatus); + } + + } catch (SNException e) { + e.printStackTrace(); + String message = String.format("Couldn't delete the embedded invite for the document %s. %s %s" + , documentId, e.getMessage(), e.getCause() + ); + redirectAttributes.addFlashAttribute("message", message); + } finally { + redirectAttributes.addFlashAttribute("fillableFields", new FillableFields()); + redirectAttributes.addFlashAttribute("documentList", provider.get().getUserDocuments()); + redirectAttributes.addFlashAttribute("deleteInviteForm", new DocumentInfo()); + final DocumentInfo documentInfo; + try { + documentInfo = setAttributeForCreateEmbeddedInvite(documentId, model, redirectAttributes); + redirectAttributes.addFlashAttribute("documentInfo", documentInfo); + } catch (SNException ignored) { + } + } + + return "redirect:/examples/embeddedSigning"; } private boolean isUserNotAuthorized() { @@ -511,17 +735,17 @@ private void updateDocumentList() { } } - private Document.Field createExampleField(int x, int y, String roleName) { + private Document.Field createExampleField(int x, int y, String roleName, Document.FieldType fieldType, boolean required, String labelMessage) { Document.Field sign = new Document.Field(); sign.x = x; sign.y = y; sign.height = 30; sign.width = 150; - sign.type = Document.FieldType.SIGNATURE; - sign.required = true; + sign.type = fieldType; + sign.required = required; sign.pageNumber = 0; sign.role = roleName; - sign.label = "Your signature here"; + sign.label = labelMessage; return sign; } @@ -533,4 +757,10 @@ private GroupInvite.InviteAction createGroupInviteAction(String documentId, Stri signAction.email = email; return signAction; } + + private String getTemplateNameFromReferer(HttpServletRequest request) { + String referer = request.getHeader("referer"); + int index = (request.getHeader("origin") + "/examples/").length(); + return referer.substring(index); + } } diff --git a/example-app/src/main/java/com/signnow/examples/model/EmbeddedSigningModel.java b/example-app/src/main/java/com/signnow/examples/model/EmbeddedSigningModel.java new file mode 100644 index 0000000..ebf77fb --- /dev/null +++ b/example-app/src/main/java/com/signnow/examples/model/EmbeddedSigningModel.java @@ -0,0 +1,77 @@ +package com.signnow.examples.model; + +import com.signnow.library.dto.Document; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: oleg.k + * Date: 16.03.2021 + * Time: 13:07 + */ +public class EmbeddedSigningModel { + public DocumentInfo documentInfo; + public List invites = new ArrayList<>(); + + public List getInvites() { + return invites; + } + + public DocumentInfo getDocumentInfo() { + return documentInfo; + } + + public void setDocumentInfo(DocumentInfo documentInfo) { + this.documentInfo = documentInfo; + } + + public static class InviteInfo { + public String email; + public String roleId; + public int order = 1; + public String authMethod; + public List availableRoles = new ArrayList<>(); + public List availableAuthMethods = new ArrayList<>(); + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public String getAuthMethod() { + return authMethod; + } + + public void setAuthMethod(String auth_method) { + this.authMethod = auth_method; + } + + public List getAvailableRoles() { + return availableRoles; + } + + public List getAvailableAuthMethods() { + return availableAuthMethods; + } + } +} diff --git a/example-app/src/main/java/com/signnow/examples/model/GenerateEmbeddedInviteLinkForm.java b/example-app/src/main/java/com/signnow/examples/model/GenerateEmbeddedInviteLinkForm.java new file mode 100644 index 0000000..bb58a97 --- /dev/null +++ b/example-app/src/main/java/com/signnow/examples/model/GenerateEmbeddedInviteLinkForm.java @@ -0,0 +1,66 @@ +package com.signnow.examples.model; + +import java.util.HashSet; +import java.util.Set; + +/** + * User: oleg.k + * Date: 22.03.2021 + * Time: 22:58 + */ +public class GenerateEmbeddedInviteLinkForm { + public DocumentInfo documentInfo = new DocumentInfo(); + public Set fields = new HashSet<>(); + public String fieldId; + public String auth_method; + public int link_expiration = 15; + public String embeddedInvites; + + public String getFieldId() { + return fieldId; + } + + public void setFieldId(String fieldId) { + this.fieldId = fieldId; + } + + public DocumentInfo getDocumentInfo() { + return documentInfo; + } + + public void setDocumentInfo(DocumentInfo documentInfo) { + this.documentInfo = documentInfo; + } + + public Set getFields() { + return fields; + } + + public void setFields(Set fields) { + this.fields = fields; + } + + public String getEmbeddedInvites() { + return embeddedInvites; + } + + public void setEmbeddedInvites(String embeddedInvites) { + this.embeddedInvites = embeddedInvites; + } + + public String getAuth_method() { + return auth_method; + } + + public void setAuth_method(String auth_method) { + this.auth_method = auth_method; + } + + public int getLink_expiration() { + return link_expiration; + } + + public void setLink_expiration(int link_expiration) { + this.link_expiration = link_expiration; + } +} diff --git a/example-app/src/main/resources/templates/addFillableFields.html b/example-app/src/main/resources/templates/addFillableFields.html new file mode 100644 index 0000000..58beb85 --- /dev/null +++ b/example-app/src/main/resources/templates/addFillableFields.html @@ -0,0 +1,17 @@ + + + + Adding fillable fields example + + + +Latest action result: +

+ +

Adding fillable fields:

+ + +

+

Back to examples list

+ + diff --git a/example-app/src/main/resources/templates/add_fillable_fields.html b/example-app/src/main/resources/templates/add_fillable_fields.html deleted file mode 100644 index e2f70d9..0000000 --- a/example-app/src/main/resources/templates/add_fillable_fields.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - Adding fillable fields example - - - -Latest action result: -

- -

Adding fillable fields:

-
-

Choose document: - -

-

Signature field role name

-

Text field role name

-

-
-

Back to examples list

- - \ No newline at end of file diff --git a/example-app/src/main/resources/templates/embeddedSigning.html b/example-app/src/main/resources/templates/embeddedSigning.html new file mode 100644 index 0000000..6445acb --- /dev/null +++ b/example-app/src/main/resources/templates/embeddedSigning.html @@ -0,0 +1,142 @@ + + + + + Embedded signing example + + + + + +Latest action result: +

+ +

Create embedded signing invites

+
    +
  1. +

    Upload file

    + +
  2. +
  3. +

    Pick file and add fields

    + +
  4. +
  5. +

    Delete embedded invites from document

    +
    + + +
    +
  6. +
  7. +
    +

    + Create embedded invites

    +
    + + + + + + + + + + + + + + + + + + + + + +
    SignerEmailRoleOrderAuth method
    + + + +
    + +
    +
    +
    +

    +
    +                
    +            
    +
    +
  8. + +
  9. +

    +
    +
    + + + + + + + + + + + + + + + + + + + +
    FieldIdAuth methodExpiration
    + + + +
    +
    + +
    +
    +

    +
    +                
    +            
    +
    +
  10. +
+ +

+

Back to examples list

+ + diff --git a/example-app/src/main/resources/templates/examples.html b/example-app/src/main/resources/templates/examples.html index 50c7baa..d8762a5 100644 --- a/example-app/src/main/resources/templates/examples.html +++ b/example-app/src/main/resources/templates/examples.html @@ -19,6 +19,7 @@

Examples:

  • Invite on multiple templates for multiple signers

  • Setting the initial values for template fields

  • +
  • Embedded signing

  • - \ No newline at end of file + diff --git a/example-app/src/main/resources/templates/fragments/formsFragments.html b/example-app/src/main/resources/templates/fragments/formsFragments.html new file mode 100644 index 0000000..9acba8d --- /dev/null +++ b/example-app/src/main/resources/templates/fragments/formsFragments.html @@ -0,0 +1,32 @@ + + + + + Form fragments + + +
    + + + + + + + + + +
    File to upload:
    +
    +
    +

    Choose document: + +

    +

    Signature field role name

    +

    Text field role name

    +

    +
    + + diff --git a/example-app/src/main/resources/templates/uploadDocument.html b/example-app/src/main/resources/templates/uploadDocument.html new file mode 100644 index 0000000..952132e --- /dev/null +++ b/example-app/src/main/resources/templates/uploadDocument.html @@ -0,0 +1,14 @@ + + + + Upload document example + + + + Latest action result: +

    +

    Upload document:

    +
    +

    Back to examples list

    + + diff --git a/example-app/src/main/resources/templates/upload_document.html b/example-app/src/main/resources/templates/upload_document.html deleted file mode 100644 index 6d551ac..0000000 --- a/example-app/src/main/resources/templates/upload_document.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - Upload document example - - - - Latest action result: -

    - -

    Upload document:

    -
    - - - - - - - - - -
    File to upload:
    -
    -

    Back to examples list

    - - \ No newline at end of file