From 899b8ed9535d15613a15f7cb2b71374db3bfd42e Mon Sep 17 00:00:00 2001 From: Petro Vakulenko Date: Fri, 22 May 2026 17:18:42 +0200 Subject: [PATCH 1/2] Sample apps synchronization between each other --- .env.example | 27 +++- Dockerfile | 23 +++- LICENSE.md | 21 +++ pom.xml | 4 + .../javasampleapp/config/AppConfig.java | 57 ++++++++ .../config/SecurityHeadersFilter.java | 31 +++++ .../controllers/GlobalExceptionHandler.java | 55 ++++++++ .../controllers/RoutingController.java | 128 ++++++++---------- .../IndexController.java | 5 +- .../IndexController.java | 16 +-- .../IndexController.java | 3 +- .../IndexController.java | 12 +- .../IndexController.java | 12 +- .../IndexController.java | 12 +- .../IndexController.java | 3 +- .../IndexController.java | 3 +- .../IndexController.java | 3 +- .../IndexController.java | 3 +- .../IndexController.java | 3 +- .../HROnboardingSystem/IndexController.java | 10 +- .../IndexController.java | 9 +- .../IndexController.java | 11 +- .../IndexController.java | 3 +- .../IndexController.java | 3 +- .../UploadEmbeddedSender/IndexController.java | 3 +- src/main/resources/application.properties | 16 ++- src/main/resources/views/index.html | 2 +- .../controllers/RoutingControllerTest.java | 51 +++++++ 28 files changed, 375 insertions(+), 154 deletions(-) create mode 100644 LICENSE.md create mode 100644 src/main/java/com/signnow/javasampleapp/config/AppConfig.java create mode 100644 src/main/java/com/signnow/javasampleapp/config/SecurityHeadersFilter.java create mode 100644 src/main/java/com/signnow/javasampleapp/controllers/GlobalExceptionHandler.java create mode 100644 src/test/java/com/signnow/javasampleapp/controllers/RoutingControllerTest.java diff --git a/.env.example b/.env.example index 7015999..52e7665 100644 --- a/.env.example +++ b/.env.example @@ -2,9 +2,26 @@ ## signNow API SDK configuration ## -## Replace these dummy values with your actual credentials except SIGNNOW_API_HOST +# Required — replace placeholders with real credentials before running. SIGNNOW_API_HOST=https://api.signnow.com -SIGNNOW_API_BASIC_TOKEN=test -SIGNNOW_API_USERNAME=test -SIGNNOW_API_PASSWORD=test -SIGNNOW_DOWNLOADS_DIR=~/Downloads +SIGNNOW_API_BASIC_TOKEN= +SIGNNOW_API_USERNAME= +SIGNNOW_API_PASSWORD= + +# Path inside the running environment where downloaded files are written. +# Inside Docker, leave as /tmp (or another writable path). The ~ does not expand in containers. +SIGNNOW_DOWNLOADS_DIR=/tmp + +## +## Sample-app runtime configuration +## + +# Public base URL used to build redirect URIs sent to the SignNow embed widget. +# Must match the host the browser reaches this app on. +APP_BASE_URL=http://localhost:8080 + +# Demo-only signer credentials used by samples that generate limited signer tokens. +# For production deployments replace with per-tenant values or remove the affected samples. +SIGNNOW_DEMO_USER_EMAIL=example@example.com +SIGNNOW_DEMO_USER_PASSWORD=example +SIGNNOW_SIGNER_EMAIL=test@example.com diff --git a/Dockerfile b/Dockerfile index 89d56fd..2018677 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,21 @@ -FROM maven:3.9.9-eclipse-temurin-23 AS build +FROM maven:3.9.9-eclipse-temurin-17 AS build WORKDIR /app COPY pom.xml ./ +RUN mvn -B dependency:go-offline COPY src ./src -RUN mvn clean install package -DskipTests +RUN mvn -B clean package -DskipTests -FROM openjdk:23-jdk-slim -VOLUME /tmp -COPY --from=build /app/target/java-sample-app-0.0.1-SNAPSHOT.jar app.jar -ENTRYPOINT ["java", "-jar", "/app.jar"] +FROM eclipse-temurin:17-jre +RUN apt-get update \ + && apt-get install -y --no-install-recommends curl \ + && rm -rf /var/lib/apt/lists/* \ + && groupadd -r app && useradd -r -g app -d /home/app -m app +WORKDIR /home/app +COPY --from=build /app/target/java-sample-app-0.0.1-SNAPSHOT.jar /home/app/app.jar +RUN chown -R app:app /home/app +USER app +ENV JAVA_OPTS="-XX:MaxRAMPercentage=75 -XX:+UseG1GC" +EXPOSE 8080 +HEALTHCHECK --interval=30s --timeout=3s --start-period=20s --retries=3 \ + CMD curl -fsS http://127.0.0.1:8080/actuator/health || exit 1 +ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar /home/app/app.jar"] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..dd4e228 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# The MIT License + +Copyright (c) 2003-present SignNow + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pom.xml b/pom.xml index 73260f4..0772a39 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-actuator + org.springframework.boot spring-boot-starter-test diff --git a/src/main/java/com/signnow/javasampleapp/config/AppConfig.java b/src/main/java/com/signnow/javasampleapp/config/AppConfig.java new file mode 100644 index 0000000..12dcd1f --- /dev/null +++ b/src/main/java/com/signnow/javasampleapp/config/AppConfig.java @@ -0,0 +1,57 @@ +package com.signnow.javasampleapp.config; + +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * Runtime config holder for sample controllers that are instantiated via reflection + * and cannot receive Spring-injected dependencies. Spring populates the static fields + * at startup through {@link Holder}; controllers read them via the static accessors. + */ +public final class AppConfig { + + private AppConfig() {} + + private static volatile String baseUrl = "http://localhost:8080"; + private static volatile String demoUserEmail = "example@example.com"; + private static volatile String demoUserPassword = "example"; + + public static String baseUrl() { + return baseUrl; + } + + public static String sampleUrl(String exampleName) { + return baseUrl + "/samples/" + exampleName; + } + + public static String demoUserEmail() { + return demoUserEmail; + } + + public static String demoUserPassword() { + return demoUserPassword; + } + + @Component + static class Holder { + + Holder(@Value("${app.base-url:http://localhost:8080}") String baseUrl, + @Value("${signnow.api.demo_user_email:example@example.com}") String demoUserEmail, + @Value("${signnow.api.demo_user_password:example}") String demoUserPassword) { + AppConfig.baseUrl = trimTrailingSlash(baseUrl); + AppConfig.demoUserEmail = demoUserEmail; + AppConfig.demoUserPassword = demoUserPassword; + } + + @PostConstruct + void ready() { + // Touch volatile fields to publish writes; initialization happens in the constructor. + } + + private static String trimTrailingSlash(String url) { + if (url == null || url.isEmpty()) return url; + return url.endsWith("/") ? url.substring(0, url.length() - 1) : url; + } + } +} diff --git a/src/main/java/com/signnow/javasampleapp/config/SecurityHeadersFilter.java b/src/main/java/com/signnow/javasampleapp/config/SecurityHeadersFilter.java new file mode 100644 index 0000000..26b039b --- /dev/null +++ b/src/main/java/com/signnow/javasampleapp/config/SecurityHeadersFilter.java @@ -0,0 +1,31 @@ +package com.signnow.javasampleapp.config; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class SecurityHeadersFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + response.setHeader("X-Content-Type-Options", "nosniff"); + response.setHeader("X-Frame-Options", "SAMEORIGIN"); + response.setHeader("Referrer-Policy", "strict-origin-when-cross-origin"); + response.setHeader("Permissions-Policy", "geolocation=(), microphone=(), camera=()"); + if (request.isSecure()) { + response.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains"); + } + filterChain.doFilter(request, response); + } +} diff --git a/src/main/java/com/signnow/javasampleapp/controllers/GlobalExceptionHandler.java b/src/main/java/com/signnow/javasampleapp/controllers/GlobalExceptionHandler.java new file mode 100644 index 0000000..306ec6e --- /dev/null +++ b/src/main/java/com/signnow/javasampleapp/controllers/GlobalExceptionHandler.java @@ -0,0 +1,55 @@ +package com.signnow.javasampleapp.controllers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.signnow.core.exception.SignNowApiException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +import java.util.LinkedHashMap; +import java.util.Map; + +@ControllerAdvice +public class GlobalExceptionHandler { + + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + @ExceptionHandler(HttpMessageNotReadableException.class) + public ResponseEntity> handleMalformedBody(HttpMessageNotReadableException e) { + log.warn("Malformed request body: {}", e.getMostSpecificCause().getMessage()); + return problem(HttpStatus.BAD_REQUEST, "Malformed request body"); + } + + @ExceptionHandler({JsonProcessingException.class, IllegalArgumentException.class, ClassCastException.class}) + public ResponseEntity> handleBadInput(Exception e) { + log.warn("Rejected request due to invalid input: {}", e.getMessage()); + return problem(HttpStatus.BAD_REQUEST, "Invalid request payload"); + } + + @ExceptionHandler(SignNowApiException.class) + public ResponseEntity> handleSignNowApi(SignNowApiException e) { + log.error("SignNow API call failed", e); + return problem(HttpStatus.BAD_GATEWAY, "Upstream SignNow API error"); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity> handleUnexpected(Exception e) { + log.error("Unhandled exception", e); + return problem(HttpStatus.INTERNAL_SERVER_ERROR, "Internal server error"); + } + + private ResponseEntity> problem(HttpStatus status, String message) { + Map body = new LinkedHashMap<>(); + body.put("success", false); + body.put("status", status.value()); + body.put("message", message); + return ResponseEntity.status(status) + .contentType(MediaType.APPLICATION_JSON) + .body(body); + } +} diff --git a/src/main/java/com/signnow/javasampleapp/controllers/RoutingController.java b/src/main/java/com/signnow/javasampleapp/controllers/RoutingController.java index e6475d1..5414b62 100644 --- a/src/main/java/com/signnow/javasampleapp/controllers/RoutingController.java +++ b/src/main/java/com/signnow/javasampleapp/controllers/RoutingController.java @@ -1,7 +1,11 @@ package com.signnow.javasampleapp.controllers; import com.fasterxml.jackson.databind.ObjectMapper; +import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @@ -9,107 +13,91 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.regex.Pattern; @Controller public class RoutingController { + private static final Logger log = LoggerFactory.getLogger(RoutingController.class); + + private static final Pattern EXAMPLE_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_]+$"); + private static final String CONTROLLER_PACKAGE = "com.signnow.samples."; + private static final String CONTROLLER_CLASS = ".IndexController"; + private static final String FALLBACK_ERROR_HTML = + "

404 - Page Not Found

"; + @GetMapping("/") public ResponseEntity root() throws IOException { - try (var inputStream = getClass().getResourceAsStream("/static/error.html")) { - if (inputStream == null) { - return ResponseEntity.status(404) - .header("Content-Type", "text/html") - .body("

404 - Page Not Found

"); - } - String html = new String(inputStream.readAllBytes()); - return ResponseEntity.status(404) - .header("Content-Type", "text/html") - .body(html); - } + return notFound(); } @GetMapping("/samples/{exampleName}") public ResponseEntity routeExample( @PathVariable String exampleName, @RequestParam Map queryParams - ) throws IOException { - if (!exampleName.matches("^[a-zA-Z0-9_]+$")) { - try (var inputStream = getClass().getResourceAsStream("/static/error.html")) { - if (inputStream == null) { - return ResponseEntity.status(404) - .header("Content-Type", "text/html") - .body("

404 - Page Not Found

"); - } - String html = new String(inputStream.readAllBytes()); - return ResponseEntity.status(404) - .header("Content-Type", "text/html") - .body(html); - } + ) throws IOException, SignNowApiException { + if (!EXAMPLE_NAME_PATTERN.matcher(exampleName).matches()) { + log.warn("Rejected sample GET with invalid name: {}", exampleName); + return notFound(); } - String controllerPath = "com.signnow.samples." + exampleName + ".IndexController"; try { - Class controllerClass = Class.forName(controllerPath); - Object controllerInstance = controllerClass.getDeclaredConstructor().newInstance(); - if (controllerInstance instanceof ExampleInterface) { - return ((ExampleInterface) controllerInstance).handleGet(queryParams); + ExampleInterface controller = loadExampleController(exampleName); + if (controller != null) { + return controller.handleGet(queryParams); } + } catch (ReflectiveOperationException e) { + log.error("Failed to instantiate example controller '{}'", exampleName, e); } catch (Exception e) { - // Log the error - System.err.println("Error loading example controller: " + e.getMessage()); - } - try (var inputStream = getClass().getResourceAsStream("/static/error.html")) { - if (inputStream == null) { - return ResponseEntity.status(404) - .header("Content-Type", "text/html") - .body("

404 - Page Not Found

"); - } - String html = new String(inputStream.readAllBytes()); - return ResponseEntity.status(404) - .header("Content-Type", "text/html") - .body(html); + log.error("Example '{}' failed to handle GET", exampleName, e); + throw e; } + return notFound(); } @PostMapping("/api/samples/{exampleName}") public ResponseEntity handleFormSubmission( @PathVariable String exampleName, - @RequestBody String formData) throws IOException { - if (!exampleName.matches("^[a-zA-Z0-9_]+$")) { - try (var inputStream = getClass().getResourceAsStream("/static/error.html")) { - if (inputStream == null) { - return ResponseEntity.status(404) - .header("Content-Type", "text/html") - .body("

404 - Page Not Found

"); - } - String html = new String(inputStream.readAllBytes()); - return ResponseEntity.status(404) - .header("Content-Type", "text/html") - .body(html); - } + @RequestBody String formData) throws IOException, SignNowApiException { + if (!EXAMPLE_NAME_PATTERN.matcher(exampleName).matches()) { + log.warn("Rejected sample POST with invalid name: {}", exampleName); + return notFound(); } - String controllerPath = "com.signnow.samples." + exampleName + ".IndexController"; try { - Class controllerClass = Class.forName(controllerPath); - Object controllerInstance = controllerClass.getDeclaredConstructor().newInstance(); - if (controllerInstance instanceof ExampleInterface) { - return ((ExampleInterface) controllerInstance).handlePost(formData); + ExampleInterface controller = loadExampleController(exampleName); + if (controller != null) { + return controller.handlePost(formData); } - } catch (Exception e) { + } catch (ReflectiveOperationException e) { + log.error("Failed to instantiate example controller '{}'", exampleName, e); Map errorResponse = new HashMap<>(); - errorResponse.put("error", e.getMessage()); + errorResponse.put("error", "Example not available"); return ResponseEntity.status(500) - .header("Content-Type", "application/json") + .contentType(MediaType.APPLICATION_JSON) .body(new ObjectMapper().writeValueAsString(errorResponse)); + } catch (Exception e) { + log.error("Example '{}' failed to handle POST", exampleName, e); + throw e; } + return notFound(); + } + + private ExampleInterface loadExampleController(String exampleName) throws ReflectiveOperationException { + String controllerPath = CONTROLLER_PACKAGE + exampleName + CONTROLLER_CLASS; + Class controllerClass = Class.forName(controllerPath); + Object controllerInstance = controllerClass.getDeclaredConstructor().newInstance(); + if (controllerInstance instanceof ExampleInterface example) { + return example; + } + return null; + } + + private ResponseEntity notFound() throws IOException { try (var inputStream = getClass().getResourceAsStream("/static/error.html")) { - if (inputStream == null) { - return ResponseEntity.status(404) - .header("Content-Type", "text/html") - .body("

404 - Page Not Found

"); - } - String html = new String(inputStream.readAllBytes()); + String html = inputStream == null + ? FALLBACK_ERROR_HTML + : new String(inputStream.readAllBytes()); return ResponseEntity.status(404) - .header("Content-Type", "text/html") + .contentType(MediaType.TEXT_HTML) .body(html); } } diff --git a/src/main/java/com/signnow/samples/EmbeddedEditingAndSigningDG/IndexController.java b/src/main/java/com/signnow/samples/EmbeddedEditingAndSigningDG/IndexController.java index d8f388a..e3bf8ab 100644 --- a/src/main/java/com/signnow/samples/EmbeddedEditingAndSigningDG/IndexController.java +++ b/src/main/java/com/signnow/samples/EmbeddedEditingAndSigningDG/IndexController.java @@ -14,6 +14,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import com.signnow.api.document.response.DocumentDownloadGetResponse; @@ -198,7 +199,7 @@ private void updateDocumentGroupRecipients(ApiClient client, String documentGrou } private String createEmbeddedEditLink(ApiClient client, String documentGroupId) throws SignNowApiException { - String redirectUrl = "http://localhost:8080/samples/EmbeddedEditingAndSigningDG?page=page2-embedded-sending&document_group_id=" + documentGroupId; + String redirectUrl = AppConfig.sampleUrl("EmbeddedEditingAndSigningDG") + "?page=page2-embedded-sending&document_group_id=" + documentGroupId; var request = new com.signnow.api.embeddededitor.request.DocumentGroupEmbeddedEditorLinkPostRequest( redirectUrl, "self", 15 @@ -218,7 +219,7 @@ private String createEmbeddedInvite(ApiClient client, String documentGroupId, St String recipient1Email = findEmailByRoleName(recipientsResp, ROLE_RECIPIENT_1); String recipient2Email = findEmailByRoleName(recipientsResp, ROLE_RECIPIENT_2); - String redirectUrl = "http://localhost:8080/samples/EmbeddedEditingAndSigningDG?page=page4-status-download&document_group_id=" + documentGroupId; + String redirectUrl = AppConfig.sampleUrl("EmbeddedEditingAndSigningDG") + "?page=page4-status-download&document_group_id=" + documentGroupId; // -- Build invite list java.util.List inviteSteps = new LinkedList<>(); diff --git a/src/main/java/com/signnow/samples/EmbeddedSenderWithFormAndFirstSigner/IndexController.java b/src/main/java/com/signnow/samples/EmbeddedSenderWithFormAndFirstSigner/IndexController.java index 948fe3f..e1e5fdd 100644 --- a/src/main/java/com/signnow/samples/EmbeddedSenderWithFormAndFirstSigner/IndexController.java +++ b/src/main/java/com/signnow/samples/EmbeddedSenderWithFormAndFirstSigner/IndexController.java @@ -10,6 +10,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -39,10 +40,7 @@ @Controller public class IndexController implements ExampleInterface { - // Demo configuration - in production, these would come from environment/config private static final String DOCUMENT_GROUP_TEMPLATE_ID = "8e36720a436041ea837dc543ec00a3bc3559df45"; - private static final String USER_EMAIL = "example@example.com"; // Demo user - private static final String USER_PASSWORD = "example"; // Demo password // Demo field names that might exist in templates private static final String CUSTOMER_NAME_FIELD = "CustomerName"; @@ -52,8 +50,6 @@ public class IndexController implements ExampleInterface { private static final String PREPARE_CONTRACT_ROLE = "Prepare Contract"; private static final String CUSTOMER_SIGN_ROLE = "Customer to Sign"; - // Demo URL constants - private static final String REDIRECT_BASE_URL = "http://localhost:8080/samples/EmbeddedSenderWithFormAndFirstSigner"; private static final String SIGNING_URL_BASE = "https://app.signnow.com/webapp/documentgroup/signing"; @Override @@ -126,7 +122,7 @@ private ResponseEntity prepareDocumentGroup(Map data, ApiClie // Demo: Step 3 - Add recipients to Document Group with different emails for different roles String customerEmail = email; // Email from form for "Customer to Sign" - String preparerEmail = USER_EMAIL; // Email from config for "Prepare Contract" + String preparerEmail = AppConfig.demoUserEmail(); // Email from config for "Prepare Contract" Map addRecipientsResponse = updateDocumentGroupRecipients( client, documentGroupId, customerEmail, preparerEmail ); @@ -339,7 +335,7 @@ private String getEmailForRole(String recipientName, String customerEmail, Strin private Map createEmbeddedSendingUrl(ApiClient client, String documentGroupId) throws SignNowApiException { // Demo: Create embedded sending URL with redirect back to demo app - String redirectUrl = REDIRECT_BASE_URL + "?page=signing-page&document_group_id=" + documentGroupId; + String redirectUrl = AppConfig.sampleUrl("EmbeddedSenderWithFormAndFirstSigner") + "?page=signing-page&document_group_id=" + documentGroupId; var embeddedSendingRequest = new com.signnow.api.embeddedsending.request.DocumentGroupEmbeddedSendingLinkPostRequest( redirectUrl, // Demo: Redirect back to our demo app @@ -372,7 +368,7 @@ private Map createSigningLink(ApiClient client, String documentG String accessToken = (String) limitedTokenResponse.get("access_token"); // Demo: Build signing URL with parameters - redirect to status page after signing - String redirectUrl = REDIRECT_BASE_URL + "?page=status-page&document_group_id=" + documentGroupId; + String redirectUrl = AppConfig.sampleUrl("EmbeddedSenderWithFormAndFirstSigner") + "?page=status-page&document_group_id=" + documentGroupId; String signingUrl = SIGNING_URL_BASE + "?" + "document_group_id=" + documentGroupId + @@ -390,8 +386,8 @@ private Map createSigningLink(ApiClient client, String documentG private Map generateLimitedToken(ApiClient client, String documentGroupId) throws SignNowApiException { // Demo: Create token request with limited scope for document group var tokenRequest = new com.signnow.api.auth.request.TokenPostRequest( - USER_EMAIL, - USER_PASSWORD, + AppConfig.demoUserEmail(), + AppConfig.demoUserPassword(), "limited_signer_scope_token_for_document_group_invite/" + documentGroupId, "password", "" diff --git a/src/main/java/com/signnow/samples/EmbeddedSenderWithFormCreditLoanAgreement/IndexController.java b/src/main/java/com/signnow/samples/EmbeddedSenderWithFormCreditLoanAgreement/IndexController.java index 9f64ebb..dc2bf45 100644 --- a/src/main/java/com/signnow/samples/EmbeddedSenderWithFormCreditLoanAgreement/IndexController.java +++ b/src/main/java/com/signnow/samples/EmbeddedSenderWithFormCreditLoanAgreement/IndexController.java @@ -14,6 +14,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import com.signnow.api.document.response.DocumentDownloadGetResponse; @@ -98,7 +99,7 @@ private void prefillFields(ApiClient client, String documentId, String fullName) } private String getEmbeddedSendingLink(ApiClient client, String documentId) throws SignNowApiException { - String redirectUrl = "http://localhost:8080/samples/EmbeddedSenderWithFormCreditLoanAgreement?page=download-with-status&document_id=" + documentId; + String redirectUrl = AppConfig.sampleUrl("EmbeddedSenderWithFormCreditLoanAgreement") + "?page=download-with-status&document_id=" + documentId; DocumentEmbeddedSendingLinkPostRequest request = new DocumentEmbeddedSendingLinkPostRequest("invite", redirectUrl, 16, "self"); request.withDocumentId(documentId); diff --git a/src/main/java/com/signnow/samples/EmbeddedSenderWithFormDG/IndexController.java b/src/main/java/com/signnow/samples/EmbeddedSenderWithFormDG/IndexController.java index d3fab8b..50f014e 100644 --- a/src/main/java/com/signnow/samples/EmbeddedSenderWithFormDG/IndexController.java +++ b/src/main/java/com/signnow/samples/EmbeddedSenderWithFormDG/IndexController.java @@ -10,6 +10,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -39,19 +40,12 @@ @Controller public class IndexController implements ExampleInterface { - // Demo configuration - in production, these would come from environment/config private static final String DOCUMENT_GROUP_TEMPLATE_ID = "c8040bbc40804b89b63a0fa8c79b42a7ae4818c1"; - private static final String USER_EMAIL = "example@example.com"; // Demo user - - // Demo field names that might exist in templates // Demo recipient roles private static final String PREPARE_CONTRACT_ROLE = "Prepare Contract"; private static final String CUSTOMER_SIGN_ROLE = "Customer to Sign"; - // Demo URL constants - private static final String REDIRECT_BASE_URL = "http://localhost:8080/samples/EmbeddedSenderWithFormDG"; - @Override public ResponseEntity handleGet(Map queryParams) throws IOException { // Demo: Simple HTML page serving - use classpath resource @@ -121,7 +115,7 @@ private ResponseEntity prepareDocumentGroup(Map data, ApiClie // Demo: Step 3 - Add recipients to Document Group with different emails for different roles String customerEmail = email; // Email from form for "Customer to Sign" - String preparerEmail = USER_EMAIL; // Email from config for "Prepare Contract" + String preparerEmail = AppConfig.demoUserEmail(); // Email from config for "Prepare Contract" Map addRecipientsResponse = updateDocumentGroupRecipients( client, documentGroupId, customerEmail, preparerEmail ); @@ -324,7 +318,7 @@ private Map updateDocumentGroupRecipients( private Map createEmbeddedSendingUrl(ApiClient client, String documentGroupId) throws SignNowApiException { // Demo: Create embedded sending URL with redirect back to status page - String redirectUrl = REDIRECT_BASE_URL + "?" + + String redirectUrl = AppConfig.sampleUrl("EmbeddedSenderWithFormDG") + "?" + "page=status-page&document_group_id=" + documentGroupId; var embeddedSendingRequest = new com.signnow.api.embeddedsending.request.DocumentGroupEmbeddedSendingLinkPostRequest( diff --git a/src/main/java/com/signnow/samples/EmbeddedSenderWithFormDGAdjunct/IndexController.java b/src/main/java/com/signnow/samples/EmbeddedSenderWithFormDGAdjunct/IndexController.java index 7424ad6..108f171 100644 --- a/src/main/java/com/signnow/samples/EmbeddedSenderWithFormDGAdjunct/IndexController.java +++ b/src/main/java/com/signnow/samples/EmbeddedSenderWithFormDGAdjunct/IndexController.java @@ -10,6 +10,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -40,19 +41,12 @@ @Controller public class IndexController implements ExampleInterface { - // Demo configuration - in production, these would come from environment/config private static final String DOCUMENT_GROUP_TEMPLATE_ID = "e486ebf3fb814d55888bcbf75ceaf62b6db0de5a"; - private static final String USER_EMAIL = "example@example.com"; // Demo user - - // Demo field names that might exist in templates // Demo recipient roles private static final String PREPARE_CONTRACT_ROLE = "Prepare Contract"; private static final String CUSTOMER_SIGN_ROLE = "Customer to Sign"; - // Demo URL constants - private static final String REDIRECT_BASE_URL = "http://localhost:8080/samples/EmbeddedSenderWithFormDGAdjunct"; - @Override public ResponseEntity handleGet(Map queryParams) throws IOException { // Demo: Simple HTML page serving @@ -122,7 +116,7 @@ private ResponseEntity prepareDocumentGroup(Map data, ApiClie // Demo: Step 3 - Add recipients to Document Group with different emails for different roles String customerEmail = email; // Email from form for "Customer to Sign" - String preparerEmail = USER_EMAIL; // Email from config for "Prepare Contract" + String preparerEmail = AppConfig.demoUserEmail(); // Email from config for "Prepare Contract" Map addRecipientsResponse = updateDocumentGroupRecipients( client, documentGroupId, customerEmail, preparerEmail ); @@ -325,7 +319,7 @@ private Map updateDocumentGroupRecipients( private Map createEmbeddedSendingUrl(ApiClient client, String documentGroupId) throws SignNowApiException { // Demo: Create embedded sending URL with redirect back to status page - String redirectUrl = REDIRECT_BASE_URL + "?" + + String redirectUrl = AppConfig.sampleUrl("EmbeddedSenderWithFormDGAdjunct") + "?" + "page=status-page&document_group_id=" + documentGroupId; var embeddedSendingRequest = new com.signnow.api.embeddedsending.request.DocumentGroupEmbeddedSendingLinkPostRequest( diff --git a/src/main/java/com/signnow/samples/EmbeddedSenderWithFormDGConstr/IndexController.java b/src/main/java/com/signnow/samples/EmbeddedSenderWithFormDGConstr/IndexController.java index 8b5a413..2fd2202 100644 --- a/src/main/java/com/signnow/samples/EmbeddedSenderWithFormDGConstr/IndexController.java +++ b/src/main/java/com/signnow/samples/EmbeddedSenderWithFormDGConstr/IndexController.java @@ -10,6 +10,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -40,19 +41,12 @@ @Controller public class IndexController implements ExampleInterface { - // Demo configuration - in production, these would come from environment/config private static final String DOCUMENT_GROUP_TEMPLATE_ID = "625ae557229d48d78728c281f46d3d8494f235a4"; - private static final String USER_EMAIL = "example@example.com"; // Demo user - - // Demo field names that might exist in templates // Demo recipient roles private static final String PREPARE_CONTRACT_ROLE = "Prepare Contract"; private static final String CUSTOMER_SIGN_ROLE = "Customer to Sign"; - // Demo URL constants - private static final String REDIRECT_BASE_URL = "http://localhost:8080/samples/EmbeddedSenderWithFormDGConstr"; - @Override public ResponseEntity handleGet(Map queryParams) throws IOException { // Demo: Simple HTML page serving @@ -122,7 +116,7 @@ private ResponseEntity prepareDocumentGroup(Map data, ApiClie // Demo: Step 3 - Add recipients to Document Group with different emails for different roles String customerEmail = email; // Email from form for "Customer to Sign" - String preparerEmail = USER_EMAIL; // Email from config for "Prepare Contract" + String preparerEmail = AppConfig.demoUserEmail(); // Email from config for "Prepare Contract" Map addRecipientsResponse = updateDocumentGroupRecipients( client, documentGroupId, customerEmail, preparerEmail ); @@ -325,7 +319,7 @@ private Map updateDocumentGroupRecipients( private Map createEmbeddedSendingUrl(ApiClient client, String documentGroupId) throws SignNowApiException { // Demo: Create embedded sending URL with redirect back to status page - String redirectUrl = REDIRECT_BASE_URL + "?" + + String redirectUrl = AppConfig.sampleUrl("EmbeddedSenderWithFormDGConstr") + "?" + "page=status-page&document_group_id=" + documentGroupId; var embeddedSendingRequest = new com.signnow.api.embeddedsending.request.DocumentGroupEmbeddedSendingLinkPostRequest( diff --git a/src/main/java/com/signnow/samples/EmbeddedSenderWithoutFormFile/IndexController.java b/src/main/java/com/signnow/samples/EmbeddedSenderWithoutFormFile/IndexController.java index 7de60e3..5f2293b 100644 --- a/src/main/java/com/signnow/samples/EmbeddedSenderWithoutFormFile/IndexController.java +++ b/src/main/java/com/signnow/samples/EmbeddedSenderWithoutFormFile/IndexController.java @@ -11,6 +11,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import com.signnow.api.document.response.DocumentDownloadGetResponse; @@ -76,7 +77,7 @@ private String getEmbeddedSendingLink(ApiClient client) throws SignNowApiExcepti CloneTemplatePostResponse cloneResponse = createDocumentFromTemplate(client); String documentId = cloneResponse.getId(); - String redirectUrl = "http://localhost:8080/samples/EmbeddedSenderWithoutFormFile?page=download-with-status&document_id=" + documentId; + String redirectUrl = AppConfig.sampleUrl("EmbeddedSenderWithoutFormFile") + "?page=download-with-status&document_id=" + documentId; DocumentEmbeddedSendingLinkPostRequest request = new DocumentEmbeddedSendingLinkPostRequest("document", redirectUrl, 16, "self"); request.withDocumentId(documentId); diff --git a/src/main/java/com/signnow/samples/EmbeddedSignerConsentForm/IndexController.java b/src/main/java/com/signnow/samples/EmbeddedSignerConsentForm/IndexController.java index 8119e9f..1d66276 100644 --- a/src/main/java/com/signnow/samples/EmbeddedSignerConsentForm/IndexController.java +++ b/src/main/java/com/signnow/samples/EmbeddedSignerConsentForm/IndexController.java @@ -17,6 +17,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -87,7 +88,7 @@ private String getEmbeddedInviteLink(ApiClient client, String documentId, String DocumentInviteLinkPostResponse embeddedInviteResponse = (DocumentInviteLinkPostResponse) client.send(embeddedInvite).getResponse(); - String redirectUrl = "http://localhost:8080/samples/EmbeddedSignerConsentForm?page=download-container&document_id=" + documentId; + String redirectUrl = AppConfig.sampleUrl("EmbeddedSignerConsentForm") + "?page=download-container&document_id=" + documentId; return embeddedInviteResponse.getData().getLink() + "&redirect_uri=" + java.net.URLEncoder.encode(redirectUrl, "UTF-8"); } diff --git a/src/main/java/com/signnow/samples/EmbeddedSignerConsumerServices/IndexController.java b/src/main/java/com/signnow/samples/EmbeddedSignerConsumerServices/IndexController.java index bd9a9d0..a5ad43c 100644 --- a/src/main/java/com/signnow/samples/EmbeddedSignerConsumerServices/IndexController.java +++ b/src/main/java/com/signnow/samples/EmbeddedSignerConsumerServices/IndexController.java @@ -19,6 +19,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -154,7 +155,7 @@ private String getEmbeddedInviteLink(ApiClient client, String documentId, String DocumentInviteLinkPostResponse embeddedInviteResponse = (DocumentInviteLinkPostResponse) client.send(embeddedInvite).getResponse(); - String redirectUrl = "http://localhost:8080/samples/EmbeddedSignerConsumerServices?page=finish&document_id=" + documentId; + String redirectUrl = AppConfig.sampleUrl("EmbeddedSignerConsumerServices") + "?page=finish&document_id=" + documentId; return embeddedInviteResponse.getData().getLink() + "&redirect_uri=" + java.net.URLEncoder.encode(redirectUrl, "UTF-8"); } diff --git a/src/main/java/com/signnow/samples/EmbeddedSignerPatientIntakeForm/IndexController.java b/src/main/java/com/signnow/samples/EmbeddedSignerPatientIntakeForm/IndexController.java index 7dea0b9..f84c31f 100644 --- a/src/main/java/com/signnow/samples/EmbeddedSignerPatientIntakeForm/IndexController.java +++ b/src/main/java/com/signnow/samples/EmbeddedSignerPatientIntakeForm/IndexController.java @@ -19,6 +19,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -160,7 +161,7 @@ private String getEmbeddedInviteLink(ApiClient client, String documentId, String DocumentInviteLinkPostResponse embeddedInviteResponse = (DocumentInviteLinkPostResponse) client.send(embeddedInvite).getResponse(); - String redirectUrl = "http://localhost:8080/samples/EmbeddedSignerPatientIntakeForm?page=download-container&document_id=" + documentId; + String redirectUrl = AppConfig.sampleUrl("EmbeddedSignerPatientIntakeForm") + "?page=download-container&document_id=" + documentId; return embeddedInviteResponse.getData().getLink() + "&redirect_uri=" + java.net.URLEncoder.encode(redirectUrl, "UTF-8"); } diff --git a/src/main/java/com/signnow/samples/EmbeddedSignerWithFormInsurance/IndexController.java b/src/main/java/com/signnow/samples/EmbeddedSignerWithFormInsurance/IndexController.java index d2c8753..ee3df4c 100644 --- a/src/main/java/com/signnow/samples/EmbeddedSignerWithFormInsurance/IndexController.java +++ b/src/main/java/com/signnow/samples/EmbeddedSignerWithFormInsurance/IndexController.java @@ -18,6 +18,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import com.signnow.api.document.response.DocumentDownloadGetResponse; @@ -108,7 +109,7 @@ private String getEmbeddedInviteLink(ApiClient client, String documentId, String DocumentInviteLinkPostResponse embeddedInviteResponse = (DocumentInviteLinkPostResponse) client.send(embeddedInvite).getResponse(); - String redirectUrl = "http://localhost:8080/samples/EmbeddedSignerWithFormInsurance?page=download-container&document_id=" + documentId; + String redirectUrl = AppConfig.sampleUrl("EmbeddedSignerWithFormInsurance") + "?page=download-container&document_id=" + documentId; return embeddedInviteResponse.getData().getLink() + "&redirect_uri=" + java.net.URLEncoder.encode(redirectUrl, "UTF-8"); } diff --git a/src/main/java/com/signnow/samples/HROnboardingSystem/IndexController.java b/src/main/java/com/signnow/samples/HROnboardingSystem/IndexController.java index 9c7da8a..f0505b0 100644 --- a/src/main/java/com/signnow/samples/HROnboardingSystem/IndexController.java +++ b/src/main/java/com/signnow/samples/HROnboardingSystem/IndexController.java @@ -29,6 +29,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -57,10 +58,6 @@ @Controller public class IndexController implements ExampleInterface { - // Demo configuration - in production, these would come from environment/config - private static final String USER_EMAIL = "example@example.com"; // Demo user - private static final String USER_PASSWORD = "example"; // Demo password - // Template IDs for HR onboarding documents private static final String I9_FORM_TEMPLATE_ID = "940989288b8b4c62a950b908333b5b21efd6a174"; private static final String NDA_TEMPLATE_ID = "a4f523d0cb234ffc99b0badc9e6f59111f76abc2"; @@ -72,9 +69,6 @@ public class IndexController implements ExampleInterface { private static final String TEXT_FIELD_156 = "Text Field 156"; private static final String EMAIL_FIELD = "Email"; - // Demo URL constants - private static final String REDIRECT_BASE_URL = "http://localhost:8080/samples/HROnboardingSystem"; - @Override public ResponseEntity handleGet(Map queryParams) throws IOException { // Demo: Simple HTML page serving - use classpath resource @@ -279,7 +273,7 @@ private Map sendInvite(ApiClient client, String documentGroupId, null, null, null, - REDIRECT_BASE_URL + "?page=status-page&document_group_id=" + documentGroupId, + AppConfig.sampleUrl("HROnboardingSystem") + "?page=status-page&document_group_id=" + documentGroupId, null, "self", null diff --git a/src/main/java/com/signnow/samples/ISVWithFormAndOneClickSendBasicPrefill/IndexController.java b/src/main/java/com/signnow/samples/ISVWithFormAndOneClickSendBasicPrefill/IndexController.java index 69d9623..d5ab9aa 100644 --- a/src/main/java/com/signnow/samples/ISVWithFormAndOneClickSendBasicPrefill/IndexController.java +++ b/src/main/java/com/signnow/samples/ISVWithFormAndOneClickSendBasicPrefill/IndexController.java @@ -10,6 +10,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -38,18 +39,12 @@ @Controller public class IndexController implements ExampleInterface { - // Demo configuration - in production, these would come from environment/config private static final String DOCUMENT_GROUP_TEMPLATE_ID = "6e79b9e6f9624984a7f054a7171d1644d0fb9934"; - private static final String USER_EMAIL = "example@example.com"; // Demo user - private static final String USER_PASSWORD = "example"; // Demo password // Demo field names that might exist in templates private static final String NAME_FIELD = "Name"; private static final String EMAIL_FIELD = "Email"; - // Demo URL constants - private static final String REDIRECT_BASE_URL = "http://localhost:8080/samples/ISVWithFormAndOneClickSendBasicPrefill"; - @Override public ResponseEntity handleGet(Map queryParams) throws IOException { // Demo: Simple HTML page serving @@ -267,7 +262,7 @@ private Map sendInvite(ApiClient client, String documentGroupId, null, null, null, - REDIRECT_BASE_URL + "?page=status-page&document_group_id=" + documentGroupId, // redirectUri + AppConfig.sampleUrl("ISVWithFormAndOneClickSendBasicPrefill") + "?page=status-page&document_group_id=" + documentGroupId, // redirectUri null, "self", // redirectTarget null diff --git a/src/main/java/com/signnow/samples/ISVWithFormAndOneClickSendMergeFields/IndexController.java b/src/main/java/com/signnow/samples/ISVWithFormAndOneClickSendMergeFields/IndexController.java index d6c06bc..c4c285d 100644 --- a/src/main/java/com/signnow/samples/ISVWithFormAndOneClickSendMergeFields/IndexController.java +++ b/src/main/java/com/signnow/samples/ISVWithFormAndOneClickSendMergeFields/IndexController.java @@ -12,6 +12,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -41,10 +42,7 @@ @Controller public class IndexController implements ExampleInterface { - // Demo configuration - in production, these would come from environment/config private static final String DOCUMENT_GROUP_TEMPLATE_ID = "8e36720a436041ea837dc543ec00a3bc3559df45"; - private static final String USER_EMAIL = "example@example.com"; // Demo user - private static final String USER_PASSWORD = "example"; // Demo password // Demo field names that might exist in templates private static final String CUSTOMER_NAME_FIELD = "CustomerName"; @@ -54,9 +52,6 @@ public class IndexController implements ExampleInterface { private static final String PREPARE_CONTRACT_ROLE = "Prepare Contract"; private static final String CUSTOMER_SIGN_ROLE = "Customer to Sign"; - // Demo URL constants - private static final String REDIRECT_BASE_URL = "http://localhost:8080/samples/ISVWithFormAndOneClickSendMergeFields"; - @Override public ResponseEntity handleGet(Map queryParams) throws IOException { // Demo: Simple HTML page serving - use classpath resource @@ -386,7 +381,7 @@ private Map sendInvite(ApiClient client, String documentGroupId, emailToUse = email; // Use form email for "Prepare Contract" } else if (CUSTOMER_SIGN_ROLE.equals(recipient.getName())) { // Use config email for "Customer to Sign" - emailToUse = USER_EMAIL; + emailToUse = AppConfig.demoUserEmail(); } // Create invite email for this recipient @@ -411,7 +406,7 @@ private Map sendInvite(ApiClient client, String documentGroupId, null, null, null, - REDIRECT_BASE_URL + "?page=status-page&document_group_id=" + documentGroupId, // redirectUri + AppConfig.sampleUrl("ISVWithFormAndOneClickSendMergeFields") + "?page=status-page&document_group_id=" + documentGroupId, // redirectUri null, "self", // redirectTarget null diff --git a/src/main/java/com/signnow/samples/MedicalInsuranceClaimForm/IndexController.java b/src/main/java/com/signnow/samples/MedicalInsuranceClaimForm/IndexController.java index 3331810..87e8afa 100644 --- a/src/main/java/com/signnow/samples/MedicalInsuranceClaimForm/IndexController.java +++ b/src/main/java/com/signnow/samples/MedicalInsuranceClaimForm/IndexController.java @@ -21,6 +21,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -170,7 +171,7 @@ private String getEmbeddedInviteLink(ApiClient client, String documentId, String DocumentInviteLinkPostResponse embeddedInviteResponse = (DocumentInviteLinkPostResponse) client.send(embeddedInvite).getResponse(); - String redirectUrl = "http://localhost:8080/samples/MedicalInsuranceClaimForm?page=download-container&document_id=" + documentId; + String redirectUrl = AppConfig.sampleUrl("MedicalInsuranceClaimForm") + "?page=download-container&document_id=" + documentId; return embeddedInviteResponse.getData().getLink() + "&redirect_uri=" + java.net.URLEncoder.encode(redirectUrl, "UTF-8"); } diff --git a/src/main/java/com/signnow/samples/PrefillAndEmbeddedSendingAgreement/IndexController.java b/src/main/java/com/signnow/samples/PrefillAndEmbeddedSendingAgreement/IndexController.java index 5f72e3b..fed4802 100644 --- a/src/main/java/com/signnow/samples/PrefillAndEmbeddedSendingAgreement/IndexController.java +++ b/src/main/java/com/signnow/samples/PrefillAndEmbeddedSendingAgreement/IndexController.java @@ -14,6 +14,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import com.signnow.api.document.response.DocumentDownloadGetResponse; @@ -97,7 +98,7 @@ private void prefillFields(ApiClient client, String documentId, String name) thr } private String getEmbeddedSendingLink(ApiClient client, String documentId) throws SignNowApiException { - String redirectUrl = "http://localhost:8080/samples/PrefillAndEmbeddedSendingAgreement?page=download-with-status&document_id=" + documentId; + String redirectUrl = AppConfig.sampleUrl("PrefillAndEmbeddedSendingAgreement") + "?page=download-with-status&document_id=" + documentId; DocumentEmbeddedSendingLinkPostRequest request = new DocumentEmbeddedSendingLinkPostRequest("document", redirectUrl, 16, "self"); request.withDocumentId(documentId); diff --git a/src/main/java/com/signnow/samples/UploadEmbeddedSender/IndexController.java b/src/main/java/com/signnow/samples/UploadEmbeddedSender/IndexController.java index 42ee12c..3c3ad8c 100644 --- a/src/main/java/com/signnow/samples/UploadEmbeddedSender/IndexController.java +++ b/src/main/java/com/signnow/samples/UploadEmbeddedSender/IndexController.java @@ -20,6 +20,7 @@ import com.signnow.core.ApiClient; import com.signnow.core.exception.SignNowApiException; import com.signnow.javasampleapp.ExampleInterface; +import com.signnow.javasampleapp.config.AppConfig; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -143,7 +144,7 @@ private Map createDocumentGroup(ApiClient client, String documen private Map createEmbeddedSendingUrl(ApiClient client, String documentGroupId) throws SignNowApiException { // Return URL after embedded sending to redirect to status page - String redirectUrl = "http://localhost:8080/samples/UploadEmbeddedSender?" + + String redirectUrl = AppConfig.sampleUrl("UploadEmbeddedSender") + "?" + "page=status-page&document_group_id=" + documentGroupId; DocumentGroupEmbeddedSendingLinkPostRequest embeddedSendingRequest = new DocumentGroupEmbeddedSendingLinkPostRequest( diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 0379837..a13a71e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,3 +1,17 @@ spring.application.name=java-sample-app +server.port=${PORT:8080} server.error.whitelabel.enabled=false -signnow.api.signer_email=test@example.com + +# App configuration +app.base-url=${APP_BASE_URL:http://localhost:8080} +signnow.api.signer_email=${SIGNNOW_SIGNER_EMAIL:test@example.com} +signnow.api.demo_user_email=${SIGNNOW_DEMO_USER_EMAIL:example@example.com} +signnow.api.demo_user_password=${SIGNNOW_DEMO_USER_PASSWORD:example} + +# Actuator - restrict to health/info only +management.endpoints.web.exposure.include=health,info +management.endpoint.health.show-details=never + +# Logging +logging.level.com.signnow=INFO +logging.pattern.level=%5p [%X{traceId:-},%X{spanId:-}] diff --git a/src/main/resources/views/index.html b/src/main/resources/views/index.html index c676ca0..d762440 100644 --- a/src/main/resources/views/index.html +++ b/src/main/resources/views/index.html @@ -2,7 +2,7 @@ - + Sample Apps diff --git a/src/test/java/com/signnow/javasampleapp/controllers/RoutingControllerTest.java b/src/test/java/com/signnow/javasampleapp/controllers/RoutingControllerTest.java new file mode 100644 index 0000000..eb4efa8 --- /dev/null +++ b/src/test/java/com/signnow/javasampleapp/controllers/RoutingControllerTest.java @@ -0,0 +1,51 @@ +package com.signnow.javasampleapp.controllers; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(controllers = {RoutingController.class, GlobalExceptionHandler.class}) +class RoutingControllerTest { + + @Autowired + MockMvc mvc; + + @Test + void root_returns404ErrorPage() throws Exception { + mvc.perform(get("/")) + .andExpect(status().isNotFound()) + .andExpect(content().contentTypeCompatibleWith("text/html")); + } + + @Test + void getExample_withInvalidName_returns404() throws Exception { + mvc.perform(get("/samples/has-dash")) + .andExpect(status().isNotFound()); + } + + @Test + void getExample_withPathTraversal_returns404() throws Exception { + mvc.perform(get("/samples/..%2Fevil")) + .andExpect(status().isNotFound()); + } + + @Test + void getExample_withUnknownButValidName_returns404() throws Exception { + mvc.perform(get("/samples/NoSuchSample_xyz")) + .andExpect(status().isNotFound()); + } + + @Test + void postExample_withInvalidName_returns404() throws Exception { + mvc.perform(post("/api/samples/bad-name") + .contentType("application/json") + .content("{}")) + .andExpect(status().isNotFound()); + } +} From 7aeffb6e6536d0f0aa396253621056b6f682cdf6 Mon Sep 17 00:00:00 2001 From: Petro Vakulenko Date: Fri, 22 May 2026 17:57:16 +0200 Subject: [PATCH 2/2] Sample apps synchronization between each other --- pom.xml | 2 +- .../IndexController.java | 11 ----------- .../index.html | 18 ++++++++---------- .../samples/UploadEmbeddedSender/index.html | 18 ++++++++---------- 4 files changed, 17 insertions(+), 32 deletions(-) diff --git a/pom.xml b/pom.xml index 0772a39..776aa61 100644 --- a/pom.xml +++ b/pom.xml @@ -46,7 +46,7 @@ com.signnow signnow-java-sdk - 3.5.0 + 3.5.1 diff --git a/src/main/java/com/signnow/samples/ISVWithFormAndOneClickSendMergeFields/IndexController.java b/src/main/java/com/signnow/samples/ISVWithFormAndOneClickSendMergeFields/IndexController.java index c4c285d..e541ab7 100644 --- a/src/main/java/com/signnow/samples/ISVWithFormAndOneClickSendMergeFields/IndexController.java +++ b/src/main/java/com/signnow/samples/ISVWithFormAndOneClickSendMergeFields/IndexController.java @@ -349,17 +349,6 @@ private Map sendInvite(ApiClient client, String documentGroupId, // Get recipients to use their roles var recipientsResponse = getDocumentGroupRecipients(client, documentGroupId); - - // Debug: Print recipients response as JSON - try { - ObjectMapper mapper = new ObjectMapper(); - String jsonResponse = mapper.writeValueAsString(recipientsResponse); - System.out.println("DEBUG: DocumentGroupRecipientsGetResponse JSON:"); - System.out.println(jsonResponse); - } catch (Exception e) { - System.out.println("DEBUG: Error serializing DocumentGroupRecipientsGetResponse: " + e.getMessage()); - } - var recipients = recipientsResponse.getData().getRecipients(); // Check if we have recipients diff --git a/src/main/resources/static/samples/EmbeddedSenderWithFormAndFirstSigner/index.html b/src/main/resources/static/samples/EmbeddedSenderWithFormAndFirstSigner/index.html index 8755d97..b30f0b7 100644 --- a/src/main/resources/static/samples/EmbeddedSenderWithFormAndFirstSigner/index.html +++ b/src/main/resources/static/samples/EmbeddedSenderWithFormAndFirstSigner/index.html @@ -166,18 +166,16 @@

No signers found

// Redirect to signing URL window.location.href = result.signing_url; } else { - signingContent.innerHTML = ` -
- Error: ${result.message || 'Failed to create signing URL'} -
- `; + const errorDiv = document.createElement('div'); + errorDiv.className = 'alert alert-danger'; + errorDiv.textContent = 'Error: ' + (result.message || 'Failed to create signing URL'); + signingContent.replaceChildren(errorDiv); } } catch (error) { - signingContent.innerHTML = ` -
- Error: ${error.message} -
- `; + const errorDiv = document.createElement('div'); + errorDiv.className = 'alert alert-danger'; + errorDiv.textContent = 'Error: ' + error.message; + signingContent.replaceChildren(errorDiv); } } diff --git a/src/main/resources/static/samples/UploadEmbeddedSender/index.html b/src/main/resources/static/samples/UploadEmbeddedSender/index.html index ec95746..39ddb91 100644 --- a/src/main/resources/static/samples/UploadEmbeddedSender/index.html +++ b/src/main/resources/static/samples/UploadEmbeddedSender/index.html @@ -91,18 +91,16 @@

No signers found

// Redirect to embedded sending URL window.location.href = result.embedded_url; } else { - uploadContent.innerHTML = ` -
- Error: ${result.message || 'Failed to upload document and create embedded sending link'} -
- `; + const errorDiv = document.createElement('div'); + errorDiv.className = 'alert alert-danger'; + errorDiv.textContent = 'Error: ' + (result.message || 'Failed to upload document and create embedded sending link'); + uploadContent.replaceChildren(errorDiv); } } catch (error) { - uploadContent.innerHTML = ` -
- Error: ${error.message} -
- `; + const errorDiv = document.createElement('div'); + errorDiv.className = 'alert alert-danger'; + errorDiv.textContent = 'Error: ' + error.message; + uploadContent.replaceChildren(errorDiv); console.error(error); } }