diff --git a/.factorypath b/.factorypath deleted file mode 100644 index b4510fae..00000000 --- a/.factorypath +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.gitignore b/.gitignore index d847435d..918b88e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,9 @@ # idea .idea/ -#*.iml - +*.iml # target dir -.settings/* -target/* -.classpath -.project - +target/ # Compiled class file *.class diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index e0f15db2..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "java.configuration.updateBuildConfiguration": "automatic" -} \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index f054c89d..00000000 --- a/README.md +++ /dev/null @@ -1,206 +0,0 @@ -# Java Launcher Code Examples - -### Github repo: [code-examples-java](./) -## Introduction -This repo is a Java Spring Boot application that demonstrates: - -1. **Embedded Signing Ceremony.** - [Source.](./src/main/java/com/docusign/controller/examples/EG001ControllerEmbeddedSigning.java) - This example sends an envelope, and then uses an embedded signing ceremony for the first signer. - With embedded signing, the DocuSign signing ceremony is initiated from your website. -1. **Send an envelope with a remote (email) signer and cc recipient.** - [Source.](./src/main/java/com/docusign/controller/examples/EG002ControllerSigningViaEmail.java) - The envelope includes a pdf, Word, and HTML document. - Anchor text ([AutoPlace](https://support.docusign.com/en/guides/AutoPlace-New-DocuSign-Experience)) is used to position the signing fields in the documents. -1. **List envelopes in the user's account.** - [Source.](./src/main/java/com/docusign/controller/examples/EG003ControllerListEnvelopes.java) -1. **Get an envelope's basic information.** - [Source.](./src/main/java/com/docusign/controller/examples/EG004ControllerEnvelopeInfo.java) - The example lists the basic information about an envelope, including its overall status. -1. **List an envelope's recipients and their current status.** - [Source.](./src/main/java/com/docusign/controller/examples/EG005ControllerEnvelopeRecipients.java) -1. **List an envelope's documents.** - [Source.](./src/main/java/com/docusign/controller/examples/EG006ControllerEnvelopeDocs.java) -1. **Download an envelope's documents.** The example can download individual - documents, the documents concatenated together, or a zip file of the documents. - [Source.](./src/main/java/com/docusign/controller/examples/EG007ControllerEnvelopeGetDoc.java) -1. **Programmatically create a template.** - [Source.](./src/main/java/com/docusign/controller/examples/EG008ControllerCreateTemplate.java) -1. **Send an envelope using a template.** - [Source.](./src/main/java/com/docusign/controller/examples/EG009ControllerUseTemplate.java) -1. **Send an envelope and upload its documents with multipart binary transfer.** - [Source.](./src/main/java/com/docusign/controller/examples/EG010ControllerSendBinaryDocs.java) - Binary transfer is 33% more efficient than using Base64 encoding. -1. **Embedded sending.** - [Source.](./src/main/java/com/docusign/controller/examples/EG011ControllerEmbeddedSending.java) -1. **Embedded DocuSign web tool (NDSE).** - [Source.](./src/main/java/com/docusign/controller/examples/EG012ControllerEmbeddedConsole.java) -1. **Embedded Signing Ceremony from a template with an added document.** - [Source.](./src/main/java/com/docusign/controller/examples/EG013ControllerAddDocToTemplate.java) - This example sends an envelope based on a template. - In addition to the template's document(s), the example adds an - additional document to the envelope by using the - [Composite Templates](https://developers.docusign.com/esign-rest-api/guides/features/templates#composite-templates) - feature. -1. **Payments example: an order form, with online payment by credit card.** - [Source.](./src/main/java/com/docusign/controller/examples/EG014ControllerCollectPayment.java) -1. **Get the envelope tab data.** - Retrieve the tab (field) values for all of the envelope's recipients. - [Source.](./src/main/java/com/docusign/controller/examples/EG015ControllerGetTabValues.java) -1. **Set envelope tab values.** - The example creates an envelope and sets the initial values for its tabs (fields). Some of the tabs - are set to be read-only, others can be updated by the recipient. The example also stores - metadata with the envelope. - [Source.](./src/main/java/com/docusign/controller/examples/EG016ControllerSetTabValues.java) -1. **Set template tab values.** - The example creates an envelope using a template and sets the initial values for its tabs (fields). - The example also stores metadata with the envelope. - [Source.](./src/main/java/com/docusign/controller/examples/EG017ControllerSetTemplateTabValues.java) -1. **Get the envelope custom field data (metadata).** - The example retrieves the custom metadata (custom data fields) stored with the envelope. - [Source.](./src/main/java/com/docusign/controller/examples/EG018ControllerEnvelopeCustomFieldValues.java) -1. **Requiring an Access Code for a Recipient** - [Source.](./src/main/java/com/docusign/controller/examples/EG019ControllerAccessCodeAuthentication.java) - This example sends and envelope that requires an access-code for the purpose of multi-factor authentication. -1. **Requiring SMS authentication for a recipient** - [Source.](./src/main/java/com/docusign/controller/examples/EG020ControllerSmsAuthentication.java) - This example sends and envelope that requires entering in a six digit code from an text message for the purpose of multi-factor authentication. -1. **Requiring Phone authentication for a recipient** - [Source.](./src/main/java/com/docusign/controller/examples/EG021ControllerPhoneAuthentication.java) - This example sends and envelope that requires entering in a voice-based response code for the purpose of multi-factor authentication. -1. **Requiring Knowledge-Based Authentication (KBA) for a Recipient** - [Source.](./src/main/java/com/docusign/controller/examples/EG022ControllerKBAAuthentication.java) - This example sends and envelope that requires passing a Public records check to validate identity for the purpose of multi-factor authentication. -1. **Requiring ID Verification (IDV) for a recipient** - [Source.](./src/main/java/com/docusign/controller/examples/EG023ControllerIdvAuthentication.java) - This example sends and envelope that requires the recipient to upload a government issued id. - **Creating a permission profile** - [Source.](./src/main/java/com/docusign/controller/examples/EG024ControllerPermissionCreate.java) - This code example demonstrates how to create a user group's permission profile using the [Create Profile](https://developers.docusign.com/esign-rest-api/reference/UserGroups/Groups/create) method. -1. **Setting a permission profile** - [Source.](./src/main/java/com/docusign/controller/examples/EG025ControllerPermissionSetUserGroups.java) - This code example demonstrates how to set a user group's permission profile using the [Update Group](https://developers.docusign.com/esign-rest-api/reference/UserGroups/Groups/update) method. - You must have already created permissions profile and group of users. -1. **Updating individual permission settings** - [Source.](./src/main/java/com/docusign/controller/examples/EG026ControllerPermissionChangeSingleSetting.java) - This code example demonstrates how to update individual settings for a specific permission profile using the [Update Permission Profile](https://developers.docusign.com/esign-rest-api/reference/Accounts/AccountPermissionProfiles/update) method. - You must have already created permissions profile and group of users. -1. **Deleting a permission profile** - [Source.](./src/main/java/com/docusign/controller/examples/EG027ControllerPermissionDelete.java) - This code example demonstrates how to an account's permission profile using the [Delete AccountPermissionProfiles](https://developers.docusign.com/esign-rest-api/reference/Accounts/AccountPermissionProfiles/delete) method. -1. **Creating a brand** - [Source.](./src/main/java/com/docusign/controller/examples/EG028ControllerCreateBrand.java) - This example creates brand profile for an account using the [Create Brand](https://developers.docusign.com/esign-rest-api/reference/Accounts/AccountBrands/create) method. -1. **Applying a brand to an envelope** - [Source.](./src/main/java/com/docusign/controller/examples/EG029ControllerApplyBrandToEnvelope.java) - This code example demonstrates how to apply a brand you've created to an envelope using the [Create Envelope](https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/create) method. - First, creates the envelope and then applies brand to it. - Anchor text ([AutoPlace](https://support.docusign.com/en/guides/AutoPlace-New-DocuSign-Experience)) is used to position the signing fields in the documents. -1. **Applying a brand to a template** - [Source.](./src/main/java/com/docusign/controller/examples/EG030ControllerApplyBrandToTemplate.java) - This code example demonstrates how to apply a brand you've created to a template using using the [Create Envelope](https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/create) method. - You must have at least one created template and brand. - -1. **Bulk sending envelopes to multiple recipients** - [Source.](./src/main/java/com/docusign/controller/examples/EG031ControllerBulkSendEnvelopes.java) - This example creates and sends a bulk envelope by generating a bulk recipient list and initiating a bulk send. - - - -## Included OAuth grant types: - -* Authentication with Docusign via [Authorization Code Grant flow](https://developers.docusign.com/esign-rest-api/guides/authentication/oauth2-code-grant) . -When the token expires, the user is asked to re-authenticate. -The **refresh token** is not used in this example. - -* Authentication with DocuSign via the [JSON Web Token (JWT) Grant](https://developers.docusign.com/esign-rest-api/guides/authentication/oauth2-jsonwebtoken). -When the token expires, it updates automatically. - - -## Installation - -### Prerequisites -1. A DocuSign Developer Sandbox account (email and password) on [demo.docusign.net](https://demo.docusign.net). - Create a [free account](https://go.docusign.com/sandbox/productshot/?elqCampaignId=16533). -1. A DocuSign Integration Key (a client ID) that is configured to use the - OAuth Authorization Code flow. - You will need the **Integration Key** itself, and its **secret**. To - use JSON Web token, you will need the **Integration Key** itself, the - **RSA Secret Key** and an API user ID for the user you are impersonating. - - If you use this example on your own workstation, - the Integration key must include following **Redirect URI**s: - * http://localhost:8080/login&type=acg - * http://localhost:8080/login&type=jwt - - If you will not be running the example on your own workstation, - use the appropriate DNS name and port instead of `localhost:8080`. - An example Redirect URI: http://myserver.it.mycompany.com/login - -1. Java 11. -1. A name and email for a signer, and a name and email for a cc recipient. - The signer and the cc email cannot be the same. -1. Maven - -### Short installation instructions -* Download or clone this repository. -* The project includes a Maven pom file. -* Create a file `code-examples-java/src/main/resources/application-dev.properties` and configure the project by overriding necessary properties from the `application.properties` file. **Don't add this file into the Git index.** -* Add VM argument `-Dspring.profiles.active=dev` to your IDE -* Note that IntelliJ Community Edition does not directly support - Spring Boot applications. - -### Build and run -Examples are built as a dedicated application with embedded TomCat server. Build: -``` bash -$ cd code-examples-java -$ mvn package -``` -Run: -``` bash -$ cd target -$ java -Dspring.profiles.active=dev -jar code-examples-java-1.0-SNAPSHOT.war -``` - -### IntelliJ Ultimate installation - -See the [IntelliJ Ultimate instructions](https://github.com/docusign/eg-03-java-auth-code-grant/blob/master/docs/Readme_IntelliJ_Ultimate.md). - -## Configure the example - -Configure the example via the properties file: -`eg-03-java-auth-code-grant/src/main/resources/application-dev.properties`. - -Add the client id, secret, signer name and email to the file. -Also add the URL that the application will use (the **DS_APP_URL** setting). -By default, this is http://localhost:8080 - -You must also add a **Redirect URI** to the client id's settings in -DocuSign. The Redirect URIs are `/login&type=acg` and `/login&type=jwt` appended to the DS_APP_URL setting. -Eg http://localhost:8080/login&type=acg - -### Payments code example -To use the payments example, create a -test payments gateway for your developer sandbox account. - -See the -[PAYMENTS_INSTALLATION.md](https://github.com/docusign/eg-03-java-auth-code-grant/blob/master/PAYMENTS_INSTALLATION.md) -file for instructions. - -Then add the payment gateway account id to the **application.properties** file. - -## Using the examples with other authentication flows - -The examples in this repository can also be used with the -JWT OAuth flow. -See the [Authentication guide](https://developers.docusign.com/esign-rest-api/guides/authentication) -for information on choosing the right authentication flow for your application. - -## License and additional information - -### License -This repository uses the MIT License. See the LICENSE file for more information. - -### Pull Requests -Pull requests are welcomed. Pull requests will only be considered if their content -uses the MIT License. diff --git a/code-examples-java.iml b/code-examples-java.iml deleted file mode 100644 index 6d509eea..00000000 --- a/code-examples-java.iml +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/Readme_IntelliJ_Ultimate.md b/docs/Readme_IntelliJ_Ultimate.md index 071ab7e4..10b61712 100644 --- a/docs/Readme_IntelliJ_Ultimate.md +++ b/docs/Readme_IntelliJ_Ultimate.md @@ -13,7 +13,7 @@ required due to its support for Spring Boot and JSP view pages. ![IntelliJ Import project](install_fig_1.png) **Step 3.** Use the popup file chooser to select the -**code-examples-java** directory. +**eg-03-java-auth-code-grant** directory. **Step 4.** The **Import Project** wizard will open. It's a series of screens. On the first screen, select @@ -21,7 +21,23 @@ series of screens. On the first screen, select ![IntelliJ Import Maven project](install_fig_2.png) -**Step 5.** Click **Finish** and the project will +**Step 5.** Check the **Import Maven projects automatically** line. + +![Import Maven projects automatically](install_fig_3.png) + +**Step 6.** Select the example project. + +![Choose the example project](install_fig_4.png) + +**Step 7.** Select Java 1.8 or later. + +![Choose Java version](install_fig_5.png) + +**Step 8.** Enter the project's name. + +![Enter the project name](install_fig_6.png) + +**Step 9.** Click **Finish** and the project will be displayed in the IDE. ## Configuring the project diff --git a/docs/install_fig_3.png b/docs/install_fig_3.png new file mode 100644 index 00000000..6cd1dbbe Binary files /dev/null and b/docs/install_fig_3.png differ diff --git a/docs/install_fig_4.png b/docs/install_fig_4.png new file mode 100644 index 00000000..7f7348ec Binary files /dev/null and b/docs/install_fig_4.png differ diff --git a/docs/install_fig_5.png b/docs/install_fig_5.png new file mode 100644 index 00000000..7a38a8af Binary files /dev/null and b/docs/install_fig_5.png differ diff --git a/docs/install_fig_6.png b/docs/install_fig_6.png new file mode 100644 index 00000000..1b5cabd7 Binary files /dev/null and b/docs/install_fig_6.png differ diff --git a/docs/install_fig_7.png b/docs/install_fig_7.png index 21a101f7..b16ca0e2 100644 Binary files a/docs/install_fig_7.png and b/docs/install_fig_7.png differ diff --git a/pom.xml b/pom.xml index a8236905..36e58e1a 100644 --- a/pom.xml +++ b/pom.xml @@ -2,58 +2,95 @@ 4.0.0 + com.docusign + eg-03-java-auth-code-grant + 1.0-SNAPSHOT + jar + + Spring Boot Blank Project (from https://github.com/making/spring-boot-blank) + org.springframework.boot spring-boot-starter-parent - 2.2.1.RELEASE - + 1.3.1.RELEASE - com.docusign - code-examples-java - 1.0-SNAPSHOT - war - - Docusign Java Tutorial - - 11 - 2.1.1.RELEASE - 2.6 - 3.5.0 + UTF-8 + com.docusign.App + 1.8 + 1.18.6 + 1.16 + 2.3.3 + + org.springframework.boot + spring-boot-configuration-processor + true + org.springframework.boot spring-boot-starter-security + + org.springframework.security + spring-security-jwt + + + + org.springframework.security.oauth + spring-security-oauth2 + + + com.docusign + docusign-esign-java + 2.9.0 + org.springframework.boot spring-boot-starter-web - org.springframework.security.oauth.boot - spring-security-oauth2-autoconfigure - ${oauth2.version} + org.springframework.security + spring-security-web + + + org.springframework.boot + spring-boot-starter-jdbc + + + com.h2database + h2 - org.projectlombok lombok - true + ${lombok.version} + provided - org.apache.commons - commons-lang3 + org.bgee.log4jdbc-log4j2 + log4jdbc-log4j2-jdbc4.1 + ${log4jdbc.log4j2.version} + - org.freemarker - freemarker + org.springframework.boot + spring-boot-starter-test + test - com.google.code.gson - gson + com.jayway.restassured + rest-assured + ${rest.assured.version} + test + + + org.springframework.security + spring-security-config + 3.2.5.RELEASE @@ -68,26 +105,19 @@ provided - - commons-io - commons-io - ${commonsio.version} - - - com.docusign - docusign-esign-java - ${docusign.version} - - org.springframework.boot spring-boot-maven-plugin - - true - + + + org.springframework + springloaded + ${spring-loaded.version} + + diff --git a/src/main/java/com/docusign/App.java b/src/main/java/com/docusign/App.java index 9c829ef7..856ee4a2 100644 --- a/src/main/java/com/docusign/App.java +++ b/src/main/java/com/docusign/App.java @@ -3,14 +3,14 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; - +import org.springframework.context.annotation.ComponentScan; @SpringBootApplication -@EnableAutoConfiguration(exclude={JmxAutoConfiguration.class}) +@ComponentScan +@EnableAutoConfiguration public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } -} +} \ No newline at end of file diff --git a/src/main/java/com/docusign/AppConfig.java b/src/main/java/com/docusign/AppConfig.java new file mode 100644 index 00000000..6524f800 --- /dev/null +++ b/src/main/java/com/docusign/AppConfig.java @@ -0,0 +1,70 @@ +package com.docusign; + +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.boot.context.embedded.ServletContextInitializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.web.context.request.RequestContextListener; +import org.springframework.web.servlet.config.annotation.*; +import org.springframework.web.servlet.view.InternalResourceViewResolver; +import org.springframework.web.servlet.view.JstlView; + +import javax.servlet.SessionTrackingMode; +import java.util.HashSet; +import java.util.Set; + +@EnableWebMvc +@Configuration +public class AppConfig extends WebMvcConfigurerAdapter { + + @Bean + @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) + public DSConfiguration config() { + return new DSConfiguration(); + } + + @Bean + public ServletContextInitializer servletContextInitializer() { + return servletContext -> { + Set set = new HashSet<>(); + set.add(SessionTrackingMode.COOKIE); + servletContext.setSessionTrackingModes(set); + }; + } + + @Bean + public RequestContextListener requestContextListener() { + return new RequestContextListener(); + } + + @Override + public void configureViewResolvers(ViewResolverRegistry registry) { + InternalResourceViewResolver resolver = new InternalResourceViewResolver(); + resolver.setPrefix("/WEB-INF/templates/views/"); + resolver.setSuffix(".jsp"); + resolver.setViewClass(JstlView.class); + registry.viewResolver(resolver); + } + + @Override + public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { + configurer.enable(); + } + + + private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { + "classpath:/META-INF/resources/", + "classpath:/resources/", + "classpath:/public/" + }; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + + if (!registry.hasMappingForPattern("/**")) { + registry.addResourceHandler("/**") + .addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/docusign/DSConfiguration.java b/src/main/java/com/docusign/DSConfiguration.java index c536e46e..9988325b 100644 --- a/src/main/java/com/docusign/DSConfiguration.java +++ b/src/main/java/com/docusign/DSConfiguration.java @@ -1,46 +1,52 @@ package com.docusign; -import lombok.Getter; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; - @Component -@Getter public class DSConfiguration { - @Value("${com.docusign.github.example-uri}") - private String exampleUrl; + @Value("${DS_APP_URL:{APP_URL}}") + public String appUrl; - @Value("${com.docusign.documentation-path}") - private String documentationPath; + @Value("${DS_SIGNER_EMAIL:{USER_EMAIL}}") + private String signerEmail; - @Value("${DS_TARGET_ACCOUNT_ID}") - private String targetAccountId; + public String getSignerEmail() { + return signerEmail; + } - @Value("${DS_APP_URL}") - private String appUrl; + public void setSignerEmail(String value) { + signerEmail = value; + } - @Value("${DS_SIGNER_EMAIL:{USER_EMAIL}}") - private String signerEmail; + public String getSignerName() { + return signerName; + } + + public void setSignerName(String signerName) { + this.signerName = signerName; + } @Value("${DS_SIGNER_NAME:{USER_NAME}}") private String signerName; - @Value("${Gateway_Account_Id}") - private String gatewayAccountId; - + public String gatewayAccountId; @Value("${Gateway_Name}") - private String gatewayName; - + public String gatewayName; @Value("${Gateway_Display_Name}") - private String gatewayDisplayName; - - public String getDsReturnUrl() { - return appUrl + "/ds-return"; - } - - public String getDsPingUrl() { - return appUrl + "/"; - } + public String gatewayDisplayName; + + public boolean production = false; + public boolean debug = true; // Send debugging statements to console + public String sessionSecret = "12345"; // Secret for encrypting session cookie content + public boolean allowSilentAuthentication = true; // a user can be silently authenticated if they have an + // active login session on another tab of the same browser + // Set if you want a specific DocuSign AccountId, If null, the user's default account will be used. + public String targetAccountId = null; + public String demoDocPath = "demo_documents"; + public String docDocx = "World_Wide_Corp_Battle_Plan_Trafalgar.docx"; + public String docPdf = "World_Wide_Corp_lorem.pdf"; + public String githubExampleUrl = "https://github.com/docusign/eg-03-java-auth-code-grant/blob/master/src/main/java/"; + public String documentation = null; } diff --git a/src/main/java/com/docusign/WebSecurityConfig.java b/src/main/java/com/docusign/WebSecurityConfig.java index bcfd93d6..7ddb175e 100644 --- a/src/main/java/com/docusign/WebSecurityConfig.java +++ b/src/main/java/com/docusign/WebSecurityConfig.java @@ -1,142 +1,33 @@ package com.docusign; -import com.docusign.security.OAuthProperties; -import com.docusign.security.jwt.JWTAuthorizationCodeResourceDetails; -import com.docusign.security.jwt.JWTOAuth2RestTemplate; -import com.docusign.security.jwt.JWTUserInfoTokenService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2SsoProperties; -import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; -import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; +import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.SecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.oauth2.client.OAuth2ClientContext; -import org.springframework.security.oauth2.client.OAuth2RestTemplate; -import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter; -import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter; -import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; -import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; -import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; -import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; -import org.springframework.web.filter.CompositeFilter; +import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; -import java.util.Arrays; -import java.util.List; -import javax.servlet.Filter; - -@EnableOAuth2Client +@EnableOAuth2Sso @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { - @Autowired - private OAuth2ClientContext oAuth2ClientContext; - - @Bean - @ConfigurationProperties("authorization.code.grant.sso") - public OAuthProperties authCodeGrantSso() { - return new OAuthProperties(); - } - - @Bean - @ConfigurationProperties("jwt.grant.sso") - public OAuthProperties jwtGrantSso() { - return new OAuthProperties(); - } - - @Bean - @ConfigurationProperties("authorization.code.grant.client") - public AuthorizationCodeResourceDetails authCodeGrantClient() { - return new AuthorizationCodeResourceDetails(); - } - - @Bean - @ConfigurationProperties("jwt.grant.client") - public JWTAuthorizationCodeResourceDetails jwtCodeGrantClient() { - return new JWTAuthorizationCodeResourceDetails(); - } - - @Bean - @ConfigurationProperties("common.client.resource") - public ResourceServerProperties userInfoResource() { - return new ResourceServerProperties(); - } - - @Bean - public FilterRegistrationBean oAuth2ClientFilterRegistration(OAuth2ClientContextFilter oAuth2ClientContextFilter) { - FilterRegistrationBean registration = new FilterRegistrationBean<>(); - registration.setFilter(oAuth2ClientContextFilter); - registration.setOrder(-100); - return registration; - } - - private OAuth2ClientAuthenticationProcessingFilter authCodeGrantFilter() { - OAuth2SsoProperties authCodeGrantSso = authCodeGrantSso(); - AuthorizationCodeResourceDetails authCodeGrantClient = authCodeGrantClient(); - ResourceServerProperties userInfoResource = userInfoResource(); - OAuth2ClientAuthenticationProcessingFilter filter = - new OAuth2ClientAuthenticationProcessingFilter(authCodeGrantSso.getLoginPath()); - OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(authCodeGrantClient, oAuth2ClientContext); - filter.setRestTemplate(restTemplate); - ResourceServerTokenServices tokenServices = new UserInfoTokenServices(userInfoResource.getUserInfoUri(), - authCodeGrantClient.getClientId()); - filter.setTokenServices(tokenServices); - return filter; - } - - private OAuth2ClientAuthenticationProcessingFilter jwtGrantFilter() { - OAuth2SsoProperties authCodeGrantSso = jwtGrantSso(); - JWTAuthorizationCodeResourceDetails jwtCodeGrantClient = jwtCodeGrantClient(); - OAuth2ClientAuthenticationProcessingFilter filter = - new OAuth2ClientAuthenticationProcessingFilter(authCodeGrantSso.getLoginPath()); - OAuth2RestTemplate restTemplate = new JWTOAuth2RestTemplate(jwtCodeGrantClient, oAuth2ClientContext); - filter.setRestTemplate(restTemplate); - ResourceServerTokenServices tokenServices = new JWTUserInfoTokenService(jwtCodeGrantClient); - filter.setTokenServices(tokenServices); - return filter; - } - @Override protected void configure(HttpSecurity http) throws Exception { http .antMatcher("/**") .authorizeRequests() - .antMatchers( "/", "/login**", "/error**", "/assets/**","/ds/mustAuthenticate**", - "/ds/authenticate**") - .permitAll() - .anyRequest() - .authenticated() + .antMatchers( "/", "/login**", "/error**", "/assets/**","/ds/mustAuthenticate**") + .permitAll() + .anyRequest() + .authenticated() .and() - .exceptionHandling() - .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/ds/mustAuthenticate")) + .exceptionHandling() + .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/ds/mustAuthenticate")) .and() - .logout().logoutSuccessUrl("/").permitAll() + .logout().logoutSuccessUrl("/").permitAll() .and() - .csrf().disable(); - http.apply(new CombinedAuthenticationConfigurer(Arrays.asList(authCodeGrantFilter(), - jwtGrantFilter()))); - } - - private static class CombinedAuthenticationConfigurer - extends SecurityConfigurerAdapter { - - CompositeFilter filter = new CompositeFilter(); - - CombinedAuthenticationConfigurer(List filters) { - filter.setFilters(filters); - } + .csrf().disable(); - @Override - public void configure(HttpSecurity builder) { - builder.addFilterAfter(this.filter, - AbstractPreAuthenticatedProcessingFilter.class); - } } } diff --git a/src/main/java/com/docusign/common/DiffField.java b/src/main/java/com/docusign/common/DiffField.java deleted file mode 100644 index 19ae5588..00000000 --- a/src/main/java/com/docusign/common/DiffField.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.docusign.common; - -import java.lang.reflect.Field; -import java.util.Locale; - -import org.apache.commons.lang3.StringUtils; - -import lombok.Value; - - -/** - * This class represents 'diff' between values of two objects. The 'diff' is a - * simple object consists field name and fields value of comparing objects. The - * value is represented as a string. - */ -@Value -public class DiffField { - - private String name; - private String leftValue; - private String rightValue; - - /** - * Creates an instance of the DiffField by a {@link Field} and two objects. - * @param field the object which provides information about a single field of a class - * @param left the first comparing object, can be null - * @param right the second comparing object, can be null - * @return created object containing field name and string representation of values - * @throws IllegalAccessException when an application tries to reflectively get a field - */ - public static DiffField create(Field field, Object left, Object right) throws IllegalAccessException { - field.setAccessible(true); - return new DiffField(field.getName(), getValue(field, left), getValue(field, right)); - } - - /** - * Creates an instance of the DiffField by {@link Field} and two objects. - * Field name is formatted to better readability. - * @param field the object which provides information about a single field of a class - * @param left the first comparing object, can be null - * @param right the second comparing object, can be null - * @return created object containing field name and string representation of values - * @throws IllegalAccessException when an application tries to reflectively get a field - */ - public static DiffField createWithFormattedName(Field field, Object left, Object right) - throws IllegalAccessException { - field.setAccessible(true); - return new DiffField(formatMemberName(field.getName()), getValue(field, left), getValue(field, right)); - } - - /** - * Convert a member name represented as string in a Camel style for better - * readability. E.g. name 'permissionProfileName' will be transformed to - * "permission profile name" string. - * @param name the name of the field - * @return formatted name - */ - public static String formatMemberName(String name) { - String[] tokens = StringUtils.splitByCharacterTypeCamelCase(name); - return String.join(" ", tokens).toLowerCase(Locale.ENGLISH); - } - - private static String getValue(Field field, Object object) throws IllegalAccessException { - if (object == null) { - return "-"; - } - return "" + field.get(object); - } -} diff --git a/src/main/java/com/docusign/common/DocumentType.java b/src/main/java/com/docusign/common/DocumentType.java deleted file mode 100644 index 9d099802..00000000 --- a/src/main/java/com/docusign/common/DocumentType.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.docusign.common; - -import javax.ws.rs.core.MediaType; - -/** - * This enumeration represents supported document types for signing. - */ -public enum DocumentType { - HTML("html", MediaType.TEXT_HTML), - DOCX("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"), - PDF("pdf", "application/pdf"); - - private final String defaultFileExtention; - private final String mime; - - DocumentType(String defaultFileExtention, String mime) { - this.defaultFileExtention = defaultFileExtention; - this.mime = mime; - } - - public String getDefaultFileExtention() { - return defaultFileExtention; - } - - public String getMime() { - return mime; - } -} diff --git a/src/main/java/com/docusign/common/Languages.java b/src/main/java/com/docusign/common/Languages.java deleted file mode 100644 index e7dea874..00000000 --- a/src/main/java/com/docusign/common/Languages.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.docusign.common; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import lombok.Value; - - -/** - * Supported languages. - */ -public final class Languages { - - private static final List SUPPORTED_LANGUAGES = createList(); - - private Languages() {} - - - public static List getSupportedLanguages() { - return Collections.unmodifiableList(SUPPORTED_LANGUAGES); - } - - private static List createList() { - List languages = new ArrayList<>(); - languages.add(new Lang("ar", "Arabic")); - languages.add(new Lang("hy", "Armenian")); - languages.add(new Lang("id", "Bahasa Indonesia")); - languages.add(new Lang("ms", "Bahasa Malay")); - languages.add(new Lang("bg", "Bulgarian")); - languages.add(new Lang("zh_CN", "Chinese Simplified")); - languages.add(new Lang("zh_TW", "Chinese Traditional")); - languages.add(new Lang("hr", "Croatian")); - languages.add(new Lang("cs", "Czech")); - languages.add(new Lang("da", "Danish")); - languages.add(new Lang("nl", "Dutch")); - languages.add(new Lang("en_GB", "English UK")); - languages.add(new Lang("en", "English US")); - languages.add(new Lang("et", "Estonian")); - languages.add(new Lang("fa", "Farsi")); - languages.add(new Lang("fi", "Finnish")); - languages.add(new Lang("fr", "French")); - languages.add(new Lang("fr_CA", "French Canada")); - languages.add(new Lang("de", "German")); - languages.add(new Lang("el", "Greek")); - languages.add(new Lang("he", "Hebrew")); - languages.add(new Lang("hi", "Hindi")); - languages.add(new Lang("hu", "Hungarian")); - languages.add(new Lang("it", "Italian")); - languages.add(new Lang("ja", "Japanese")); - languages.add(new Lang("ko", "Korean")); - languages.add(new Lang("lv", "Latvian")); - languages.add(new Lang("lt", "Lithuanian")); - languages.add(new Lang("no", "Norwegian")); - languages.add(new Lang("pl", "Polish")); - languages.add(new Lang("pt", "Portuguese")); - languages.add(new Lang("pt_BR", "Portuguese Brasil")); - languages.add(new Lang("ro", "Romanian")); - languages.add(new Lang("ru", "Russian")); - languages.add(new Lang("sr", "Serbian")); - languages.add(new Lang("sk", "Slovak")); - languages.add(new Lang("sl", "Slovenian")); - languages.add(new Lang("es", "Spanish")); - languages.add(new Lang("es_MX", "Spanish Latin America")); - languages.add(new Lang("sv", "Swedish")); - languages.add(new Lang("th", "Thai")); - languages.add(new Lang("tr", "Turkish")); - languages.add(new Lang("uk", "Ukranian")); - languages.add(new Lang("vi", "Vietnamese")); - return languages; - } - - @Value - public static class Lang { - private String code; - private String name; - } -} diff --git a/src/main/java/com/docusign/common/Utils.java b/src/main/java/com/docusign/common/Utils.java deleted file mode 100644 index dd9fe265..00000000 --- a/src/main/java/com/docusign/common/Utils.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.docusign.common; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import org.apache.commons.codec.binary.StringUtils; -import org.apache.commons.lang3.ObjectUtils; - - -public final class Utils { - - private Utils() {} - - /** - * Examines member values of simple Java objects. All values will be - * represented as string. This functions creates a list of all non static - * field values. One of the comparing object can be equal null - * but not both. - * @param left the first comparing object - * @param right the second comparing object - * @param formatName whether format member names - * @return list of {@link DiffField} objects - * @throws IllegalArgumentException if both of comparable objects is - * null or if objects belong to different classes - */ - public static List compareFields(Object left, Object right, boolean formatName) { - if (left == null && right == null) { - throw new IllegalArgumentException("Both comparing objects are null"); - } - - if (ObjectUtils.allNotNull(left, right) && !left.getClass().equals(right.getClass())) { - throw new IllegalArgumentException("Can't compare objects because they belong to different classes"); - } - - Field[] classFields = ObjectUtils.defaultIfNull(left, right).getClass().getDeclaredFields(); - List fields = new ArrayList<>(); - for (Field field : classFields) { - if (Modifier.isStatic(field.getModifiers())) { - continue; - } - - try { - if (formatName) { - fields.add(DiffField.createWithFormattedName(field, left, right)); - } else { - fields.add(DiffField.create(field, left, right)); - } - } catch (IllegalArgumentException | IllegalAccessException e) { - fields.add(new DiffField("?", "?", "?")); - } - } - return fields; - } - - /** - * Searches different member values of simple Java objects. All values will - * be represented as string. This functions creates a list of all non - * static field values. One of the comparing object can be equal - * null but not both. - * @param left the first comparing object - * @param right the second comparing object - * @param formatName whether format member names - * @return list of {@link DiffField} objects - * @throws IllegalArgumentException if both of comparable objects is - * null or if objects belong to different classes - */ - public static List findDifferentFields(Object left, Object right, boolean formatName) { - return compareFields(left, right, formatName) - .stream() - .filter(field -> !StringUtils.equals(field.getLeftValue(), field.getRightValue())) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/com/docusign/controller/GlobalControllerAdvice.java b/src/main/java/com/docusign/controller/GlobalControllerAdvice.java index 3bb8a174..a16cf81b 100644 --- a/src/main/java/com/docusign/controller/GlobalControllerAdvice.java +++ b/src/main/java/com/docusign/controller/GlobalControllerAdvice.java @@ -2,17 +2,12 @@ import com.docusign.DSConfiguration; import com.docusign.esign.client.auth.OAuth; -import com.docusign.model.AuthType; -import com.docusign.model.AuthTypeItem; import com.docusign.model.Locals; import com.docusign.model.Session; import com.docusign.model.User; - -import com.docusign.utils.AccountsConverter; -import java.util.stream.Collectors; -import org.apache.commons.lang3.StringUtils; import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; @@ -20,59 +15,46 @@ import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.context.WebApplicationContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Optional; - - -/** - * This class provides common model attributes for all pages. If you want to - * refresh tokens use the next code to access to expiration time: - *
- * {@literal @}Autowired
- * private TokenStore tokenStore;
- * ....
- * Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
- * OAuth2Authentication oauth = (OAuth2Authentication) authentication;
- * OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) oauth.getDetails();
- * String accessToken = details.getTokenValue();
- * OAuth2AccessToken token = tokenStore.readAccessToken(accessToken);
- * 
- * Object token contains all necessary information. - */ + @ControllerAdvice -@Scope(WebApplicationContext.SCOPE_SESSION) +@Scope("session") public class GlobalControllerAdvice { private static final String BASE_URI_SUFFIX = "/restapi"; private static final String ERROR_ACCOUNT_NOT_FOUND = "Could not find account information for the user"; - private final DSConfiguration config; - private final Session session; - private final User user; - private Optional account; - private AuthType authTypeSelected = AuthType.AGC; + @Value("${DS_TARGET_ACCOUNT_ID:}") + private String TargetAccountId; @Autowired - public GlobalControllerAdvice(DSConfiguration config, Session session, User user, Optional account) { - this.config = config; - this.session = session; - this.user = user; - this.account = account; - } + DSConfiguration config; + + @Autowired + private HttpSession httpSession; + + @Autowired + Session session; - @ModelAttribute("authTypes") - public List authTypes() { - return AuthTypeItem.list(); + @Autowired + User user; + + @Autowired(required = false) + private OAuth.Account Account; + + @ModelAttribute("accessToken") + public String getAccessToken() { + return (String) httpSession.getAttribute("accessToken"); } - @ModelAttribute("authTypeSelected") - public AuthTypeItem authTypeSelected() { - return AuthTypeItem.convert(authTypeSelected); + @ModelAttribute("basePath") + public String getBaseUri() { + return (String) httpSession.getAttribute("basePath"); } @ModelAttribute("documentOptions") @@ -80,72 +62,109 @@ public List populateDocumentOptions() { return new ArrayList<>(); } + @ModelAttribute("envelopeId") + public String populateEnvelopeId() { + return (String) httpSession.getAttribute("envelopeId"); + } + @ModelAttribute("showDoc") public boolean populateShowDoc() { - return StringUtils.isNotBlank(config.getDocumentationPath()); + return (config.documentation != null); } @ModelAttribute("locals") - public Locals populateLocals() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + public Locals populateLocals(HttpServletRequest request) throws Exception { + Authentication authentication = SecurityContextHolder + .getContext().getAuthentication(); + + + Locals locals = new Locals(); + locals.setDsConfig(config); + locals.setSession(session); + locals.setMessages(""); + locals.setUser(null); if (!(authentication instanceof OAuth2Authentication)) { - return new Locals(config, session, null, ""); + return locals; } OAuth2Authentication oauth = (OAuth2Authentication) authentication; OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) oauth.getDetails(); - Authentication oauthUser = oauth.getUserAuthentication(); + Authentication user1 = oauth.getUserAuthentication(); - if (oauthUser != null && oauthUser.isAuthenticated()) { - user.setName(oauthUser.getName()); + if (user1 != null && user1.isAuthenticated()) { + user.setName(user1.getName()); + locals.setUser(user); user.setAccessToken(details.getTokenValue()); - - if (account.isEmpty()) { - account = Optional.of(getDefaultAccountInfo(getOAuthAccounts(oauth))); + httpSession.setAttribute("accessToken", details.getTokenValue()); +// user.refreshToken = accessToken.getRefreshToken().getValue(); +// user.tokenExpirationTimestamp = ((30 * 60 * 1000) + System.currentTimeMillis()); + + if (Account == null) { + LinkedHashMap list = (LinkedHashMap) oauth.getUserAuthentication().getDetails(); + ArrayList accounts = (ArrayList) list.get("accounts"); + Account = getDefaultAccountInfo(this.convetToAccounts(accounts)); + httpSession.setAttribute("Account", Account); } - OAuth.Account oauthAccount = account.orElseThrow(() -> new NoSuchElementException(ERROR_ACCOUNT_NOT_FOUND)); - session.setAccountId(oauthAccount.getAccountId()); - session.setAccountName(oauthAccount.getAccountName()); - session.setBasePath(oauthAccount.getBaseUri() + BASE_URI_SUFFIX); + session.setAccountId(Account.getAccountId()); + session.setAccountName(Account.getAccountName()); + session.setBasePath(Account.getBaseUri() + BASE_URI_SUFFIX); + + httpSession.setAttribute("accountId", Account.getAccountId()); + httpSession.setAttribute("accountName", Account.getAccountName()); + httpSession.setAttribute("basePath", Account.getBaseUri() + BASE_URI_SUFFIX); } - return new Locals(config, session, user, ""); + + return locals; } - @SuppressWarnings("unchecked") - private static List getOAuthAccounts(OAuth2Authentication oauth) { - Map userAuthenticationDetails = (Map) oauth.getUserAuthentication().getDetails(); - List> oauthAccounts = (ArrayList>) userAuthenticationDetails.get("accounts"); - return oauthAccounts.stream() - .map(AccountsConverter::convert) - .collect(Collectors.toList()); + private List convetToAccounts(ArrayList accounts) { + List accts = new ArrayList<>(); + for (Object account : accounts) { + LinkedHashMap acct = (LinkedHashMap) account; + OAuth.Account acct1 = new OAuth.Account(); + acct1.setAccountId((String) acct.get("account_id")); + acct1.setIsDefault(String.valueOf(acct.get("is_default"))); + acct1.setAccountName((String) acct.get("account_name")); + acct1.setBaseUri((String) acct.get("base_uri")); + accts.add(acct1); + } + + return accts; } - private OAuth.Account getDefaultAccountInfo(List accounts) { - if (StringUtils.isBlank(config.getTargetAccountId())) { - return getDefaultAccount(accounts); + private OAuth.Account getDefaultAccountInfo(List accounts) throws Exception { + + OAuth.Account account; + + if (!TargetAccountId.isEmpty()) { + account = getAccountById(accounts); + if (account == null) + throw new Exception(ERROR_ACCOUNT_NOT_FOUND); + } else { + account = getDefaultAccount(accounts); } - return getAccountById(accounts); + return account; } - private static OAuth.Account getDefaultAccount(List accounts) { - for (OAuth.Account oauthAccount : accounts) { - if ("true".equals(oauthAccount.getIsDefault())) { - return oauthAccount; - } + private OAuth.Account getDefaultAccount(List accounts) { + for (OAuth.Account account : accounts) { + if (account.getIsDefault() == "true") + return account; } return null; } private OAuth.Account getAccountById(List accounts) { - for (OAuth.Account oauthAccount : accounts) { - if (StringUtils.equals(oauthAccount.getAccountId(), config.getTargetAccountId())) { - return oauthAccount; - } + + for (OAuth.Account account : accounts) { + if (account.getAccountId() == TargetAccountId) + return account; } + return null; } -} +} \ No newline at end of file diff --git a/src/main/java/com/docusign/controller/IndexController.java b/src/main/java/com/docusign/controller/IndexController.java index ccee7808..b6018e3f 100644 --- a/src/main/java/com/docusign/controller/IndexController.java +++ b/src/main/java/com/docusign/controller/IndexController.java @@ -1,91 +1,41 @@ package com.docusign.controller; -import com.docusign.model.AuthType; import com.docusign.model.Session; -import com.docusign.security.OAuthProperties; -import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.view.RedirectView; - @Controller public class IndexController { - private static final String ATTR_ENVELOPE_ID = "qpEnvelopeId"; - private static final String ATTR_STATE = "state"; - private static final String ATTR_EVENT = "event"; - private static final String ATTR_TITLE = "title"; - - @Autowired - private Session session; - @Autowired - private OAuthProperties jwtGrantSso; + Session session; - @Autowired - private OAuthProperties authCodeGrantSso; - - @GetMapping(path = "/") + @RequestMapping(path = "/",method = RequestMethod.GET) public String index(ModelMap model) { - model.addAttribute(ATTR_TITLE,"Home"); - return "pages/index"; - } - @GetMapping(path = "/ds/mustAuthenticate") - public ModelAndView mustAuthenticateController(ModelMap model) { - model.addAttribute(ATTR_TITLE, "Authenticate with DocuSign"); - if (session.isRefreshToken()) { - return new ModelAndView(getRedirectView(session.getAuthTypeSelected())); - } - return new ModelAndView("pages/ds_must_authenticate"); + model.addAttribute("title","Home"); + return "pages/index"; } - @RequestMapping(path = "/ds/authenticate", method = RequestMethod.POST) - public RedirectView authenticate(ModelMap model, - @RequestBody MultiValueMap formParams) { - if (!formParams.containsKey("selectAuthType")) { - model.addAttribute("message", - "select option with selectAuthType name must be provided"); - return new RedirectView("pages/error"); - } - List selectAuthTypeObject = formParams.get("selectAuthType"); - AuthType authTypeSelected = AuthType.valueOf(selectAuthTypeObject.get(0)); - return getRedirectView(authTypeSelected); + @RequestMapping(path = "/ds/mustAuthenticate",method = RequestMethod.GET) + public String mustAuthenticateController(ModelMap model) { + model.addAttribute("title", "Authenticate with DocuSign"); + return "pages/ds_must_authenticate"; } - @GetMapping(path = "/ds-return") - public String returnController(@RequestParam(value=ATTR_STATE, required = false) String state, - @RequestParam(value=ATTR_EVENT, required = false) String event, - @RequestParam(value="envelopeId", required = false) String envelopeId, - ModelMap model) { - model.addAttribute(ATTR_TITLE , "Return from DocuSign"); - model.addAttribute(ATTR_EVENT, event); - model.addAttribute(ATTR_STATE, state); - model.addAttribute(ATTR_ENVELOPE_ID, envelopeId); + @RequestMapping(path = "/ds-return",method = RequestMethod.GET) + public String returnController(@RequestParam(value="state", required = false) String state, + @RequestParam(value="event", required = false) String event, + @RequestParam(value="envelopeId", required = false) String envelopeId, + ModelMap model) { + model.addAttribute("title" , "Return from DocuSign"); + model.addAttribute("event", event); + model.addAttribute("state", state); + model.addAttribute("qpEnvelopeId", envelopeId); return "pages/ds_return"; } - - private RedirectView getRedirectView(AuthType authTypeSelected) { - session.setAuthTypeSelected(authTypeSelected); - RedirectView redirect = new RedirectView(getLoginPath(authTypeSelected)); - redirect.setExposeModelAttributes(false); - return redirect; - } - - private String getLoginPath(AuthType authTypeSelected) { - OAuthProperties oAuth2SsoProperties = authCodeGrantSso; - if (authTypeSelected.equals(AuthType.JWT)) { - oAuth2SsoProperties = jwtGrantSso; - } - return oAuth2SsoProperties.getLoginPath(); - } } diff --git a/src/main/java/com/docusign/controller/examples/AbstractController.java b/src/main/java/com/docusign/controller/examples/AbstractController.java deleted file mode 100644 index 9f8f4494..00000000 --- a/src/main/java/com/docusign/controller/examples/AbstractController.java +++ /dev/null @@ -1,178 +0,0 @@ -package com.docusign.controller.examples; - -import com.docusign.DSConfiguration; -import com.docusign.esign.api.AccountsApi; -import com.docusign.esign.api.EnvelopesApi; -import com.docusign.esign.client.ApiClient; -import com.docusign.esign.client.ApiException; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; -import org.springframework.security.oauth2.client.OAuth2ClientContext; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; - -import java.io.IOException; -import java.util.Objects; -import javax.servlet.http.HttpServletResponse; - - -/** - * Abstract base class for all controllers. It handles all requests which are - * registered by the derived classes and delegates an example specific action - * back to the appropriate controllers. If a user had not been authorized, it - * redirects him onto an authentication page. Derived classes must override an - * abstract method {@link #doWork(WorkArguments, ModelMap, HttpServletResponse)} - * to do something. This method is called from the POST request. Optionally you - * can override method {@link #onInitModel(WorkArguments, ModelMap)} to add - * example specific attributes into a page. - */ -@Controller -public abstract class AbstractController { - - protected static final String MODEL_ENVELOPE_OK = "envelopeOk"; - private static final String REDIRECT_PREFIX = "redirect:"; - private static final String REDIRECT_AUTHENTICATION_PAGE = REDIRECT_PREFIX + "/ds/mustAuthenticate"; - private static final String EXAMPLE_PAGES_PATH = "pages/examples/"; - protected static final String BEARER_AUTHENTICATION = "Bearer "; - protected static final String DONE_EXAMPLE_PAGE = "pages/example_done"; - protected static final String DONE_EXAMPLE_PAGE_COMPARE = "pages/example_done_compare"; - protected static final String ERROR_PAGE = "error.jsp"; - - @Autowired - private OAuth2ClientContext oAuth2ClientContext; - - @Autowired - private Session session; - - protected final String exampleName; - protected final String title; - private final String pagePath; - protected final DSConfiguration config; - - public AbstractController(DSConfiguration config, String exampleName, String title) { - this.config = config; - this.exampleName = exampleName; - pagePath = EXAMPLE_PAGES_PATH + exampleName; - this.title = title; - } - - @GetMapping - public String get(WorkArguments args, ModelMap model) { - if (isTokenExpired()) { - return REDIRECT_AUTHENTICATION_PAGE; - } - - try { - onInitModel(args, model); - return pagePath; - } catch (ApiException exception) { - return handleException(exception, model); - } - } - - @PostMapping - public Object create(WorkArguments args, ModelMap model, HttpServletResponse response) { - if (isTokenExpired()) { - return REDIRECT_AUTHENTICATION_PAGE; - } - - try { - return doWork(args, model, response); - } catch (IOException|ApiException exception) { - return handleException(exception, model); - } - } - - /** - * This method is called from the GET request and it should initialize a - * model. Override this method if it is necessary to add example specific - * attributes into the model. - * @param args the session attributes - * @param model the model data - * @throws ApiException if calling eSign API has failed - */ - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - Class clazz = Objects.requireNonNullElse(getClass().getEnclosingClass(), getClass()); - String srcPath = String.join("", config.getExampleUrl(), clazz.getName().replace('.', '/'), ".java"); - model.addAttribute("csrfToken", ""); - model.addAttribute("title", title); - model.addAttribute("sourceFile", clazz.getSimpleName() + ".java"); - model.addAttribute("sourceUrl", srcPath); - model.addAttribute("documentation", config.getDocumentationPath() + exampleName); - } - - /** - * This method is called from the POST request. Example pages must override - * it. Overridden method should return an Object means URL to redirect. - * @param args the work arguments got from the current page - * @param model page model holder - * @param response for HTTP-specific functionality in sending a response - * @return {@link Object}. Possible types and values: null, - * {@link String} representation of the URL or Spring RedirectView object, - * (see {@link org.springframework.web.servlet.view.RedirectView RedirectView}) - * @throws ApiException if calling eSignature API has failed - * @throws IOException if I/O operation has failed - */ - protected abstract Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException; - - /** - * Creates new instance of the eSignature API client. - * @param basePath URL to eSignature REST API - * @param userAccessToken user's access token - * @return an instance of the {@link ApiClient} - */ - protected static ApiClient createApiClient(String basePath, String userAccessToken) { - ApiClient apiClient = new ApiClient(basePath); - apiClient.addDefaultHeader(HttpHeaders.AUTHORIZATION, BEARER_AUTHENTICATION + userAccessToken); - // it is a workaround to NPE, see DSPW-61 - apiClient.addAuthorization("docusignAccessCode", null); - return apiClient; - } - - /** - * Creates a new instance of the eSignature EnvelopesApi. This method - * creates an instance of the ApiClient class silently. - * @param basePath URL to eSignature REST API - * @param userAccessToken user's access token - * @return an instance of the {@link EnvelopesApi} - */ - protected static EnvelopesApi createEnvelopesApi(String basePath, String userAccessToken) { - ApiClient apiClient = createApiClient(basePath, userAccessToken); - return new EnvelopesApi(apiClient); - } - - /** - * Creates a new instance of the eSignature AccountsApi. This method - * creates an instance of the ApiClient class silently. - * @param basePath URL to eSignature REST API - * @param userAccessToken user's access token - * @return an instance of the {@link AccountsApi} - */ - protected static AccountsApi createAccountsApi(String basePath, String userAccessToken) { - ApiClient apiClient = createApiClient(basePath, userAccessToken); - return new AccountsApi(apiClient); - } - - private String handleException(Exception exception, ModelMap model) { - new DoneExample() - .withTitle(exampleName) - .withName(title) - .withMessage(exception.getMessage()) - .withStackTrace(exception.getStackTrace()) - .addToModel(model); - return ERROR_PAGE; - } - - private boolean isTokenExpired() { - OAuth2AccessToken accessToken = oAuth2ClientContext.getAccessToken(); - boolean tokenExpired = accessToken != null && accessToken.isExpired(); - session.setRefreshToken(tokenExpired); - return tokenExpired; - } -} diff --git a/src/main/java/com/docusign/controller/examples/DsModelUtils.java b/src/main/java/com/docusign/controller/examples/DsModelUtils.java deleted file mode 100644 index e7a0921a..00000000 --- a/src/main/java/com/docusign/controller/examples/DsModelUtils.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.docusign.controller.examples; - -import com.docusign.esign.model.AccountRoleSettings; -import com.docusign.esign.model.SettingsMetadata; - - -/** - * Helper functions to create and modify objects provided by the DocuSign API. - */ -public final class DsModelUtils { - - public static final String FALSE = "false"; - public static final String TRUE = "true"; - - private DsModelUtils() {} - - /** - * Creates {@link AccountRoleSettings} with default values. Now eSignature - * API has an error. It requires a member 'signingUiVersion' but this class - * doesn't contain such member. - * @return a new AccountRoleSettings object - */ - public static AccountRoleSettings createDefaultRoleSettings() { - return new AccountRoleSettings() - .allowAccountManagement(TRUE) - .useNewDocuSignExperienceInterface("optional") - .canCreateWorkspaces(TRUE) - .allowBulkSending(TRUE) - .allowEnvelopeSending(TRUE) - .allowESealRecipients(FALSE) - .allowSignerAttachments(TRUE) - .allowTaggingInSendAndCorrect(TRUE) - .allowWetSigningOverride(TRUE) - .allowedAddressBookAccess("personalAndShared") - .allowedClickwrapsAccess("create") - .allowedTemplateAccess("share") - .enableRecipientViewingNotifications(TRUE) - .enableSequentialSigningInterface(TRUE) - .receiveCompletedSelfSignedDocumentsAsEmailLinks(FALSE) - .useNewSendingInterface(TRUE) - .allowSupplementalDocuments(TRUE) - .supplementalDocumentsMustView(TRUE) - .supplementalDocumentsMustAccept(TRUE) - .supplementalDocumentsMustRead(TRUE) - .disableDocumentUpload(FALSE) - .disableOtherActions(FALSE) - .allowAutoTagging(TRUE) - .allowApiAccess(TRUE) - .allowApiAccessToAccount(TRUE) - .allowApiSendingOnBehalfOfOthers(FALSE) - .allowApiSequentialSigning(FALSE) - .enableApiRequestLogging(TRUE) - .allowDocuSignDesktopClient(FALSE) - .allowSendersToSetRecipientEmailLanguage(TRUE) - .allowVaulting(FALSE) - .allowedToBeEnvelopeTransferRecipient(TRUE) - .enableTransactionPointIntegration(FALSE) - .powerFormRole("admin") - .allowPowerFormsAdminToAccessAllPowerFormEnvelopes(FALSE) - .vaultingMode("none") - .signingUiVersionMetadata(new SettingsMetadata()); - } -} diff --git a/src/main/java/com/docusign/controller/examples/EG001ControllerEmbeddedSigning.java b/src/main/java/com/docusign/controller/examples/EG001ControllerEmbeddedSigning.java index 9534f183..72609e13 100644 --- a/src/main/java/com/docusign/controller/examples/EG001ControllerEmbeddedSigning.java +++ b/src/main/java/com/docusign/controller/examples/EG001ControllerEmbeddedSigning.java @@ -1,88 +1,100 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; import com.docusign.esign.api.EnvelopesApi; import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.Document; -import com.docusign.esign.model.EnvelopeDefinition; -import com.docusign.esign.model.EnvelopeSummary; -import com.docusign.esign.model.RecipientViewRequest; -import com.docusign.esign.model.Recipients; -import com.docusign.esign.model.Signer; -import com.docusign.esign.model.ViewUrl; -import com.docusign.model.Session; -import com.docusign.model.User; - -import org.springframework.beans.factory.annotation.Autowired; +import com.docusign.esign.model.*; +import com.sun.jersey.core.util.Base64; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.servlet.view.RedirectView; +import javax.annotation.PostConstruct; import java.io.IOException; import java.util.Arrays; -import javax.servlet.http.HttpServletResponse; - - -/** - * Embedded Signing Ceremony.
- * This example sends an envelope, and then uses an embedded signing ceremony - * for the first signer. Embedded signing provides a smoother user experience - * for the signer: the DocuSign signing ceremony is initiated from your site. - */ @Controller @RequestMapping("/eg001") -public class EG001ControllerEmbeddedSigning extends AbstractController { +public class EG001ControllerEmbeddedSigning extends EGController { - private static final String DOCUMENT_FILE_NAME = "World_Wide_Corp_lorem.pdf"; - private static final String DOCUMENT_NAME = "Lorem Ipsum"; - private static final int ANCHOR_OFFSET_Y = 20; - private static final int ANCHOR_OFFSET_X = 10; - private static final String SIGNER_CLIENT_ID = "1000"; + private static final String signerClientId = "1000"; + // Url that will be pinged by the DocuSign Signing Ceremony via Ajax; + private String dsPingUrl; //= config.appUrl + "/"; + private String dsReturnUrl;// = config.appUrl + "/ds-return"; + + @PostConstruct + public void init(){ + dsPingUrl = config.appUrl + "/"; + dsReturnUrl = config.appUrl + "/ds-return"; + } - private final Session session; - private final User user; + @Override + protected void addSpecialAttributes(ModelMap model) {} + + @Override + protected String getEgName() { + return "eg001"; + } + @Override + protected String getTitle() { + return "Embedded Signing Ceremony"; + } - @Autowired - public EG001ControllerEmbeddedSigning(DSConfiguration config, Session session, User user) { - super(config, "eg001", "Embedded Signing Ceremony"); - this.session = session; - this.user = user; + @Override + protected String getResponseTitle() { + return null; } @Override protected Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException { + String accessToken, String basePath) throws ApiException, IOException { + // Data for this method + // accessToken (argument) + // basePath (argument) String signerName = args.getSignerName(); String signerEmail = args.getSignerEmail(); - String accountId = session.getAccountId(); + String accountId = args.getAccountId(); // Step 1. Create the envelope definition EnvelopeDefinition envelope = makeEnvelope(signerEmail, signerName); // Step 2. Call DocuSign to create the envelope - ApiClient apiClient = createApiClient(session.getBasePath(), user.getAccessToken()); + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); - EnvelopeSummary envelopeSummary = envelopesApi.createEnvelope(accountId, envelope); + EnvelopeSummary results = envelopesApi.createEnvelope(accountId, envelope); - String envelopeId = envelopeSummary.getEnvelopeId(); - session.setEnvelopeId(envelopeId); + String envelopeId = results.getEnvelopeId(); + + // Save for future use within the example launcher + session.setAttribute("envelopeId", envelopeId); + args.setEnvelopeId(envelopeId); // Step 3. create the recipient view, the Signing Ceremony RecipientViewRequest viewRequest = makeRecipientViewRequest(signerEmail, signerName); - ViewUrl viewUrl = envelopesApi.createRecipientView(accountId, envelopeId, viewRequest); + // call the CreateRecipientView API + ViewUrl results1 = envelopesApi.createRecipientView(accountId, envelopeId, viewRequest); // Step 4. Redirect the user to the Signing Ceremony // Don't use an iFrame! // State can be stored/recovered using the framework's session or a // query parameter on the returnUrl (see the makeRecipientViewRequest method) - return new RedirectView(viewUrl.getUrl()); + String redirectUrl = results1.getUrl(); + // save the redirect info + args.setRedirectUrl("redirect:" + redirectUrl); + + return null; } private RecipientViewRequest makeRecipientViewRequest(String signerEmail, String signerName) { + // Data for this method + // signerEmail (argument) + // signerName (argument) + // dsReturnUrl (class constant) url on this app that DocuSign will redirect to + // signerClientId (class constant) the id of the signer in this app + // dsPingUrl (class constant) optional url in this app that DocuSign signing ceremony should ping + RecipientViewRequest viewRequest = new RecipientViewRequest(); // Set the url where you want the recipient to go once they are done signing // should typically be a callback route somewhere in your app. @@ -91,7 +103,7 @@ private RecipientViewRequest makeRecipientViewRequest(String signerEmail, String // the DocuSign signing ceremony. It's usually better to use // the session mechanism of your web framework. Query parameters // can be changed/spoofed very easily. - viewRequest.setReturnUrl(config.getDsReturnUrl() + "?state=123"); + viewRequest.setReturnUrl(dsReturnUrl + "?state=123"); // How has your app authenticated the user? In addition to your app's // authentication, you can include authenticate steps from DocuSign. @@ -102,42 +114,76 @@ private RecipientViewRequest makeRecipientViewRequest(String signerEmail, String // we used to create the envelope. viewRequest.setEmail(signerEmail); viewRequest.setUserName(signerName); - viewRequest.setClientUserId(SIGNER_CLIENT_ID); + viewRequest.setClientUserId(signerClientId); // DocuSign recommends that you redirect to DocuSign for the // Signing Ceremony. There are multiple ways to save state. // To maintain your application's session, use the pingUrl // parameter. It causes the DocuSign Signing Ceremony web page - // (not the DocuSign server) to send pings via AJAX to your app. - // NOTE: The pings will only be sent if the pingUrl is an https address + // (not the DocuSign server) to send pings via AJAX to your + // app, viewRequest.setPingFrequency("600"); // seconds - viewRequest.setPingUrl(config.getDsPingUrl()); + // NOTE: The pings will only be sent if the pingUrl is an https address + viewRequest.setPingUrl(dsPingUrl); // optional setting return viewRequest; } - private static EnvelopeDefinition makeEnvelope(String signerEmail, String signerName) throws IOException { + private EnvelopeDefinition makeEnvelope(String signerEmail, String signerName) throws IOException { + // Data for this method + // signerEmail (argument) + // signerName (argument) + // signerClientId (class constant) the id of the signer in this app + String docPdf = config.docPdf; // location of the source document + + byte[] buffer = readFile(docPdf); + EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition(); + envelopeDefinition.setEmailSubject("Please sign this document"); + Document doc1 = new Document(); + + String doc1b64 = new String(Base64.encode(buffer)); + + doc1.setDocumentBase64(doc1b64); + doc1.setName("Lorem Ipsum"); // can be different from actual file name + doc1.setFileExtension("pdf"); + doc1.setDocumentId("3"); + + // The order in the docs array determines the order in the envelope + envelopeDefinition.setDocuments(Arrays.asList(doc1)); + // Create a signer recipient to sign the document, identified by name and email // We set the clientUserId to enable embedded signing for the recipient - Signer signer = new Signer(); - signer.setEmail(signerEmail); - signer.setName(signerName); - signer.clientUserId(SIGNER_CLIENT_ID); - signer.recipientId("1"); - signer.setTabs(EnvelopeHelpers.createSingleSignerTab("/sn1/", ANCHOR_OFFSET_Y, ANCHOR_OFFSET_X)); + // We're setting the parameters via the object creation + Signer signer1 = new Signer(); + signer1.setEmail(signerEmail); + signer1.setName(signerName); + signer1.clientUserId(signerClientId); + signer1.recipientId("1"); + + // Create signHere fields (also known as tabs) on the documents, + // We're using anchor (autoPlace) positioning + // + // The DocuSign platform seaches throughout your envelope's + // documents for matching anchor strings. + SignHere signHere1 = new SignHere(); + signHere1.setAnchorString("/sn1/"); + signHere1.setAnchorUnits("pixels"); + signHere1.setAnchorYOffset("20"); + signHere1.setAnchorXOffset("10"); + + // Tabs are set per recipient / signer + Tabs signer1Tabs = new Tabs(); + signer1Tabs.setSignHereTabs(Arrays.asList(signHere1)); + signer1.setTabs(signer1Tabs); // Add the recipient to the envelope object Recipients recipients = new Recipients(); - recipients.setSigners(Arrays.asList(signer)); - - EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition(); - envelopeDefinition.setEmailSubject("Please sign this document"); + recipients.setSigners(Arrays.asList(signer1)); envelopeDefinition.setRecipients(recipients); - Document doc = EnvelopeHelpers.createDocumentFromFile(DOCUMENT_FILE_NAME, DOCUMENT_NAME, "3"); - envelopeDefinition.setDocuments(Arrays.asList(doc)); + // Request that the envelope be sent by setting |status| to "sent". // To request that the envelope be created as a draft, set to "created" - envelopeDefinition.setStatus(EnvelopeHelpers.ENVELOPE_STATUS_SENT); + envelopeDefinition.setStatus("sent"); return envelopeDefinition; } diff --git a/src/main/java/com/docusign/controller/examples/EG002ControllerSigningViaEmail.java b/src/main/java/com/docusign/controller/examples/EG002ControllerSigningViaEmail.java index 16baca91..7785a1dd 100644 --- a/src/main/java/com/docusign/controller/examples/EG002ControllerSigningViaEmail.java +++ b/src/main/java/com/docusign/controller/examples/EG002ControllerSigningViaEmail.java @@ -1,130 +1,194 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; -import com.docusign.common.DocumentType; import com.docusign.esign.api.EnvelopesApi; +import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.CarbonCopy; -import com.docusign.esign.model.EnvelopeDefinition; -import com.docusign.esign.model.EnvelopeSummary; -import com.docusign.esign.model.Signer; -import com.docusign.esign.model.Tabs; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - -import org.springframework.beans.factory.annotation.Autowired; +import com.docusign.esign.model.*; +import com.sun.jersey.core.util.Base64; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import java.io.IOException; import java.util.Arrays; -import javax.servlet.http.HttpServletResponse; - - -/** - * Send an envelope with a remote (email) signer and cc recipient.
- * The envelope includes a pdf, Word, and HTML document. Anchor text is used to - * position the signing fields in the documents. - */ @Controller @RequestMapping("/eg002") -public class EG002ControllerSigningViaEmail extends AbstractController { - - private static final String HTML_DOCUMENT_FILE_NAME = "templates/candy-bonbon.ftl"; - private static final String HTML_DOCUMENT_NAME = "Order acknowledgement"; - private static final String PDF_DOCUMENT_FILE_NAME = "World_Wide_Corp_lorem.pdf"; - private static final String PDF_DOCUMENT_NAME = "Lorem Ipsum"; - private static final String DOCX_DOCUMENT_FILE_NAME = "World_Wide_Corp_Battle_Plan_Trafalgar.docx"; - private static final String DOCX_DOCUMENT_NAME = "Battle Plan"; - private static final int ANCHOR_OFFSET_Y = 10; - private static final int ANCHOR_OFFSET_X = 20; - - private final Session session; - private final User user; - - @Autowired - public EG002ControllerSigningViaEmail(DSConfiguration config, Session session, User user) { - super(config, "eg002", "Signing request by email"); - this.session = session; - this.user = user; +public class EG002ControllerSigningViaEmail extends EGController { + + @Override + protected void addSpecialAttributes(ModelMap model) { + } + + @Override + protected String getEgName() { + return "eg002"; + } + + @Override + protected String getTitle() { + return "Signing request by email"; + } + + @Override + protected String getResponseTitle() { + return "Envelope sent"; } @Override protected Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException { - // Set status for the makeEnvelope method - if (!EnvelopeHelpers.ENVELOPE_STATUS_CREATED.equalsIgnoreCase(args.getStatus())) { - args.setStatus(EnvelopeHelpers.ENVELOPE_STATUS_SENT); - } + String accessToken, String basePath) throws ApiException, IOException { + // Data for this method + // accessToken (argument) + // basePath (argument) + String accountId = args.getAccountId(); - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); - EnvelopeDefinition envelope = makeEnvelope(args); - EnvelopeSummary results = envelopesApi.createEnvelope(session.getAccountId(), envelope); + // Set status for the makeEnvelope method + if (!"created".equalsIgnoreCase(args.getStatus())) { + args.setStatus("sent"); + } + EnvelopeDefinition env = makeEnvelope(args); + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); + EnvelopeSummary results = envelopesApi.createEnvelope(accountId, env); // process results - session.setEnvelopeId(results.getEnvelopeId()); - DoneExample.createDefault(title) - .withMessage("The envelope has been created and sent!
Envelope ID " - + results.getEnvelopeId() + ".") - .withJsonObject(results) - .addToModel(model); - return DONE_EXAMPLE_PAGE; + args.setEnvelopeId(results.getEnvelopeId()); + session.setAttribute("envelopeId", results.getEnvelopeId()); + setMessage("The envelope has been created and sent!
Envelope ID " + args.getEnvelopeId() + "."); + return results; } - // document 1 (html) has tag **signature_1** - // document 2 (docx) has tag /sn1/ - // document 3 (pdf) has tag /sn1/ - // - // The envelope has two recipients. - // recipient 1 - signer - // recipient 2 - cc - // The envelope will be sent first to the signer. After it is signed, - // a copy is sent to the cc person. - public static EnvelopeDefinition makeEnvelope(WorkArguments args) throws IOException { - // The DocuSign platform searches throughout your envelope's documents - // for matching anchor strings. So the signHere2 tab will be used in - // both document 2 and 3 since they use the same anchor string for - // their "signer 1" tabs. - Tabs signerTabs = EnvelopeHelpers.createSignerTabs( - EnvelopeHelpers.createSignHere("**signature_1**", ANCHOR_OFFSET_Y, ANCHOR_OFFSET_X), - EnvelopeHelpers.createSignHere("/sn1/", ANCHOR_OFFSET_Y, ANCHOR_OFFSET_X)); - - // Create a signer recipient to sign the document, identified by name - // and email. We're setting the parameters via the object creation. - // RoutingOrder (lower means earlier) determines the order of deliveries + private EnvelopeDefinition makeEnvelope(WorkArguments args) throws IOException { + // Data for this method + // config.docDocx Name of the docx demo document + // config.docPdf Name of the pdf demo document + String signerEmail = args.getSignerEmail(); + String signerName = args.getSignerName(); + String ccEmail = args.getCcEmail(); + String ccName = args.getCcName(); + String status = args.getStatus(); + + + // document 1 (html) has tag **signature_1** + // document 2 (docx) has tag /sn1/ + // document 3 (pdf) has tag /sn1/ + // + // The envelope has two recipients. + // recipient 1 - signer + // recipient 2 - cc + // The envelope will be sent first to the signer. + // After it is signed, a copy is sent to the cc person. + // read files from a local directory + // The reads could raise an exception if the file is not available! + String doc2DocxBytes = new String(Base64.encode(readFile(config.docDocx))); + String doc3PdfBytes = new String(Base64.encode(readFile(config.docPdf))); + // create the envelope definition + EnvelopeDefinition env = new EnvelopeDefinition(); + env.setEmailSubject("Please sign this document set"); + Document doc1 = new Document(); + String b64 = new String(Base64.encode(document1(args).getBytes())); + doc1.setDocumentBase64(b64); + doc1.setName("Order acknowledgement"); // can be different from actual file name + doc1.setFileExtension("html"); // Source data format. Signed docs are always pdf. + doc1.setDocumentId("1"); // a label used to reference the doc + Document doc2 = new Document(); + doc2.setDocumentBase64(doc2DocxBytes); + doc2.setName("Battle Plan"); // can be different from actual file name + doc2.setFileExtension("docx"); + doc2.setDocumentId("2"); + Document doc3 = new Document(); + doc3.setDocumentBase64(doc3PdfBytes); + doc3.setName("Lorem Ipsum"); // can be different from actual file name + doc3.setFileExtension("pdf"); + doc3.setDocumentId("3"); + + // The order in the docs array determines the order in the envelope + env.setDocuments(Arrays.asList(doc1, doc2, doc3)); + + // create a signer recipient to sign the document, identified by name and email + // We're setting the parameters via the object creation + Signer signer1 = new Signer(); + signer1.setEmail(signerEmail); + signer1.setName(signerName); + signer1.setRecipientId("1"); + signer1.setRoutingOrder("1"); + + // routingOrder (lower means earlier) determines the order of deliveries // to the recipients. Parallel routing order is supported by using the // same integer as the order for two or more recipients. - Signer signer = new Signer(); - signer.setEmail(args.getSignerEmail()); - signer.setName(args.getSignerName()); - signer.setRecipientId("1"); - signer.setRoutingOrder("1"); - signer.setTabs(signerTabs); // create a cc recipient to receive a copy of the documents, identified by name and email - CarbonCopy cc = new CarbonCopy(); - cc.setEmail(args.getCcEmail()); - cc.setName(args.getCcName()); - cc.setRecipientId("2"); - cc.setRoutingOrder("2"); + // We're setting the parameters via setters + CarbonCopy cc1 = new CarbonCopy(); + cc1.setEmail(ccEmail); + cc1.setName(ccName); + cc1.setRecipientId("2"); + cc1.setRoutingOrder("2"); + // Create signHere fields (also known as tabs) on the documents, + // We're using anchor (autoPlace) positioning + // + // The DocuSign platform searches throughout your envelope's + // documents for matching anchor strings. So the + // signHere2 tab will be used in both document 2 and 3 since they + // use the same anchor string for their "signer 1" tabs. + SignHere signHere1 = new SignHere(); + signHere1.setAnchorString("**signature_1**"); + signHere1.setAnchorUnits("pixels"); + signHere1.setAnchorYOffset("10"); + signHere1.setAnchorXOffset("20"); + SignHere signHere2 = new SignHere(); + signHere2.setAnchorString("/sn1/"); + signHere2.setAnchorUnits("pixels"); + signHere2.setAnchorYOffset("10"); + signHere2.setAnchorXOffset("20"); + + // Tabs are set per recipient / signer + Tabs signer1Tabs = new Tabs(); + signer1Tabs.setSignHereTabs(Arrays.asList(signHere1, signHere2)); + signer1.setTabs(signer1Tabs); + + // Add the recipients to the envelope object + Recipients recipients = new Recipients(); + recipients.setSigners(Arrays.asList(signer1)); + recipients.setCarbonCopies(Arrays.asList(cc1)); + env.setRecipients(recipients); - // The order in the docs array determines the order in the envelope - byte[] htmlDocument = EnvelopeHelpers.createHtmlFromTemplateFile(HTML_DOCUMENT_FILE_NAME, "args", args); - EnvelopeDefinition envelope = new EnvelopeDefinition(); - envelope.setEmailSubject("Please sign this document set"); - envelope.setDocuments(Arrays.asList( - EnvelopeHelpers.createDocument(htmlDocument, HTML_DOCUMENT_NAME, - DocumentType.HTML.getDefaultFileExtention(), "1"), - EnvelopeHelpers.createDocumentFromFile(DOCX_DOCUMENT_FILE_NAME, DOCX_DOCUMENT_NAME, "2"), - EnvelopeHelpers.createDocumentFromFile(PDF_DOCUMENT_FILE_NAME, PDF_DOCUMENT_NAME, "3"))); - envelope.setRecipients(EnvelopeHelpers.createRecipients(signer, cc)); // Request that the envelope be sent by setting |status| to "sent". // To request that the envelope be created as a draft, set to "created" - envelope.setStatus(args.getStatus()); + env.setStatus(status); + return env; + } - return envelope; + private String document1(WorkArguments args) { + // Data for this method + String signerEmail = args.getSignerEmail(); + String signerName = args.getSignerName(); + String ccEmail = args.getCcEmail(); + String ccName = args.getCcName(); + + + return " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "

World Wide Corp

\n" + + "

Order Processing Division

\n" + + "

Ordered by " + signerName + "

\n" + + "

Email: " + signerEmail + "

\n" + + "

Copy to: " + ccName + ", " + ccEmail + "

\n" + + "

\n" + + " Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie. Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée. Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice. Donut jujubes oat cake jelly-o. Dessert bear claw chocolate cake gummies lollipop sugar plum ice cream gummies cheesecake.\n" + + "

\n" + + " \n" + + "

Agreed: **signature_1**/

\n" + + " \n" + + " "; } } diff --git a/src/main/java/com/docusign/controller/examples/EG003ControllerListEnvelopes.java b/src/main/java/com/docusign/controller/examples/EG003ControllerListEnvelopes.java index d363b2ee..bb6dc508 100644 --- a/src/main/java/com/docusign/controller/examples/EG003ControllerListEnvelopes.java +++ b/src/main/java/com/docusign/controller/examples/EG003ControllerListEnvelopes.java @@ -1,60 +1,57 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; import com.docusign.esign.api.EnvelopesApi; import com.docusign.esign.api.EnvelopesApi.ListStatusChangesOptions; +import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; - -import javax.servlet.http.HttpServletResponse; - -import org.springframework.beans.factory.annotation.Autowired; +import com.docusign.esign.model.EnvelopesInformation; +import org.joda.time.LocalDate; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; - -/** - * List envelopes in the user's account.
- * List the envelopes created in the last 30 days. This example demonstrates - * how to query DocuSign about envelopes sent by the current user. - */ @Controller @RequestMapping("/eg003") -public class EG003ControllerListEnvelopes extends AbstractController { - - private static final int FROM_DATE_OFFSET_DAYS = 30; - private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd"); +public class EG003ControllerListEnvelopes extends EGController { + @Override + protected void addSpecialAttributes(ModelMap model) { + } - private final Session session; - private final User user; + @Override + protected String getEgName() { + return "eg003"; + } + @Override + protected String getTitle() { + return "List envelopes"; + } - @Autowired - public EG003ControllerListEnvelopes(DSConfiguration config, Session session, User user) { - super(config, "eg003", "List envelopes"); - this.session = session; - this.user = user; + @Override + protected String getResponseTitle() { + return "List envelopes results"; } @Override // ***DS.snippet.0.start - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) throws ApiException { - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); + protected Object doWork(WorkArguments args, ModelMap model, + String accessToken, String basePath) throws ApiException { + // Data for this method + // accessToken (argument) + // basePath (argument) + String accountId = args.getAccountId(); + + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); ListStatusChangesOptions options = envelopesApi.new ListStatusChangesOptions(); - LocalDate date = LocalDate.now().minusDays(FROM_DATE_OFFSET_DAYS); - options.setFromDate(DATE_FORMATTER.format(date)); - - DoneExample.createDefault(title) - .withMessage("Results from the Envelopes::listStatusChanges method:") - .withJsonObject(envelopesApi.listStatusChanges(session.getAccountId(), options)) - .addToModel(model); - return DONE_EXAMPLE_PAGE; + + LocalDate date = LocalDate.now().minusDays(30); + options.setFromDate(date.toString("yyyy/MM/dd")); + + EnvelopesInformation results = envelopesApi.listStatusChanges(accountId, options); + setMessage("Results from the Envelopes::listStatusChanges method:"); + return results; } // ***DS.snippet.0.end } diff --git a/src/main/java/com/docusign/controller/examples/EG004ControllerEnvelopeInfo.java b/src/main/java/com/docusign/controller/examples/EG004ControllerEnvelopeInfo.java index 9d6e90b0..1fe8bd91 100644 --- a/src/main/java/com/docusign/controller/examples/EG004ControllerEnvelopeInfo.java +++ b/src/main/java/com/docusign/controller/examples/EG004ControllerEnvelopeInfo.java @@ -1,61 +1,58 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; import com.docusign.esign.api.EnvelopesApi; +import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; +import javax.servlet.http.HttpSession; - -/** - * Get an envelope's basic information and status.
- * List the basic information about an envelope, including its overall status. - * Additional API/SDK methods may be used to get additional information about - * the envelope, its documents, recipients, etc. This example demonstrates - * how to obtain the latest information about an envelope from DocuSign. Often - * an alternative is to use Connect to enable DocuSign to proactively send your - * application updates when the status of an envelope changes. - */ @Controller @RequestMapping("/eg004") -public class EG004ControllerEnvelopeInfo extends AbstractController { +public class EG004ControllerEnvelopeInfo extends EGController { - private final Session session; - private final User user; + @Autowired + HttpSession session; + @Override + protected void addSpecialAttributes(ModelMap model) { + model.addAttribute("envelopeOk", session.getAttribute("envelopeId") != null); + } - @Autowired - public EG004ControllerEnvelopeInfo(DSConfiguration config, Session session, User user) { - super(config, "eg004", "Get envelope information"); - this.session = session; - this.user = user; + @Override + protected String getEgName() { + return "eg004"; + } + + @Override + protected String getTitle() { + return "Get envelope information"; } @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - model.addAttribute(MODEL_ENVELOPE_OK, StringUtils.isNotBlank(session.getEnvelopeId())); + protected String getResponseTitle() { + return "Get envelope status results"; } @Override // ***DS.snippet.0.start - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) throws ApiException { + protected Object doWork(WorkArguments args, ModelMap model, + String accessToken, String basePath) throws ApiException { + // Data for this method + // accessToken (argument) + // basePath (argument) + String accountId = args.getAccountId(); + String envelopeId = args.getEnvelopeId(); + + // Step 1. get envelope info - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); - DoneExample.createDefault(title) - .withJsonObject(envelopesApi.getEnvelope(session.getAccountId(), session.getEnvelopeId())) - .withMessage("Results from the Envelopes::get method:") - .addToModel(model); - return DONE_EXAMPLE_PAGE; + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); + setMessage("Results from the Envelopes::get method:"); + return envelopesApi.getEnvelope(accountId, envelopeId); } // ***DS.snippet.0.end } diff --git a/src/main/java/com/docusign/controller/examples/EG005ControllerEnvelopeRecipients.java b/src/main/java/com/docusign/controller/examples/EG005ControllerEnvelopeRecipients.java index ba10749f..ffdc46e2 100644 --- a/src/main/java/com/docusign/controller/examples/EG005ControllerEnvelopeRecipients.java +++ b/src/main/java/com/docusign/controller/examples/EG005ControllerEnvelopeRecipients.java @@ -1,56 +1,58 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; import com.docusign.esign.api.EnvelopesApi; +import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; - -/** - * List an envelope's recipients and their status.
- * List the envelope's recipients, including their current status. - */ +import javax.servlet.http.HttpSession; +import java.io.IOException; @Controller @RequestMapping("/eg005") -public class EG005ControllerEnvelopeRecipients extends AbstractController { +public class EG005ControllerEnvelopeRecipients extends EGController { - private final Session session; - private final User user; + @Autowired + HttpSession session; + + @Override + protected void addSpecialAttributes(ModelMap model) { + model.addAttribute("envelopeOk", session.getAttribute("envelopeId") != null); + } + @Override + protected String getEgName() { + return "eg005"; + } - @Autowired - public EG005ControllerEnvelopeRecipients(DSConfiguration config, Session session, User user) { - super(config, "eg005", "List envelope recipients"); - this.session = session; - this.user = user; + @Override + protected String getTitle() { + return "List envelope recipients"; } @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - model.addAttribute(MODEL_ENVELOPE_OK, StringUtils.isNotBlank(session.getEnvelopeId())); + protected String getResponseTitle() { + return "List envelope recipients result"; } @Override // ***DS.snippet.0.start - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) throws ApiException { + protected Object doWork(WorkArguments args, ModelMap model, + String accessToken, String basePath) throws ApiException { + // Data for this method + // accessToken (argument) + // basePath (argument) + String accountId = args.getAccountId(); + String envelopeId = args.getEnvelopeId(); + // Step 1. get envelope recipients - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); - DoneExample.createDefault(title) - .withJsonObject(envelopesApi.listRecipients(session.getAccountId(), session.getEnvelopeId())) - .withMessage("Results from the EnvelopeRecipients::list method:") - .addToModel(model); - return DONE_EXAMPLE_PAGE; + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); + setMessage("Results from the EnvelopeRecipients::list method:"); + return envelopesApi.listRecipients(accountId, envelopeId); } // ***DS.snippet.0.end } diff --git a/src/main/java/com/docusign/controller/examples/EG006ControllerEnvelopeDocs.java b/src/main/java/com/docusign/controller/examples/EG006ControllerEnvelopeDocs.java index 3ffd3367..3bcfd846 100644 --- a/src/main/java/com/docusign/controller/examples/EG006ControllerEnvelopeDocs.java +++ b/src/main/java/com/docusign/controller/examples/EG006ControllerEnvelopeDocs.java @@ -1,83 +1,94 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; import com.docusign.esign.api.EnvelopesApi; +import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; import com.docusign.esign.model.EnvelopeDocument; import com.docusign.esign.model.EnvelopeDocumentsResult; -import com.docusign.model.DoneExample; -import com.docusign.model.EnvelopeDocumentInfo; -import com.docusign.model.Session; -import com.docusign.model.User; - -import java.util.ArrayList; -import java.util.List; - -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang3.StringUtils; +import org.json.JSONArray; +import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; +import javax.servlet.http.HttpSession; - -/** - * List an envelope's documents.
- * A Certificate of Completion document is also associated with every - * envelope. This method is often used to dynamically create a list of an - * envelope's documents in preparation for enabling your user to download one - * or more of the documents. - */ @Controller @RequestMapping("/eg006") -public class EG006ControllerEnvelopeDocs extends AbstractController { - - private final Session session; - private final User user; +public class EG006ControllerEnvelopeDocs extends EGController { @Autowired - public EG006ControllerEnvelopeDocs(DSConfiguration config, Session session, User user) { - super(config, "eg006", "List envelope documents"); - this.session = session; - this.user = user; + HttpSession session; + + @Override + protected void addSpecialAttributes(ModelMap model) { + model.addAttribute("envelopeOk", null != session.getAttribute("envelopeId")); + } + + @Override + protected String getEgName() { + return "eg006"; } @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - model.addAttribute(MODEL_ENVELOPE_OK, StringUtils.isNotBlank(session.getEnvelopeId())); + protected String getTitle() { + return "List envelope documents"; + } + + @Override + protected String getResponseTitle() { + return "List envelope documents result"; } @Override // ***DS.snippet.0.start - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) throws ApiException { - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); + protected Object doWork(WorkArguments args, ModelMap model, + String accessToken, String basePath) throws ApiException { + // Data for this method + // accessToken (argument) + // basePath (argument) + String accountId = args.getAccountId(); + String envelopeId = args.getEnvelopeId(); + + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); // Step 1. List the envelope's documents - EnvelopeDocumentsResult result = envelopesApi.listDocuments(session.getAccountId(), session.getEnvelopeId()); + EnvelopeDocumentsResult result = envelopesApi.listDocuments(accountId, envelopeId); // Step 2. Process results // Save the envelopeId and its list of documents in the session so // they can be used in example 7 (download a document) - List envelopeDocItems = new ArrayList<>(); - session.setEnvelopeDocuments(envelopeDocItems); - envelopeDocItems.add(new EnvelopeDocumentInfo("Combined", "content", "combined")); - envelopeDocItems.add(new EnvelopeDocumentInfo("Zip archive", "zip", "archive")); + JSONArray envelopeDocItems = new JSONArray(); + envelopeDocItems.put(createStandardDoc("Combined", "content", "combined")); + envelopeDocItems.put(createStandardDoc("Zip archive", "zip", "archive")); for (EnvelopeDocument doc : result.getEnvelopeDocuments()) { - String documentName = doc.getName(); - if (StringUtils.equals(doc.getDocumentId(), "certificate")) { - documentName = "Certificate of completion"; - } - envelopeDocItems.add(new EnvelopeDocumentInfo(documentName, doc.getType(), doc.getDocumentId())); + JSONObject o = new JSONObject() + .put("documentId", doc.getDocumentId()) + .put("name", doc.getDocumentId() == "certificate" ? "Certificate of completion" : doc.getName()) + .put("type", doc.getType()); + + envelopeDocItems.put(o); } - DoneExample.createDefault(title) - .withJsonObject(result) - .withMessage("Results from the EnvelopeDocuments::list method:") - .addToModel(model); - return DONE_EXAMPLE_PAGE; + JSONObject envelopeDocuments = new JSONObject(); + envelopeDocuments.put("envelopeId", envelopeId); + envelopeDocuments.put("documents", envelopeDocItems); + + session.setAttribute("envelopeDocuments", envelopeDocuments); + setMessage("Results from the EnvelopeDocuments::list method:"); + + return result; + } + + private JSONObject createStandardDoc(String name, String type, String documentId) { + JSONObject o = new JSONObject(); + o.put("name", name); + o.put("type", type); + o.put("documentId", documentId); + return o; } // ***DS.snippet.0.end } diff --git a/src/main/java/com/docusign/controller/examples/EG007ControllerEnvelopeGetDoc.java b/src/main/java/com/docusign/controller/examples/EG007ControllerEnvelopeGetDoc.java index f04d7ebf..7734a4c7 100644 --- a/src/main/java/com/docusign/controller/examples/EG007ControllerEnvelopeGetDoc.java +++ b/src/main/java/com/docusign/controller/examples/EG007ControllerEnvelopeGetDoc.java @@ -1,122 +1,125 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; -import com.docusign.common.DocumentType; import com.docusign.esign.api.EnvelopesApi; +import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.model.EnvelopeDocumentInfo; import com.docusign.model.OptionItem; -import com.docusign.model.Session; -import com.docusign.model.User; - -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang3.StringUtils; +import org.json.JSONArray; +import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; - -import java.io.IOException; -import java.net.URLConnection; +import javax.servlet.http.HttpSession; import java.util.ArrayList; -import java.util.List; - -import javax.servlet.http.HttpServletResponse; - -/** - * Download a document from an envelope.
- * An envelope's documents can be downloaded one by one or as a complete set. - */ @Controller @RequestMapping("/eg007") -public class EG007ControllerEnvelopeGetDoc extends AbstractController { - - private static final String MODEL_DOCUMENTS_OK = "documentsOk"; - private static final String MODEL_DOCUMENT_OPTIONS = "documentOptions"; - private static final String HTTP_CONTENT_DISPOSITION_VALUE = "inline;filename="; - private static final String ZIP_EXTENSION = "zip"; - - private final Session session; - private final User user; - - +public class EG007ControllerEnvelopeGetDoc extends EGController { @Autowired - public EG007ControllerEnvelopeGetDoc(DSConfiguration config, Session session, User user) { - super(config, "eg007", "Download a document"); - this.session = session; - this.user = user; - } + HttpSession session; @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - model.addAttribute(MODEL_ENVELOPE_OK, StringUtils.isNotBlank(session.getEnvelopeId())); - List envelopeDocuments = session.getEnvelopeDocuments(); - boolean documentsOk = envelopeDocuments != null; - model.addAttribute(MODEL_DOCUMENTS_OK, documentsOk); - if (!documentsOk) { + protected void addSpecialAttributes(ModelMap model) { + model.addAttribute("envelopeOk", session.getAttribute("envelopeId") != null); + boolean documentsOk = session.getAttribute("envelopeDocuments") != null; + model.addAttribute("documentsOk", documentsOk); + if (documentsOk == false) { return; } + JSONObject envelopeDocuments = (JSONObject) session.getAttribute("envelopeDocuments"); + JSONArray documents = (JSONArray) envelopeDocuments.get("documents"); ArrayList documentOptions = new ArrayList<>(); - for (EnvelopeDocumentInfo docInfo : envelopeDocuments) { - OptionItem doc = new OptionItem(docInfo.getName(), docInfo.getDocumentId()); + for (int i = 0; i < documents.length(); i++) { + OptionItem doc = new OptionItem(); + + doc.setText(documents.getJSONObject(i).getString("name")); + doc.setDocumentId(documents.getJSONObject(i).getString("documentId")); documentOptions.add(doc); } - model.addAttribute(MODEL_DOCUMENT_OPTIONS, documentOptions); + model.addAttribute("documentOptions", documentOptions); + } + + @Override + protected String getEgName() { + return "eg007"; + } + + @Override + protected String getTitle() { + return "Download a document"; + } + + @Override + protected String getResponseTitle() { + return null; } @Override // ***DS.snippet.0.start protected Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException { - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); + String accessToken, String basePath) throws ApiException { + // Data for this method + // accessToken (argument) + // basePath (argument) + String accountId = args.getAccountId(); + String envelopeId = args.getEnvelopeId(); + String documentId = args.getDocumentId(); + JSONObject envelopeDocuments = args.getEnvelopeDocuments(); // stored by EG006ControllerEnvelopeDocs + + + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); // Step 1. EnvelopeDocuments::get. // Exceptions will be caught by the calling function - String documentId = args.getDocSelect(); - byte[] results = envelopesApi.getDocument(session.getAccountId(), session.getEnvelopeId(), documentId); + byte[] results = envelopesApi.getDocument(accountId, envelopeId, documentId); // Step 2. process results - List envelopeDocuments = session.getEnvelopeDocuments(); - EnvelopeDocumentInfo docItem = find(envelopeDocuments, documentId); - - String docName = docItem.getName(); - String docType = docItem.getType(); - String pdfExtention = DocumentType.PDF.getDefaultFileExtention(); - if (StringUtils.equalsAny(docType, "content", "summary", pdfExtention)) { - docName = addExtension(docName, pdfExtention); + JSONArray documents = envelopeDocuments.getJSONArray("documents"); + JSONObject docItem = find(documents, documentId); + + String docName = docItem.getString("name"); + boolean hasPDFsuffix = docName.toUpperCase().endsWith(".PDF"); + boolean pdfFile = hasPDFsuffix; + // Add .pdf if it's a content or summary doc and doesn't already end in .pdf + String docType = docItem.getString("type"); + if (("content".equals(docType) || "summary".equals(docType)) && !hasPDFsuffix) { + docName += ".pdf"; + pdfFile = true; } - if (ZIP_EXTENSION.equals(docType)) { - docName = addExtension(docName, ZIP_EXTENSION); + // Add .zip as appropriate + if ("zip".equals(docType)) { + docName += ".zip"; } - - response.setContentType(URLConnection.guessContentTypeFromName(docName)); - response.setContentLength(results.length); - response.setHeader(HttpHeaders.CONTENT_DISPOSITION, HTTP_CONTENT_DISPOSITION_VALUE + docName); - response.getOutputStream().write(results); - response.flushBuffer(); - return null; - } - - private static String addExtension(String fileName, String extension) { - if (FilenameUtils.isExtension(fileName, extension)) { - return fileName; + // Return the file information + // See https://stackoverflow.com/a/30625085/64904 + String mimetype; + if (pdfFile) { + mimetype = "application/pdf"; + } else if ("zip".equals(docType)) { + mimetype = "application/zip"; + } else { + mimetype = "application/octet-stream"; } - return String.join(".", fileName, extension); + args.setRedirectUrl(null); + return new JSONObject() + .put("mimetype", mimetype) + .put("docName", docName) + .put("fileBytes", results); } - private static EnvelopeDocumentInfo find(List documents, String documentId) { - for (EnvelopeDocumentInfo docInfo : documents) { - if (StringUtils.equalsIgnoreCase(docInfo.getDocumentId(), documentId)) { - return docInfo; + private JSONObject find(JSONArray documents, String documentId) { + for (int i = 0; i < documents.length(); i++) { + JSONObject item = documents.getJSONObject(i); + if (item.getString("documentId").equalsIgnoreCase(documentId)) { + return item; } } - - throw new ExampleException("Requested document is not found.", null); + return null; } // ***DS.snippet.0.end } diff --git a/src/main/java/com/docusign/controller/examples/EG008ControllerCreateTemplate.java b/src/main/java/com/docusign/controller/examples/EG008ControllerCreateTemplate.java index 0521ac6e..afb0352a 100644 --- a/src/main/java/com/docusign/controller/examples/EG008ControllerCreateTemplate.java +++ b/src/main/java/com/docusign/controller/examples/EG008ControllerCreateTemplate.java @@ -1,163 +1,179 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; import com.docusign.esign.api.TemplatesApi; import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.CarbonCopy; -import com.docusign.esign.model.Checkbox; -import com.docusign.esign.model.Document; -import com.docusign.esign.model.EnvelopeTemplate; -import com.docusign.esign.model.EnvelopeTemplateResults; -import com.docusign.esign.model.List; -import com.docusign.esign.model.ListItem; -import com.docusign.esign.model.Radio; -import com.docusign.esign.model.RadioGroup; -import com.docusign.esign.model.SignHere; -import com.docusign.esign.model.Signer; -import com.docusign.esign.model.Tabs; -import com.docusign.esign.model.TemplateSummary; -import com.docusign.esign.model.Text; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; +import com.docusign.esign.model.*; +import com.sun.jersey.core.util.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; - +import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.Arrays; -import java.util.Locale; - -import javax.servlet.http.HttpServletResponse; - -/** - * Create a template.
- * Create a template with two roles, signer and cc. The template - * includes three documents. - */ @Controller @RequestMapping("/eg008") -public class EG008ControllerCreateTemplate extends AbstractController { +public class EG008ControllerCreateTemplate extends EGController { - private static final String TEMPLATE_NAME = "Example Signer and CC template"; - private static final String PDF_DOCUMENT_FILE_NAME = "World_Wide_Corp_fields.pdf"; - private static final String PDF_DOCUMENT_NAME = "Lorem Ipsum"; - private static final String DOCUMENT_ID = "1"; - private static final String PAGE_NUMBER = "1"; - private static final String FALSE = "false"; + @Autowired + protected HttpSession session; - private final Session session; - private final User user; + @Override + protected void addSpecialAttributes(ModelMap model) { + } + @Override + protected String getEgName() { + return "eg008"; + } - @Autowired - public EG008ControllerCreateTemplate(DSConfiguration config, Session session, User user) { - super(config, "eg008", "Create a template"); - this.session = session; - this.user = user; + @Override + protected String getTitle() { + return "Create a template"; + } + + @Override + protected String getResponseTitle() { + return "Template results"; } @Override // ***DS.snippet.0.start - protected Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException { + protected EnvelopeDocumentsResult doWork(WorkArguments args, ModelMap model, + String accessToken, String basePath) throws ApiException, IOException { + // Data for this method + // accessToken (argument) + // basePath (argument) + // config.appUrl (url of the application itself) + String accountId = args.getAccountId(); + String templateName = "Example Signer and CC template"; + + // Step 1. list existing templates - ApiClient apiClient = createApiClient(session.getBasePath(), user.getAccessToken()); + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); TemplatesApi templatesApi = new TemplatesApi(apiClient); TemplatesApi.ListTemplatesOptions options = templatesApi.new ListTemplatesOptions(); - options.setSearchText(TEMPLATE_NAME); - String accountId = session.getAccountId(); + options.setSearchText(templateName); + // get the results EnvelopeTemplateResults results = templatesApi.listTemplates(accountId, options); - // Step 2. Process results. If template do not exist, create one + // Step 2. process results. Template found? + String templateId; + String resultsTemplateName; + boolean createdNewTemplate; if (Integer.parseInt(results.getResultSetSize()) > 0) { - EnvelopeTemplate template = results.getEnvelopeTemplates().get(0); - DoneExample.createDefault(title) - .withMessage(String.format( - "The template already exists in your account.
Template name: %s, ID %s.", - template.getName(), template.getTemplateId())) - .addToModel(model); + // Yes. Save the template id and name + EnvelopeTemplateResult template = results.getEnvelopeTemplates().get(0); + templateId = template.getTemplateId(); + resultsTemplateName = template.getName(); + createdNewTemplate = false; } else { - session.setTemplateName(TEMPLATE_NAME); - TemplateSummary template = templatesApi.createTemplate(accountId, makeTemplate()); - DoneExample.createDefault(title) - .withMessage(String.format( - "The template has been created!
Template name: %s, ID %s.", - template.getName(), template.getTemplateId())) - .addToModel(model); + // No. Make a new template + // Prepare request + args.setTemplateName("Example Signer and CC template"); + EnvelopeTemplate templateReqObject = makeTemplate(args); + // Call DocuSign + TemplateSummary template = templatesApi.createTemplate(accountId, templateReqObject); + // process result + templateId = template.getTemplateId(); + resultsTemplateName = template.getName(); + createdNewTemplate = true; } - return DONE_EXAMPLE_PAGE; - } + // Save templateId + session.setAttribute("templateId", templateId); + String msg = createdNewTemplate ? + "The template has been created!" : + "The template already exists in your account."; - // document 1 (pdf) has tag /sn1/ - // - // The template has two recipient roles. - // recipient 1 - signer - // recipient 2 - cc - // The template will be sent first to the signer. - // After it is signed, a copy is sent to the cc person. - private EnvelopeTemplate makeTemplate() throws IOException { - Document doc = EnvelopeHelpers.createDocumentFromFile(PDF_DOCUMENT_FILE_NAME, PDF_DOCUMENT_NAME, "1"); + setMessage(msg + "
Template name: " + resultsTemplateName + ", ID " + templateId + "."); - // Tabs are set per recipient / signer - Tabs signer1Tabs = new Tabs(); - signer1Tabs.setCheckboxTabs(Arrays.asList( - createCheckbox("ckAuthorization", "75", "417"), - createCheckbox("ckAuthentication", "75", "447"), - createCheckbox("ckAgreement", "75", "478"), - createCheckbox("ckAcknowledgement", "75", "508"))); - signer1Tabs.setListTabs(Arrays.asList(createList())); - signer1Tabs.setRadioGroupTabs(Arrays.asList(createRadioGroup())); - signer1Tabs.setSignHereTabs(Arrays.asList(createSignHere())); - signer1Tabs.textTabs(Arrays.asList( - createText("text", "153", "230"), - createText("numbersOnly", "153", "260"))); + return null; + } + + private EnvelopeTemplate makeTemplate(WorkArguments args) throws IOException { + // document 1 (pdf) has tag /sn1/ + // + // The template has two recipient roles. + // recipient 1 - signer + // recipient 2 - cc + // The template will be sent first to the signer. + // After it is signed, a copy is sent to the cc person. + // read file from a local directory + // The reads could raise an exception if the file is not available! + byte[] docPdfBytes = readFile("World_Wide_Corp_fields.pdf"); + // add the documents + Document doc = new Document(); + String docB64 = new String(Base64.encode(docPdfBytes)); + doc.setDocumentBase64(docB64); + doc.setName("Lorem Ipsum"); // can be different from actual file name + doc.setFileExtension("pdf"); + doc.setDocumentId("1"); // create a signer recipient to sign the document, identified by name and email + // We're setting the parameters via the object creation + Signer signer1 = new Signer(); + signer1.setRoleName("signer"); + signer1.setRecipientId("1"); + signer1.setRoutingOrder("1"); // routingOrder (lower means earlier) determines the order of deliveries // to the recipients. Parallel routing order is supported by using the // same integer as the order for two or more recipients. - Signer signer = new Signer(); - signer.setRoleName(EnvelopeHelpers.SIGNER_ROLE_NAME); - signer.setRecipientId("1"); - signer.setRoutingOrder("1"); - signer.setTabs(signer1Tabs); // create a cc recipient to receive a copy of the documents, identified by name and email + // We're setting the parameters via setters CarbonCopy cc1 = new CarbonCopy(); - cc1.setRoleName(EnvelopeHelpers.CC_ROLE_NAME); + cc1.setRoleName("cc"); cc1.setRoutingOrder("2"); cc1.setRecipientId("2"); + // Create fields using absolute positioning: + SignHere signHere = new SignHere(); + signHere.setDocumentId("1"); + signHere.setPageNumber("1"); + signHere.setXPosition("191"); + signHere.setYPosition("148"); - // Create the overall template definition. The order in the docs array - // determines the order in the envelope. - EnvelopeTemplate template = new EnvelopeTemplate(); - template.setDocuments(Arrays.asList(doc)); - template.setEmailSubject("Please sign this document"); - template.setName(session.getTemplateName()); - template.setDescription("Example template created via the API"); - template.setShared(FALSE); - template.setRecipients(EnvelopeHelpers.createRecipients(signer, cc1)); - template.setStatus(EnvelopeHelpers.ENVELOPE_STATUS_CREATED); + Checkbox check1 = new Checkbox(); + check1.setDocumentId("1"); + check1.setPageNumber("1"); + check1.setXPosition("75"); + check1.setYPosition("417"); + check1.setTabLabel("ckAuthorization"); - return template; - } + Checkbox check2 = new Checkbox(); + check2.setDocumentId("1"); + check2.setPageNumber("1"); + check2.setXPosition("75"); + check2.setYPosition("447"); + check2.setTabLabel("ckAuthentication"); + + Checkbox check3 = new Checkbox(); + check3.setDocumentId("1"); + check3.setPageNumber("1"); + check3.setXPosition("75"); + check3.setYPosition("478"); + check3.setTabLabel("ckAgreement"); - private static List createList() { - List list = new List(); - list.setDocumentId(DOCUMENT_ID); - list.setPageNumber(PAGE_NUMBER); - list.setXPosition("142"); - list.setYPosition("291"); - list.setFont("helvetica"); - list.setFontSize("size14"); - list.setTabLabel("list"); - list.setRequired(FALSE); - list.setListItems(Arrays.asList( + Checkbox check4 = new Checkbox(); + check4.setDocumentId("1"); + check4.setPageNumber("1"); + check4.setXPosition("75"); + check4.setYPosition("508"); + check4.setTabLabel("ckAcknowledgement"); + + List list1 = new List(); + list1.setDocumentId("1"); + list1.setPageNumber("1"); + list1.setXPosition("142"); + list1.setYPosition("291"); + list1.setFont("helvetica"); + list1.setFontSize("size14"); + list1.setTabLabel("list"); + list1.setRequired("false"); + list1.setListItems(Arrays.asList( createListItem("Red"), createListItem("Orange"), createListItem("Yellow"), @@ -166,19 +182,26 @@ private static List createList() { createListItem("Indigo"), createListItem("Violet") )); - return list; - } + // The SDK can't create a number tab at this time. Bug DCM-2732 + // Until it is fixed, use a text tab instead. + // , number = docusign.Number.constructFromObject({ + // documentId: "1", pageNumber: "1", xPosition: "163", yPosition: "260", + // font: "helvetica", fontSize: "size14", tabLabel: "numbersOnly", + // height: "23", width: "84", required: "false"}) + Text textInsteadOfNumber = new Text(); + textInsteadOfNumber.setDocumentId("1"); + textInsteadOfNumber.setPageNumber("1"); + textInsteadOfNumber.setXPosition("153"); + textInsteadOfNumber.setYPosition("260"); + textInsteadOfNumber.setFont("helvetica"); + textInsteadOfNumber.setFontSize("size14"); + textInsteadOfNumber.setTabLabel("numbersOnly"); + textInsteadOfNumber.setHeight(23); + textInsteadOfNumber.setWidth(84); + textInsteadOfNumber.required("false"); - private static ListItem createListItem(String color) { - ListItem item = new ListItem(); - item.setText(color); - item.setValue(color.toLowerCase(Locale.ENGLISH)); - return item; - } - - private static RadioGroup createRadioGroup() { RadioGroup radioGroup = new RadioGroup(); - radioGroup.setDocumentId(DOCUMENT_ID); + radioGroup.setDocumentId("1"); radioGroup.setGroupName("radio1"); radioGroup.setRadios(Arrays.asList( @@ -186,51 +209,68 @@ private static RadioGroup createRadioGroup() { createRadio("red", "74"), createRadio("blue", "220") )); - return radioGroup; + + Text text = new Text(); + text.setDocumentId("1"); + text.setPageNumber("1"); + text.setXPosition("153"); + text.setYPosition("230"); + text.setFont("helvetica"); + text.setFontSize("size14"); + text.setTabLabel("text"); + text.setHeight(23); + text.setWidth(84); + text.required("false"); + + // Tabs are set per recipient / signer + Tabs signer1Tabs = new Tabs(); + signer1Tabs.setCheckboxTabs(Arrays.asList(check1, check2, check3, check4)); + signer1Tabs.setListTabs(Arrays.asList(list1)); + // numberTabs: [number], + signer1Tabs.setRadioGroupTabs(Arrays.asList(radioGroup)); + signer1Tabs.setSignHereTabs(Arrays.asList(signHere)); + signer1Tabs.textTabs(Arrays.asList(text, textInsteadOfNumber)); + + signer1.setTabs(signer1Tabs); + + // Add the recipients to the env object + Recipients recipients = new Recipients(); + recipients.setSigners(Arrays.asList(signer1)); + recipients.setCarbonCopies(Arrays.asList(cc1)); + + // create the envelope template definition object + EnvelopeTemplateDefinition envelopeTemplateDefinition = new EnvelopeTemplateDefinition(); + envelopeTemplateDefinition.setDescription("Example template created via the API"); + envelopeTemplateDefinition.setName(args.getTemplateName()); + envelopeTemplateDefinition.setShared("false"); + + // create the overall template definition + EnvelopeTemplate template = new EnvelopeTemplate(); + // The order in the docs array determines the order in the env + template.setDocuments(Arrays.asList(doc)); + template.setEmailSubject("Please sign this document"); + template.setEnvelopeTemplateDefinition(envelopeTemplateDefinition); + template.setRecipients(recipients); + template.setStatus("created"); + + return template; } - private static Radio createRadio(String value, String xPosition) { + private ListItem createListItem(String color) { + ListItem item = new ListItem(); + item.setText(color); + item.setValue(color.toLowerCase()); + return item; + } + + private Radio createRadio(String value, String xPosition) { Radio radio = new Radio(); - radio.setPageNumber(PAGE_NUMBER); + radio.setPageNumber("1"); radio.setValue(value); radio.setXPosition(xPosition); radio.setYPosition("384"); - radio.setRequired(FALSE); + radio.setRequired("false"); return radio; } - - private static Checkbox createCheckbox(String label, String xPosition, String yPosition) { - Checkbox check = new Checkbox(); - check.setDocumentId(DOCUMENT_ID); - check.setPageNumber(PAGE_NUMBER); - check.setXPosition(xPosition); - check.setYPosition(yPosition); - check.setTabLabel(label); - return check; - } - - private static Text createText(String label, String xPosition, String yPosition) { - Text text = new Text(); - text.setDocumentId(DOCUMENT_ID); - text.setPageNumber(PAGE_NUMBER); - text.setXPosition(xPosition); - text.setYPosition(yPosition); - text.setFont("helvetica"); - text.setFontSize("size14"); - text.setTabLabel(label); - text.setHeight("23"); - text.setWidth("84"); - text.setRequired(FALSE); - return text; - } - - private static SignHere createSignHere() { - SignHere signHere = new SignHere(); - signHere.setDocumentId(DOCUMENT_ID); - signHere.setPageNumber(PAGE_NUMBER); - signHere.setXPosition("191"); - signHere.setYPosition("148"); - return signHere; - } // ***DS.snippet.0.end } diff --git a/src/main/java/com/docusign/controller/examples/EG009ControllerUseTemplate.java b/src/main/java/com/docusign/controller/examples/EG009ControllerUseTemplate.java index d8183f8c..37094e01 100644 --- a/src/main/java/com/docusign/controller/examples/EG009ControllerUseTemplate.java +++ b/src/main/java/com/docusign/controller/examples/EG009ControllerUseTemplate.java @@ -1,95 +1,88 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; import com.docusign.esign.api.EnvelopesApi; -import com.docusign.esign.api.TemplatesApi; import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; import com.docusign.esign.model.EnvelopeDefinition; import com.docusign.esign.model.EnvelopeSummary; -import com.docusign.esign.model.EnvelopeTemplateResults; import com.docusign.esign.model.TemplateRole; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import java.util.Arrays; -import javax.servlet.http.HttpServletResponse; - - -/** - * Send an envelope using a template.
- * The envelope is defined by the template. The signer and cc recipient name - * and email are used to fill in the template's roles. This example - * demonstrates a common pattern for DocuSign integrations: envelopes will be - * sent programmatically, based on a template. If the template definition needs - * to be updated, the DocuSign web tool can be used to easily update the - * template, thus avoiding the need to make software changes. - */ @Controller @RequestMapping("/eg009") -public class EG009ControllerUseTemplate extends AbstractController { +public class EG009ControllerUseTemplate extends EGController { - private static final String MODEL_LIST_TEMPLATE = "listTemplates"; + private String message; - private final Session session; - private final User user; + @Override + protected void addSpecialAttributes(ModelMap model) { + model.addAttribute("templateOk", session.getAttribute("templateId") != null); + } + @Override + protected String getResponseTitle() { + return "Envelope sent"; + } - @Autowired - public EG009ControllerUseTemplate(DSConfiguration config, Session session, User user) { - super(config, "eg009", "Envelope sent"); - this.session = session; - this.user = user; + @Override + protected String getEgName() { + return "eg009"; } @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - ApiClient apiClient = createApiClient(session.getBasePath(), user.getAccessToken()); - TemplatesApi templatesApi = new TemplatesApi(apiClient); - EnvelopeTemplateResults templates = templatesApi.listTemplates(session.getAccountId()); - model.addAttribute(MODEL_LIST_TEMPLATE, templates.getEnvelopeTemplates()); + protected String getTitle() { + return "Send envelope using a template"; } @Override // ***DS.snippet.0.start - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) throws ApiException { - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); + protected Object doWork(WorkArguments args, ModelMap model, + String accessToken, String basePath) throws ApiException { + // Data for this method + // accessToken (argument) + // basePath (argument) + String accountId = args.getAccountId(); + + + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); EnvelopeDefinition envelope = makeEnvelope(args); - EnvelopeSummary result = envelopesApi.createEnvelope(session.getAccountId(), envelope); - - session.setEnvelopeId(result.getEnvelopeId()); - DoneExample.createDefault(title) - .withJsonObject(result) - .withMessage("The envelope has been created and sent!
Envelope ID " - + result.getEnvelopeId() + ".") - .addToModel(model); - return DONE_EXAMPLE_PAGE; + EnvelopeSummary result = envelopesApi.createEnvelope(accountId, envelope); + // process result + session.setAttribute("envelopeId", result.getEnvelopeId()); + setMessage("The envelope has been created and sent!
Envelope ID " + result.getEnvelopeId() + "."); + return result; } - private static EnvelopeDefinition makeEnvelope(WorkArguments args) { - TemplateRole signer = new TemplateRole(); - signer.setEmail(args.getSignerEmail()); - signer.setName(args.getSignerName()); - signer.setRoleName(EnvelopeHelpers.SIGNER_ROLE_NAME); + private EnvelopeDefinition makeEnvelope(WorkArguments args) { + // Data for this method + String signerEmail = args.getSignerEmail(); + String signerName = args.getSignerName(); + String ccEmail = args.getCcEmail(); + String ccName = args.getCcName(); + String templateId = args.getTemplateId(); + + + EnvelopeDefinition env = new EnvelopeDefinition(); + env.setTemplateId(templateId); - TemplateRole cc = new TemplateRole(); - cc.setEmail(args.getCcEmail()); - cc.setName(args.getCcName()); - cc.setRoleName(EnvelopeHelpers.CC_ROLE_NAME); + TemplateRole signer1 = new TemplateRole(); + signer1.setEmail(signerEmail); + signer1.setName(signerName); + signer1.setRoleName("signer"); - EnvelopeDefinition envelope = new EnvelopeDefinition(); - envelope.setTemplateId(args.getTemplateId()); - envelope.setTemplateRoles(Arrays.asList(signer, cc)); - envelope.setStatus(EnvelopeHelpers.ENVELOPE_STATUS_SENT); + TemplateRole cc1 = new TemplateRole(); + cc1.setEmail(ccEmail); + cc1.setName(ccName); + cc1.setRoleName("cc"); - return envelope; + env.setTemplateRoles(Arrays.asList(signer1, cc1)); + env.setStatus("sent"); + return env; } // ***DS.snippet.0.end } diff --git a/src/main/java/com/docusign/controller/examples/EG010ControllerSendBinaryDocs.java b/src/main/java/com/docusign/controller/examples/EG010ControllerSendBinaryDocs.java index c25df617..e350958b 100644 --- a/src/main/java/com/docusign/controller/examples/EG010ControllerSendBinaryDocs.java +++ b/src/main/java/com/docusign/controller/examples/EG010ControllerSendBinaryDocs.java @@ -1,211 +1,283 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; -import com.docusign.common.DocumentType; -import com.docusign.esign.model.CarbonCopy; -import com.docusign.esign.model.Signer; -import com.docusign.esign.model.Tabs; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - -import lombok.Value; - +import com.docusign.esign.model.*; import org.json.JSONArray; import org.json.JSONObject; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; -import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.RequestMapping; import javax.net.ssl.HttpsURLConnection; -import javax.servlet.http.HttpServletResponse; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.core.MediaType; - +import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; -import java.net.HttpURLConnection; +import java.io.InputStreamReader; import java.net.URL; -import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.List; - -/** - * Send an envelope using binary document transfer.
- * The envelope includes a pdf, doc, and HTML document. Multipart data transfer - * is used to send the documents in binary format to DocuSign. - */ @Controller @RequestMapping("/eg010") -public class EG010ControllerSendBinaryDocs extends AbstractController { - - private static final String HYPHENS = "--"; - private static final String LINE_DELIMITER = "\r\n"; - private static final String BOUNDARY_DELIMITER = "multipartboundary_multipartboundary"; - - private static final String HTML_DOCUMENT_FILE_NAME = "templates/candy-bonbon.ftl"; - private static final String HTML_DOCUMENT_NAME = "Order acknowledgement"; - private static final String PDF_DOCUMENT_FILE_NAME = "World_Wide_Corp_lorem.pdf"; - private static final String PDF_DOCUMENT_NAME = "Lorem Ipsum"; - private static final String DOCX_DOCUMENT_FILE_NAME = "World_Wide_Corp_Battle_Plan_Trafalgar.docx"; - private static final String DOCX_DOCUMENT_NAME = "Battle Plan"; - private static final int ANCHOR_OFFSET_Y = 10; - private static final int ANCHOR_OFFSET_X = 20; - - private final Session session; - private final User user; - - - @Autowired - public EG010ControllerSendBinaryDocs(DSConfiguration config, Session session, User user) { - super(config, "eg010", "Send envelope with multipart mime"); - this.session = session; - this.user = user; +public class EG010ControllerSendBinaryDocs extends EGController { + + @Override + protected void addSpecialAttributes(ModelMap model) { + } + + @Override + protected String getEgName() { + return "eg010"; + } + + @Override + protected String getTitle() { + return "Send envelope with multipart mime"; + } + + @Override + protected String getResponseTitle() { + return "Envelope sent"; } @Override // ***DS.snippet.0.start - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) throws IOException { - // Step 1. Gather documents and their headers - List documents = List.of( - new DocumentInfo(HTML_DOCUMENT_NAME, "1", DocumentType.HTML, - EnvelopeHelpers.createHtmlFromTemplateFile(HTML_DOCUMENT_FILE_NAME, "args", args)), - new DocumentInfo(DOCX_DOCUMENT_NAME, "2", DocumentType.DOCX, - EnvelopeHelpers.readFile(DOCX_DOCUMENT_FILE_NAME)), - new DocumentInfo(PDF_DOCUMENT_NAME, "3", DocumentType.PDF, - EnvelopeHelpers.readFile(PDF_DOCUMENT_FILE_NAME)) - ); - - // Step 2. Make the envelope JSON request body - JSONObject envelopeJSON = makeEnvelopeJSON(args, documents); + protected EnvelopeDocumentsResult doWork(WorkArguments args, ModelMap model, + String accessToken, String basePath) throws IOException { + // Data for this method + // accessToken (argument) + // basePath (argument) + // config.docDocx (filename for the docx source doc) + // config.docPdf (filename for the pdf source doc) + String accountId = args.getAccountId(); + + + // Step 1. Make the envelope JSON request body + JSONObject envelopeJSON = makeEnvelopeJSON(args); + Object results = null; + + // Step 2. Gather documents and their headers + // Read files from a local directory + // The reads could raise an exception if the file is not available! + List documents = Arrays.asList( + createDocumentObject(envelopeJSON, "text/html", + document1(args).getBytes(), 0), + createDocumentObject(envelopeJSON, + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + readFile(config.docDocx), 1), + createDocumentObject(envelopeJSON, "application/pdf", + readFile(config.docPdf), 2) + ); // Step 3. Create the multipart body - URL uri = new URL(String.format("%s/v2.1/accounts/%s/envelopes", session.getBasePath(), session.getAccountId())); - String contentType = String.join("",MediaType.MULTIPART_FORM_DATA, "; boundary=", BOUNDARY_DELIMITER); + String CRLF = "\r\n", boundary = "multipartboundary_multipartboundary", hyphens = "--"; + + URL uri = new URL(basePath + + "/v2/accounts/" + accountId + "/envelopes"); + HttpsURLConnection connection = (HttpsURLConnection) uri.openConnection(); - connection.setRequestMethod(HttpMethod.POST); - connection.setRequestProperty(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); - connection.setRequestProperty(HttpHeaders.CONTENT_TYPE, contentType); - connection.setRequestProperty(HttpHeaders.AUTHORIZATION, BEARER_AUTHENTICATION + user.getAccessToken()); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Accept", "application/json"); + connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); + connection.setRequestProperty("Authorization", "Bearer " + accessToken); connection.setDoOutput(true); - // See https://developers.docusign.com/esign-rest-api/guides/requests-and-responses DataOutputStream buffer = new DataOutputStream(connection.getOutputStream()); - writeBoundaryHeader(buffer, MediaType.APPLICATION_JSON, "form-data"); - buffer.writeBytes(envelopeJSON.toString(DoneExample.JSON_INDENT_FACTOR)); - - for (DocumentInfo docInfo : documents) { - String content = String.format("file; filename=\"%s\";documentid=%s", docInfo.getName(), docInfo.getId()); - buffer.writeBytes(LINE_DELIMITER); - writeBoundaryHeader(buffer, docInfo.getDocType().getMime(), content); - buffer.write(docInfo.getData()); + buffer.writeBytes(hyphens); + buffer.writeBytes(boundary); + buffer.writeBytes(CRLF); + buffer.writeBytes("Content-Type: application/json"); + buffer.writeBytes(CRLF); + buffer.writeBytes("Content-Disposition: form-data"); + buffer.writeBytes(CRLF); + buffer.writeBytes(CRLF); + buffer.writeBytes(envelopeJSON.toString(4)); + + // Loop to add the documents. + // See section Multipart Form Requests on page https://developers.docusign.com/esign-rest-api/guides/requests-and-responses + for (JSONObject d : documents) { + buffer.writeBytes(CRLF); + buffer.writeBytes(hyphens); + buffer.writeBytes(boundary); + buffer.writeBytes(CRLF); + buffer.writeBytes("Content-Type:" + d.getString("mime")); + buffer.writeBytes(CRLF); + buffer.writeBytes("Content-Disposition: file; filename=\"" + d.getString("filename") + ";documentid=" + d.getString("documentId")); + buffer.writeBytes(CRLF); + buffer.writeBytes(CRLF); + buffer.write((byte[]) d.get("bytes")); } - writeClosingBoundary(buffer); + // Add closing boundary + buffer.writeBytes(CRLF); + buffer.writeBytes(hyphens); + buffer.writeBytes(boundary); + buffer.writeBytes(hyphens); + buffer.writeBytes(CRLF); + + buffer.flush(); int responseCode = connection.getResponseCode(); - if (responseCode < HttpURLConnection.HTTP_OK || responseCode >= HttpURLConnection.HTTP_MULT_CHOICE) { - String error = StreamUtils.copyToString(connection.getErrorStream(), StandardCharsets.UTF_8); - throw new ExampleException(error, null); + System.out.println("Response Code : " + responseCode); + + BufferedReader in; + if (responseCode >= 200 && responseCode < 300) { + in = new BufferedReader( + new InputStreamReader(connection.getInputStream())); + } else { + in = new BufferedReader( + new InputStreamReader(connection.getErrorStream())); } - String responseString = StreamUtils.copyToString(connection.getInputStream(), StandardCharsets.UTF_8); - JSONObject obj = new JSONObject(responseString); + String inputLine; + StringBuffer response = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + JSONObject obj = new JSONObject(response.toString()); String envelopeId = obj.getString("envelopeId"); - session.setEnvelopeId(envelopeId); + session.setAttribute("envelopeId", envelopeId); + setMessage("The envelope has been created and sent!
Envelope ID " + envelopeId+"."); - DoneExample.createDefault(title) - .withMessage("The envelope has been created and sent!
Envelope ID " + envelopeId + ".") - .addToModel(model); - return DONE_EXAMPLE_PAGE; + return null; } - private static void writeBoundaryHeader(DataOutputStream buffer, - String contentType, String contentDisposition) throws IOException { - buffer.writeBytes(HYPHENS); - buffer.writeBytes(BOUNDARY_DELIMITER); - buffer.writeBytes(LINE_DELIMITER); - buffer.writeBytes(String.join(": ", HttpHeaders.CONTENT_TYPE, contentType)); - buffer.writeBytes(LINE_DELIMITER); - buffer.writeBytes(String.join(": ", HttpHeaders.CONTENT_DISPOSITION, contentDisposition)); - buffer.writeBytes(LINE_DELIMITER); - buffer.writeBytes(LINE_DELIMITER); - } + private JSONObject createDocumentObject(JSONObject envelopeJSON, String mime, byte[] bytes, int index) { - private static void writeClosingBoundary(DataOutputStream buffer) throws IOException { - buffer.writeBytes(LINE_DELIMITER); - buffer.writeBytes(HYPHENS); - buffer.writeBytes(BOUNDARY_DELIMITER); - buffer.writeBytes(HYPHENS); - buffer.writeBytes(LINE_DELIMITER); - buffer.flush(); + JSONObject doc = envelopeJSON.getJSONArray("documents").getJSONObject(index); + + return new JSONObject() + .put("mime", mime) + .put("filename", doc.getString("name")) + .put("documentId", doc.getString("documentId")) + .put("bytes", bytes); } - // document 1 (html) has tag **signature_1** - // document 2 (docx) has tag /sn1/ - // document 3 (pdf) has tag /sn1/ - // - // The envelope has two recipients. - // recipient 1 - signer - // recipient 2 - cc - // The envelope will be sent first to the signer. - // After it is signed, a copy is sent to the cc person. - private static JSONObject makeEnvelopeJSON(WorkArguments args, List documents) { - // The DocuSign platform searches throughout your envelope's documents for - // matching anchor strings. So the signHere2 tab will be used in both document - // 2 and 3 since they use the same anchor string for their "signer 1" tabs. - Tabs signerTabs = EnvelopeHelpers.createSignerTabs( - EnvelopeHelpers.createSignHere("**signature_1**", ANCHOR_OFFSET_Y, ANCHOR_OFFSET_X), - EnvelopeHelpers.createSignHere("/sn1/", ANCHOR_OFFSET_Y, ANCHOR_OFFSET_X)); + private JSONObject makeEnvelopeJSON(WorkArguments args) { + // Data for this method + String signerEmail = args.getSignerEmail(); + String signerName = args.getSignerName(); + String ccEmail = args.getCcEmail(); + String ccName = args.getCcName(); + + + // document 1 (html) has tag **signature_1** + // document 2 (docx) has tag /sn1/ + // document 3 (pdf) has tag /sn1/ + // + // The envelope has two recipients. + // recipient 1 - signer + // recipient 2 - cc + // The envelope will be sent first to the signer. + // After it is signed, a copy is sent to the cc person. + + // create the envelope definition + JSONObject envJSON = new JSONObject(); + envJSON.put("emailSubject", "Please sign this document set"); + + // add the documents + JSONObject doc1 = new JSONObject(), doc2 = new JSONObject(), doc3 = new JSONObject(); + + doc1.put("name", "Order acknowledgement"); // can be different from actual file name + doc1.put("fileExtension", "html"); // Source data format. Signed docs are always pdf. + doc1.put("documentId", "1"); // a label used to reference the doc + doc2.put("name", "Battle Plan"); // can be different from actual file name + doc2.put("fileExtension", "docx"); + doc2.put("documentId", "2"); + doc3.put("name", "Lorem Ipsum"); // can be different from actual file name + doc3.put("fileExtension", "pdf"); + doc3.put("documentId", "3"); + + // The order in the docs array determines the order in the envelope + envJSON.put("documents", new JSONArray() + .put(doc1) + .put(doc2) + .put(doc3)); // create a signer recipient to sign the document, identified by name and email - // RoutingOrder (lower means earlier) determines the order of deliveries + // We're setting the parameters via the object creation + Signer signer1 = new Signer(); + signer1.setEmail(signerEmail); + signer1.setName(signerName); + signer1.setRecipientId("1"); + signer1.setRoutingOrder("1"); + // routingOrder (lower means earlier) determines the order of deliveries // to the recipients. Parallel routing order is supported by using the // same integer as the order for two or more recipients. - Signer signer = new Signer(); - signer.setEmail(args.getSignerEmail()); - signer.setName(args.getSignerName()); - signer.setRecipientId("1"); - signer.setRoutingOrder("1"); - signer.setTabs(signerTabs); // create a cc recipient to receive a copy of the documents, identified by name and email - CarbonCopy cc = new CarbonCopy(); - cc.setEmail(args.getCcEmail()); - cc.setName(args.getCcName()); - cc.setRecipientId("2"); - cc.setRoutingOrder("2"); - - // The order in the documents array determines the order in the envelope - JSONArray jsonDocuments = new JSONArray(); - for (DocumentInfo docInfo : documents) { - JSONObject jsonDoc = new JSONObject(); - jsonDoc.put("name", docInfo.getName()); - jsonDoc.put("fileExtension", docInfo.getDocType().getDefaultFileExtention()); - jsonDoc.put("documentId", docInfo.getId()); - jsonDocuments.put(jsonDoc); - } + // We're setting the parameters via setters + CarbonCopy cc1 = new CarbonCopy(); + cc1.setEmail(ccEmail); + cc1.setName(ccName); + cc1.setRoutingOrder("2"); + cc1.recipientId("2"); + // Create signHere fields (also known as tabs) on the documents, + // We're using anchor (autoPlace) positioning + // + // The DocuSign platform searches throughout your envelope's + // documents for matching anchor strings. So the + // signHere2 tab will be used in both document 2 and 3 since they + // use the same anchor string for their "signer 1" tabs. + SignHere signHere1 = new SignHere(); + signHere1.setAnchorString("**signature_1**"); + signHere1.setAnchorYOffset("10"); + signHere1.setAnchorUnits("pixels"); + signHere1.setAnchorXOffset("20"); + SignHere signHere2 = new SignHere(); + signHere2.setAnchorString("/sn1/"); + signHere2.setAnchorYOffset("10"); + signHere2.setAnchorUnits("pixels"); + signHere2.setAnchorXOffset("20"); - // Create the envelope definition. Request that the envelope be sent by - // setting |status| to "sent". To request that the envelope be created - // as a draft, set to "created" - JSONObject envelopeJSON = new JSONObject(); - envelopeJSON.put("emailSubject", "Please sign this document set"); - envelopeJSON.put("documents", jsonDocuments); - envelopeJSON.put("recipients", new JSONObject(EnvelopeHelpers.createRecipients(signer, cc))); - envelopeJSON.put("status", EnvelopeHelpers.ENVELOPE_STATUS_SENT); + // Tabs are set per recipient / signer + Tabs signer1Tabs = new Tabs(); + signer1Tabs.setSignHereTabs(Arrays.asList(signHere1, signHere2)); + signer1.setTabs(signer1Tabs); - return envelopeJSON; + // Add the recipients to the envelope object + Recipients recipients = new Recipients(); + recipients.setSigners(Arrays.asList(signer1)); + recipients.setCarbonCopies(Arrays.asList(cc1)); + + envJSON.put("recipients", new JSONObject(recipients)); + + // Request that the envelope be sent by setting |status| to "sent". + // To request that the envelope be created as a draft, set to "created" + envJSON.put("status", "sent"); + + return envJSON; } - @Value - private static class DocumentInfo { - private String name; - private String id; - private DocumentType docType; - private byte[] data; + private String document1(WorkArguments args) { + // Data for this method + String signerEmail = args.getSignerEmail(); + String signerName = args.getSignerName(); + String ccEmail = args.getCcEmail(); + String ccName = args.getCcName(); + + + return " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "

World Wide Corp

\n" + + "

Order Processing Division

\n" + + "

Ordered by " + signerName + "

\n" + + "

Email: " + signerEmail + "

\n" + + "

Copy to: " + ccName + ", " + ccEmail + "

\n" + + "

\n" + + " Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie. Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée. Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice. Donut jujubes oat cake jelly-o. Dessert bear claw chocolate cake gummies lollipop sugar plum ice cream gummies cheesecake.\n" + + "

\n" + + " \n" + + "

Agreed: **signature_1**/

\n" + + " \n" + + " "; } // ***DS.snippet.0.end } diff --git a/src/main/java/com/docusign/controller/examples/EG011ControllerEmbeddedSending.java b/src/main/java/com/docusign/controller/examples/EG011ControllerEmbeddedSending.java index 465248a8..3df8bb64 100644 --- a/src/main/java/com/docusign/controller/examples/EG011ControllerEmbeddedSending.java +++ b/src/main/java/com/docusign/controller/examples/EG011ControllerEmbeddedSending.java @@ -1,74 +1,89 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; import com.docusign.esign.api.EnvelopesApi; +import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.EnvelopeDefinition; +import com.docusign.esign.model.EnvelopeDocumentsResult; import com.docusign.esign.model.EnvelopeSummary; import com.docusign.esign.model.ReturnUrlRequest; import com.docusign.esign.model.ViewUrl; -import com.docusign.model.Session; -import com.docusign.model.User; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.servlet.view.RedirectView; - import java.io.IOException; -import javax.servlet.http.HttpServletResponse; - - -/** - * Embedded Sending
- * An envelope will be created in draft mode. A browser will then be redirected - * to the DocuSign web tool where the envelope can be (optionally) updated and - * then sent. The envelope includes a pdf, Word, and HTML document. - */ @Controller @RequestMapping("/eg011") -public class EG011ControllerEmbeddedSending extends AbstractController { +public class EG011ControllerEmbeddedSending extends EGController { + @Override + protected void addSpecialAttributes(ModelMap model) { - private final Session session; - private final User user; + } + @Override + protected String getEgName() { + return "eg011"; + } - @Autowired - public EG011ControllerEmbeddedSending(DSConfiguration config, Session session, User user) { - super(config, "eg011", "Signing request by email"); - this.session = session; - this.user = user; + @Override + protected String getTitle() { + return "Signing request by email"; + } + + @Override + protected String getResponseTitle() { + return null; } + @Autowired + EG002ControllerSigningViaEmail controller2; + @Override // ***DS.snippet.0.start - protected Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException { - String accountId = session.getAccountId(); - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); + protected EnvelopeDocumentsResult doWork(WorkArguments args, ModelMap model, + String accessToken, String basePath) throws ApiException, IOException { + // Data for this method + // accessToken (argument) + // basePath (argument) + // config.appUrl (url of the application itself) + String startingView = args.getStartingView(); + String accountId = args.getAccountId(); + + + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); // Step 1. Make the envelope with "created" (draft) status - args.setStatus(EnvelopeHelpers.ENVELOPE_STATUS_CREATED); - EnvelopeDefinition env = EG002ControllerSigningViaEmail.makeEnvelope(args); - EnvelopeSummary results = envelopesApi.createEnvelope(accountId, env); + args.setStatus("created"); + EnvelopeSummary results = (EnvelopeSummary) controller2.doWork(args, model, accessToken, basePath); String envelopeId = results.getEnvelopeId(); - // Step 2. Create the sender view. - // Set the url where you want the recipient to go once they are done - // signing should typically be a callback route somewhere in your app. + // Step 2. create the sender view + // Call the CreateSenderView API + // Exceptions will be caught by the calling function + // + // Prepare the request + String returnUrl = config.appUrl + "/ds-return"; ReturnUrlRequest viewRequest = new ReturnUrlRequest(); - viewRequest.setReturnUrl(config.getDsReturnUrl()); - ViewUrl viewUrl = envelopesApi.createSenderView(accountId, envelopeId, viewRequest); + // Set the url where you want the recipient to go once they are done signing + // should typically be a callback route somewhere in your app. + viewRequest.setReturnUrl(returnUrl); + // Call the API + ViewUrl result1 = envelopesApi.createSenderView(accountId, envelopeId, viewRequest); + // Process result // Switch to Recipient and Documents view if requested by the user - String url = viewUrl.getUrl(); - if ("recipient".equalsIgnoreCase(args.getStartingView())) { + String url = result1.getUrl(); + System.out.println("startingView: " + startingView); + if ("recipient".equalsIgnoreCase(startingView)) { url = url.replace("send=1", "send=0"); } - return new RedirectView(url); + System.out.println("Sender view URL: " + url); + args.setRedirectUrl("redirect:" + url); + return null; } // ***DS.snippet.0.end } diff --git a/src/main/java/com/docusign/controller/examples/EG012ControllerEmbeddedConsole.java b/src/main/java/com/docusign/controller/examples/EG012ControllerEmbeddedConsole.java index 53a99994..af7d20a9 100644 --- a/src/main/java/com/docusign/controller/examples/EG012ControllerEmbeddedConsole.java +++ b/src/main/java/com/docusign/controller/examples/EG012ControllerEmbeddedConsole.java @@ -1,72 +1,90 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; import com.docusign.esign.api.EnvelopesApi; +import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; import com.docusign.esign.model.ConsoleViewRequest; +import com.docusign.esign.model.EnvelopeDocumentsResult; import com.docusign.esign.model.ViewUrl; -import com.docusign.model.Session; -import com.docusign.model.User; - -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.servlet.view.RedirectView; - +import javax.servlet.http.HttpSession; -/** - * Embedded the DocuSign Web UI.
- * Redirect the user to the DocuSign Web UI. The starting view can be either - * an envelope's documents or the front page of the Web UI. The user does not - * necessarily return from the DocuSign Web UI, so using this API call is often a final - * step for the application. You can also open the WebUI in a new tab for the user - */ @Controller @RequestMapping("/eg012") -public class EG012ControllerEmbeddedConsole extends AbstractController { +public class EG012ControllerEmbeddedConsole extends EGController { + + @Autowired + private HttpSession session; - private final Session session; - private final User user; + @Override + protected void addSpecialAttributes(ModelMap model) { + model.addAttribute("envelopeOk", session.getAttribute("envelopeId") != null); + } + @Override + protected String getEgName() { + return "eg012"; + } - @Autowired - public EG012ControllerEmbeddedConsole(DSConfiguration config, Session session, User user) { - super(config, "eg012", "Embedded DocuSign web tool"); - this.session = session; - this.user = user; + @Override + protected String getTitle() { + return "Embedded DocuSign web tool"; } @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - model.addAttribute(MODEL_ENVELOPE_OK, StringUtils.isNotBlank(session.getEnvelopeId())); + protected String getResponseTitle() { + return null; } @Override // ***DS.snippet.0.start - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) throws ApiException { - args.setDsReturnUrl(config.getDsReturnUrl()); + protected EnvelopeDocumentsResult doWork(WorkArguments args, ModelMap model, + String accessToken, String basePath) throws ApiException { + // Data for this method + // accessToken (argument) + // basePath (argument) + // config.appUrl (url of the application itself) + + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); + + // Step 1. create the NDSE view + args.setDsReturnUrl(config.appUrl + "/ds-return"); + ConsoleViewRequest viewRequest = makeConsoleViewRequest(args); + + // Step 2. Call the CreateSenderView API + // Exceptions will be caught by the calling function + ViewUrl results = envelopesApi.createConsoleView(args.getAccountId(), viewRequest); + // process results + args.setRedirectUrl("redirect:" + results.getUrl()); + System.out.println("NDSE view URL: " + results.getUrl()); + + return null; + } + + private ConsoleViewRequest makeConsoleViewRequest(WorkArguments args) { + // Data for this method + String returnUrl = args.getDsReturnUrl(); + String startingView = args.getStartingView(); + String envelopeId = args.getEnvelopeId(); - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); - // Step 1. Create the Console / Web UI view. - // Set the URL where you want the recipient to go once they are finished in - // the Web UI. There are cases where a user will never click "FINISH" within - // the Web UI, you cannot assume control will be passed back to your application. ConsoleViewRequest viewRequest = new ConsoleViewRequest(); - viewRequest.setReturnUrl(config.getDsReturnUrl()); - String envelopeId = session.getEnvelopeId(); - if ("envelope".equalsIgnoreCase(args.getStartingView()) && envelopeId != null) { + // Set the url where you want the recipient to go once they are done + // with the NDSE. It is usually the case that the + // user will never "finish" with the NDSE. + // Assume that control will not be passed back to your app. + viewRequest.setReturnUrl(returnUrl); + + if ("envelope".equalsIgnoreCase(startingView) && envelopeId != null) { viewRequest.setEnvelopeId(envelopeId); } - // Step 2. Call the CreateSenderView API - ViewUrl viewUrl = envelopesApi.createConsoleView(session.getAccountId(), viewRequest); - return new RedirectView(viewUrl.getUrl()); + return viewRequest; } // ***DS.snippet.0.end } diff --git a/src/main/java/com/docusign/controller/examples/EG013ControllerAddDocToTemplate.java b/src/main/java/com/docusign/controller/examples/EG013ControllerAddDocToTemplate.java index d431219e..3142a18e 100644 --- a/src/main/java/com/docusign/controller/examples/EG013ControllerAddDocToTemplate.java +++ b/src/main/java/com/docusign/controller/examples/EG013ControllerAddDocToTemplate.java @@ -1,182 +1,252 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; -import com.docusign.common.DocumentType; import com.docusign.esign.api.EnvelopesApi; -import com.docusign.esign.api.TemplatesApi; import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.CarbonCopy; -import com.docusign.esign.model.CompositeTemplate; -import com.docusign.esign.model.EnvelopeDefinition; -import com.docusign.esign.model.EnvelopeSummary; -import com.docusign.esign.model.EnvelopeTemplateResults; -import com.docusign.esign.model.InlineTemplate; -import com.docusign.esign.model.RecipientViewRequest; -import com.docusign.esign.model.ServerTemplate; -import com.docusign.esign.model.Signer; -import com.docusign.esign.model.Tabs; -import com.docusign.esign.model.ViewUrl; -import com.docusign.model.Session; -import com.docusign.model.User; - +import com.docusign.esign.model.*; +import com.sun.jersey.core.util.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.servlet.view.RedirectView; +import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.Arrays; -import javax.servlet.http.HttpServletResponse; - - -/** - * Embedded Signing Ceremony from a template with an added document.
- * This example sends an envelope based on a template. In addition to the - * template's document(s), the example adds an additional document to the - * envelope by using the Composite Templates. - */ @Controller @RequestMapping("/eg013") -public class EG013ControllerAddDocToTemplate extends AbstractController { +public class EG013ControllerAddDocToTemplate extends EGController { - private static final String MODEL_LIST_TEMPLATE = "listTemplates"; - private static final String SIGNER_CLIENT_ID = "1000"; - private static final String HTML_DOCUMENT_FILE_NAME = "templates/candy-bonbon2.ftl"; - private static final String HTML_DOCUMENT_NAME = "Appendix 1--Sales order"; - private static final int ANCHOR_OFFSET_Y = 10; - private static final int ANCHOR_OFFSET_X = 20; + // The id of the signer within this application. + private static final String signerClientId = "1000"; - private final Session session; - private final User user; + @Autowired + private HttpSession session; + @Override + protected void addSpecialAttributes(ModelMap model) { + model.addAttribute("templateOk", null != session.getAttribute("templateId")); + } - @Autowired - public EG013ControllerAddDocToTemplate(DSConfiguration config, Session session, User user) { - super(config, "eg013", "Embedded Signing Ceremony from template and extra doc"); - this.session = session; - this.user = user; + @Override + protected String getEgName() { + return "eg013"; } @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - ApiClient apiClient = createApiClient(session.getBasePath(), user.getAccessToken()); - TemplatesApi templatesApi = new TemplatesApi(apiClient); - EnvelopeTemplateResults templates = templatesApi.listTemplates(session.getAccountId()); - model.addAttribute(MODEL_LIST_TEMPLATE, templates.getEnvelopeTemplates()); + protected String getTitle() { + return "Embedded Signing Ceremony from template and extra doc"; + } + + @Override + protected String getResponseTitle() { + return null; } @Override // ***DS.snippet.0.start - protected Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException { - String accountId = session.getAccountId(); - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); + protected EnvelopeDocumentsResult doWork(WorkArguments args, ModelMap model, + String accessToken, String basePath) throws ApiException { + // Data for this method + // accessToken (argument) + // basePath (argument) + // config.appUrl (url of the application itself) + // signerClientId (class constant) + String accountId = args.getAccountId(); + + + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); // Step 1. Make the envelope request body - args.setDsReturnUrl(config.getDsReturnUrl()); - args.setDsPingUrl(config.getDsPingUrl()); - args.setSignerClientId(SIGNER_CLIENT_ID); + // prepare arguments + args.setDsReturnUrl(config.appUrl + "/ds-return"); + args.setDsPingUrl(config.appUrl + "/"); + args.setSignerClientId(signerClientId); EnvelopeDefinition envelope = makeEnvelope(args); // Step 2. call Envelopes::create API method + // Exceptions will be caught by the calling function EnvelopeSummary results = envelopesApi.createEnvelope(accountId, envelope); + // process results + String envelopeId = results.getEnvelopeId(); + System.out.println("Envelope was created. EnvelopeId " + envelopeId); // Step 3. create the recipient view, the Signing Ceremony RecipientViewRequest viewRequest = makeRecipientViewRequest(args); - ViewUrl viewUrl = envelopesApi.createRecipientView(accountId, results.getEnvelopeId(), viewRequest); - return new RedirectView(viewUrl.getUrl()); + ViewUrl results1 = envelopesApi.createRecipientView(accountId, envelopeId, viewRequest); + args.setRedirectUrl("redirect:" + results1.getUrl()); + return null; } - private static RecipientViewRequest makeRecipientViewRequest(WorkArguments args) { + private RecipientViewRequest makeRecipientViewRequest(WorkArguments args) { + // Data for this method + String returnUrl = args.getDsReturnUrl(); + String signerEmail = args.getSignerEmail(); + String signerName = args.getSignerName(); + String signerClientId = args.getSignerClientId(); + String pingUrl = args.getDsPingUrl(); + + RecipientViewRequest viewRequest = new RecipientViewRequest(); // Set the url where you want the recipient to go once they are done signing // should typically be a callback route somewhere in your app. - viewRequest.setReturnUrl(args.getDsReturnUrl()); + viewRequest.setReturnUrl(returnUrl); // How has your app authenticated the user? In addition to your app's // authentication, you can include authenticate steps from DocuSign. // Eg, SMS authentication viewRequest.setAuthenticationMethod("none"); // Recipient information must match embedded recipient info // we used to create the envelope. - viewRequest.setEmail(args.getSignerEmail()); - viewRequest.setUserName(args.getSignerName()); - viewRequest.setClientUserId(args.getSignerClientId()); + viewRequest.setEmail(signerEmail); + viewRequest.setUserName(signerName); + viewRequest.setClientUserId(signerClientId); // DocuSign recommends that you redirect to DocuSign for the // Signing Ceremony. There are multiple ways to save state. // To maintain your application's session, use the pingUrl // parameter. It causes the DocuSign Signing Ceremony web page - // (not the DocuSign server) to send pings via AJAX to your app. + // (not the DocuSign server) to send pings via AJAX to your + // app, + viewRequest.setPingFrequency("600"); // seconds // NOTE: The pings will only be sent if the pingUrl is an https address - viewRequest.setPingFrequency("600"); - viewRequest.setPingUrl(args.getDsPingUrl()); + viewRequest.setPingUrl(pingUrl); // optional setting return viewRequest; } - // The envelope request object uses Composite Template to include in the envelope: - // 1. A template stored on the DocuSign service - // 2. An additional document which is a custom HTML source document - private EnvelopeDefinition makeEnvelope(WorkArguments args) throws IOException { - // Create a signer and cc recipients for the signer role of the server template + private EnvelopeDefinition makeEnvelope(WorkArguments args) { + // Data for this method + String signerEmail = args.getSignerEmail(); + String signerName = args.getSignerName(); + String ccEmail = args.getCcEmail(); + String ccName = args.getCcName(); + String signerClientId = args.getSignerClientId(); + String templateId = args.getTemplateId(); + + // The envelope request object uses Composite Template to + // include in the envelope: + // 1. A template stored on the DocuSign service + // 2. An additional document which is a custom HTML source document + + // Create Recipients for server template. Note that Recipients object + // is used, not TemplateRole + // + // Create a signer recipient for the signer role of the server template + Signer signer1 = new Signer(); + signer1.setEmail(signerEmail); + signer1.setName(signerName); + signer1.setRoleName("signer"); + signer1.setRecipientId("1"); + // Adding clientUserId transforms the template recipient + // into an embedded recipient: + signer1.setClientUserId(signerClientId); + // Create the cc recipient CarbonCopy cc1 = new CarbonCopy(); - cc1.setEmail(args.getCcEmail()); - cc1.setName(args.getCcName()); - cc1.setRoleName(EnvelopeHelpers.CC_ROLE_NAME); + cc1.setEmail(ccEmail); + cc1.setName(ccName); + cc1.setRoleName("cc"); cc1.setRecipientId("2"); + // Recipients object: + Recipients recipientsServerTemplate = new Recipients(); + recipientsServerTemplate.setCarbonCopies(Arrays.asList(cc1)); + recipientsServerTemplate.setSigners(Arrays.asList(signer1)); // create a composite template for the Server Template CompositeTemplate compTemplate1 = new CompositeTemplate(); compTemplate1.setCompositeTemplateId("1"); ServerTemplate serverTemplates = new ServerTemplate(); serverTemplates.setSequence("1"); - serverTemplates.setTemplateId(args.getTemplateId()); - compTemplate1.setServerTemplates(Arrays.asList(serverTemplates)); + serverTemplates.setTemplateId(templateId); + compTemplate1.setServerTemplates(Arrays.asList(serverTemplates)); // Add the roles via an inlineTemplate InlineTemplate inlineTemplate = new InlineTemplate(); inlineTemplate.setSequence("1"); - inlineTemplate.setRecipients(EnvelopeHelpers.createRecipients(createSigner(args), cc1)); + inlineTemplate.setRecipients(recipientsServerTemplate); compTemplate1.setInlineTemplates(Arrays.asList(inlineTemplate)); - - // The signer recipient for the added document with a tab definition: - Tabs signer1Tabs = EnvelopeHelpers.createSingleSignerTab("**signature_1**", ANCHOR_OFFSET_Y, ANCHOR_OFFSET_X); - Signer signer1AddedDoc = createSigner(args); + // The signer recipient for the added document with + // a tab definition: + SignHere signHere1 = new SignHere(); + signHere1.setAnchorString("**signature_1**"); + signHere1.setAnchorYOffset("10"); + signHere1.setAnchorUnits("pixels"); + signHere1.setAnchorXOffset("20"); + + Tabs signer1Tabs = new Tabs(); + signer1Tabs.setSignHereTabs(Arrays.asList(signHere1)); + // Signer definition for the added document + Signer signer1AddedDoc = new Signer(); + signer1AddedDoc.setEmail(signerEmail); + signer1AddedDoc.setName(signerName); + signer1AddedDoc.setClientUserId(signerClientId); + signer1AddedDoc.setRoleName("signer"); + signer1AddedDoc.setRecipientId("1"); signer1AddedDoc.setTabs(signer1Tabs); + // Recipients object for the added document: + Recipients recipientsAddedDoc = new Recipients(); + recipientsAddedDoc.carbonCopies(Arrays.asList(cc1)); + recipientsAddedDoc.signers(Arrays.asList(signer1AddedDoc)); // create the HTML document - byte[] htmlDoc = EnvelopeHelpers.createHtmlFromTemplateFile(HTML_DOCUMENT_FILE_NAME, "args", args); - - // create a composite template for the added document and add the recipients via an inlineTemplate + Document doc1 = new Document(); + + String doc1b64 = new String(Base64.encode(document1(args))); + doc1.setDocumentBase64(doc1b64); + doc1.setName("Appendix 1--Sales order"); // can be different from actual file name + doc1.setFileExtension("html"); + doc1.setDocumentId("1"); + // create a composite template for the added document CompositeTemplate compTemplate2 = new CompositeTemplate(); compTemplate2.setCompositeTemplateId("2"); + // Add the recipients via an inlineTemplate InlineTemplate inlineTemplate2 = new InlineTemplate(); inlineTemplate2.setSequence("2"); - inlineTemplate2.setRecipients(EnvelopeHelpers.createRecipients(signer1AddedDoc, cc1)); + inlineTemplate2.setRecipients(recipientsAddedDoc); compTemplate2.setInlineTemplates(Arrays.asList(inlineTemplate2)); - compTemplate2.setDocument(EnvelopeHelpers.createDocument(htmlDoc, HTML_DOCUMENT_NAME, - DocumentType.HTML.getDefaultFileExtention(), "1")); + compTemplate2.setDocument(doc1); EnvelopeDefinition env = new EnvelopeDefinition(); - env.setStatus(EnvelopeHelpers.ENVELOPE_STATUS_SENT); + env.setStatus("sent"); env.setCompositeTemplates(Arrays.asList(compTemplate1, compTemplate2)); return env; } - // Adding clientUserId transforms the template recipient into an embedded recipient - private static Signer createSigner(WorkArguments args) { - Signer signer = new Signer(); - signer.setEmail(args.getSignerEmail()); - signer.setName(args.getSignerName()); - signer.setRoleName(EnvelopeHelpers.SIGNER_ROLE_NAME); - signer.setRecipientId("1"); - signer.setClientUserId(args.getSignerClientId()); - return signer; + private String document1(WorkArguments args) { + // Data for this method + String signerEmail = args.getSignerEmail(); + String signerName = args.getSignerName(); + String ccEmail = args.getCcEmail(); + String ccName = args.getCcName(); + String item = args.getItem(); + String quantity = args.getQuantity(); + + + return " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "

World Wide Corp

\n" + + "

Order Processing Division

\n" + + "

Ordered by " + signerName + "

\n" + + "

Email: " + signerEmail + "

\n" + + "

Copy to: " + ccName + "," + ccEmail + "

\n" + + "

Item: " + item + ", quantity: " + quantity + " at market price.

\n" + + "

\n" + + " Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie. Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée. Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice. Donut jujubes oat cake jelly-o. Dessert bear claw chocolate cake gummies lollipop sugar plum ice cream gummies cheesecake.\n" + + "

\n" + + " \n" + + "

Agreed: **signature_1**/

\n" + + " \n" + + " "; } // ***DS.snippet.0.end } diff --git a/src/main/java/com/docusign/controller/examples/EG014ControllerCollectPayment.java b/src/main/java/com/docusign/controller/examples/EG014ControllerCollectPayment.java index 6c2fe0ad..d2902ce8 100644 --- a/src/main/java/com/docusign/controller/examples/EG014ControllerCollectPayment.java +++ b/src/main/java/com/docusign/controller/examples/EG014ControllerCollectPayment.java @@ -1,241 +1,320 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; -import com.docusign.common.DocumentType; import com.docusign.esign.api.EnvelopesApi; +import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.CarbonCopy; -import com.docusign.esign.model.EnvelopeDefinition; -import com.docusign.esign.model.EnvelopeSummary; -import com.docusign.esign.model.FormulaTab; -import com.docusign.esign.model.List; -import com.docusign.esign.model.ListItem; -import com.docusign.esign.model.PaymentDetails; -import com.docusign.esign.model.PaymentLineItem; -import com.docusign.esign.model.Signer; -import com.docusign.esign.model.Tabs; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - -import org.springframework.beans.factory.annotation.Autowired; +import com.docusign.esign.model.*; +import com.sun.jersey.core.util.Base64; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import java.io.IOException; import java.util.Arrays; -import javax.servlet.http.HttpServletResponse; - - -/** - * Send an envelope with an order form, including a payment field.
- * This class programmatically constructs the order form. For many use cases, - * it would be better to create the order form as a template using the DocuSign - * web tool as a WYSIWYG form designer.
- * Note: This example will only work if the sender's DocuSign account is - * set up with a DocuSign payment gateway. Next properties should are set: - *
    - *
  • Gateway_Account_Id
  • - *
  • Gateway_Name
  • - *
  • Gateway_Display_Name
  • - *
- * Since the Payment Gateway ID is set in the configuration file, you will need - * to run your own instance of this project to set it. - */ @Controller @RequestMapping("/eg014") -public class EG014ControllerCollectPayment extends AbstractController { - - private static final String MODEL_GATEWAY_OK = "gatewayOk"; - private static final String HTML_DOCUMENT_FILE_NAME = "templates/order-form.ftl"; - private static final String HTML_DOCUMENT_NAME = "Order form"; - // Order form constants - private static final String TRUE = "true"; - private static final String FALSE = "false"; - private static final int L1_PRICE = 5; - private static final int L2_PRICE = 150; - private static final int ANCHOR_OFFSET_Y = 10; - private static final int ANCHOR_OFFSET_X = 20; - private static final String DEFAULT_FONT = "helvetica"; - private static final String DEFAULT_FONT_SIZE = "size11"; - private static final String ANCHOR_UNITS = "pixels"; - - private final Session session; - private final User user; - - - @Autowired - public EG014ControllerCollectPayment(DSConfiguration config, Session session, User user) { - super(config, "eg014", "Envelope sent"); - this.session = session; - this.user = user; +public class EG014ControllerCollectPayment extends EGController { + @Override + protected void addSpecialAttributes(ModelMap model) { + model.addAttribute("gatewayOk", null != config.gatewayAccountId); + } + + @Override + protected String getEgName() { + return "eg014"; + } + + @Override + protected String getTitle() { + return "Envelope sent"; } @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - model.addAttribute(MODEL_GATEWAY_OK, null != config.getGatewayAccountId()); + protected String getResponseTitle() { + return null; } @Override // ***DS.snippet.0.start - protected Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException { - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); + protected Object doWork(WorkArguments args, ModelMap model, String accessToken, String basePath) throws ApiException, IOException { + // Data for this method + // accessToken (argument) + // basePath (argument) + String signerName = args.getSignerName(); + String signerEmail = args.getSignerEmail(); + String ccEmail = args.getCcEmail(); + String ccName = args.getCcName(); + String status = args.getStatus(); + String accountId = args.getAccountId(); + + + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); // Step 1. Make the envelope request body - EnvelopeDefinition envelope = makeEnvelope(args); + EnvelopeDefinition envelope = makeEnvelope(signerEmail, signerName, + ccEmail, ccName, status); // Step 2. call Envelopes::create API method - EnvelopeSummary results = envelopesApi.createEnvelope(session.getAccountId(), envelope); - - DoneExample.createDefault(title) - .withMessage(String.join("", "The envelope has been created and sent!
Envelope ID ", - results.getEnvelopeId(), ".")) - .addToModel(model); - return DONE_EXAMPLE_PAGE; + // Exceptions will be caught by the calling function + EnvelopeSummary results = envelopesApi.createEnvelope(accountId, envelope); + // process results + String envelopeId = results.getEnvelopeId(); + System.out.println("Envelope was created.EnvelopeId " + envelopeId); + this.setMessage("The envelope has been created and sent!
Envelope ID " + results.getEnvelopeId() + "."); + return null; } - // document 1 (html) has multiple tags: - // /l1q/ and /l2q/ -- quantities: drop down - // /l1e/ and /l2e/ -- extended: payment lines - // /l3t/ -- total -- formula - // - // The envelope has two recipients. - // recipient 1 - signer - // recipient 2 - cc - // The envelope will be sent first to the signer. - // After it is signed, a copy is sent to the cc person. - private EnvelopeDefinition makeEnvelope(WorkArguments args) throws IOException { - byte[] htmlDoc = EnvelopeHelpers.createHtmlFromTemplateFile(HTML_DOCUMENT_FILE_NAME, "args", args); + private EnvelopeDefinition makeEnvelope(String signerEmail, String signerName, + String ccEmail, String ccName, String status) throws IOException { + // Data for this method + // signerEmail (argument) + // signerName (argument) + // ccEmail (argument) + // ccName (argument) + // status (argument) + // payment gateway configuration settings: + // config.gatewayAccountId + // config.gatewayName + // config.gatewayDisplayName + + + // document 1 (html) has multiple tags: + // /l1q/ and /l2q/ -- quantities: drop down + // /l1e/ and /l2e/ -- extended: payment lines + // /l3t/ -- total -- formula + // + // The envelope has two recipients. + // recipient 1 - signer + // recipient 2 - cc + // The envelope will be sent first to the signer. + // After it is signed, a copy is sent to the cc person. + + /////////////////////////////////////////////////////////////////// + // // + // NOTA BENA: This method programmatically constructs the // + // order form. For many use cases, it would be // + // better to create the order form as a template // + // using the DocuSign web tool as a WYSIWYG // + // form designer. // + // // + /////////////////////////////////////////////////////////////////// + + // Order form constants + String l1Name = "Harmonica"; + int l1Price = 5; + String l1Description = "${"+l1Price+"} each" + , l2Name = "Xylophone"; + int l2Price = 150; + String l2Description = "${"+l2Price+"} each"; + int currencyMultiplier = 100; + + // read file from a local directory + // The read could raise an exception if the file is not available! + String doc1HTML1 = new String(readFile("order_form.html")); + //string doc1HTML1 = fs.readFileSync(path.resolve(demoDocsPath, doc1File), + // { encoding: 'utf8'}); + + // Substitute values into the HTML + // Substitute for: {signerName}, {signerEmail}, {ccName}, {ccEmail} + String doc1HTML2 = doc1HTML1.replace("{signerName}", signerName) + .replace("{signerEmail}", signerEmail) + .replace("{ccName}", ccName) + .replace("{ccEmail}", ccEmail); + + + // create the envelope definition + EnvelopeDefinition env = new EnvelopeDefinition(); + env.setEmailSubject("Please complete your order"); + + // add the documents + Document doc1 = new Document(); + + String doc1b64 = new String(Base64.encode(doc1HTML2)); + + doc1.setDocumentBase64(doc1b64); + doc1.setName("Order form"); // can be different from actual file name + doc1.setFileExtension("html"); // Source data format. Signed docs are always pdf. + doc1.setDocumentId("1"); // a label used to reference the doc + env.setDocuments(Arrays.asList(doc1)); // create a signer recipient to sign the document, identified by name and email + // We're setting the parameters via the object creation + Signer signer1 = new Signer() + .email(signerEmail) + .name(signerName) + .recipientId("1") + .routingOrder("1"); // routingOrder (lower means earlier) determines the order of deliveries // to the recipients. Parallel routing order is supported by using the // same integer as the order for two or more recipients. - Signer signer = new Signer() - .email(args.getSignerEmail()) - .name(args.getSignerName()) - .recipientId("1") - .routingOrder("1"); - signer.setTabs(createTabs()); // create a cc recipient to receive a copy of the documents, identified by name and email - CarbonCopy cc = new CarbonCopy() - .email(args.getCcEmail()) - .name(args.getCcName()) + // We're setting the parameters via setters + CarbonCopy cc1 = new CarbonCopy() + .email(ccEmail) + .name(ccName) .routingOrder("2") .recipientId("2"); + // Create signHere fields (also known as tabs) on the documents, + // We're using anchor (autoPlace) positioning + SignHere signHere1 = new SignHere() + .anchorString("/sn1/") + .anchorYOffset("10") + .anchorUnits("pixels") + .anchorXOffset("20"); - // Create the envelope definition - // Request that the envelope be sent by setting |status| to "sent". - // To request that the envelope be created as a draft, set to "created" - EnvelopeDefinition envelope = new EnvelopeDefinition(); - envelope.setEmailSubject("Please complete your order"); - envelope.setDocuments(Arrays.asList(EnvelopeHelpers.createDocument(htmlDoc, - HTML_DOCUMENT_NAME, DocumentType.HTML.getDefaultFileExtention(), "1"))); - envelope.setRecipients(EnvelopeHelpers.createRecipients(signer, cc)); - envelope.setStatus(EnvelopeHelpers.ENVELOPE_STATUS_SENT); - - return envelope; - } + ListItem listItem0 = new ListItem() + .text("none") + .value("0") + , listItem1 = new ListItem() + .text("1") + .value("1") + , listItem2 = new ListItem() + .text("2") + .value("2") + , listItem3 = new ListItem() + .text("3") + .value("3") + , listItem4 = new ListItem() + .text("4") + .value("4") + , listItem5 = new ListItem() + .text("5") + .value("5") + , listItem6 = new ListItem() + .text("6") + .value("6") + , listItem7 = new ListItem() + .text("7") + .value("7") + , listItem8 = new ListItem() + .text("8") + .value("8") + , listItem9 = new ListItem() + .text("9") + .value("9") + , listItem10 = new ListItem() + .text("10") + .value("10"); + + List listl1q = new List() + .font("helvetica") + .fontSize("size11") + .anchorString( "/l1q/") + .anchorYOffset("-10") + .anchorUnits("pixels") + .anchorXOffset("0") + .listItems(Arrays.asList(listItem0, listItem1, listItem2, + listItem3, listItem4, listItem5, listItem6, + listItem7, listItem8, listItem9, listItem10 )) + .required("true") + .tabLabel("l1q") + , + listl2q = new List() + .font("helvetica") + .fontSize("size11") + .anchorString( "/l2q/") + .anchorYOffset("-10") + .anchorUnits("pixels") + .anchorXOffset("0") + .listItems(Arrays.asList(listItem0, listItem1, listItem2, + listItem3, listItem4, listItem5, listItem6, + listItem7, listItem8, listItem9, listItem10 )) + .required("true") + .tabLabel("l2q"); - // creates tabs per signer - private Tabs createTabs() { - java.util.List listItems = java.util.List.of( - new ListItem().text("none").value("0"), - new ListItem().text("1").value("1"), - new ListItem().text("2").value("2"), - new ListItem().text("3").value("3"), - new ListItem().text("4").value("4"), - new ListItem().text("5").value("5"), - new ListItem().text("6").value("6"), - new ListItem().text("7").value("7"), - new ListItem().text("8").value("8"), - new ListItem().text("9").value("9"), - new ListItem().text("10").value("10")); - List listl1q = createList("/l1q/", "l1q", listItems); - List listl2q = createList("/l2q/", "l2q", listItems); - - // Create two formula tabs for the extended price on the line items and formula for the total - FormulaTab formulal1e = createFormulaTab("/l1e/", "l1e", L1_PRICE); - FormulaTab formulal2e = createFormulaTab("/l2e/", "l2e", L2_PRICE); - FormulaTab formulal3t = new FormulaTab() - .font(DEFAULT_FONT) - .bold(TRUE) - .fontSize("size12") - .anchorString("/l3t/") + // create two formula tabs for the extended price on the line items + FormulaTab formulal1e = new FormulaTab() + .font("helvetica") + .fontSize("size11") + .anchorString("/l1e/") + .anchorYOffset("-8") + .anchorUnits("pixels") + .anchorXOffset("105") + .tabLabel("l1e") + .formula("[l1q] * {"+l1Price+"}") + .roundDecimalPlaces("0") + .required("true") + .locked("true") + .disableAutoSize("false"), + formulal2e = new FormulaTab() + .font("helvetica") + .fontSize("size11") + .anchorString("/l2e/") .anchorYOffset("-8") - .anchorUnits(ANCHOR_UNITS) - .anchorXOffset("50") - .tabLabel("l3t") - .formula("[l1e] + [l2e]") + .anchorUnits("pixels") + .anchorXOffset("105") + .tabLabel("l2e") + .formula("[l2q] * {"+l2Price+"}") .roundDecimalPlaces("0") - .required(TRUE) - .locked(TRUE) - .disableAutoSize(FALSE); + .required("true") + .locked("true") + .disableAutoSize("false"), + // Formula for the total + formulal3t = new FormulaTab() + .font("helvetica") + .bold("true") + .fontSize("size12") + .anchorString("/l3t/") + .anchorYOffset("-8") + .anchorUnits("pixels") + .anchorXOffset("50") + .tabLabel("l3t") + .formula("[l1e] + [l2e]") + .roundDecimalPlaces("0") + .required("true") + .locked("true") + .disableAutoSize("false"); + // Payment line items PaymentLineItem paymentLineIteml1 = new PaymentLineItem() - .name("Harmonica") - .description(String.format("${%d} each", L1_PRICE)) - .amountReference("l1e"); - PaymentLineItem paymentLineIteml2 = new PaymentLineItem() - .name("Xylophone") - .description(String.format("${%d} each", L2_PRICE)) + .name(l1Name) + .description(l1Description) + .amountReference("l1e"), + paymentLineIteml2 = new PaymentLineItem() + .name(l2Name) + .description(l2Description) .amountReference("l2e"); PaymentDetails paymentDetails = new PaymentDetails() - .gatewayAccountId(config.getGatewayAccountId()) + .gatewayAccountId(config.gatewayAccountId) .currencyCode("USD") - .gatewayName(config.getGatewayName()) - .gatewayDisplayName(config.getGatewayDisplayName()) + .gatewayName(config.gatewayName) + .gatewayDisplayName(config.gatewayDisplayName) .lineItems(Arrays.asList(paymentLineIteml1, paymentLineIteml2)); // Hidden formula for the payment itself FormulaTab formulaPayment = new FormulaTab() - .tabLabel("payment") - .formula("([l1e] + [l2e]) * {100}") + .tabLabel("payment") + .formula("([l1e] + [l2e]) * {"+currencyMultiplier+"}") .roundDecimalPlaces("0") .paymentDetails(paymentDetails) - .hidden(TRUE) - .required(TRUE) - .locked(TRUE) + .hidden("true") + .required("true") + .locked("true") .documentId("1") .pageNumber("1") .xPosition("0") .yPosition("0"); - Tabs signerTabs = EnvelopeHelpers.createSingleSignerTab("/sn1/", ANCHOR_OFFSET_Y, ANCHOR_OFFSET_X); - signerTabs.listTabs(Arrays.asList(listl1q, listl2q)); - signerTabs.formulaTabs(Arrays.asList(formulal1e, formulal2e, formulal3t, formulaPayment)); - return signerTabs; - } + // Tabs are set per recipient / signer + Tabs signer1Tabs = new Tabs() + .signHereTabs(Arrays.asList(signHere1)) + .listTabs(Arrays.asList(listl1q, listl2q)) + .formulaTabs(Arrays.asList(formulal1e, formulal2e, formulal3t, formulaPayment)); + signer1.setTabs(signer1Tabs); - private static List createList(String anchor, String label, java.util.List items) { - return new List() - .font(DEFAULT_FONT) - .fontSize(DEFAULT_FONT_SIZE) - .anchorString(anchor) - .anchorYOffset("-10") - .anchorUnits(ANCHOR_UNITS) - .anchorXOffset("0") - .listItems(items) - .required(TRUE) - .tabLabel(label); - } + // Add the recipients to the envelope object + Recipients recipients = new Recipients() + .signers(Arrays.asList(signer1)) + .carbonCopies(Arrays.asList(cc1)); - private static FormulaTab createFormulaTab(String anchor, String label, int price) { - return new FormulaTab() - .font(DEFAULT_FONT) - .fontSize(DEFAULT_FONT_SIZE) - .anchorString(anchor) - .anchorYOffset("-8") - .anchorUnits(ANCHOR_UNITS) - .anchorXOffset("105") - .tabLabel(label) - .formula(String.format("[l1q] * {%d}", price)) - .roundDecimalPlaces("0") - .required(TRUE) - .locked(TRUE) - .disableAutoSize(FALSE); + env.setRecipients(recipients); + + // Request that the envelope be sent by setting |status| to "sent". + // To request that the envelope be created as a draft, set to "created" + env.setStatus("sent"); + + return env; } // ***DS.snippet.0.end } diff --git a/src/main/java/com/docusign/controller/examples/EG015ControllerGetTabValues.java b/src/main/java/com/docusign/controller/examples/EG015ControllerGetTabValues.java deleted file mode 100644 index aba862d8..00000000 --- a/src/main/java/com/docusign/controller/examples/EG015ControllerGetTabValues.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.docusign.controller.examples; - -import com.docusign.DSConfiguration; -import com.docusign.esign.api.EnvelopesApi; -import com.docusign.esign.client.ApiException; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; - -import javax.servlet.http.HttpServletResponse; - - -/** - * List an envelope's recipients and their status.
- * List the envelope's recipients, including their current status. - */ -@Controller -@RequestMapping("/eg015") -public class EG015ControllerGetTabValues extends AbstractController { - - private final Session session; - private final User user; - - - @Autowired - public EG015ControllerGetTabValues(DSConfiguration config, Session session, User user) { - super(config, "eg015", "Get Envelope Tabs"); - this.session = session; - this.user = user; - } - - @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - model.addAttribute(MODEL_ENVELOPE_OK, StringUtils.isNotBlank(session.getEnvelopeId())); - } - - @Override - // ***DS.snippet.0.start - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) throws ApiException { - // Step 1. get envelope recipients - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); - DoneExample.createDefault(title) - .withJsonObject(envelopesApi.getFormData(session.getAccountId(), session.getEnvelopeId())) - .withMessage("Results from the Envelope::GetFormData method:") - .addToModel(model); - return DONE_EXAMPLE_PAGE; - } - // ***DS.snippet.0.end -} diff --git a/src/main/java/com/docusign/controller/examples/EG016ControllerSetTabValues.java b/src/main/java/com/docusign/controller/examples/EG016ControllerSetTabValues.java deleted file mode 100644 index 462a923c..00000000 --- a/src/main/java/com/docusign/controller/examples/EG016ControllerSetTabValues.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.docusign.controller.examples; - -import com.docusign.DSConfiguration; -import com.docusign.esign.api.EnvelopesApi; -import com.docusign.esign.client.ApiClient; -import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.*; -import com.docusign.model.Session; -import com.docusign.model.User; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.servlet.view.RedirectView; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import javax.servlet.http.HttpServletResponse; - - -@Controller -@RequestMapping("/eg016") -public class EG016ControllerSetTabValues extends AbstractController { - - private static final String DOCUMENT_FILE_NAME = "World_Wide_Corp_salary.pdf"; - private static final String DOCUMENT_NAME = "Lorem Ipsum"; - private static final int ANCHOR_OFFSET_Y = 20; - private static final int ANCHOR_OFFSET_X = 10; - private static final String SIGNER_CLIENT_ID = "1000"; - - private final Session session; - private final User user; - - - @Autowired - public EG016ControllerSetTabValues(DSConfiguration config, Session session, User user) { - super(config, "eg016", "Set envelope tab values"); - this.session = session; - this.user = user; - } - - @Override - protected Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException { - String signerName = args.getSignerName(); - String signerEmail = args.getSignerEmail(); - String accountId = session.getAccountId(); - - // Step 1. Create the envelope definition - EnvelopeDefinition envelope = makeEnvelope(signerEmail, signerName); - - // Step 2. Call DocuSign to create the envelope - ApiClient apiClient = createApiClient(session.getBasePath(), user.getAccessToken()); - EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); - EnvelopeSummary envelopeSummary = envelopesApi.createEnvelope(accountId, envelope); - - String envelopeId = envelopeSummary.getEnvelopeId(); - session.setEnvelopeId(envelopeId); - - // Step 3. create the recipient view, the Signing Ceremony - RecipientViewRequest viewRequest = makeRecipientViewRequest(signerEmail, signerName); - ViewUrl viewUrl = envelopesApi.createRecipientView(accountId, envelopeId, viewRequest); - - // Step 4. Redirect the user to the Signing Ceremony - // Don't use an iFrame! - // State can be stored/recovered using the framework's session or a - // query parameter on the returnUrl (see the makeRecipientViewRequest method) - return new RedirectView(viewUrl.getUrl()); - } - - private RecipientViewRequest makeRecipientViewRequest(String signerEmail, String signerName) { - RecipientViewRequest viewRequest = new RecipientViewRequest(); - // Set the url where you want the recipient to go once they are done signing - // should typically be a callback route somewhere in your app. - // The query parameter is included as an example of how - // to save/recover state information during the redirect to - // the DocuSign signing ceremony. It's usually better to use - // the session mechanism of your web framework. Query parameters - // can be changed/spoofed very easily. - viewRequest.setReturnUrl(config.getDsReturnUrl() + "?state=123"); - - // How has your app authenticated the user? In addition to your app's - // authentication, you can include authenticate steps from DocuSign. - // Eg, SMS authentication - viewRequest.setAuthenticationMethod("none"); - - // Recipient information must match embedded recipient info - // we used to create the envelope. - viewRequest.setEmail(signerEmail); - viewRequest.setUserName(signerName); - viewRequest.setClientUserId(SIGNER_CLIENT_ID); - - // DocuSign recommends that you redirect to DocuSign for the - // Signing Ceremony. There are multiple ways to save state. - // To maintain your application's session, use the pingUrl - // parameter. It causes the DocuSign Signing Ceremony web page - // (not the DocuSign server) to send pings via AJAX to your app. - // NOTE: The pings will only be sent if the pingUrl is an https address - viewRequest.setPingFrequency("600"); // seconds - viewRequest.setPingUrl(config.getDsPingUrl()); - - return viewRequest; - } - - private static EnvelopeDefinition makeEnvelope(String signerEmail, String signerName) throws IOException { - // Create a signer recipient to sign the document, identified by name and email - // We set the clientUserId to enable embedded signing for the recipient - Signer signer = new Signer(); - signer.setEmail(signerEmail); - signer.setName(signerName); - signer.clientUserId(SIGNER_CLIENT_ID); - signer.recipientId("1"); - - SignHere signHere = new SignHere(); - signHere.setAnchorString("/sn1"); - signHere.setAnchorUnits("pixels"); - signHere.setAnchorYOffset("10"); - signHere.setAnchorXOffset("20"); - - Text textLegal = new Text(); - textLegal.setAnchorString("/legal/"); - textLegal.setAnchorUnits("pixels"); - textLegal.setAnchorYOffset("-9"); - textLegal.setAnchorXOffset("5"); - textLegal.setFont("helvetica"); - textLegal.setFontSize("size11"); - textLegal.setBold("true"); - textLegal.setValue(signerName); - textLegal.setLocked("false"); - textLegal.setTabId("legal_name"); - textLegal.setTabLabel("Legal Name"); - - Text textFamiliar = new Text(); - textFamiliar.setAnchorString("/familar/"); - textFamiliar.setAnchorUnits("pixels"); - textFamiliar.setAnchorYOffset("-9"); - textFamiliar.setAnchorXOffset("5"); - textFamiliar.setFont("helvetica"); - textFamiliar.setFontSize("size11"); - textFamiliar.setBold("true"); - textFamiliar.setValue(signerName); - textFamiliar.setLocked("false"); - textFamiliar.setTabId("familiar_name"); - textFamiliar.setTabLabel("Familiar Name"); - - Integer salary = 123000; - - Text textSalary = new Text(); - textSalary.setAnchorString("/salary/"); - textSalary.setAnchorUnits("pixels"); - textSalary.setAnchorYOffset("-9"); - textSalary.setAnchorXOffset("5"); - textSalary.setFont("helvetica"); - textSalary.setFontSize("size11"); - textSalary.setBold("true"); - textSalary.setValue(String.format("$ %,.2d", salary)); - textSalary.setLocked("true"); - textSalary.setTabId("salary"); - textSalary.setTabLabel("salary"); - - TextCustomField salaryCustomField = new TextCustomField(); - salaryCustomField.setName("salary"); - salaryCustomField.setRequired("false"); - salaryCustomField.setShow("true"); // Yes, included in the CoC - salaryCustomField.setValue(String.valueOf(salary)); - - CustomFields cf = new CustomFields(); - cf.setTextCustomFields(Arrays.asList(salaryCustomField)); - - Tabs tabs = new Tabs(); - tabs.setSignHereTabs(Arrays.asList(signHere)); - tabs.setTextTabs(Arrays.asList(textLegal, textFamiliar, textSalary)); - - signer.setTabs(tabs); - - - // Add the recipient to the envelope object - Recipients recipients = new Recipients(); - recipients.setSigners(Arrays.asList(signer)); - - EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition(); - envelopeDefinition.setEmailSubject("Please sign this document from the Java SDK"); - envelopeDefinition.setRecipients(recipients); - Document doc = EnvelopeHelpers.createDocumentFromFile(DOCUMENT_FILE_NAME, DOCUMENT_NAME, "3"); - envelopeDefinition.setDocuments(Arrays.asList(doc)); - envelopeDefinition.setCustomFields(cf); - // Request that the envelope be sent by setting |status| to "sent". - // To request that the envelope be created as a draft, set to "created" - envelopeDefinition.setStatus(EnvelopeHelpers.ENVELOPE_STATUS_SENT); - - return envelopeDefinition; - } -} diff --git a/src/main/java/com/docusign/controller/examples/EG017ControllerSetTemplateTabValues.java b/src/main/java/com/docusign/controller/examples/EG017ControllerSetTemplateTabValues.java deleted file mode 100644 index 8a5ecd6f..00000000 --- a/src/main/java/com/docusign/controller/examples/EG017ControllerSetTemplateTabValues.java +++ /dev/null @@ -1,217 +0,0 @@ -package com.docusign.controller.examples; - -import com.docusign.DSConfiguration; -import com.docusign.esign.api.EnvelopesApi; -import com.docusign.esign.client.ApiClient; -import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.*; -import com.docusign.esign.model.Checkbox; -import com.docusign.esign.model.List; -import com.docusign.model.Session; -import com.docusign.model.User; -import com.sun.xml.bind.v2.runtime.unmarshaller.XsiNilLoader; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.servlet.view.RedirectView; - -import javax.servlet.http.HttpServletResponse; -import java.awt.*; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; - - -@Controller -@RequestMapping("/eg017") -public class EG017ControllerSetTemplateTabValues extends AbstractController { - - private static final String DOCUMENT_FILE_NAME = "World_Wide_Corp_salary.pdf"; - private static final String DOCUMENT_NAME = "Lorem Ipsum"; - private static final int ANCHOR_OFFSET_Y = 20; - private static final int ANCHOR_OFFSET_X = 10; - private static final String SIGNER_CLIENT_ID = "1000"; - - private final Session session; - private final User user; - - - @Autowired - public EG017ControllerSetTemplateTabValues(DSConfiguration config, Session session, User user) { - super(config, "eg017", "Set template tab values"); - this.session = session; - this.user = user; - } - - @Override - protected Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException { - String signerName = args.getSignerName(); - String signerEmail = args.getSignerEmail(); - String accountId = session.getAccountId(); - String ccName = args.getCcName(); - String ccEmail = args.getCcEmail(); - String templateId = args.getTemplateId(); - - // Step 1. Create the envelope definition - EnvelopeDefinition envelope = makeEnvelope(signerEmail, signerName, ccName, ccEmail, templateId); - - // Step 2. Call DocuSign to create the envelope - ApiClient apiClient = createApiClient(session.getBasePath(), user.getAccessToken()); - EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); - EnvelopeSummary envelopeSummary = envelopesApi.createEnvelope(accountId, envelope); - - String envelopeId = envelopeSummary.getEnvelopeId(); - session.setEnvelopeId(envelopeId); - - // Step 3. create the recipient view, the Signing Ceremony - RecipientViewRequest viewRequest = makeRecipientViewRequest(signerEmail, signerName); - ViewUrl viewUrl = envelopesApi.createRecipientView(accountId, envelopeId, viewRequest); - - // Step 4. Redirect the user to the Signing Ceremony - // Don't use an iFrame! - // State can be stored/recovered using the framework's session or a - // query parameter on the returnUrl (see the makeRecipientViewRequest method) - return new RedirectView(viewUrl.getUrl()); - } - - private RecipientViewRequest makeRecipientViewRequest(String signerEmail, String signerName) { - RecipientViewRequest viewRequest = new RecipientViewRequest(); - // Set the url where you want the recipient to go once they are done signing - // should typically be a callback route somewhere in your app. - // The query parameter is included as an example of how - // to save/recover state information during the redirect to - // the DocuSign signing ceremony. It's usually better to use - // the session mechanism of your web framework. Query parameters - // can be changed/spoofed very easily. - viewRequest.setReturnUrl(config.getDsReturnUrl() + "?state=123"); - - // How has your app authenticated the user? In addition to your app's - // authentication, you can include authenticate steps from DocuSign. - // Eg, SMS authentication - viewRequest.setAuthenticationMethod("none"); - - // Recipient information must match embedded recipient info - // we used to create the envelope. - viewRequest.setEmail(signerEmail); - viewRequest.setUserName(signerName); - viewRequest.setClientUserId(SIGNER_CLIENT_ID); - - // DocuSign recommends that you redirect to DocuSign for the - // Signing Ceremony. There are multiple ways to save state. - // To maintain your application's session, use the pingUrl - // parameter. It causes the DocuSign Signing Ceremony web page - // (not the DocuSign server) to send pings via AJAX to your app. - // NOTE: The pings will only be sent if the pingUrl is an https address - viewRequest.setPingFrequency("600"); // seconds - viewRequest.setPingUrl(config.getDsPingUrl()); - - return viewRequest; - } - - private static EnvelopeDefinition makeEnvelope(String signerEmail, String signerName, String ccEmail, String ccName, String templateId) throws IOException { - // Create a signer recipient to sign the document, identified by name and email - // We set the clientUserId to enable embedded signing for the recipient - - List list1 = new List(); - list1.setValue("green"); - list1.setDocumentId("1"); - list1.setPageNumber("1"); - list1.setTabLabel("list"); - - // Checkboxes - Checkbox ck1 = new Checkbox(); - ck1.setTabLabel("ckAuthorization"); - ck1.setSelected("true"); - - Checkbox ck2 = new Checkbox(); - ck2.setTabLabel("ckAgreement"); - ck2.setSelected("true"); - - Radio radio = new Radio(); - radio.setValue("white"); - radio.setSelected("true"); - - RadioGroup rg = new RadioGroup(); - rg.setGroupName("radio1"); - rg.setRadios(Arrays.asList(radio)); - - - Text txt = new Text(); - txt.setTabLabel("text"); - txt.setValue("Jabberywocky!"); - - // We can also add a new tab (field) to the ones already in the template: - - Text txtExtra = new Text(); - txtExtra.setDocumentId("1"); - txtExtra.setPageNumber("1"); - txtExtra.setXPosition("280"); - txtExtra.setYPosition("172"); - txtExtra.setFont("helvetica"); - txtExtra.setFontSize("size14"); - txtExtra.setTabLabel("added text field"); - txtExtra.setHeight("23"); - txtExtra.setWidth("84"); - txtExtra.setRequired("false"); - txtExtra.setBold("true"); - txtExtra.setValue(signerName); - txtExtra.setLocked("false"); - txtExtra.setTabId("name"); - - - // Add the tabs model (including the SignHere tab) to the signer. - // The Tabs object wants arrays of the different field/tab types - // Tabs are set per recipient / signer - - Tabs tabs = new Tabs(); - - tabs.setTextTabs(Arrays.asList(txt, txtExtra)); - tabs.setRadioGroupTabs(Arrays.asList(rg)); - tabs.setCheckboxTabs(Arrays.asList(ck1, ck2)); - tabs.setListTabs(Arrays.asList(list1)); - - - // create a signer recipient to sign the document, identified by name and email - // We're setting the parameters via the object creation - TemplateRole signer = new TemplateRole(); - signer.setEmail(signerEmail); - signer.setName(signerName); - // Setting the client_user_id marks the signer as embedded - signer.setClientUserId(SIGNER_CLIENT_ID); - signer.setRoleName("signer"); - signer.setTabs(tabs); - - TemplateRole cc = new TemplateRole(); - cc.setEmail(ccEmail); - cc.setName(ccName); - cc.setRoleName("cc"); - - // Create an envelope custom field to save our application"s - // Data about the envelope - TextCustomField customField = new TextCustomField(); - customField.setName("app metadata item"); - customField.setValue("1234567"); - customField.setRequired("false"); - customField.show("true"); //Yes, include in the CoC - - - CustomFields cf = new CustomFields(); - cf.setTextCustomFields(Arrays.asList(customField)); - - - // Next, create the top level envelope definition and populate it. - EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition(); - envelopeDefinition.setEmailSubject("Please sign this document from the Java SDK"); - // The Recipients object wants arrays for each recipient type - envelopeDefinition.setTemplateId(templateId); - envelopeDefinition.setTemplateRoles(Arrays.asList(signer, cc)); - envelopeDefinition.setCustomFields(cf); - // Request that the envelope be sent by setting |status| to "sent". - // To request that the envelope be created as a draft, set to "created" - envelopeDefinition.setStatus(EnvelopeHelpers.ENVELOPE_STATUS_SENT); - - return envelopeDefinition; - } -} diff --git a/src/main/java/com/docusign/controller/examples/EG018ControllerEnvelopeCustomFieldValues.java b/src/main/java/com/docusign/controller/examples/EG018ControllerEnvelopeCustomFieldValues.java deleted file mode 100644 index cd2eb53e..00000000 --- a/src/main/java/com/docusign/controller/examples/EG018ControllerEnvelopeCustomFieldValues.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.docusign.controller.examples; - -import com.docusign.DSConfiguration; -import com.docusign.esign.api.EnvelopesApi; -import com.docusign.esign.client.ApiException; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; - -import javax.servlet.http.HttpServletResponse; - - -/** - * List an envelope's recipients and their status.
- * List the envelope's recipients, including their current status. - */ -@Controller -@RequestMapping("/eg018") -public class EG018ControllerEnvelopeCustomFieldValues extends AbstractController { - - private final Session session; - private final User user; - - - @Autowired - public EG018ControllerEnvelopeCustomFieldValues(DSConfiguration config, Session session, User user) { - super(config, "eg018", "Get Envelope Custom Field Data"); - this.session = session; - this.user = user; - } - - @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - model.addAttribute(MODEL_ENVELOPE_OK, StringUtils.isNotBlank(session.getEnvelopeId())); - } - - @Override - // ***DS.snippet.0.start - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) throws ApiException { - // Step 1. get envelope recipients - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); - DoneExample.createDefault(title) - .withJsonObject(envelopesApi.listCustomFields(session.getAccountId(), session.getEnvelopeId())) - .withMessage("Results from the Envelope::GetFormData method:") - .addToModel(model); - return DONE_EXAMPLE_PAGE; - } - // ***DS.snippet.0.end -} diff --git a/src/main/java/com/docusign/controller/examples/EG019ControllerAccessCodeAuthentication.java b/src/main/java/com/docusign/controller/examples/EG019ControllerAccessCodeAuthentication.java index fbede6e0..a528d2ef 100644 --- a/src/main/java/com/docusign/controller/examples/EG019ControllerAccessCodeAuthentication.java +++ b/src/main/java/com/docusign/controller/examples/EG019ControllerAccessCodeAuthentication.java @@ -1,109 +1,114 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; import com.docusign.esign.api.EnvelopesApi; +import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.Document; -import com.docusign.esign.model.EnvelopeDefinition; -import com.docusign.esign.model.EnvelopeSummary; -import com.docusign.esign.model.Recipients; -import com.docusign.esign.model.SignHere; -import com.docusign.esign.model.Signer; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - -import org.springframework.beans.factory.annotation.Autowired; +import com.docusign.esign.model.*; +import com.sun.jersey.core.util.Base64; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import java.io.IOException; import java.util.Arrays; -import javax.servlet.http.HttpServletResponse; - - -/** - * Send an envelope with a recipient using Access Code Authentication. Signer - * has to enter an access code for opening document to sign. - */ @Controller @RequestMapping("/eg019") -public class EG019ControllerAccessCodeAuthentication extends AbstractController { +public class EG019ControllerAccessCodeAuthentication extends EGController { - private static final String DOCUMENT_FILE_NAME = "World_Wide_Corp_lorem.pdf"; - private static final String DOCUMENT_NAME = "Lorem"; + @Override + protected void addSpecialAttributes(ModelMap model) { + } - private final Session session; - private final User user; + @Override + protected String getEgName() { + return "eg019"; + } + @Override + protected String getTitle() { + return "Signing request by email"; + } - @Autowired - public EG019ControllerAccessCodeAuthentication(DSConfiguration config, Session session, User user) { - super(config, "eg019", "Signing request by email"); - this.session = session; - this.user = user; + @Override + protected String getResponseTitle() { + return "Envelope sent"; } @Override // ***DS.snippet.0.start protected Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException { - // Step 1: Construct your API headers - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); - - // Step 2: Construct your envelope JSON body - EnvelopeDefinition envelope = createEnvelope(args); - - // Step 3: Call the eSignature REST API - EnvelopeSummary results = envelopesApi.createEnvelope(session.getAccountId(), envelope); - - session.setEnvelopeId(results.getEnvelopeId()); - DoneExample.createDefault(title) - .withJsonObject(results) - .withMessage("The envelope has been created and sent!
Envelope ID " - + session.getEnvelopeId() + ".") - .addToModel(model); - - return DONE_EXAMPLE_PAGE; - } - - private static EnvelopeDefinition createEnvelope(WorkArguments args) throws IOException { - Document doc = EnvelopeHelpers.createDocumentFromFile(DOCUMENT_FILE_NAME, DOCUMENT_NAME, "1"); - - SignHere signHere = new SignHere(); - signHere.setName("SignHereTab"); - signHere.setXPosition("75"); - signHere.setYPosition("572"); - signHere.setTabLabel("SignHereTab"); - signHere.setPageNumber("1"); - signHere.setDocumentId(doc.getDocumentId()); + String accessToken, String basePath) throws ApiException, IOException { + // Data for this method + // accessToken (argument) + // basePath (argument) + String accountId = args.getAccountId(); // represents your {ACCOUNT_ID} + + // Set status for the makeEnvelope method + if (!"created".equalsIgnoreCase(args.getStatus())) { + args.setStatus("sent"); + } + + // Step 2: Construct your API headers + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); + + // Step 3: Construct your envelope JSON body + String signerEmail = args.getSignerEmail(); // represents your {SIGNER_EMAIL} + String signerName = args.getSignerName(); // represents your {SIGNER_NAME} + String status = args.getStatus(); + + EnvelopeDefinition env = new EnvelopeDefinition(); + env.setEmailSubject("Please Sign"); + env.setEnvelopeIdStamping("true"); + env.setEmailBlurb("Sample text for email body"); + env.setStatus("sent"); + + Document doc1 = new Document(); + doc1.setDocumentBase64("JVBERi0xLjMNJeLjz9MNCjUgMCBvYmoNPDwvTGluZWFyaXplZCAxL0wgNDI3MTAvTyA3L0UgMzg3NDMvTiAxL1QgNDI0OTEvSCBbIDg5NiAxODVdPj4NZW5kb2JqDSAgICAgICAgICAgICAgICAgICAgDQp4cmVmDQo1IDMwDQowMDAwMDAwMDE2IDAwMDAwIG4NCjAwMDAwMDEwODEgMDAwMDAgbg0KMDAwMDAwMTE0MSAwMDAwMCBuDQowMDAwMDAxMzE4IDAwMDAwIG4NCjAwMDAwMDE0NzkgMDAwMDAgbg0KMDAwMDAwMTg0OCAwMDAwMCBuDQowMDAwMDAxOTk2IDAwMDAwIG4NCjAwMDAwMDIxOTcgMDAwMDAgbg0KMDAwMDAwMjYyMSAwMDAwMCBuDQowMDAwMDAyNjU2IDAwMDAwIG4NCjAwMDAwMDMzOTYgMDAwMDAgbg0KMDAwMDAwMzkwMSAwMDAwMCBuDQowMDAwMDA0NDExIDAwMDAwIG4NCjAwMDAwMDUwMTEgMDAwMDAgbg0KMDAwMDAwNTUzMCAwMDAwMCBuDQowMDAwMDA2MDQ5IDAwMDAwIG4NCjAwMDAwMDY1ODcgMDAwMDAgbg0KMDAwMDAwNjk4MyAwMDAwMCBuDQowMDAwMDA5NjkwIDAwMDAwIG4NCjAwMDAwMTYzMjUgMDAwMDAgbg0KMDAwMDAxNjU0NyAwMDAwMCBuDQowMDAwMDE3MDg3IDAwMDAwIG4NCjAwMDAwMTczMDYgMDAwMDAgbg0KMDAwMDAxNzYwMCAwMDAwMCBuDQowMDAwMDE5NTcxIDAwMDAwIG4NCjAwMDAwMTk3OTUgMDAwMDAgbg0KMDAwMDAyMDE3MiAwMDAwMCBuDQowMDAwMDMwNTAxIDAwMDAwIG4NCjAwMDAwMzA3MzMgMDAwMDAgbg0KMDAwMDAwMDg5NiAwMDAwMCBuDQp0cmFpbGVyDQo8PC9TaXplIDM1L1Jvb3QgNiAwIFIvSW5mbyA0IDAgUi9JRFs8OTNEREQ1RjRBQjk1NTU2NTVFMUFFQkU3Mjc4OTFGNzQ+PDUyRUNGNjUzRTlDQTM4NDNBMEI2MTY0ODI1RkZENjJDPl0vUHJldiA0MjQ4MT4+DQpzdGFydHhyZWYNCjANCiUlRU9GDQogICAgICAgICAgICAgICAgDQozNCAwIG9iag08PC9GaWx0ZXIvRmxhdGVEZWNvZGUvSSAxMTYvTGVuZ3RoIDEwNC9TIDQwPj5zdHJlYW0NCmjeYmBgkGZgYN7DAASTHjGgAmYgZmHgWIAqKg3FDAzKDHxMFuwPghsKmWZIBDAwHWSPkN3Q6/iEfYJ8QZRXQboC94Y6hx0sPJUM+o5hC27whJ88ADWDhYFhSRiQZgTiRwABBgBLlxXzDQplbmRzdHJlYW0NZW5kb2JqDTYgMCBvYmoNPDwvTWV0YWRhdGEgMiAwIFIvUGFnZXMgMSAwIFIvVHlwZS9DYXRhbG9nPj4NZW5kb2JqDTcgMCBvYmoNPDwvQ29udGVudHNbMTQgMCBSIDE1IDAgUiAxNiAwIFIgMTcgMCBSIDE4IDAgUiAxOSAwIFIgMjAgMCBSIDIxIDAgUl0vQ3JvcEJveFswIDAgNjEyIDc5Ml0vTWVkaWFCb3hbMCAwIDYxMiA3OTJdL1BhcmVudCAxIDAgUi9SZXNvdXJjZXMgOCAwIFIvUm90YXRlIDAvVHlwZS9QYWdlPj4NZW5kb2JqDTggMCBvYmoNPDwvQ29sb3JTcGFjZTw8L0NzMSAxMyAwIFI+Pi9Gb250PDwvVFQxIDkgMCBSL1RUMyAxMCAwIFIvVFQ1IDExIDAgUi9UVDYgMTIgMCBSPj4vUHJvY1NldFsvUERGL1RleHQvSW1hZ2VCL0ltYWdlQy9JbWFnZUldL1hPYmplY3Q8PC9JbTEgMzMgMCBSPj4+Pg1lbmRvYmoNOSAwIG9iag08PC9CYXNlRm9udC9aUFFQU0ErVHJlYnVjaGV0TVMvRW5jb2RpbmcvTWFjUm9tYW5FbmNvZGluZy9GaXJzdENoYXIgMzIvRm9udERlc2NyaXB0b3IgMjQgMCBSL0xhc3RDaGFyIDExOC9TdWJ0eXBlL1RydWVUeXBlL1R5cGUvRm9udC9XaWR0aHNbMzAxIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgNTk4IDYxMyAwIDAgMCAwIDI3OCAwIDAgNTA2IDAgMCAwIDAgMCAwIDAgMCAwIDAgODUyIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDU1NyA1NDUgMzcwIDAgMCAyODUgMCAwIDI5NSA4MzAgNTQ2IDUzNyA1NTcgMCAzODkgNDA1IDAgNTQ2IDQ5MF0+Pg1lbmRvYmoNMTAgMCBvYmoNPDwvQmFzZUZvbnQvTVVLRlJOK0NhbGlicmkvRmlyc3RDaGFyIDMzL0ZvbnREZXNjcmlwdG9yIDI2IDAgUi9MYXN0Q2hhciAzMy9TdWJ0eXBlL1RydWVUeXBlL1RvVW5pY29kZSAyNyAwIFIvVHlwZS9Gb250L1dpZHRoc1syMjZdPj4NZW5kb2JqDTExIDAgb2JqDTw8L0Jhc2VGb250L0hGQU1aRitDYWxpYnJpLUJvbGQvRmlyc3RDaGFyIDMzL0ZvbnREZXNjcmlwdG9yIDI5IDAgUi9MYXN0Q2hhciA0NS9TdWJ0eXBlL1RydWVUeXBlL1RvVW5pY29kZSAzMCAwIFIvVHlwZS9Gb250L1dpZHRoc1syMjYgNjA2IDQ3NCAzNTUgNTAzIDUzNyA0OTQgNTM3IDM5OSAyNDYgMjc2IDQzMCA1MDddPj4NZW5kb2JqDTEyIDAgb2JqDTw8L0Jhc2VGb250L1VHSkVDSCtIZWx2ZXRpY2EvRW5jb2RpbmcvTWFjUm9tYW5FbmNvZGluZy9GaXJzdENoYXIgMzIvRm9udERlc2NyaXB0b3IgMzIgMCBSL0xhc3RDaGFyIDEyMi9TdWJ0eXBlL1RydWVUeXBlL1R5cGUvRm9udC9XaWR0aHNbMjc4IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAyNzggMzMzIDI3OCAwIDAgMCA1NTYgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDY2NyA2NjcgNzIyIDcyMiAwIDAgMCAwIDI3OCAwIDY2NyA1NTYgMCA3MjIgNzc4IDY2NyAwIDcyMiAwIDYxMSA3MjIgMCAwIDY2NyAwIDAgMCAwIDAgMCAwIDAgNTU2IDU1NiA1MDAgNTU2IDU1NiAyNzggNTU2IDU1NiAyMjIgMCA1MDAgMjIyIDgzMyA1NTYgNTU2IDU1NiAwIDMzMyA1MDAgMjc4IDU1NiA1MDAgNzIyIDUwMCA1MDAgNTAwXT4+DWVuZG9iag0xMyAwIG9iag1bL0lDQ0Jhc2VkIDIyIDAgUl0NZW5kb2JqDTE0IDAgb2JqDTw8L0ZpbHRlci9GbGF0ZURlY29kZS9MZW5ndGggNjcwPj5z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ACiiigAooooAKKKKACiiigApaKKAP//ZDQplbmRzdHJlYW0NZW5kb2JqDTEgMCBvYmoNPDwvQ291bnQgMS9LaWRzWzcgMCBSXS9UeXBlL1BhZ2VzPj4NZW5kb2JqDTIgMCBvYmoNPDwvTGVuZ3RoIDMzNjYvU3VidHlwZS9YTUwvVHlwZS9NZXRhZGF0YT4+c3RyZWFtDQo8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzAxNiA5MS4xNjM2MTYsIDIwMTgvMTAvMjktMTY6NTg6NDkgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6cGRmPSJodHRwOi8vbnMuYWRvYmUuY29tL3BkZi8xLjMvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIj4KICAgICAgICAgPHhtcDpDcmVhdGVEYXRlPjIwMTgtMDMtMjFUMTM6NTg6MDdaPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5Xb3JkPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDE5LTA1LTI2VDExOjE3OjE3LTA3OjAwPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxOS0wNS0yNlQxMToxNzoxNy0wNzowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHBkZjpLZXl3b3Jkcy8+CiAgICAgICAgIDxwZGY6UHJvZHVjZXI+TWFjIE9TIFggMTAuMTEuNiBRdWFydHogUERGQ29udGV4dDwvcGRmOlByb2R1Y2VyPgogICAgICAgICA8ZGM6Zm9ybWF0PmFwcGxpY2F0aW9uL3BkZjwvZGM6Zm9ybWF0PgogICAgICAgICA8ZGM6dGl0bGU+CiAgICAgICAgICAgIDxyZGY6QWx0PgogICAgICAgICAgICAgICA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPk1pY3Jvc29mdCBXb3JkIC0gV29ybGRfV2lkZV9Db3JwX2xvcmVtLmRvY3g8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6QWx0PgogICAgICAgICA8L2RjOnRpdGxlPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD51dWlkOjJlNjBiMTYyLTY4MmQtNGZjOS1hYjFjLTcwMTQ0OTllMGQ0OTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOkluc3RhbmNlSUQ+dXVpZDplYmRjZjYzOS02NWNjLTQ0YTgtODEyMi02ZDA2YWFjNzI3MDI8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4NCmVuZHN0cmVhbQ1lbmRvYmoNMyAwIG9iag1bXQ1lbmRvYmoNNCAwIG9iag08PC9BQVBMOktleXdvcmRzIDMgMCBSL0NyZWF0aW9uRGF0ZShEOjIwMTgwMzIxMTM1ODA3WikvQ3JlYXRvcihXb3JkKS9LZXl3b3JkcygpL01vZERhdGUoRDoyMDE5MDUyNjExMTcxNy0wNycwMCcpL1Byb2R1Y2VyKE1hYyBPUyBYIDEwLjExLjYgUXVhcnR6IFBERkNvbnRleHQpL1RpdGxlKE1pY3Jvc29mdCBXb3JkIC0gV29ybGRfV2lkZV9Db3JwX2xvcmVtLmRvY3gpPj4NZW5kb2JqDXhyZWYNCjAgNQ0KMDAwMDAwMDAwMCA2NTUzNSBmDQowMDAwMDM4NzQzIDAwMDAwIG4NCjAwMDAwMzg3OTQgMDAwMDAgbg0KMDAwMDA0MjIzNyAwMDAwMCBuDQowMDAwMDQyMjU1IDAwMDAwIG4NCnRyYWlsZXINCjw8L1NpemUgNS9JRFs8OTNEREQ1RjRBQjk1NTU2NTVFMUFFQkU3Mjc4OTFGNzQ+PDUyRUNGNjUzRTlDQTM4NDNBMEI2MTY0ODI1RkZENjJDPl0+Pg0Kc3RhcnR4cmVmDQoxMTYNCiUlRU9GDQo="); + doc1.setDocumentId("1"); + doc1.setFileExtension("pdf"); + doc1.setName("Lorem"); + + env.setDocuments(Arrays.asList(doc1)); + + SignHere signHere1 = new SignHere(); + signHere1.setName("SignHereTab"); + signHere1.setXPosition("75"); + signHere1.setYPosition("572"); + signHere1.setTabLabel("SignHereTab"); + signHere1.setPageNumber("1"); + signHere1.setDocumentId("1"); // A 1- to 8-digit integer or 32-character GUID to match recipient IDs on your own systems. // This value is referenced in the Tabs element below to assign tabs on a per-recipient basis. - signHere.setRecipientId("1"); - - Signer signer = new Signer(); - signer.setName(args.getSignerName()); - signer.setEmail(args.getSignerEmail()); - signer.setRoutingOrder("1"); - signer.setStatus(EnvelopeHelpers.SIGNER_STATUS_CREATED); - signer.setDeliveryMethod(EnvelopeHelpers.DELIVERY_METHOD_EMAIL); - signer.setRecipientId(signHere.getRecipientId()); - signer.setTabs(EnvelopeHelpers.createSignerTabs(signHere)); - signer.setAccessCode(args.getAccessCode()); + signHere1.setRecipientId("1"); // represents your {RECIPIENT_ID} + + Tabs signer1Tabs = new Tabs(); + signer1Tabs.setSignHereTabs(Arrays.asList(signHere1)); + + Signer signer1 = new Signer(); + signer1.setName(signerName); + signer1.setEmail(signerEmail); + signer1.setRoutingOrder("1"); + signer1.setStatus("Created"); + signer1.setDeliveryMethod("Email"); + signer1.setRecipientId("1"); // represents yoru {RECIPIENT_ID} + signer1.setTabs(signer1Tabs); + signer1.setAccessCode("NJ9@D1"); // represents your {ACCESS_CODE} Recipients recipients = new Recipients(); - recipients.setSigners(Arrays.asList(signer)); + recipients.setSigners(Arrays.asList(signer1)); + env.setRecipients(recipients); - EnvelopeDefinition envelope = new EnvelopeDefinition(); - envelope.setEmailSubject("Please Sign"); - envelope.setDocuments(Arrays.asList(doc)); - envelope.setEnvelopeIdStamping("true"); - envelope.setEmailBlurb("Sample text for email body"); - envelope.setStatus(EnvelopeHelpers.ENVELOPE_STATUS_SENT); - envelope.setRecipients(recipients); + // Step 4: Call the eSignature REST API + EnvelopeSummary results = envelopesApi.createEnvelope(accountId, env); - return envelope; + // process results + args.setEnvelopeId(results.getEnvelopeId()); + session.setAttribute("envelopeId", results.getEnvelopeId()); + setMessage("The envelope has been created and sent!
Envelope ID " + args.getEnvelopeId() + "."); + return results; } + // ***DS.snippet.0.end -} +} \ No newline at end of file diff --git a/src/main/java/com/docusign/controller/examples/EG020ControllerSmsAuthentication.java b/src/main/java/com/docusign/controller/examples/EG020ControllerSmsAuthentication.java index ae548bf7..f56783aa 100644 --- a/src/main/java/com/docusign/controller/examples/EG020ControllerSmsAuthentication.java +++ b/src/main/java/com/docusign/controller/examples/EG020ControllerSmsAuthentication.java @@ -1,118 +1,120 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; import com.docusign.esign.api.EnvelopesApi; +import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.Document; -import com.docusign.esign.model.EnvelopeDefinition; -import com.docusign.esign.model.EnvelopeSummary; -import com.docusign.esign.model.RecipientSMSAuthentication; -import com.docusign.esign.model.Recipients; -import com.docusign.esign.model.SignHere; -import com.docusign.esign.model.Signer; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - -import org.springframework.beans.factory.annotation.Autowired; +import com.docusign.esign.model.*; +import com.sun.jersey.core.util.Base64; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import java.io.IOException; import java.util.Arrays; -import java.util.List; -import javax.servlet.http.HttpServletResponse; - - -/** - * Send an envelope with a recipient using SMS Authentication. - */ @Controller @RequestMapping("/eg020") -public class EG020ControllerSmsAuthentication extends AbstractController { +public class EG020ControllerSmsAuthentication extends EGController { - private static final List RECIPIENT_PHONE_NUMBERS = List.of("415-555-1212", "415-555-3434"); - private static final String DOCUMENT_FILE_NAME = "World_Wide_Corp_lorem.pdf"; - private static final String DOCUMENT_NAME = "Lorem"; + @Override + protected void addSpecialAttributes(ModelMap model) { + } - private final Session session; - private final User user; + @Override + protected String getEgName() { + return "eg020"; + } + @Override + protected String getTitle() { + return "Signing request by email"; + } - @Autowired - public EG020ControllerSmsAuthentication(DSConfiguration config, Session session, User user) { - super(config, "eg020", "Signing request by email"); - this.session = session; - this.user = user; + @Override + protected String getResponseTitle() { + return "Envelope sent"; } @Override // ***DS.snippet.0.start protected Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException { - String accountId = session.getAccountId(); + String accessToken, String basePath) throws ApiException, IOException { + // Data for this method + // accessToken (argument) + // basePath (argument) + String accountId = args.getAccountId(); + + // Set status for the makeEnvelope method + if (!"created".equalsIgnoreCase(args.getStatus())) { + args.setStatus("sent"); + } // Step 2: Construct your API headers - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); // Step 3: Construct your envelope JSON body - EnvelopeDefinition envelope = createEnvelope(args.getSignerName(), args.getSignerEmail()); - - // Step 4: Call the eSignature REST API - EnvelopeSummary results = envelopesApi.createEnvelope(accountId, envelope); - - session.setEnvelopeId(results.getEnvelopeId()); - DoneExample.createDefault(title) - .withJsonObject(results) - .withMessage("The envelope has been created and sent!
Envelope ID " - + results.getEnvelopeId() + ".") - .addToModel(model); - - return DONE_EXAMPLE_PAGE; - } - - private static EnvelopeDefinition createEnvelope(String signerName, String signerEmail) throws IOException { - Document doc = EnvelopeHelpers.createDocumentFromFile(DOCUMENT_FILE_NAME, DOCUMENT_NAME, "1"); - - SignHere signHere = new SignHere(); - signHere.setName("SignHereTab"); - signHere.setXPosition("75"); - signHere.setYPosition("572"); - signHere.setTabLabel("SignHereTab"); - signHere.setPageNumber("1"); - signHere.setDocumentId(doc.getDocumentId()); + String signerEmail = args.getSignerEmail(); + String signerName = args.getSignerName(); + String status = args.getStatus(); + + EnvelopeDefinition env = new EnvelopeDefinition(); + env.setEmailSubject("Please Sign"); + env.setEnvelopeIdStamping("true"); + env.setEmailBlurb("Sample text for email body"); + env.setStatus("sent"); + + Document doc1 = new Document(); + doc1.setDocumentBase64("JVBERi0xLjMNJeLjz9MNCjUgMCBvYmoNPDwvTGluZWFyaXplZCAxL0wgNDI3MTAvTyA3L0UgMzg3NDMvTiAxL1QgNDI0OTEvSCBbIDg5NiAxODVdPj4NZW5kb2JqDSAgICAgICAgICAgICAgICAgICAgDQp4cmVmDQo1IDMwDQowMDAwMDAwMDE2IDAwMDAwIG4NCjAwMDAwMDEwODEgMDAwMDAgbg0KMDAwMDAwMTE0MSAwMDAwMCBuDQowMDAwMDAxMzE4IDAwMDAwIG4NCjAwMDAwMDE0NzkgMDAwMDAgbg0KMDAwMDAwMTg0OCAwMDAwMCBuDQowMDAwMDAxOTk2IDAwMDAwIG4NCjAwMDAwMDIxOTcgMDAwMDAgbg0KMDAwMDAwMjYyMSAwMDAwMCBuDQowMDAwMDAyNjU2IDAwMDAwIG4NCjAwMDAwMDMzOTYgMDAwMDAgbg0KMDAwMDAwMzkwMSAwMDAwMCBuDQowMDAwMDA0NDExIDAwMDAwIG4NCjAwMDAwMDUwMTEgMDAwMDAgbg0KMDAwMDAwNTUzMCAwMDAwMCBuDQowMDAwMDA2MDQ5IDAwMDAwIG4NCjAwMDAwMDY1ODcgMDAwMDAgbg0KMDAwMDAwNjk4MyAwMDAwMCBuDQowMDAwMDA5NjkwIDAwMDAwIG4NCjAwMDAwMTYzMjUgMDAwMDAgbg0KMDAwMDAxNjU0NyAwMDAwMCBuDQowMDAwMDE3MDg3IDAwMDAwIG4NCjAwMDAwMTczMDYgMDAwMDAgbg0KMDAwMDAxNzYwMCAwMDAwMCBuDQowMDAwMDE5NTcxIDAwMDAwIG4NCjAwMDAwMTk3OTUgMDAwMDAgbg0KMDAwMDAyMDE3MiAwMDAwMCBuDQowMDAwMDMwNTAxIDAwMDAwIG4NCjAwMDAwMzA3MzMgMDAwMDAgbg0KMDAwMDAwMDg5NiAwMDAwMCBuDQp0cmFpbGVyDQo8PC9TaXplIDM1L1Jvb3QgNiAwIFIvSW5mbyA0IDAgUi9JRFs8OTNEREQ1RjRBQjk1NTU2NTVFMUFFQkU3Mjc4OTFGNzQ+PDUyRUNGNjUzRTlDQTM4NDNBMEI2MTY0ODI1RkZENjJDPl0vUHJldiA0MjQ4MT4+DQpzdGFydHhyZWYNCjANCiUlRU9GDQogICAgICAgICAgICAgICAgDQozNCAwIG9iag08PC9GaWx0ZXIvRmxhdGVEZWNvZGUvSSAxMTYvTGVuZ3RoIDEwNC9TIDQwPj5zdHJlYW0NCmjeYmBgkGZgYN7DAASTHjGgAmYgZmHgWIAqKg3FDAzKDHxMFuwPghsKmWZIBDAwHWSPkN3Q6/iEfYJ8QZRXQboC94Y6hx0sPJUM+o5hC27whJ88ADWDhYFhSRiQZgTiRwABBgBLlxXzDQplbmRzdHJlYW0NZW5kb2JqDTYgMCBvYmoNPDwvTWV0YWRhdGEgMiAwIFIvUGFnZXMgMSAwIFIvVHlwZS9DYXRhbG9nPj4NZW5kb2JqDTcgMCBvYmoNPDwvQ29udGVudHNbMTQgMCBSIDE1IDAgUiAxNiAwIFIgMTcgMCBSIDE4IDAgUiAxOSAwIFIgMjAgMCBSIDIxIDAgUl0vQ3JvcEJveFswIDAgNjEyIDc5Ml0vTWVkaWFCb3hbMCAwIDYxMiA3OTJdL1BhcmVudCAxIDAgUi9SZXNvdXJjZXMgOCAwIFIvUm90YXRlIDAvVHlwZS9QYWdlPj4NZW5kb2JqDTggMCBvYmoNPDwvQ29sb3JTcGFjZTw8L0NzMSAxMyAwIFI+Pi9Gb250PDwvVFQxIDkgMCBSL1RUMyAxMCAwIFIvVFQ1IDExIDAgUi9UVDYgMTIgMCBSPj4vUHJvY1NldFsvUERGL1RleHQvSW1hZ2VCL0ltYWdlQy9JbWFnZUldL1hPYmplY3Q8PC9JbTEgMzMgMCBSPj4+Pg1lbmRvYmoNOSAwIG9iag08PC9CYXNlRm9udC9aUFFQU0ErVHJlYnVjaGV0TVMvRW5jb2RpbmcvTWFjUm9tYW5FbmNvZGluZy9GaXJzdENoYXIgMzIvRm9udERlc2NyaXB0b3IgMjQgMCBSL0xhc3RDaGFyIDExOC9TdWJ0eXBlL1RydWVUeXBlL1R5cGUvRm9udC9XaWR0aHNbMzAxIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgNTk4IDYxMyAwIDAgMCAwIDI3OCAwIDAgNTA2IDAgMCAwIDAgMCAwIDAgMCAwIDAgODUyIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDU1NyA1NDUgMzcwIDAgMCAyODUgMCAwIDI5NSA4MzAgNTQ2IDUzNyA1NTcgMCAzODkgNDA1IDAgNTQ2IDQ5MF0+Pg1lbmRvYmoNMTAgMCBvYmoNPDwvQmFzZUZvbnQvTVVLRlJOK0NhbGlicmkvRmlyc3RDaGFyIDMzL0ZvbnREZXNjcmlwdG9yIDI2IDAgUi9MYXN0Q2hhciAzMy9TdWJ0eXBlL1RydWVUeXBlL1RvVW5pY29kZSAyNyAwIFIvVHlwZS9Gb250L1dpZHRoc1syMjZdPj4NZW5kb2JqDTExIDAgb2JqDTw8L0Jhc2VGb250L0hGQU1aRitDYWxpYnJpLUJvbGQvRmlyc3RDaGFyIDMzL0ZvbnREZXNjcmlwdG9yIDI5IDAgUi9MYXN0Q2hhciA0NS9TdWJ0eXBlL1RydWVUeXBlL1RvVW5pY29kZSAzMCAwIFIvVHlwZS9Gb250L1dpZHRoc1syMjYgNjA2IDQ3NCAzNTUgNTAzIDUzNyA0OTQgNTM3IDM5OSAyNDYgMjc2IDQzMCA1MDddPj4NZW5kb2JqDTEyIDAgb2JqDTw8L0Jhc2VGb250L1VHSkVDSCtIZWx2ZXRpY2EvRW5jb2RpbmcvTWFjUm9tYW5FbmNvZGluZy9GaXJzdENoYXIgMzIvRm9udERlc2NyaXB0b3IgMzIgMCBSL0xhc3RDaGFyIDEyMi9TdWJ0eXBlL1RydWVUeXBlL1R5cGUvRm9udC9XaWR0aHNbMjc4IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAyNzggMzMzIDI3OCAwIDAgMCA1NTYgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDY2NyA2NjcgNzIyIDcyMiAwIDAgMCAwIDI3OCAwIDY2NyA1NTYgMCA3MjIgNzc4IDY2NyAwIDcyMiAwIDYxMSA3MjIgMCAwIDY2NyAwIDAgMCAwIDAgMCAwIDAgNTU2IDU1NiA1MDAgNTU2IDU1NiAyNzggNTU2IDU1NiAyMjIgMCA1MDAgMjIyIDgzMyA1NTYgNTU2IDU1NiAwIDMzMyA1MDAgMjc4IDU1NiA1MDAgNzIyIDUwMCA1MDAgNTAwXT4+DWVuZG9iag0xMyAwIG9iag1bL0lDQ0Jhc2VkIDIyIDAgUl0NZW5kb2JqDTE0IDAgb2JqDTw8L0ZpbHRlci9GbGF0ZURlY29kZS9MZW5ndGggNjcwPj5z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ACiiigAooooAKKKKACiiigApaKKAP//ZDQplbmRzdHJlYW0NZW5kb2JqDTEgMCBvYmoNPDwvQ291bnQgMS9LaWRzWzcgMCBSXS9UeXBlL1BhZ2VzPj4NZW5kb2JqDTIgMCBvYmoNPDwvTGVuZ3RoIDMzNjYvU3VidHlwZS9YTUwvVHlwZS9NZXRhZGF0YT4+c3RyZWFtDQo8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzAxNiA5MS4xNjM2MTYsIDIwMTgvMTAvMjktMTY6NTg6NDkgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6cGRmPSJodHRwOi8vbnMuYWRvYmUuY29tL3BkZi8xLjMvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIj4KICAgICAgICAgPHhtcDpDcmVhdGVEYXRlPjIwMTgtMDMtMjFUMTM6NTg6MDdaPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5Xb3JkPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDE5LTA1LTI2VDExOjE3OjE3LTA3OjAwPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxOS0wNS0yNlQxMToxNzoxNy0wNzowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHBkZjpLZXl3b3Jkcy8+CiAgICAgICAgIDxwZGY6UHJvZHVjZXI+TWFjIE9TIFggMTAuMTEuNiBRdWFydHogUERGQ29udGV4dDwvcGRmOlByb2R1Y2VyPgogICAgICAgICA8ZGM6Zm9ybWF0PmFwcGxpY2F0aW9uL3BkZjwvZGM6Zm9ybWF0PgogICAgICAgICA8ZGM6dGl0bGU+CiAgICAgICAgICAgIDxyZGY6QWx0PgogICAgICAgICAgICAgICA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPk1pY3Jvc29mdCBXb3JkIC0gV29ybGRfV2lkZV9Db3JwX2xvcmVtLmRvY3g8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6QWx0PgogICAgICAgICA8L2RjOnRpdGxlPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD51dWlkOjJlNjBiMTYyLTY4MmQtNGZjOS1hYjFjLTcwMTQ0OTllMGQ0OTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOkluc3RhbmNlSUQ+dXVpZDplYmRjZjYzOS02NWNjLTQ0YTgtODEyMi02ZDA2YWFjNzI3MDI8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4NCmVuZHN0cmVhbQ1lbmRvYmoNMyAwIG9iag1bXQ1lbmRvYmoNNCAwIG9iag08PC9BQVBMOktleXdvcmRzIDMgMCBSL0NyZWF0aW9uRGF0ZShEOjIwMTgwMzIxMTM1ODA3WikvQ3JlYXRvcihXb3JkKS9LZXl3b3JkcygpL01vZERhdGUoRDoyMDE5MDUyNjExMTcxNy0wNycwMCcpL1Byb2R1Y2VyKE1hYyBPUyBYIDEwLjExLjYgUXVhcnR6IFBERkNvbnRleHQpL1RpdGxlKE1pY3Jvc29mdCBXb3JkIC0gV29ybGRfV2lkZV9Db3JwX2xvcmVtLmRvY3gpPj4NZW5kb2JqDXhyZWYNCjAgNQ0KMDAwMDAwMDAwMCA2NTUzNSBmDQowMDAwMDM4NzQzIDAwMDAwIG4NCjAwMDAwMzg3OTQgMDAwMDAgbg0KMDAwMDA0MjIzNyAwMDAwMCBuDQowMDAwMDQyMjU1IDAwMDAwIG4NCnRyYWlsZXINCjw8L1NpemUgNS9JRFs8OTNEREQ1RjRBQjk1NTU2NTVFMUFFQkU3Mjc4OTFGNzQ+PDUyRUNGNjUzRTlDQTM4NDNBMEI2MTY0ODI1RkZENjJDPl0+Pg0Kc3RhcnR4cmVmDQoxMTYNCiUlRU9GDQo="); + doc1.setDocumentId("1"); + doc1.setFileExtension("pdf"); + doc1.setName("Lorem"); + + env.setDocuments(Arrays.asList(doc1)); + + SignHere signHere1 = new SignHere(); + signHere1.setName("SignHereTab"); + signHere1.setXPosition("75"); + signHere1.setYPosition("572"); + signHere1.setTabLabel("SignHereTab"); + signHere1.setPageNumber("1"); + signHere1.setDocumentId("1"); // A 1- to 8-digit integer or 32-character GUID to match recipient IDs on your own systems. // This value is referenced in the Tabs element below to assign tabs on a per-recipient basis. - signHere.setRecipientId("1"); + signHere1.setRecipientId("1"); // represents your {RECIPIENT_ID} + + Tabs signer1Tabs = new Tabs(); + signer1Tabs.setSignHereTabs(Arrays.asList(signHere1)); + String providedNumber = "415-555-1212"; // represents your {PHONE_NUMBER} RecipientSMSAuthentication smsAuth = new RecipientSMSAuthentication(); - smsAuth.setSenderProvidedNumbers(RECIPIENT_PHONE_NUMBERS); - - Signer signer = new Signer(); - signer.setName(signerName); - signer.setEmail(signerEmail); - signer.setRoutingOrder("1"); - signer.setStatus(EnvelopeHelpers.SIGNER_STATUS_CREATED); - signer.setDeliveryMethod(EnvelopeHelpers.DELIVERY_METHOD_EMAIL); - signer.setRecipientId(signHere.getRecipientId()); - signer.setTabs(EnvelopeHelpers.createSignerTabs(signHere)); - signer.setRequireIdLookup("true"); - signer.setSmsAuthentication(smsAuth); - signer.setIdCheckConfigurationName("SMS Auth $"); + smsAuth.setSenderProvidedNumbers(Arrays.asList(providedNumber)); + + Signer signer1 = new Signer(); + signer1.setName(signerName); + signer1.setEmail(signerEmail); + signer1.setRoutingOrder("1"); + signer1.setStatus("Created"); + signer1.setDeliveryMethod("Email"); + signer1.setRecipientId("1"); // represents your {RECIPIENT_ID} + signer1.setTabs(signer1Tabs); + signer1.setRequireIdLookup("true"); + signer1.setSmsAuthentication(smsAuth); + signer1.setIdCheckConfigurationName("SMS Auth $"); Recipients recipients = new Recipients(); - recipients.setSigners(Arrays.asList(signer)); + recipients.setSigners(Arrays.asList(signer1)); + env.setRecipients(recipients); - EnvelopeDefinition envelope = new EnvelopeDefinition(); - envelope.setEmailSubject("Please Sign"); - envelope.setDocuments(Arrays.asList(doc)); - envelope.setEnvelopeIdStamping("true"); - envelope.setEmailBlurb("Sample text for email body"); - envelope.setStatus(EnvelopeHelpers.ENVELOPE_STATUS_SENT); - envelope.setRecipients(recipients); + // Step 4: Call the eSignature REST API + EnvelopeSummary results = envelopesApi.createEnvelope(accountId, env); - return envelope; + // process results + args.setEnvelopeId(results.getEnvelopeId()); + session.setAttribute("envelopeId", results.getEnvelopeId()); + setMessage("The envelope has been created and sent!
Envelope ID " + args.getEnvelopeId() + "."); + return results; } + // ***DS.snippet.0.end -} +} \ No newline at end of file diff --git a/src/main/java/com/docusign/controller/examples/EG021ControllerPhoneAuthentication.java b/src/main/java/com/docusign/controller/examples/EG021ControllerPhoneAuthentication.java index f1341eb1..0abeed64 100644 --- a/src/main/java/com/docusign/controller/examples/EG021ControllerPhoneAuthentication.java +++ b/src/main/java/com/docusign/controller/examples/EG021ControllerPhoneAuthentication.java @@ -1,116 +1,121 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; import com.docusign.esign.api.EnvelopesApi; +import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.Document; -import com.docusign.esign.model.EnvelopeDefinition; -import com.docusign.esign.model.EnvelopeSummary; -import com.docusign.esign.model.RecipientPhoneAuthentication; -import com.docusign.esign.model.Recipients; -import com.docusign.esign.model.SignHere; -import com.docusign.esign.model.Signer; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - -import org.springframework.beans.factory.annotation.Autowired; +import com.docusign.esign.model.*; +import com.sun.jersey.core.util.Base64; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import java.io.IOException; import java.util.Arrays; -import java.util.List; -import javax.servlet.http.HttpServletResponse; - -/** - * Send an envelope with a recipient using Phone Authentication. - */ @Controller @RequestMapping("/eg021") -public class EG021ControllerPhoneAuthentication extends AbstractController { +public class EG021ControllerPhoneAuthentication extends EGController { - private static final List RECIPIENT_PHONE_NUMBERS = List.of("415-555-1212", "415-555-3434"); - private static final String DOCUMENT_FILE_NAME = "World_Wide_Corp_lorem.pdf"; - private static final String DOCUMENT_NAME = "Lorem"; + @Override + protected void addSpecialAttributes(ModelMap model) { + } - private final Session session; - private final User user; + @Override + protected String getEgName() { + return "eg021"; + } + @Override + protected String getTitle() { + return "Signing request by email"; + } - @Autowired - public EG021ControllerPhoneAuthentication(DSConfiguration config, Session session, User user) { - super(config, "eg021", "Signing request by email"); - this.session = session; - this.user = user; + @Override + protected String getResponseTitle() { + return "Envelope sent"; } @Override // ***DS.snippet.0.start protected Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException { - // Step 1: Construct your API headers - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); - - // Step 2: Construct your envelope JSON body - EnvelopeDefinition envelope = createEnvelope(args.getSignerName(), args.getSignerEmail()); - - // Step 3: Call the eSignature REST API - EnvelopeSummary results = envelopesApi.createEnvelope(session.getAccountId(), envelope); - - session.setEnvelopeId(results.getEnvelopeId()); - DoneExample.createDefault(title) - .withJsonObject(results) - .withMessage("The envelope has been created and sent!
Envelope ID " - + results.getEnvelopeId() + ".") - .addToModel(model); - - return DONE_EXAMPLE_PAGE; - } - - private static EnvelopeDefinition createEnvelope(String signerName, String signerEmail) throws IOException { - Document doc = EnvelopeHelpers.createDocumentFromFile(DOCUMENT_FILE_NAME, DOCUMENT_NAME, "1"); - - SignHere signHere = new SignHere(); - signHere.setName("SignHereTab"); - signHere.setXPosition("75"); - signHere.setYPosition("572"); - signHere.setTabLabel("SignHereTab"); - signHere.setPageNumber("1"); - signHere.setDocumentId(doc.getDocumentId()); + String accessToken, String basePath) throws ApiException, IOException { + // Data for this method + // accessToken (argument) + // basePath (argument) + String accountId = args.getAccountId(); //represents your {ACCOUNT_ID} + + // Set status for the makeEnvelope method + if (!"created".equalsIgnoreCase(args.getStatus())) { + args.setStatus("sent"); + } + + // Step 2: Construct your API headers + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); + + // Step 3: Construct your envelope JSON body + String signerEmail = args.getSignerEmail(); + String signerName = args.getSignerName(); + String status = args.getStatus(); + + EnvelopeDefinition env = new EnvelopeDefinition(); + env.setEmailSubject("Please Sign"); + env.setEnvelopeIdStamping("true"); + env.setEmailBlurb("Sample text for email body"); + env.setStatus("sent"); + + Document doc1 = new Document(); + doc1.setDocumentBase64("JVBERi0xLjMNJeLjz9MNCjUgMCBvYmoNPDwvTGluZWFyaXplZCAxL0wgNDI3MTAvTyA3L0UgMzg3NDMvTiAxL1QgNDI0OTEvSCBbIDg5NiAxODVdPj4NZW5kb2JqDSAgICAgICAgICAgICAgICAgICAgDQp4cmVmDQo1IDMwDQowMDAwMDAwMDE2IDAwMDAwIG4NCjAwMDAwMDEwODEgMDAwMDAgbg0KMDAwMDAwMTE0MSAwMDAwMCBuDQowMDAwMDAxMzE4IDAwMDAwIG4NCjAwMDAwMDE0NzkgMDAwMDAgbg0KMDAwMDAwMTg0OCAwMDAwMCBuDQowMDAwMDAxOTk2IDAwMDAwIG4NCjAwMDAwMDIxOTcgMDAwMDAgbg0KMDAwMDAwMjYyMSAwMDAwMCBuDQowMDAwMDAyNjU2IDAwMDAwIG4NCjAwMDAwMDMzOTYgMDAwMDAgbg0KMDAwMDAwMzkwMSAwMDAwMCBuDQowMDAwMDA0NDExIDAwMDAwIG4NCjAwMDAwMDUwMTEgMDAwMDAgbg0KMDAwMDAwNTUzMCAwMDAwMCBuDQowMDAwMDA2MDQ5IDAwMDAwIG4NCjAwMDAwMDY1ODcgMDAwMDAgbg0KMDAwMDAwNjk4MyAwMDAwMCBuDQowMDAwMDA5NjkwIDAwMDAwIG4NCjAwMDAwMTYzMjUgMDAwMDAgbg0KMDAwMDAxNjU0NyAwMDAwMCBuDQowMDAwMDE3MDg3IDAwMDAwIG4NCjAwMDAwMTczMDYgMDAwMDAgbg0KMDAwMDAxNzYwMCAwMDAwMCBuDQowMDAwMDE5NTcxIDAwMDAwIG4NCjAwMDAwMTk3OTUgMDAwMDAgbg0KMDAwMDAyMDE3MiAwMDAwMCBuDQowMDAwMDMwNTAxIDAwMDAwIG4NCjAwMDAwMzA3MzMgMDAwMDAgbg0KMDAwMDAwMDg5NiAwMDAwMCBuDQp0cmFpbGVyDQo8PC9TaXplIDM1L1Jvb3QgNiAwIFIvSW5mbyA0IDAgUi9JRFs8OTNEREQ1RjRBQjk1NTU2NTVFMUFFQkU3Mjc4OTFGNzQ+PDUyRUNGNjUzRTlDQTM4NDNBMEI2MTY0ODI1RkZENjJDPl0vUHJldiA0MjQ4MT4+DQpzdGFydHhyZWYNCjANCiUlRU9GDQogICAgICAgICAgICAgICAgDQozNCAwIG9iag08PC9GaWx0ZXIvRmxhdGVEZWNvZGUvSSAxMTYvTGVuZ3RoIDEwNC9TIDQwPj5zdHJlYW0NCmjeYmBgkGZgYN7DAASTHjGgAmYgZmHgWIAqKg3FDAzKDHxMFuwPghsKmWZIBDAwHWSPkN3Q6/iEfYJ8QZRXQboC94Y6hx0sPJUM+o5hC27whJ88ADWDhYFhSRiQZgTiRwABBgBLlxXzDQplbmRzdHJlYW0NZW5kb2JqDTYgMCBvYmoNPDwvTWV0YWRhdGEgMiAwIFIvUGFnZXMgMSAwIFIvVHlwZS9DYXRhbG9nPj4NZW5kb2JqDTcgMCBvYmoNPDwvQ29udGVudHNbMTQgMCBSIDE1IDAgUiAxNiAwIFIgMTcgMCBSIDE4IDAgUiAxOSAwIFIgMjAgMCBSIDIxIDAgUl0vQ3JvcEJveFswIDAgNjEyIDc5Ml0vTWVkaWFCb3hbMCAwIDYxMiA3OTJdL1BhcmVudCAxIDAgUi9SZXNvdXJjZXMgOCAwIFIvUm90YXRlIDAvVHlwZS9QYWdlPj4NZW5kb2JqDTggMCBvYmoNPDwvQ29sb3JTcGFjZTw8L0NzMSAxMyAwIFI+Pi9Gb250PDwvVFQxIDkgMCBSL1RUMyAxMCAwIFIvVFQ1IDExIDAgUi9UVDYgMTIgMCBSPj4vUHJvY1NldFsvUERGL1RleHQvSW1hZ2VCL0ltYWdlQy9JbWFnZUldL1hPYmplY3Q8PC9JbTEgMzMgMCBSPj4+Pg1lbmRvYmoNOSAwIG9iag08PC9CYXNlRm9udC9aUFFQU0ErVHJlYnVjaGV0TVMvRW5jb2RpbmcvTWFjUm9tYW5FbmNvZGluZy9GaXJzdENoYXIgMzIvRm9udERlc2NyaXB0b3IgMjQgMCBSL0xhc3RDaGFyIDExOC9TdWJ0eXBlL1RydWVUeXBlL1R5cGUvRm9udC9XaWR0aHNbMzAxIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgNTk4IDYxMyAwIDAgMCAwIDI3OCAwIDAgNTA2IDAgMCAwIDAgMCAwIDAgMCAwIDAgODUyIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDU1NyA1NDUgMzcwIDAgMCAyODUgMCAwIDI5NSA4MzAgNTQ2IDUzNyA1NTcgMCAzODkgNDA1IDAgNTQ2IDQ5MF0+Pg1lbmRvYmoNMTAgMCBvYmoNPDwvQmFzZUZvbnQvTVVLRlJOK0NhbGlicmkvRmlyc3RDaGFyIDMzL0ZvbnREZXNjcmlwdG9yIDI2IDAgUi9MYXN0Q2hhciAzMy9TdWJ0eXBlL1RydWVUeXBlL1RvVW5pY29kZSAyNyAwIFIvVHlwZS9Gb250L1dpZHRoc1syMjZdPj4NZW5kb2JqDTExIDAgb2JqDTw8L0Jhc2VGb250L0hGQU1aRitDYWxpYnJpLUJvbGQvRmlyc3RDaGFyIDMzL0ZvbnREZXNjcmlwdG9yIDI5IDAgUi9MYXN0Q2hhciA0NS9TdWJ0eXBlL1RydWVUeXBlL1RvVW5pY29kZSAzMCAwIFIvVHlwZS9Gb250L1dpZHRoc1syMjYgNjA2IDQ3NCAzNTUgNTAzIDUzNyA0OTQgNTM3IDM5OSAyNDYgMjc2IDQzMCA1MDddPj4NZW5kb2JqDTEyIDAgb2JqDTw8L0Jhc2VGb250L1VHSkVDSCtIZWx2ZXRpY2EvRW5jb2RpbmcvTWFjUm9tYW5FbmNvZGluZy9GaXJzdENoYXIgMzIvRm9udERlc2NyaXB0b3IgMzIgMCBSL0xhc3RDaGFyIDEyMi9TdWJ0eXBlL1RydWVUeXBlL1R5cGUvRm9udC9XaWR0aHNbMjc4IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAyNzggMzMzIDI3OCAwIDAgMCA1NTYgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDY2NyA2NjcgNzIyIDcyMiAwIDAgMCAwIDI3OCAwIDY2NyA1NTYgMCA3MjIgNzc4IDY2NyAwIDcyMiAwIDYxMSA3MjIgMCAwIDY2NyAwIDAgMCAwIDAgMCAwIDAgNTU2IDU1NiA1MDAgNTU2IDU1NiAyNzggNTU2IDU1NiAyMjIgMCA1MDAgMjIyIDgzMyA1NTYgNTU2IDU1NiAwIDMzMyA1MDAgMjc4IDU1NiA1MDAgNzIyIDUwMCA1MDAgNTAwXT4+DWVuZG9iag0xMyAwIG9iag1bL0lDQ0Jhc2VkIDIyIDAgUl0NZW5kb2JqDTE0IDAgb2JqDTw8L0ZpbHRlci9GbGF0ZURlY29kZS9MZW5ndGggNjcwPj5z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ACiiigAooooAKKKKACiiigApaKKAP//ZDQplbmRzdHJlYW0NZW5kb2JqDTEgMCBvYmoNPDwvQ291bnQgMS9LaWRzWzcgMCBSXS9UeXBlL1BhZ2VzPj4NZW5kb2JqDTIgMCBvYmoNPDwvTGVuZ3RoIDMzNjYvU3VidHlwZS9YTUwvVHlwZS9NZXRhZGF0YT4+c3RyZWFtDQo8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzAxNiA5MS4xNjM2MTYsIDIwMTgvMTAvMjktMTY6NTg6NDkgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6cGRmPSJodHRwOi8vbnMuYWRvYmUuY29tL3BkZi8xLjMvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIj4KICAgICAgICAgPHhtcDpDcmVhdGVEYXRlPjIwMTgtMDMtMjFUMTM6NTg6MDdaPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5Xb3JkPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDE5LTA1LTI2VDExOjE3OjE3LTA3OjAwPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxOS0wNS0yNlQxMToxNzoxNy0wNzowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHBkZjpLZXl3b3Jkcy8+CiAgICAgICAgIDxwZGY6UHJvZHVjZXI+TWFjIE9TIFggMTAuMTEuNiBRdWFydHogUERGQ29udGV4dDwvcGRmOlByb2R1Y2VyPgogICAgICAgICA8ZGM6Zm9ybWF0PmFwcGxpY2F0aW9uL3BkZjwvZGM6Zm9ybWF0PgogICAgICAgICA8ZGM6dGl0bGU+CiAgICAgICAgICAgIDxyZGY6QWx0PgogICAgICAgICAgICAgICA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPk1pY3Jvc29mdCBXb3JkIC0gV29ybGRfV2lkZV9Db3JwX2xvcmVtLmRvY3g8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6QWx0PgogICAgICAgICA8L2RjOnRpdGxlPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD51dWlkOjJlNjBiMTYyLTY4MmQtNGZjOS1hYjFjLTcwMTQ0OTllMGQ0OTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOkluc3RhbmNlSUQ+dXVpZDplYmRjZjYzOS02NWNjLTQ0YTgtODEyMi02ZDA2YWFjNzI3MDI8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4NCmVuZHN0cmVhbQ1lbmRvYmoNMyAwIG9iag1bXQ1lbmRvYmoNNCAwIG9iag08PC9BQVBMOktleXdvcmRzIDMgMCBSL0NyZWF0aW9uRGF0ZShEOjIwMTgwMzIxMTM1ODA3WikvQ3JlYXRvcihXb3JkKS9LZXl3b3JkcygpL01vZERhdGUoRDoyMDE5MDUyNjExMTcxNy0wNycwMCcpL1Byb2R1Y2VyKE1hYyBPUyBYIDEwLjExLjYgUXVhcnR6IFBERkNvbnRleHQpL1RpdGxlKE1pY3Jvc29mdCBXb3JkIC0gV29ybGRfV2lkZV9Db3JwX2xvcmVtLmRvY3gpPj4NZW5kb2JqDXhyZWYNCjAgNQ0KMDAwMDAwMDAwMCA2NTUzNSBmDQowMDAwMDM4NzQzIDAwMDAwIG4NCjAwMDAwMzg3OTQgMDAwMDAgbg0KMDAwMDA0MjIzNyAwMDAwMCBuDQowMDAwMDQyMjU1IDAwMDAwIG4NCnRyYWlsZXINCjw8L1NpemUgNS9JRFs8OTNEREQ1RjRBQjk1NTU2NTVFMUFFQkU3Mjc4OTFGNzQ+PDUyRUNGNjUzRTlDQTM4NDNBMEI2MTY0ODI1RkZENjJDPl0+Pg0Kc3RhcnR4cmVmDQoxMTYNCiUlRU9GDQo="); + doc1.setDocumentId("1"); + doc1.setFileExtension("pdf"); + doc1.setName("Lorem"); + + env.setDocuments(Arrays.asList(doc1)); + + SignHere signHere1 = new SignHere(); + signHere1.setName("SignHereTab"); + signHere1.setXPosition("75"); + signHere1.setYPosition("572"); + signHere1.setTabLabel("SignHereTab"); + signHere1.setPageNumber("1"); + signHere1.setDocumentId("1"); // A 1- to 8-digit integer or 32-character GUID to match recipient IDs on your own systems. // This value is referenced in the Tabs element below to assign tabs on a per-recipient basis. - signHere.setRecipientId("1"); + signHere1.setRecipientId("1"); // represents your {RECIPIENT_ID} + Tabs signer1Tabs = new Tabs(); + signer1Tabs.setSignHereTabs(Arrays.asList(signHere1)); + + String providedNumber = "415-555-1212"; // represents your {PHONE_NUMBER} RecipientPhoneAuthentication phoneAuth = new RecipientPhoneAuthentication(); phoneAuth.setRecipMayProvideNumber("true"); - phoneAuth.setSenderProvidedNumbers(RECIPIENT_PHONE_NUMBERS); - - Signer signer = new Signer(); - signer.setName(signerName); - signer.setEmail(signerEmail); - signer.setRoutingOrder("1"); - signer.setStatus(EnvelopeHelpers.SIGNER_STATUS_CREATED); - signer.setDeliveryMethod(EnvelopeHelpers.DELIVERY_METHOD_EMAIL); - signer.setRecipientId(signHere.getRecipientId()); - signer.setTabs(EnvelopeHelpers.createSignerTabs(signHere)); - signer.setRequireIdLookup("true"); - signer.setPhoneAuthentication(phoneAuth); - signer.setIdCheckConfigurationName("Phone Auth $"); - - Recipients recipients = new Recipients(); - recipients.setSigners(Arrays.asList(signer)); - - EnvelopeDefinition envelope = new EnvelopeDefinition(); - envelope.setEmailSubject("Please Sign"); - envelope.setEnvelopeIdStamping("true"); - envelope.setEmailBlurb("Sample text for email body"); - envelope.setStatus(EnvelopeHelpers.ENVELOPE_STATUS_SENT); - envelope.setRecipients(recipients); - envelope.setDocuments(Arrays.asList(doc)); - - return envelope; + phoneAuth.setSenderProvidedNumbers(Arrays.asList(providedNumber)); + + Signer signer1 = new Signer(); + signer1.setName(signerName); + signer1.setEmail(signerEmail); + signer1.setRoutingOrder("1"); + signer1.setStatus("Created"); + signer1.setDeliveryMethod("Email"); + signer1.setRecipientId("1"); // represents your {RECIPIENT_ID} + signer1.setTabs(signer1Tabs); + signer1.setRequireIdLookup("true"); + signer1.setPhoneAuthentication(phoneAuth); + signer1.setIdCheckConfigurationName("Phone Auth $"); + + Recipients recipients = new Recipients(); // Line 103 + recipients.setSigners(Arrays.asList(signer1)); + env.setRecipients(recipients); + + // Step 4: Call the eSignature REST API + EnvelopeSummary results = envelopesApi.createEnvelope(accountId, env); + + // process results + args.setEnvelopeId(results.getEnvelopeId()); + session.setAttribute("envelopeId", results.getEnvelopeId()); + setMessage("The envelope has been created and sent!
Envelope ID " + args.getEnvelopeId() + "."); + return results; } + // ***DS.snippet.0.end -} +} \ No newline at end of file diff --git a/src/main/java/com/docusign/controller/examples/EG022ControllerKBAAuthentication.java b/src/main/java/com/docusign/controller/examples/EG022ControllerKBAAuthentication.java index dad45c63..ce32d1cc 100644 --- a/src/main/java/com/docusign/controller/examples/EG022ControllerKBAAuthentication.java +++ b/src/main/java/com/docusign/controller/examples/EG022ControllerKBAAuthentication.java @@ -1,109 +1,115 @@ package com.docusign.controller.examples; -import com.docusign.DSConfiguration; import com.docusign.esign.api.EnvelopesApi; +import com.docusign.esign.client.ApiClient; import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.Document; -import com.docusign.esign.model.EnvelopeDefinition; -import com.docusign.esign.model.EnvelopeSummary; -import com.docusign.esign.model.Recipients; -import com.docusign.esign.model.SignHere; -import com.docusign.esign.model.Signer; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - -import org.springframework.beans.factory.annotation.Autowired; +import com.docusign.esign.model.*; +import com.sun.jersey.core.util.Base64; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import java.io.IOException; import java.util.Arrays; -import javax.servlet.http.HttpServletResponse; - - -/** - * Send an envelope with a recipient using Knowledge-Based Authentication. - */ @Controller @RequestMapping("/eg022") -public class EG022ControllerKBAAuthentication extends AbstractController { +public class EG022ControllerKBAAuthentication extends EGController { - private static final String DOCUMENT_FILE_NAME = "World_Wide_Corp_lorem.pdf"; - private static final String DOCUMENT_NAME = "Lorem"; + @Override + protected void addSpecialAttributes(ModelMap model) { + } - private final Session session; - private final User user; + @Override + protected String getEgName() { + return "eg022"; + } + @Override + protected String getTitle() { + return "Signing request by email"; + } - @Autowired - public EG022ControllerKBAAuthentication(DSConfiguration config, Session session, User user) { - super(config, "eg022", "Signing request by email"); - this.session = session; - this.user = user; + @Override + protected String getResponseTitle() { + return "Envelope sent"; } @Override // ***DS.snippet.0.start protected Object doWork(WorkArguments args, ModelMap model, - HttpServletResponse response) throws ApiException, IOException { - // Step 1: Construct your API headers - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); - - // Step 2: Construct your envelope JSON body - EnvelopeDefinition envelope = createEnvelope(args.getSignerName(), args.getSignerEmail()); - - // Step 3: Call the eSignature REST API - EnvelopeSummary results = envelopesApi.createEnvelope(session.getAccountId(), envelope); - - session.setEnvelopeId(results.getEnvelopeId()); - DoneExample.createDefault(title) - .withJsonObject(results) - .withMessage("The envelope has been created and sent!
Envelope ID " - + results.getEnvelopeId() + ".") - .addToModel(model); - - return DONE_EXAMPLE_PAGE; - } - - private static EnvelopeDefinition createEnvelope(String signerName, String signerEmail) throws IOException { - Document doc = EnvelopeHelpers.createDocumentFromFile(DOCUMENT_FILE_NAME, DOCUMENT_NAME, "1"); - - SignHere signHere = new SignHere(); - signHere.setName("SignHereTab"); - signHere.setXPosition("75"); - signHere.setYPosition("572"); - signHere.setTabLabel("SignHereTab"); - signHere.setPageNumber("1"); - signHere.setDocumentId(doc.getDocumentId()); + String accessToken, String basePath) throws ApiException, IOException { + // Data for this method + // accessToken (argument) + // basePath (argument) + String accountId = args.getAccountId(); // represents your {ACCOUNT_ID} + + // Set status for the makeEnvelope method + if (!"created".equalsIgnoreCase(args.getStatus())) { + args.setStatus("sent"); + } + + // Step 2: Construct your API headers + ApiClient apiClient = new ApiClient(basePath); + apiClient.addDefaultHeader("Authorization", "Bearer " + accessToken); + EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); + + // Step 3: Construct your envelope JSON body + String signerEmail = args.getSignerEmail(); + String signerName = args.getSignerName(); + String status = args.getStatus(); + + EnvelopeDefinition env = new EnvelopeDefinition(); + env.setEmailSubject("Please Sign"); + env.setEnvelopeIdStamping("true"); + env.setEmailBlurb("Sample text for email body"); + env.setStatus("sent"); + + Document doc1 = new Document(); + doc1.setDocumentBase64("JVBERi0xLjMNJeLjz9MNCjUgMCBvYmoNPDwvTGluZWFyaXplZCAxL0wgNDI3MTAvTyA3L0UgMzg3NDMvTiAxL1QgNDI0OTEvSCBbIDg5NiAxODVdPj4NZW5kb2JqDSAgICAgICAgICAgICAgICAgICAgDQp4cmVmDQo1IDMwDQowMDAwMDAwMDE2IDAwMDAwIG4NCjAwMDAwMDEwODEgMDAwMDAgbg0KMDAwMDAwMTE0MSAwMDAwMCBuDQowMDAwMDAxMzE4IDAwMDAwIG4NCjAwMDAwMDE0NzkgMDAwMDAgbg0KMDAwMDAwMTg0OCAwMDAwMCBuDQowMDAwMDAxOTk2IDAwMDAwIG4NCjAwMDAwMDIxOTcgMDAwMDAgbg0KMDAwMDAwMjYyMSAwMDAwMCBuDQowMDAwMDAyNjU2IDAwMDAwIG4NCjAwMDAwMDMzOTYgMDAwMDAgbg0KMDAwMDAwMzkwMSAwMDAwMCBuDQowMDAwMDA0NDExIDAwMDAwIG4NCjAwMDAwMDUwMTEgMDAwMDAgbg0KMDAwMDAwNTUzMCAwMDAwMCBuDQowMDAwMDA2MDQ5IDAwMDAwIG4NCjAwMDAwMDY1ODcgMDAwMDAgbg0KMDAwMDAwNjk4MyAwMDAwMCBuDQowMDAwMDA5NjkwIDAwMDAwIG4NCjAwMDAwMTYzMjUgMDAwMDAgbg0KMDAwMDAxNjU0NyAwMDAwMCBuDQowMDAwMDE3MDg3IDAwMDAwIG4NCjAwMDAwMTczMDYgMDAwMDAgbg0KMDAwMDAxNzYwMCAwMDAwMCBuDQowMDAwMDE5NTcxIDAwMDAwIG4NCjAwMDAwMTk3OTUgMDAwMDAgbg0KMDAwMDAyMDE3MiAwMDAwMCBuDQowMDAwMDMwNTAxIDAwMDAwIG4NCjAwMDAwMzA3MzMgMDAwMDAgbg0KMDAwMDAwMDg5NiAwMDAwMCBuDQp0cmFpbGVyDQo8PC9TaXplIDM1L1Jvb3QgNiAwIFIvSW5mbyA0IDAgUi9JRFs8OTNEREQ1RjRBQjk1NTU2NTVFMUFFQkU3Mjc4OTFGNzQ+PDUyRUNGNjUzRTlDQTM4NDNBMEI2MTY0ODI1RkZENjJDPl0vUHJldiA0MjQ4MT4+DQpzdGFydHhyZWYNCjANCiUlRU9GDQogICAgICAgICAgICAgICAgDQozNCAwIG9iag08PC9GaWx0ZXIvRmxhdGVEZWNvZGUvSSAxMTYvTGVuZ3RoIDEwNC9TIDQwPj5zdHJlYW0NCmjeYmBgkGZgYN7DAASTHjGgAmYgZmHgWIAqKg3FDAzKDHxMFuwPghsKmWZIBDAwHWSPkN3Q6/iEfYJ8QZRXQboC94Y6hx0sPJUM+o5hC27whJ88ADWDhYFhSRiQZgTiRwABBgBLlxXzDQplbmRzdHJlYW0NZW5kb2JqDTYgMCBvYmoNPDwvTWV0YWRhdGEgMiAwIFIvUGFnZXMgMSAwIFIvVHlwZS9DYXRhbG9nPj4NZW5kb2JqDTcgMCBvYmoNPDwvQ29udGVudHNbMTQgMCBSIDE1IDAgUiAxNiAwIFIgMTcgMCBSIDE4IDAgUiAxOSAwIFIgMjAgMCBSIDIxIDAgUl0vQ3JvcEJveFswIDAgNjEyIDc5Ml0vTWVkaWFCb3hbMCAwIDYxMiA3OTJdL1BhcmVudCAxIDAgUi9SZXNvdXJjZXMgOCAwIFIvUm90YXRlIDAvVHlwZS9QYWdlPj4NZW5kb2JqDTggMCBvYmoNPDwvQ29sb3JTcGFjZTw8L0NzMSAxMyAwIFI+Pi9Gb250PDwvVFQxIDkgMCBSL1RUMyAxMCAwIFIvVFQ1IDExIDAgUi9UVDYgMTIgMCBSPj4vUHJvY1NldFsvUERGL1RleHQvSW1hZ2VCL0ltYWdlQy9JbWFnZUldL1hPYmplY3Q8PC9JbTEgMzMgMCBSPj4+Pg1lbmRvYmoNOSAwIG9iag08PC9CYXNlRm9udC9aUFFQU0ErVHJlYnVjaGV0TVMvRW5jb2RpbmcvTWFjUm9tYW5FbmNvZGluZy9GaXJzdENoYXIgMzIvRm9udERlc2NyaXB0b3IgMjQgMCBSL0xhc3RDaGFyIDExOC9TdWJ0eXBlL1RydWVUeXBlL1R5cGUvRm9udC9XaWR0aHNbMzAxIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgNTk4IDYxMyAwIDAgMCAwIDI3OCAwIDAgNTA2IDAgMCAwIDAgMCAwIDAgMCAwIDAgODUyIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDU1NyA1NDUgMzcwIDAgMCAyODUgMCAwIDI5NSA4MzAgNTQ2IDUzNyA1NTcgMCAzODkgNDA1IDAgNTQ2IDQ5MF0+Pg1lbmRvYmoNMTAgMCBvYmoNPDwvQmFzZUZvbnQvTVVLRlJOK0NhbGlicmkvRmlyc3RDaGFyIDMzL0ZvbnREZXNjcmlwdG9yIDI2IDAgUi9MYXN0Q2hhciAzMy9TdWJ0eXBlL1RydWVUeXBlL1RvVW5pY29kZSAyNyAwIFIvVHlwZS9Gb250L1dpZHRoc1syMjZdPj4NZW5kb2JqDTExIDAgb2JqDTw8L0Jhc2VGb250L0hGQU1aRitDYWxpYnJpLUJvbGQvRmlyc3RDaGFyIDMzL0ZvbnREZXNjcmlwdG9yIDI5IDAgUi9MYXN0Q2hhciA0NS9TdWJ0eXBlL1RydWVUeXBlL1RvVW5pY29kZSAzMCAwIFIvVHlwZS9Gb250L1dpZHRoc1syMjYgNjA2IDQ3NCAzNTUgNTAzIDUzNyA0OTQgNTM3IDM5OSAyNDYgMjc2IDQzMCA1MDddPj4NZW5kb2JqDTEyIDAgb2JqDTw8L0Jhc2VGb250L1VHSkVDSCtIZWx2ZXRpY2EvRW5jb2RpbmcvTWFjUm9tYW5FbmNvZGluZy9GaXJzdENoYXIgMzIvRm9udERlc2NyaXB0b3IgMzIgMCBSL0xhc3RDaGFyIDEyMi9TdWJ0eXBlL1RydWVUeXBlL1R5cGUvRm9udC9XaWR0aHNbMjc4IDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAyNzggMzMzIDI3OCAwIDAgMCA1NTYgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDY2NyA2NjcgNzIyIDcyMiAwIDAgMCAwIDI3OCAwIDY2NyA1NTYgMCA3MjIgNzc4IDY2NyAwIDcyMiAwIDYxMSA3MjIgMCAwIDY2NyAwIDAgMCAwIDAgMCAwIDAgNTU2IDU1NiA1MDAgNTU2IDU1NiAyNzggNTU2IDU1NiAyMjIgMCA1MDAgMjIyIDgzMyA1NTYgNTU2IDU1NiAwIDMzMyA1MDAgMjc4IDU1NiA1MDAgNzIyIDUwMCA1MDAgNTAwXT4+DWVuZG9iag0xMyAwIG9iag1bL0lDQ0Jhc2VkIDIyIDAgUl0NZW5kb2JqDTE0IDAgb2JqDTw8L0ZpbHRlci9GbGF0ZURlY29kZS9MZW5ndGggNjcwPj5z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ACiiigAooooAKKKKACiiigApaKKAP//ZDQplbmRzdHJlYW0NZW5kb2JqDTEgMCBvYmoNPDwvQ291bnQgMS9LaWRzWzcgMCBSXS9UeXBlL1BhZ2VzPj4NZW5kb2JqDTIgMCBvYmoNPDwvTGVuZ3RoIDMzNjYvU3VidHlwZS9YTUwvVHlwZS9NZXRhZGF0YT4+c3RyZWFtDQo8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzAxNiA5MS4xNjM2MTYsIDIwMTgvMTAvMjktMTY6NTg6NDkgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6cGRmPSJodHRwOi8vbnMuYWRvYmUuY29tL3BkZi8xLjMvIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIj4KICAgICAgICAgPHhtcDpDcmVhdGVEYXRlPjIwMTgtMDMtMjFUMTM6NTg6MDdaPC94bXA6Q3JlYXRlRGF0ZT4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5Xb3JkPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDE5LTA1LTI2VDExOjE3OjE3LTA3OjAwPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAxOS0wNS0yNlQxMToxNzoxNy0wNzowMDwveG1wOk1ldGFkYXRhRGF0ZT4KICAgICAgICAgPHBkZjpLZXl3b3Jkcy8+CiAgICAgICAgIDxwZGY6UHJvZHVjZXI+TWFjIE9TIFggMTAuMTEuNiBRdWFydHogUERGQ29udGV4dDwvcGRmOlByb2R1Y2VyPgogICAgICAgICA8ZGM6Zm9ybWF0PmFwcGxpY2F0aW9uL3BkZjwvZGM6Zm9ybWF0PgogICAgICAgICA8ZGM6dGl0bGU+CiAgICAgICAgICAgIDxyZGY6QWx0PgogICAgICAgICAgICAgICA8cmRmOmxpIHhtbDpsYW5nPSJ4LWRlZmF1bHQiPk1pY3Jvc29mdCBXb3JkIC0gV29ybGRfV2lkZV9Db3JwX2xvcmVtLmRvY3g8L3JkZjpsaT4KICAgICAgICAgICAgPC9yZGY6QWx0PgogICAgICAgICA8L2RjOnRpdGxlPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD51dWlkOjJlNjBiMTYyLTY4MmQtNGZjOS1hYjFjLTcwMTQ0OTllMGQ0OTwveG1wTU06RG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOkluc3RhbmNlSUQ+dXVpZDplYmRjZjYzOS02NWNjLTQ0YTgtODEyMi02ZDA2YWFjNzI3MDI8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz4NCmVuZHN0cmVhbQ1lbmRvYmoNMyAwIG9iag1bXQ1lbmRvYmoNNCAwIG9iag08PC9BQVBMOktleXdvcmRzIDMgMCBSL0NyZWF0aW9uRGF0ZShEOjIwMTgwMzIxMTM1ODA3WikvQ3JlYXRvcihXb3JkKS9LZXl3b3JkcygpL01vZERhdGUoRDoyMDE5MDUyNjExMTcxNy0wNycwMCcpL1Byb2R1Y2VyKE1hYyBPUyBYIDEwLjExLjYgUXVhcnR6IFBERkNvbnRleHQpL1RpdGxlKE1pY3Jvc29mdCBXb3JkIC0gV29ybGRfV2lkZV9Db3JwX2xvcmVtLmRvY3gpPj4NZW5kb2JqDXhyZWYNCjAgNQ0KMDAwMDAwMDAwMCA2NTUzNSBmDQowMDAwMDM4NzQzIDAwMDAwIG4NCjAwMDAwMzg3OTQgMDAwMDAgbg0KMDAwMDA0MjIzNyAwMDAwMCBuDQowMDAwMDQyMjU1IDAwMDAwIG4NCnRyYWlsZXINCjw8L1NpemUgNS9JRFs8OTNEREQ1RjRBQjk1NTU2NTVFMUFFQkU3Mjc4OTFGNzQ+PDUyRUNGNjUzRTlDQTM4NDNBMEI2MTY0ODI1RkZENjJDPl0+Pg0Kc3RhcnR4cmVmDQoxMTYNCiUlRU9GDQo="); + doc1.setDocumentId("1"); + doc1.setFileExtension("pdf"); + doc1.setName("Lorem"); + + env.setDocuments(Arrays.asList(doc1)); + + SignHere signHere1 = new SignHere(); + signHere1.setName("SignHereTab"); + signHere1.setXPosition("75"); + signHere1.setYPosition("572"); + signHere1.setTabLabel("SignHereTab"); + signHere1.setPageNumber("1"); + signHere1.setDocumentId("1"); // A 1- to 8-digit integer or 32-character GUID to match recipient IDs on your own systems. // This value is referenced in the Tabs element below to assign tabs on a per-recipient basis. - signHere.setRecipientId("1"); - - Signer signer = new Signer(); - signer.setName(signerName); - signer.setEmail(signerEmail); - signer.setRoutingOrder("1"); - signer.setStatus(EnvelopeHelpers.SIGNER_STATUS_CREATED); - signer.setDeliveryMethod(EnvelopeHelpers.DELIVERY_METHOD_EMAIL); - signer.setRecipientId(signHere.getRecipientId()); - signer.setTabs(EnvelopeHelpers.createSignerTabs(signHere)); - signer.setIdCheckConfigurationName("ID Check"); - signer.setRequireIdLookup("true"); + signHere1.setRecipientId("1"); // represents your {RECIPIENT_ID} + + Tabs signer1Tabs = new Tabs(); + signer1Tabs.setSignHereTabs(Arrays.asList(signHere1)); + + Signer signer1 = new Signer(); + signer1.setName(signerName); + signer1.setEmail(signerEmail); + signer1.setRoutingOrder("1"); + signer1.setStatus("Created"); + signer1.setDeliveryMethod("Email"); + signer1.setRecipientId("1"); // represents your {RECIPIENT_ID} + signer1.setTabs(signer1Tabs); + signer1.setIdCheckConfigurationName("ID Check"); + signer1.setRequireIdLookup("true"); Recipients recipients = new Recipients(); - recipients.setSigners(Arrays.asList(signer)); + recipients.setSigners(Arrays.asList(signer1)); + env.setRecipients(recipients); - EnvelopeDefinition envelope = new EnvelopeDefinition(); - envelope.setEmailSubject("Please Sign"); - envelope.setEnvelopeIdStamping("true"); - envelope.setEmailBlurb("Sample text for email body"); - envelope.setStatus(EnvelopeHelpers.ENVELOPE_STATUS_SENT); - envelope.setDocuments(Arrays.asList(doc)); - envelope.setRecipients(recipients); + // Step 4: Call the eSignature REST API + EnvelopeSummary results = envelopesApi.createEnvelope(accountId, env); - return envelope; + // process results + args.setEnvelopeId(results.getEnvelopeId()); + session.setAttribute("envelopeId", results.getEnvelopeId()); + setMessage("The envelope has been created and sent!
Envelope ID " + args.getEnvelopeId() + "."); + return results; } + // ***DS.snippet.0.end -} +} \ No newline at end of file diff --git a/src/main/java/com/docusign/controller/examples/EG023ControllerIdvAuthentication.java b/src/main/java/com/docusign/controller/examples/EG023ControllerIdvAuthentication.java deleted file mode 100644 index 2efc8d62..00000000 --- a/src/main/java/com/docusign/controller/examples/EG023ControllerIdvAuthentication.java +++ /dev/null @@ -1,138 +0,0 @@ -package com.docusign.controller.examples; - -import com.docusign.DSConfiguration; -import com.docusign.esign.api.AccountsApi; -import com.docusign.esign.api.EnvelopesApi; -import com.docusign.esign.client.ApiClient; -import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.AccountIdentityVerificationResponse; -import com.docusign.esign.model.AccountIdentityVerificationWorkflow; -import com.docusign.esign.model.Document; -import com.docusign.esign.model.EnvelopeDefinition; -import com.docusign.esign.model.EnvelopeSummary; -import com.docusign.esign.model.RecipientIdentityVerification; -import com.docusign.esign.model.Recipients; -import com.docusign.esign.model.SignHere; -import com.docusign.esign.model.Signer; -import com.docusign.esign.model.Tabs; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; - -import java.io.IOException; -import java.util.Base64; -import java.util.Collections; -import java.util.List; -import javax.servlet.http.HttpServletResponse; - -/** - * ID Verification authentication - */ -@Controller -@RequestMapping("/eg023") -public class EG023ControllerIdvAuthentication extends AbstractController { - - private static final Logger logger = LoggerFactory.getLogger(EG023ControllerIdvAuthentication.class); - private static final String DOCUMENT_FILE_NAME = "World_Wide_Corp_lorem.pdf"; - private static final String DOCUMENT_NAME = "Lorem"; - private final Session session; - private final User user; - - @Autowired - public EG023ControllerIdvAuthentication(DSConfiguration config, Session session, User user) { - super(config, "eg023", "ID Verification Authentication"); - this.session = session; - this.user = user; - } - - @Override - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) throws ApiException, IOException { - // Step 1: Construct your API headers - ApiClient apiClient = createApiClient(session.getBasePath(), user.getAccessToken()); - EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); - //Step 2: Retrieve the workflow ID - AccountsApi workflowDetails = new AccountsApi(apiClient); - AccountIdentityVerificationResponse workflowRes = workflowDetails.getAccountIdentityVerification(session.getAccountId()); - List identityVerification = workflowRes.getIdentityVerification(); - if (identityVerification == null || identityVerification.isEmpty()) { - throw new ApiException("Error. Cant get AccountIdentityVerificationWorkflow"); - } - String workflowId = identityVerification.get(0).getWorkflowId(); - logger.info("workflowId = " + workflowId); - // Step 3: Construct your envelope JSON body - EnvelopeDefinition envelope = createEnvelope(args.getSignerName(), args.getSignerEmail(), workflowId); - // Step 4: Create envelope - EnvelopeSummary results = envelopesApi.createEnvelope(session.getAccountId(), envelope); - - session.setEnvelopeId(results.getEnvelopeId()); - DoneExample.createDefault(title) - .withJsonObject(results) - .withMessage("The envelope has been created and sent!
Envelope ID " - + results.getEnvelopeId() + ".") - .addToModel(model); - - return DONE_EXAMPLE_PAGE; - } - - private static EnvelopeDefinition createEnvelope(String signerName, String signerEmail, - String workflowId) { - EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition(); - envelopeDefinition.setEmailSubject("Please sign"); - envelopeDefinition.setEmailBlurb("Sample text for email body"); - envelopeDefinition.setStatus("Sent"); - - - byte[] fileBytes = new byte[0]; - try { - fileBytes = EnvelopeHelpers.readFile(DOCUMENT_FILE_NAME); - } catch (IOException e) { - e.printStackTrace(); - } - Document doc1 = new Document(); - doc1.setDocumentBase64(Base64.getEncoder().encodeToString(fileBytes)); - doc1.setDocumentId("1"); - doc1.setFileExtension("pdf"); - doc1.setName("Lorem"); - - SignHere signHere1 = new SignHere(); - signHere1.setName("SignHereTab"); - signHere1.setXPosition("75"); - signHere1.setYPosition("572"); - signHere1.setTabLabel("SignHereTab"); - signHere1.setPageNumber("1"); - signHere1.setDocumentId("1"); - // A 1- to 8-digit integer or 32-character GUID to match recipient IDs on your own systems. - // This value is referenced in the Tabs element below to assign tabs on a per-recipient basis. - signHere1.setRecipientId("1"); //represents your {RECIPIENT_ID} - - Tabs signer1Tabs = new Tabs(); - signer1Tabs.setSignHereTabs(Collections.singletonList(signHere1)); - RecipientIdentityVerification workflow = new RecipientIdentityVerification(); - workflow.setWorkflowId(workflowId); - - Signer signer1 = new Signer(); - signer1.setName(signerName); - signer1.setEmail(signerEmail); - signer1.setRoleName(""); - signer1.setNote(""); - signer1.setStatus("created"); - signer1.setDeliveryMethod("email"); - signer1.setRecipientId("1"); //represents your {RECIPIENT_ID} - signer1.setIdentityVerification(workflow); - signer1.setTabs(signer1Tabs); - - Recipients recipients = new Recipients(); - recipients.setSigners(Collections.singletonList(signer1)); - - envelopeDefinition.setRecipients(recipients); - //TODO: Uncomment - //envelopeDefinition.setDocuments(Collections.singletonList(doc1)); - return envelopeDefinition; - } -} diff --git a/src/main/java/com/docusign/controller/examples/EG024ControllerPermissionCreate.java b/src/main/java/com/docusign/controller/examples/EG024ControllerPermissionCreate.java deleted file mode 100644 index 821b0eaf..00000000 --- a/src/main/java/com/docusign/controller/examples/EG024ControllerPermissionCreate.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.docusign.controller.examples; - -import java.io.IOException; - -import javax.servlet.http.HttpServletResponse; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; - -import com.docusign.DSConfiguration; -import com.docusign.esign.api.AccountsApi; -import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.PermissionProfile; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - -/** - * Permission profiles are collections of account settings that determine the - * behavior and actions available to the user groups to which they're applied. - * This code example demonstrates how to create a permission profile with the - * eSignature REST API. - */ -@Controller -@RequestMapping("/eg024") -public class EG024ControllerPermissionCreate extends AbstractController{ - - private final Session session; - private final User user; - - @Autowired - public EG024ControllerPermissionCreate(DSConfiguration config, Session session, User user) { - super(config, "eg024", "Creates a new permission profile"); - this.session = session; - this.user = user; - } - - @Override - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) - throws ApiException, IOException { - // Step 2. Construct your API headers - AccountsApi accountsApi = createAccountsApi(session.getBasePath(), user.getAccessToken()); - - // Step 3. Construct your request body - PermissionProfile profile = new PermissionProfile() - .permissionProfileName(args.getPermissionProfileName()) - .settings(DsModelUtils.createDefaultRoleSettings()); - - // Step 4. Call the eSignature REST API - PermissionProfile newProfile = accountsApi.createPermissionProfile(session.getAccountId(), profile); - - DoneExample.createDefault(title) - .withJsonObject(newProfile) - .withMessage("The permission profile is created!
Permission profile ID: " - + newProfile.getPermissionProfileId() + ".") - .addToModel(model); - return DONE_EXAMPLE_PAGE; - } -} \ No newline at end of file diff --git a/src/main/java/com/docusign/controller/examples/EG025ControllerPermissionSetUserGroups.java b/src/main/java/com/docusign/controller/examples/EG025ControllerPermissionSetUserGroups.java deleted file mode 100644 index 90b30899..00000000 --- a/src/main/java/com/docusign/controller/examples/EG025ControllerPermissionSetUserGroups.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.docusign.controller.examples; - -import java.util.List; - -import javax.servlet.http.HttpServletResponse; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; - -import com.docusign.DSConfiguration; -import com.docusign.esign.api.AccountsApi; -import com.docusign.esign.api.GroupsApi; -import com.docusign.esign.client.ApiClient; -import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.ErrorDetails; -import com.docusign.esign.model.Group; -import com.docusign.esign.model.GroupInformation; -import com.docusign.esign.model.PermissionProfileInformation; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - - -/** - * Permission profiles are collections of account settings that determine the - * behavior and actions available to the user groups to which they're applied. - * This code example demonstrates how to the permission profile for the group - * with the eSignature REST API. - */ -@Controller -@RequestMapping("/eg025") -public class EG025ControllerPermissionSetUserGroups extends AbstractController { - - private static final String MODEL_LIST_PROFILES = "listProfiles"; - private static final String MODEL_LIST_GROUPS = "listGroups"; - - private final Session session; - private final User user; - - - @Autowired - public EG025ControllerPermissionSetUserGroups(DSConfiguration config, Session session, User user) { - super(config, "eg025", "Setting a permission profile"); - this.session = session; - this.user = user; - } - - @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - String accountId = session.getAccountId(); - - AccountsApi accountsApi = createAccountsApi(session.getBasePath(), user.getAccessToken()); - PermissionProfileInformation permissionsInfo = accountsApi.listPermissions(accountId); - model.addAttribute(MODEL_LIST_PROFILES, permissionsInfo.getPermissionProfiles()); - - GroupsApi groupsApi = new GroupsApi(accountsApi.getApiClient()); - GroupInformation groupInformation = groupsApi.listGroups(accountId); - model.addAttribute(MODEL_LIST_GROUPS, groupInformation.getGroups()); - } - - @Override - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) throws ApiException { - // Step 2: Construct your API headers - ApiClient apiClient = createApiClient(session.getBasePath(), user.getAccessToken()); - GroupsApi groupsApi = new GroupsApi(apiClient); - - // Step 3: Perform request - Group newGroup = new Group() - .groupId(args.getGroupId()) - .permissionProfileId(args.getProfileId()); - GroupInformation groupInformation = new GroupInformation() - .groups(List.of(newGroup)); - GroupInformation newGroupInfo = groupsApi.updateGroups(session.getAccountId(), groupInformation); - - // Step 4: Show result - ErrorDetails errorDetails = newGroupInfo.getGroups().get(0).getErrorDetails(); - if (errorDetails != null) { - new DoneExample() - .withTitle(exampleName) - .withName(title) - .withMessage(errorDetails.getMessage()) - .addToModel(model); - return ERROR_PAGE; - } - DoneExample.createDefault(title) - .withJsonObject(newGroupInfo) - .withMessage("The permission profile was successfully set to the user group!") - .addToModel(model); - return DONE_EXAMPLE_PAGE; - } -} diff --git a/src/main/java/com/docusign/controller/examples/EG026ControllerPermissionChangeSingleSetting.java b/src/main/java/com/docusign/controller/examples/EG026ControllerPermissionChangeSingleSetting.java deleted file mode 100644 index 8844ec59..00000000 --- a/src/main/java/com/docusign/controller/examples/EG026ControllerPermissionChangeSingleSetting.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.docusign.controller.examples; - -import java.io.IOException; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.Optional; -import java.util.Random; - -import javax.servlet.http.HttpServletResponse; - -import com.docusign.model.AccountRoleSettingsPatch; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; - -import com.docusign.DSConfiguration; -import com.docusign.common.Utils; -import com.docusign.esign.api.AccountsApi; -import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.AccountRoleSettings; -import com.docusign.esign.model.PermissionProfile; -import com.docusign.esign.model.PermissionProfileInformation; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; -import com.google.gson.Gson; - -/** - * This code example demonstrates how to edit individual permission settings on - * a permission profile with the eSignature REST API. - */ -@Controller -@RequestMapping("/eg026") -public class EG026ControllerPermissionChangeSingleSetting extends AbstractController { - - private static final String MODEL_PERMISSIONS = "permissions"; - private static final String MODEL_CUR_PROFILE_NAME = "permissionProfileName"; - private static final String MODEL_CUR_PROFILE_ID = "profileId"; - private static final String MODEL_LIST_PROFILES = "listProfiles"; - - private final Session session; - private final User user; - - @Autowired - public EG026ControllerPermissionChangeSingleSetting(DSConfiguration config, Session session, User user) { - super(config, "eg026", "Change single permission setting"); - this.session = session; - this.user = user; - } - - @GetMapping("/profile") - public void getProfile(@RequestParam String profileId, ModelMap model) { - session.setPermissionProfileId(profileId); - model.addAttribute(MODEL_CUR_PROFILE_NAME, profileId); - } - - @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - AccountsApi accountsApi = createAccountsApi(session.getBasePath(), user.getAccessToken()); - - PermissionProfileInformation permissionsInfo = accountsApi.listPermissions(session.getAccountId()); - List profiles = permissionsInfo.getPermissionProfiles(); - model.addAttribute(MODEL_LIST_PROFILES, profiles); - - model.addAttribute(MODEL_CUR_PROFILE_NAME, profiles.get(0).getPermissionProfileName()); - model.addAttribute(MODEL_CUR_PROFILE_ID, profiles.get(0).getPermissionProfileId()); - findProfile(profiles, session.getPermissionProfileId()).ifPresent((PermissionProfile curProfile) -> { - model.addAttribute(MODEL_CUR_PROFILE_NAME, curProfile.getPermissionProfileName()); - model.addAttribute(MODEL_CUR_PROFILE_ID, curProfile.getPermissionProfileId()); - // model.addAttribute(MODEL_PERMISSIONS, - // Utils.compareFields(curProfile.getSettings(), null, true)); - }); - } - - @Override - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) - throws ApiException, IOException { - // Step 2. Construct your API headers - AccountsApi accountsApi = createAccountsApi(session.getBasePath(), user.getAccessToken()); - String accountId = session.getAccountId(); - String curProfileId = args.getProfileId(); - - // Step 3. Construct your request body - PermissionProfileInformation permissionsInfo = accountsApi.listPermissions(accountId); - PermissionProfile profile = findProfile(permissionsInfo.getPermissionProfiles(), curProfileId) - .orElseThrow(NoSuchElementException::new); - - // Step 4. Call the eSignature REST API - AccountRoleSettings newSettings = Objects.requireNonNullElse(profile.getSettings(), - DsModelUtils.createDefaultRoleSettings()); - profile.setSettings(changeRandomSettings(newSettings)); - PermissionProfile newProfile = accountsApi.updatePermissionProfile(accountId, curProfileId, profile); - - DoneExample.createDefault(title).withMessage("The permission profile is updated!
Permission profile ID: " - + newProfile.getPermissionProfileId() + ".").addToModel(model); - return DONE_EXAMPLE_PAGE_COMPARE; - } - - private static Optional findProfile(List profiles, String profileId) { - - return profiles.stream().filter(p -> p.getPermissionProfileId().equals(profileId)).findFirst(); - } - - // Changes random boolean properties; in a real application, changing properties - // will be read from the page or in a different way - private static AccountRoleSettings changeRandomSettings(AccountRoleSettings settings) { - Gson gson = new Gson(); - // Change this value back to: gson.fromJson(gson.toJson(settings), - // AccountRoleSettings.class); - // as soon as the signinguiversion is added back to the swagger spec. - AccountRoleSettings newSettings = gson.fromJson(gson.toJson(settings), AccountRoleSettingsPatch.class); - Random random = new Random(System.currentTimeMillis()); - - return newSettings.canCreateWorkspaces(randomBool(random)).allowEnvelopeSending(randomBool(random)) - .allowSignerAttachments(randomBool(random)).allowESealRecipients(randomBool(random)) - .allowTaggingInSendAndCorrect(randomBool(random)).allowWetSigningOverride(randomBool(random)) - .enableApiRequestLogging(randomBool(random)).enableRecipientViewingNotifications(randomBool(random)) - .allowSupplementalDocuments(randomBool(random)).disableDocumentUpload(randomBool(random)); - } - - private static String randomBool(Random random) { - if (random.nextBoolean()) { - return DsModelUtils.TRUE; - } - return DsModelUtils.FALSE; - } -} diff --git a/src/main/java/com/docusign/controller/examples/EG027ControllerPermissionDelete.java b/src/main/java/com/docusign/controller/examples/EG027ControllerPermissionDelete.java deleted file mode 100644 index 204beb0f..00000000 --- a/src/main/java/com/docusign/controller/examples/EG027ControllerPermissionDelete.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.docusign.controller.examples; - -import javax.servlet.http.HttpServletResponse; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; - -import com.docusign.DSConfiguration; -import com.docusign.esign.api.AccountsApi; -import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.PermissionProfileInformation; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - - -/** - * Permission profiles are collections of account settings that determine the - * behavior and actions available to the user groups to which they're applied. - * This code example demonstrates how to delete a permission profile with the - * eSignature REST API. - */ -@Controller -@RequestMapping("/eg027") -public class EG027ControllerPermissionDelete extends AbstractController { - - private static final String MODEL_LIST_PROFILES = "listProfiles"; - - private final Session session; - private final User user; - - - @Autowired - public EG027ControllerPermissionDelete(DSConfiguration config, Session session, User user) { - super(config, "eg027", "Deleting a permission profile"); - this.session = session; - this.user = user; - } - - @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - AccountsApi accountsApi = createAccountsApi(session.getBasePath(), user.getAccessToken()); - - PermissionProfileInformation permissionsInfo = accountsApi.listPermissions(session.getAccountId()); - model.addAttribute(MODEL_LIST_PROFILES, permissionsInfo.getPermissionProfiles()); - } - - @Override - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) throws ApiException { - // Step 2: Construct your API headers - AccountsApi accountsApi = createAccountsApi(session.getBasePath(), user.getAccessToken()); - - // Step 3: Call the eSignature Rest API to delete profile - String curProfileId = args.getProfileId(); - accountsApi.deletePermissionProfile(session.getAccountId(), curProfileId); - - // Step 4: Show 'done' (successful) page - DoneExample.createDefault(title) - .withMessage(String.join("", "The permission profile '", curProfileId, "' is deleted.")) - .addToModel(model); - return DONE_EXAMPLE_PAGE; - } -} diff --git a/src/main/java/com/docusign/controller/examples/EG028ControllerCreateBrand.java b/src/main/java/com/docusign/controller/examples/EG028ControllerCreateBrand.java deleted file mode 100644 index 9d87dd08..00000000 --- a/src/main/java/com/docusign/controller/examples/EG028ControllerCreateBrand.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.docusign.controller.examples; - -import java.io.IOException; -import java.util.List; - -import javax.servlet.http.HttpServletResponse; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; - -import com.docusign.DSConfiguration; -import com.docusign.common.Languages; -import com.docusign.esign.api.AccountsApi; -import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.Brand; -import com.docusign.esign.model.BrandsResponse; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - - -/** - * Creating a brand.
- * This code example demonstrates how to create a brand. - */ -@Controller -@RequestMapping("/eg028") -public class EG028ControllerCreateBrand extends AbstractController{ - - private static final String MODEL_LIST_LANGUAGE = "listLanguage"; - - private final Session session; - private final User user; - - - @Autowired - public EG028ControllerCreateBrand(DSConfiguration config, Session session, User user) { - super(config, "eg028", "Creating a brand"); - this.session = session; - this.user = user; - } - - @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - model.addAttribute(MODEL_LIST_LANGUAGE, Languages.getSupportedLanguages()); - } - - @Override - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) - throws ApiException, IOException { - // Step 2: Construct your API headers - AccountsApi accountsApi = createAccountsApi(session.getBasePath(), user.getAccessToken()); - - // Step 3: Construct your brand JSON body - String language = args.getLanguage(); - Brand brand = new Brand() - .brandName(args.getBrandName()) - .defaultBrandLanguage(language) - .brandLanguages(List.of(language)); - - // Step 5: Call the eSignature REST API - BrandsResponse brandsResponse = accountsApi.createBrand(session.getAccountId(), brand); - - DoneExample.createDefault(title) - .withJsonObject(brandsResponse) - .withMessage("The brand has been created!
Brand ID " - + brandsResponse.getBrands().get(0).getBrandId() + ".") - .addToModel(model); - return DONE_EXAMPLE_PAGE; - } -} diff --git a/src/main/java/com/docusign/controller/examples/EG029ControllerApplyBrandToEnvelope.java b/src/main/java/com/docusign/controller/examples/EG029ControllerApplyBrandToEnvelope.java deleted file mode 100644 index c06732e2..00000000 --- a/src/main/java/com/docusign/controller/examples/EG029ControllerApplyBrandToEnvelope.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.docusign.controller.examples; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - -import javax.servlet.http.HttpServletResponse; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; - -import com.docusign.DSConfiguration; -import com.docusign.esign.api.AccountsApi; -import com.docusign.esign.api.EnvelopesApi; -import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.BrandsResponse; -import com.docusign.esign.model.Document; -import com.docusign.esign.model.EnvelopeDefinition; -import com.docusign.esign.model.EnvelopeSummary; -import com.docusign.esign.model.Recipients; -import com.docusign.esign.model.Signer; -import com.docusign.esign.model.Tabs; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - - -/** - * Applying a brand to an envelope
- * The envelope includes a pdf document. Anchor text AutoPlace is used - * to position the signing fields in the documents. - */ -@Controller -@RequestMapping("/eg029") -public class EG029ControllerApplyBrandToEnvelope extends AbstractController { - - private static final String MODEL_LIST_BRAND = "listBrands"; - private static final String DOCUMENT_FILE_NAME = "World_Wide_Corp_lorem.pdf"; - private static final String DOCUMENT_NAME = "EG025 Lorem Ipsum"; - private static final int ANCHOR_OFFSET_Y = 10; - private static final int ANCHOR_OFFSET_X = 20; - - private final Session session; - private final User user; - - - @Autowired - public EG029ControllerApplyBrandToEnvelope(DSConfiguration config, Session session, User user) { - super(config, "eg029", "Apply brand to envelope"); - this.session = session; - this.user = user; - } - - @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - AccountsApi accountsApi = createAccountsApi(session.getBasePath(), user.getAccessToken()); - BrandsResponse brands = accountsApi.listBrands(session.getAccountId()); - model.addAttribute(MODEL_LIST_BRAND, brands.getBrands()); - } - - @Override - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) - throws ApiException, IOException { - // Step 2: Construct your API headers - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); - - // Step 3: Construct your envelope JSON body - EnvelopeDefinition envelope = makeEnvelope(args); - - // Step 5: Call the eSignature REST API - EnvelopeSummary envelopeSummary = envelopesApi.createEnvelope(session.getAccountId(), envelope); - - DoneExample.createDefault(title) - .withJsonObject(envelopeSummary) - .withMessage("The envelope has been created and sent!
Envelope ID " - + envelopeSummary.getEnvelopeId() + ".") - .addToModel(model); - return DONE_EXAMPLE_PAGE; - } - - // Creates an envelope. The envelope has one recipient who should sign an - // attached document. Attached document is read from a local directory. - // Also the envelope contains a brand Id which is created on EG024 example. - private static EnvelopeDefinition makeEnvelope(WorkArguments args) throws IOException { - // Reads a file from a local directory and create Document object. - Document document = EnvelopeHelpers.createDocumentFromFile(DOCUMENT_FILE_NAME, DOCUMENT_NAME, "1"); - - // Create a signer recipient to sign the document and associate sign tab - Tabs tabs = EnvelopeHelpers.createSingleSignerTab("/sn1/", ANCHOR_OFFSET_Y, ANCHOR_OFFSET_X); - Signer signer = new Signer() - .email(args.getSignerEmail()) - .name(args.getSignerName()) - .recipientId("1") - .routingOrder("1") - .tabs(tabs); - - Recipients recipients = new Recipients(); - recipients.setSigners(Arrays.asList(signer)); - - // Return envelope definition using created objects: the document, - // the recipients and obtained brand Id. - return new EnvelopeDefinition() - .emailSubject("EG025. Please complete your order") - .documents(List.of(document)) - .recipients(recipients) - .brandId(args.getBrandId()) - .status(EnvelopeHelpers.ENVELOPE_STATUS_SENT); - } -} diff --git a/src/main/java/com/docusign/controller/examples/EG030ControllerApplyBrandToTemplate.java b/src/main/java/com/docusign/controller/examples/EG030ControllerApplyBrandToTemplate.java deleted file mode 100644 index c6d89bc4..00000000 --- a/src/main/java/com/docusign/controller/examples/EG030ControllerApplyBrandToTemplate.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.docusign.controller.examples; - -import java.io.IOException; -import java.util.Arrays; - -import javax.servlet.http.HttpServletResponse; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; - -import com.docusign.DSConfiguration; -import com.docusign.esign.api.AccountsApi; -import com.docusign.esign.api.EnvelopesApi; -import com.docusign.esign.api.TemplatesApi; -import com.docusign.esign.client.ApiClient; -import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.BrandsResponse; -import com.docusign.esign.model.EnvelopeDefinition; -import com.docusign.esign.model.EnvelopeSummary; -import com.docusign.esign.model.EnvelopeTemplateResults; -import com.docusign.esign.model.TemplateRole; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - - -/** - * Applying a brand to a template.
- * This code example demonstrates how to apply a brand to an template. The - * envelope is defined by the template. The signer and cc recipient name and - * email are used to fill in the template's roles. This example - * demonstrates a common pattern for DocuSign integrations: envelopes will be - * sent programmatically, based on a template. If the template definition needs - * to be updated, the DocuSign web tool can be used to easily update the - * template, thus avoiding the need to make software changes. - */ -@Controller -@RequestMapping("/eg030") -public class EG030ControllerApplyBrandToTemplate extends AbstractController { - - private static final String MODEL_LIST_BRAND = "listBrands"; - private static final String MODEL_LIST_TEMPLATE = "listTemplates"; - - private final Session session; - private final User user; - - - @Autowired - public EG030ControllerApplyBrandToTemplate(DSConfiguration config, Session session, User user) { - super(config, "eg030", "Apply brand to template"); - this.session = session; - this.user = user; - } - - @Override - protected void onInitModel(WorkArguments args, ModelMap model) throws ApiException { - super.onInitModel(args, model); - String accountId = session.getAccountId(); - ApiClient apiClient = createApiClient(session.getBasePath(), user.getAccessToken()); - - AccountsApi accountsApi = new AccountsApi(apiClient); - BrandsResponse brands = accountsApi.listBrands(accountId); - model.addAttribute(MODEL_LIST_BRAND, brands.getBrands()); - - TemplatesApi templatesApi = new TemplatesApi(apiClient); - EnvelopeTemplateResults templates = templatesApi.listTemplates(accountId); - model.addAttribute(MODEL_LIST_TEMPLATE, templates.getEnvelopeTemplates()); - } - - @Override - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) - throws ApiException, IOException { - // Step 2: Construct your API headers - EnvelopesApi envelopesApi = createEnvelopesApi(session.getBasePath(), user.getAccessToken()); - - // Step 3: Construct your envelope JSON body - EnvelopeDefinition envelope = makeEnvelope(args); - - // Step 5: Call the eSignature REST API - EnvelopeSummary envelopeSummary = envelopesApi.createEnvelope(session.getAccountId(), envelope); - - DoneExample.createDefault(title) - .withJsonObject(envelopeSummary) - .withMessage("The envelope has been created and sent!
Envelope ID " - + envelopeSummary.getEnvelopeId() + ".") - .addToModel(model); - return DONE_EXAMPLE_PAGE; - } - - private static EnvelopeDefinition makeEnvelope(WorkArguments args) { - TemplateRole signer = new TemplateRole() - .email(args.getSignerEmail()) - .name(args.getSignerName()) - .roleName(EnvelopeHelpers.SIGNER_ROLE_NAME); - - TemplateRole cc = new TemplateRole() - .email(args.getCcEmail()) - .name(args.getCcName()) - .roleName(EnvelopeHelpers.CC_ROLE_NAME); - - return new EnvelopeDefinition() - .templateId(args.getTemplateId()) - .templateRoles(Arrays.asList(signer, cc)) - .brandId(args.getBrandId()) - .status(EnvelopeHelpers.ENVELOPE_STATUS_SENT); - } -} diff --git a/src/main/java/com/docusign/controller/examples/EG031ControllerBulkSendEnvelopes.java b/src/main/java/com/docusign/controller/examples/EG031ControllerBulkSendEnvelopes.java deleted file mode 100644 index a43a1de1..00000000 --- a/src/main/java/com/docusign/controller/examples/EG031ControllerBulkSendEnvelopes.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.docusign.controller.examples; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.servlet.http.HttpServletResponse; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.RequestMapping; - -import com.docusign.DSConfiguration; -import com.docusign.esign.api.BulkEnvelopesApi; -import com.docusign.esign.api.EnvelopesApi; -import com.docusign.esign.client.ApiClient; -import com.docusign.esign.client.ApiException; -import com.docusign.esign.model.BulkEnvelopeStatus; -import com.docusign.esign.model.BulkSendRequest; -import com.docusign.esign.model.BulkSendingCopy; -import com.docusign.esign.model.BulkSendingCopyRecipient; -import com.docusign.esign.model.BulkSendingList; -import com.docusign.esign.model.CustomFields; -import com.docusign.esign.model.Document; -import com.docusign.esign.model.EnvelopeDefinition; -import com.docusign.esign.model.Recipients; -import com.docusign.esign.model.Signer; -import com.docusign.esign.model.TextCustomField; -import com.docusign.model.DoneExample; -import com.docusign.model.Session; -import com.docusign.model.User; - -/** - * This code example demonstrates how to send envelopes in bulk to multiple - * recipients with the eSignature API. To accomplish the task, you will first - * create a bulk send recipients list, and then create an envelope. From there, - * you will combine the envelope and bulk list to initiate bulk send - */ -@Controller -@RequestMapping("/eg031") -public class EG031ControllerBulkSendEnvelopes extends AbstractController { - - private static final long BULK_REQUEST_DELAY = 15L; - private static final String BULK_SIGNER_EMAIL_PLACEHOLDER = "MultiBulkRecipients-%s@docusign.com"; - private static final String BULK_SIGNER_NAME_PLACEHOLDER = "Multi Bulk Recipients::%s"; - private static final String DOCX_DOCUMENT_FILE_NAME = "World_Wide_Corp_Battle_Plan_Trafalgar.docx"; - private static final String DOCX_DOCUMENT_NAME = "Battle Plan"; - - private final Session session; - private final User user; - - - @Autowired - public EG031ControllerBulkSendEnvelopes(DSConfiguration config, Session session, User user) { - super(config, "eg031", "Bulk sending envelopes to multiple recipients"); - this.session = session; - this.user = user; - } - - @Override - protected Object doWork(WorkArguments args, ModelMap model, HttpServletResponse response) - throws ApiException, IOException { - - // Step 2. Construct your API headers - ApiClient apiClient = createApiClient(session.getBasePath(), user.getAccessToken()); - String accountId = session.getAccountId(); - - // Step 3. Submit a bulk list - BulkEnvelopesApi bulkEnvelopesApi = new BulkEnvelopesApi(apiClient); - BulkSendingList sendingList = getSendingList(args); - String bulkListId = bulkEnvelopesApi.createBulkSendList(accountId, sendingList).getListId(); - - // Step 4. Create an envelope - EnvelopesApi envelopesApi = new EnvelopesApi(apiClient); - String envelopeId = envelopesApi.createEnvelope(accountId, makeEnvelope()).getEnvelopeId(); - - // Step 5. Attach your bulk list ID to the envelope - CustomFields customFields = createCustomFields(bulkListId); - envelopesApi.createCustomFields(accountId, envelopeId, customFields); - - // Step 6. Add placeholder recipients - envelopesApi.createRecipient(accountId, envelopeId, createRecipients()); - - // Step 7. Initiate bulk send - BulkSendRequest request = new BulkSendRequest(); - request.setEnvelopeOrTemplateId(envelopeId); - String batchId = bulkEnvelopesApi.createBulkSendRequest(accountId, bulkListId, request).getBatchId(); - - // Step 8. Confirm successful bulk send - try { - TimeUnit.SECONDS.sleep(BULK_REQUEST_DELAY); - // For 2000 recipients, it can take about an hour - BulkEnvelopeStatus status = bulkEnvelopesApi.get(accountId, batchId); - DoneExample.createDefault(title) - .withJsonObject(status) - .withMessage(String.join("", "Bulk request was sent to ", status.getSent(), " user lists.")) - .addToModel(model); - } catch (InterruptedException exception) { - Thread.currentThread().interrupt(); - return null; - } - - return DONE_EXAMPLE_PAGE; - } - - private static BulkSendingList getSendingList(WorkArguments args) { - List copies = List.of( - createBulkSending(args.getSignerName(), args.getSignerEmail(), args.getCcName(), args.getCcEmail()), - createBulkSending(args.getSignerName2(), args.getSignerEmail2(), args.getCcName2(), args.getCcEmail2()) - ); - return new BulkSendingList() - .name("sample.csv") - .bulkCopies(copies); - } - - private static BulkSendingCopy createBulkSending(String signerName, - String signerEmail, String ccName, String ccEmail) { - BulkSendingCopyRecipient recipient1 = new BulkSendingCopyRecipient() - .name(signerName) - .email(signerEmail) - .tabs(Collections.emptyList()) - .roleName(EnvelopeHelpers.SIGNER_ROLE_NAME); - BulkSendingCopyRecipient recipient2 = new BulkSendingCopyRecipient() - .name(ccName) - .email(ccEmail) - .tabs(Collections.emptyList()) - .roleName(EnvelopeHelpers.CC_ROLE_NAME); - return new BulkSendingCopy() - .recipients(List.of(recipient1, recipient2)) - .customFields(Collections.emptyList()); - } - - private static EnvelopeDefinition makeEnvelope() throws IOException { - Document document = EnvelopeHelpers.createDocumentFromFile(DOCX_DOCUMENT_FILE_NAME, DOCX_DOCUMENT_NAME, "1"); - return new EnvelopeDefinition() - .documents(List.of(document)) - .envelopeIdStamping(DsModelUtils.TRUE) - .emailSubject("EG031 Please sign") - .status(EnvelopeHelpers.ENVELOPE_STATUS_CREATED); - } - - private static CustomFields createCustomFields(String bulkListId) { - TextCustomField textCustomField = new TextCustomField() - .name("mailingListId") - .required(DsModelUtils.FALSE) - .show(DsModelUtils.FALSE) - .value(bulkListId); - - return new CustomFields() - .listCustomFields(Collections.emptyList()) - .textCustomFields(List.of(textCustomField)); - } - - private static Recipients createRecipients() { - List signers = List.of( - createSignerPlaceholder(EnvelopeHelpers.SIGNER_ROLE_NAME, "1"), - createSignerPlaceholder(EnvelopeHelpers.CC_ROLE_NAME, "2")); - return new Recipients() - .signers(signers); - } - - private static Signer createSignerPlaceholder(String roleName, String recipientId) { - return new Signer() - .name(String.format(BULK_SIGNER_NAME_PLACEHOLDER, roleName)) - .email(String.format(BULK_SIGNER_EMAIL_PLACEHOLDER, roleName)) - .roleName(roleName) - .note("") - .routingOrder("1") - .status(EnvelopeHelpers.SIGNER_STATUS_CREATED) - .deliveryMethod(EnvelopeHelpers.DELIVERY_METHOD_EMAIL) - .recipientId(recipientId) - .recipientType(EnvelopeHelpers.SIGNER_ROLE_NAME); - } -} \ No newline at end of file diff --git a/src/main/java/com/docusign/controller/examples/EGController.java b/src/main/java/com/docusign/controller/examples/EGController.java new file mode 100644 index 00000000..5ff6f492 --- /dev/null +++ b/src/main/java/com/docusign/controller/examples/EGController.java @@ -0,0 +1,228 @@ +package com.docusign.controller.examples; + +import com.docusign.DSConfiguration; +import com.docusign.esign.client.ApiException; +import com.docusign.model.User; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.view.RedirectView; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +@Controller +public abstract class EGController { + private final static int minimumBufferMin = 3; + + private String message; + + @Autowired + protected HttpSession session; + + @Autowired + protected DSConfiguration config; + + @Autowired + User user; + + // Base method for the example GET requests which show the example's + // form page + @RequestMapping(method = RequestMethod.GET) + public String get(ModelMap model, HttpSession session, HttpServletRequest request) { + // Check that the token is valid and will remain valid for awhile to enable the + // user to fill out the form. If the token is not available, now is the time + // to have the user authenticate or re-authenticate. + boolean tokenOk = checkToken(); + model.addAttribute("showDoc", config.documentation != null); + if (tokenOk) { + model.addAttribute("csrfToken", ""); + model.addAttribute("title", getTitle()); + addSpecialAttributes(model); + model.addAttribute("source", createSourcePath()); + model.addAttribute("documentation", config.documentation + getEgName()); + model.addAttribute("showDoc", config.documentation != null); + + return "pages/examples/" + getEgName(); + } + + session.setAttribute("eg", getEgName()); + return "redirect:/ds/mustAuthenticate"; + + } + + protected abstract void addSpecialAttributes(ModelMap model); + + // Base method for POST requests to run an example + @RequestMapping(method = RequestMethod.POST) + public Object create(WorkArguments args, + ModelMap model, + HttpSession session, + @RequestBody MultiValueMap formParams, + HttpServletResponse response, + @ModelAttribute("accessToken") String accessToken, + @ModelAttribute("basePath") String basePath) throws IOException, ApiException { + // Check again that we have a token. Only a minimal token time is + // needed since we are about to call DocuSign + boolean tokenOk = checkToken(minimumBufferMin); + if (!tokenOk) { + session.setAttribute("eg", getEgName()); + return "redirect:/ds/mustAuthenticate"; + } + + try { + loadFromSessionOrBody(args, formParams); + Object result = doWork(args, model, accessToken, basePath); + String redirectUrl = args.getRedirectUrl(); + Boolean externalRedirect = redirectUrl != null && redirectUrl.indexOf("redirect:") == 0; + if (externalRedirect) { + String url = redirectUrl.substring(9); // strip 'redirect:' + RedirectView redirect = new RedirectView(url); + redirect.setExposeModelAttributes(false); + return redirect; + } else if (redirectUrl != null) { + // show a generic template + postWork(result, model); + return redirectUrl; + } else { + // download logic + JSONObject r = (JSONObject) result; + + byte[] buffer = (byte[]) r.get("fileBytes"); + + response.setContentType(r.getString("mimetype")); + response.setContentLength(buffer.length); + response.setHeader("Content-disposition", "inline;filename=" + r.getString("docName")); + + response.getOutputStream().write(buffer); + response.flushBuffer(); + return null; + } + + } catch (Exception e) { + populateErrorModel(model, e); + throw new RuntimeException(e); + } + } + + private void loadFromSessionOrBody(WorkArguments args, MultiValueMap formParams) { + args.setAccountId((String) session.getAttribute("accountId")); + args.setEnvelopeId((String) session.getAttribute("envelopeId")); + args.setTemplateId((String) session.getAttribute("templateId")); + if (formParams.containsKey("docSelect")) { + args.setDocumentId(formParams.getFirst("docSelect")); + } else if (formParams.containsKey("startingView")) { + args.setStartingView(formParams.getFirst("startingView")); + } else if (formParams.containsKey("item")) { + args.setItem(formParams.getFirst("item")); + } else if (formParams.containsKey("quantity")) { + args.setQuantity(formParams.getFirst("quantity")); + } + args.setEnvelopeDocuments((JSONObject) session.getAttribute("envelopeDocuments")); + } + + protected void postWork(Object result, ModelMap model) { + + String title = getResponseTitle(); + String message = getMessage(); + model.addAttribute("title", title); + model.addAttribute("h1", title); + model.addAttribute("message", message); + + model.addAttribute("json", + (result != null) ? new JSONObject(result).toString(4) : null); + } + + + protected void populateErrorModel(ModelMap model, Exception e) { + model.addAttribute("err", e); + model.addAttribute("errorCode", e.getCause()); + model.addAttribute("errorMessage", e.getMessage()); + } + + private boolean checkToken() { + return checkToken(60); + } + + // Check that we have an Access Token and for how long it will last + private boolean checkToken(int minimumBufferMin) { + Authentication authentication = SecurityContextHolder + .getContext().getAuthentication(); + + if (!(authentication instanceof OAuth2Authentication)) { + return false; + } + + OAuth2Authentication oauth = (OAuth2Authentication) authentication; + return oauth.getUserAuthentication().isAuthenticated(); + + // At this point we'd like to check the expiration time of the token + // vs the minimumBufferMin argument. But it is not clear how + // to obtain the expiration time from Spring Security OAuth client library. + // See https://stackoverflow.com/questions/52432735/spring-security-oauth2-client-access-the-expires-in-value + // + // Since DocuSign tokens granted via the Authorization Code Grant flow + // normally have an 8 hour life, this issue can be punted for now since + // the default Spring Session time is 30 min. + +// return (user.accessToken != null +// || user.tokenExpirationTimestamp - (minimumBufferMin*60*1000) > System.currentTimeMillis()); + } + + protected abstract String getEgName(); + + protected abstract String getTitle(); + + protected abstract String getResponseTitle(); + + protected String createSourcePath() { + String source; + Class enclosingClass = getClass().getEnclosingClass(); + if (enclosingClass != null) { + source = enclosingClass.getName(); + } else { + source = getClass().getName(); + } + + source = source.replace('.', '/'); + return config.githubExampleUrl + source + ".java"; + } + + protected byte[] readFile(String path) throws IOException { + InputStream is = EGController.class.getResourceAsStream("/" + path); + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + int nRead; + + byte[] data = new byte[1024]; + + while ((nRead = is.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + + buffer.flush(); + return buffer.toByteArray(); + } + + protected abstract Object doWork(WorkArguments args, ModelMap model, + @ModelAttribute("accessToken") String accessToken, + @ModelAttribute("basePath") String basePath) throws ApiException, IOException; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/src/main/java/com/docusign/controller/examples/EnvelopeHelpers.java b/src/main/java/com/docusign/controller/examples/EnvelopeHelpers.java deleted file mode 100644 index 4961b596..00000000 --- a/src/main/java/com/docusign/controller/examples/EnvelopeHelpers.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.docusign.controller.examples; - -import java.io.IOException; -import java.io.StringWriter; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Base64; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -import org.apache.commons.io.FilenameUtils; -import org.springframework.core.io.ClassPathResource; -import org.springframework.util.StreamUtils; - -import com.docusign.esign.model.CarbonCopy; -import com.docusign.esign.model.Document; -import com.docusign.esign.model.Recipients; -import com.docusign.esign.model.SignHere; -import com.docusign.esign.model.Signer; -import com.docusign.esign.model.Tabs; - -import freemarker.template.Configuration; -import freemarker.template.Template; -import freemarker.template.TemplateException; - - -/** - * Utility class to create objects related to envelope. - */ -public final class EnvelopeHelpers { - - public static final String ENVELOPE_STATUS_SENT = "sent"; - public static final String ENVELOPE_STATUS_CREATED = "created"; - public static final String SIGNER_STATUS_CREATED = "Created"; - public static final String DELIVERY_METHOD_EMAIL = "Email"; - public static final String SIGNER_ROLE_NAME = "signer"; - public static final String CC_ROLE_NAME = "cc"; - - - private EnvelopeHelpers() {} - - /** - * Loads a file content and copy it into a byte array. - * @param path the absolute path within the class path - * @return the new byte array that has been loaded from the file - * @throws IOException in case of I/O errors - */ - static byte[] readFile(String path) throws IOException { - ClassPathResource resource = new ClassPathResource(path); - return StreamUtils.copyToByteArray(resource.getInputStream()); - } - - /** - * Loads a template of a html document. Usually returned object is kept - * wherever and it is used when it is needed to generate the html document. - * Html document is generated by the method. - * {@link #createHtmlFromTemplate(Template, String, Object)} - * @param path the path to template file - * @return a {@link Template} object - * @throws IOException in case of I/O errors - */ - static Template loadHtmlTemplate(String path) throws IOException { - Configuration cfg = new Configuration(Configuration.VERSION_2_3_29); - cfg.setLocale(Locale.US); - cfg.setDefaultEncoding(StandardCharsets.UTF_8.name()); - ClassPathResource resource = new ClassPathResource(path); - String source = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8); - return new Template(path, source, cfg); - } - - /** - * Generates a html document from the template. Placeholder looks like - * ${objectName.fieldName} - * @param template the template object - * @param objectName name of object to find placeholder related to a value - * @param value object which fields are used as a values in the template - * @return array of bytes representing created html document - * @throws IOException in case of template parsing error - */ - static byte[] createHtmlFromTemplate(Template template, String objectName, Object value) throws IOException { - Map input = new HashMap<>(); - input.put(objectName, value); - StringWriter stringWriter = new StringWriter(); - try { - template.process(input, stringWriter); - return stringWriter.toString().getBytes(StandardCharsets.UTF_8); - } catch (TemplateException exception) { - throw new ExampleException("Can't process html template " + template.getName(), exception); - } - } - - /** - * Generates a html document from the template file. Placeholder looks like - * ${objectName.fieldName}. It is a convenient method which loads template - * from a file using {@link #loadHtmlTemplate(String)} and creates a html - * page from the loaded template using {@link #createHtmlFromTemplate(Template, String, Object)} - * @param path the path to template file - * @param objectName name of object to find placeholder related to a value - * @param value object which fields are used as a values in the template - * @return array of bytes representing created html document - * @throws IOException in case of I/O errors or template parsing error - */ - static byte[] createHtmlFromTemplateFile(String path, String objectName, Object value) throws IOException { - Template template = loadHtmlTemplate(path); - return createHtmlFromTemplate(template, objectName, value); - } - - /** - * Loads document from a file and creates a document object that represents - * loaded document. - * @param fileName name of the file to load document; the extension of the - * loading file determines an extension of the created document - * @param docName the name of the document; it may be differ from the file - * @param docId identifier of the created document - * @return the {@link Document} object - * @throws IOException if document cannot be loaded due to some reason - */ - static Document createDocumentFromFile(String fileName, String docName, String docId) throws IOException { - byte[] buffer = readFile(fileName); - String extention = FilenameUtils.getExtension(fileName); - return createDocument(buffer, docName, extention, docId); - } - - /** - * Creates a document object from the raw data. - * @param data the raw data - * @param documentName the name of the document; it may be differ from the file - * @param fileExtention the extension of the creating file - * @param documentId identifier of the created document - * @return the {@link Document} object - */ - static Document createDocument(byte[] data, String documentName, String fileExtention, String documentId) { - Document document = new Document(); - document.setDocumentBase64(Base64.getEncoder().encodeToString(data)); - document.setName(documentName); - document.setFileExtension(fileExtention); - document.setDocumentId(documentId); - return document; - } - - /** - * Create SignHere (see {@link SignHere}) field (also known as tabs) on the - * documents using anchor (autoPlace) positioning. - * @param anchorString the anchor string; the DocuSign platform searches - * throughout your envelope's documents for matching anchor strings - * @param yOffsetPixels the y offset of anchor in pixels - * @param xOffsetPixels the x offset of anchor in pixels - * @return the {@link SignHere} object - */ - static SignHere createSignHere(String anchorString, int yOffsetPixels, int xOffsetPixels) { - SignHere signHere = new SignHere(); - signHere.setAnchorString(anchorString); - signHere.setAnchorUnits("pixels"); - signHere.setAnchorYOffset(String.valueOf(yOffsetPixels)); - signHere.setAnchorXOffset(String.valueOf(xOffsetPixels)); - return signHere; - } - - /** - * Create Tabs object containing a single SignHere (see {@link SignHere}) - * field (also known as tabs) on the documents using anchor (autoPlace) positioning. - * @param anchorString the anchor string; the DocuSign platform searches - * throughout your envelope's documents for matching anchor strings - * @param yOffsetPixels the y offset of anchor in pixels - * @param xOffsetPixels the x offset of anchor in pixels - * @return the {@link Tabs} object containing single SignHere object - */ - static Tabs createSingleSignerTab(String anchorString, int yOffsetPixels, int xOffsetPixels) { - SignHere signHere = createSignHere(anchorString, yOffsetPixels, xOffsetPixels); - return createSignerTabs(signHere); - } - - /** - * Creates {@link SignHere} fields (also known as tabs) on the document. - * @param signs the array of SignHere (see {@link SignHere}) - * @return the {@link Tabs} object containing passed SignHere objects - */ - static Tabs createSignerTabs(SignHere... signs) { - Tabs signerTabs = new Tabs(); - signerTabs.setSignHereTabs(Arrays.asList(signs)); - return signerTabs; - } - - /** - * Creates a {@link Recipients} object and add signer and cc to it. - * @param signer the signer object - * @param cc the cc object - * @return the {@link Recipients} object - */ - static Recipients createRecipients(Signer signer, CarbonCopy cc) { - Recipients recipients = new Recipients(); - recipients.setSigners(Arrays.asList(signer)); - recipients.setCarbonCopies(Arrays.asList(cc)); - return recipients; - } -} diff --git a/src/main/java/com/docusign/controller/examples/ExampleException.java b/src/main/java/com/docusign/controller/examples/ExampleException.java deleted file mode 100644 index 09d7f8b3..00000000 --- a/src/main/java/com/docusign/controller/examples/ExampleException.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.docusign.controller.examples; - -/** - * Unchecked exception caused by examples. - */ -public class ExampleException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - /** - * Constructs a new exception with the specified detail message and cause. - * @param message the detail message, which is kept as member - * @param cause the cause, which is saved as member - */ - public ExampleException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/com/docusign/controller/examples/WorkArguments.java b/src/main/java/com/docusign/controller/examples/WorkArguments.java index 8d3d3200..4505df18 100644 --- a/src/main/java/com/docusign/controller/examples/WorkArguments.java +++ b/src/main/java/com/docusign/controller/examples/WorkArguments.java @@ -1,38 +1,173 @@ package com.docusign.controller.examples; +import org.json.JSONObject; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; -import org.springframework.web.context.WebApplicationContext; - -import lombok.Data; - @Component -@Scope(value = WebApplicationContext.SCOPE_REQUEST) -@Data +@Scope("session") public class WorkArguments { + private String item; private String quantity; private String signerEmail; private String signerName; private String ccEmail; private String ccName; - private String signerEmail2; - private String signerName2; - private String ccEmail2; - private String ccName2; + private String accountId; + private String envelopeId; private String status; + private String templateName; + private String templateId; + private String redirectUrl = "pages/example_done"; private String startingView; private String dsReturnUrl; private String dsPingUrl; private String signerClientId; - private String docSelect; - private String accessCode; - private String brandName; - private String language; - private String brandId; - private String templateId; - private String profileId; - private String groupId; - private String permissionProfileName; + private String documentId; + private JSONObject envelopeDocuments; + + public String getSignerEmail() { + return signerEmail; + } + + public String getSignerName() { + return signerName; + } + + public String getAccountId() { + return accountId; + } + + public String getEnvelopeId() { + return envelopeId; + } + + public void setEnvelopeId(String envelopeId) { + this.envelopeId = envelopeId; + } + + public String getCcEmail() { + return ccEmail; + } + + public void setCcEmail(String ccEmail) { + this.ccEmail = ccEmail; + } + + public String getCcName() { + return ccName; + } + + public void setCcName(String ccName) { + this.ccName = ccName; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getRedirectUrl() { + return redirectUrl; + } + + public void setRedirectUrl(String redirectUrl) { + this.redirectUrl = redirectUrl; + } + + public String getTemplateName() { + return templateName; + } + + public void setTemplateName(String templateName) { + this.templateName = templateName; + } + + public String getTemplateId() { + return templateId; + } + + public void setTemplateId(String templateId) { + this.templateId = templateId; + } + + public String getStartingView() { + return startingView; + } + + public void setStartingView(String startingView) { + this.startingView = startingView; + } + + public String getDsReturnUrl() { + return dsReturnUrl; + } + + public void setDsReturnUrl(String dsReturnUrl) { + this.dsReturnUrl = dsReturnUrl; + } + + public String getDsPingUrl() { + return dsPingUrl; + } + + public void setDsPingUrl(String dsPingUrl) { + this.dsPingUrl = dsPingUrl; + } + + public String getSignerClientId() { + return signerClientId; + } + + public void setSignerClientId(String signerClientId) { + this.signerClientId = signerClientId; + } + + public String getItem() { + return item; + } + + public void setItem(String item) { + this.item = item; + } + + public String getQuantity() { + return quantity; + } + + public void setQuantity(String quantity) { + this.quantity = quantity; + } + + public String getDocumentId() { + return documentId; + } + + public void setDocumentId(String documentId) { + this.documentId = documentId; + } + + public void setSignerEmail(String signerEmail) { + this.signerEmail = signerEmail; + } + + public void setSignerName(String signerName) { + this.signerName = signerName; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public JSONObject getEnvelopeDocuments() { + return envelopeDocuments; + } + + public void setEnvelopeDocuments(JSONObject envelopeDocuments) { + this.envelopeDocuments = envelopeDocuments; + } } diff --git a/src/main/java/com/docusign/exception/LauncherException.java b/src/main/java/com/docusign/exception/LauncherException.java deleted file mode 100644 index a7eae46d..00000000 --- a/src/main/java/com/docusign/exception/LauncherException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.docusign.exception; - -public class LauncherException extends Exception { - - private static final long serialVersionUID = -609744571799853289L; - - public LauncherException(String message) { - super(message); - } -} diff --git a/src/main/java/com/docusign/model/AccountRoleSettingsPatch.java b/src/main/java/com/docusign/model/AccountRoleSettingsPatch.java deleted file mode 100644 index 8696d76d..00000000 --- a/src/main/java/com/docusign/model/AccountRoleSettingsPatch.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.docusign.model; - -import com.docusign.esign.model.AccountRoleSettings; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.annotations.ApiModelProperty; - -public class AccountRoleSettingsPatch extends AccountRoleSettings { - - @JsonProperty("signingUiVersion") - private String signingUiVersion = null; - - public AccountRoleSettings signingUiVersion(String signingUiVersion) { - this.signingUiVersion = signingUiVersion; - return this; - } - - /** - * - * @return signingUiVersion - **/ - @ApiModelProperty(example = "null", value = "") - public String getSigningUiVersion() { - return signingUiVersion; - } - - -} diff --git a/src/main/java/com/docusign/model/AuthType.java b/src/main/java/com/docusign/model/AuthType.java deleted file mode 100644 index 03dec8ac..00000000 --- a/src/main/java/com/docusign/model/AuthType.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.docusign.model; - -public enum AuthType { - //Authorization Code Grant - AGC("Authorization Code Grant"), - //JWT Code Grant - JWT("JSON Web Token Grant"); - - final String value; - - AuthType(String value) { - this.value = value; - } -} diff --git a/src/main/java/com/docusign/model/AuthTypeItem.java b/src/main/java/com/docusign/model/AuthTypeItem.java deleted file mode 100644 index c27c38bf..00000000 --- a/src/main/java/com/docusign/model/AuthTypeItem.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.docusign.model; - -import java.util.EnumSet; -import java.util.List; -import java.util.stream.Collectors; - -public class AuthTypeItem { - - private String key; - private String value; - - public AuthTypeItem(String key, String value) { - this.key = key; - this.value = value; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public static AuthTypeItem convert(AuthType authType) { - return new AuthTypeItem(authType.name(), authType.value); - } - - public static AuthType convert(AuthTypeItem authType) { - return AuthType.valueOf(authType.key); - } - - public static List list() { - return EnumSet.allOf(AuthType.class).stream() - .map(AuthTypeItem::convert) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/com/docusign/model/DoneExample.java b/src/main/java/com/docusign/model/DoneExample.java deleted file mode 100644 index a6d41836..00000000 --- a/src/main/java/com/docusign/model/DoneExample.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.docusign.model; - -import org.json.JSONObject; -import org.springframework.ui.ModelMap; - -import com.docusign.common.DiffField; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.With; - - -/** - * Model which is used in 'done' pages. - */ -@Data -@AllArgsConstructor -@NoArgsConstructor -@With -public class DoneExample { - private static final String MODEL_DONE_ATTRIBUTE = "done"; - public static final int JSON_INDENT_FACTOR = 4; - - private String title; - private String name; - private String message; - private String json; - private StackTraceElement[] stackTrace; - private Iterable diff; - - - /** - * Creates default DoneExample object. This method initializes title and - * name members by the same value. - * @param title the title text - * @return created instance of this class - */ - public static DoneExample createDefault(String title) { - return new DoneExample() - .withTitle(title) - .withName(title); - } - - /** - * Creates a JSON text from the object and applies it to member - * @param object the object to convert to JSON string - * @return this object - */ - public DoneExample withJsonObject(Object object) { - if (object != null) { - JSONObject jsonObject = new JSONObject(object); - json = jsonObject.toString(JSON_INDENT_FACTOR); - } - return this; - } - - /** - * Adds this object to model map using name 'done' - * @param model the model object to add - */ - public void addToModel(ModelMap model) { - model.addAttribute(MODEL_DONE_ATTRIBUTE, this); - } -} diff --git a/src/main/java/com/docusign/model/EnvelopeDocumentInfo.java b/src/main/java/com/docusign/model/EnvelopeDocumentInfo.java deleted file mode 100644 index 1525b3ea..00000000 --- a/src/main/java/com/docusign/model/EnvelopeDocumentInfo.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.docusign.model; - -import java.io.Serializable; - -import lombok.Value; - - -@Value -public class EnvelopeDocumentInfo implements Serializable { - private static final long serialVersionUID = 8423772017910727550L; - - private String name; - private String type; - private String documentId; -} diff --git a/src/main/java/com/docusign/model/Locals.java b/src/main/java/com/docusign/model/Locals.java index 9c1200d0..964d015b 100644 --- a/src/main/java/com/docusign/model/Locals.java +++ b/src/main/java/com/docusign/model/Locals.java @@ -1,15 +1,42 @@ package com.docusign.model; -import lombok.AllArgsConstructor; -import lombok.Data; import com.docusign.DSConfiguration; - -@Data -@AllArgsConstructor public class Locals { private DSConfiguration dsConfig; private Session session; private User user; private String messages; + + public DSConfiguration getDsConfig() { + return dsConfig; + } + + public void setDsConfig(DSConfiguration dsConfig) { + this.dsConfig = dsConfig; + } + + public Session getSession() { + return session; + } + + public void setSession(Session session) { + this.session = session; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public void setMessages(String messages) { + this.messages = messages; + } + + public String getMessages() { + return messages; + } } diff --git a/src/main/java/com/docusign/model/OptionItem.java b/src/main/java/com/docusign/model/OptionItem.java index 09b3fec7..dc9e8dbe 100644 --- a/src/main/java/com/docusign/model/OptionItem.java +++ b/src/main/java/com/docusign/model/OptionItem.java @@ -1,12 +1,22 @@ package com.docusign.model; -import lombok.AllArgsConstructor; -import lombok.Data; - - -@Data -@AllArgsConstructor public class OptionItem { private String text; private String documentId; + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getDocumentId() { + return documentId; + } + + public void setDocumentId(String documentId) { + this.documentId = documentId; + } } diff --git a/src/main/java/com/docusign/model/Session.java b/src/main/java/com/docusign/model/Session.java index a68e730d..d89b83a0 100644 --- a/src/main/java/com/docusign/model/Session.java +++ b/src/main/java/com/docusign/model/Session.java @@ -1,31 +1,47 @@ package com.docusign.model; import lombok.Data; - -import java.io.Serializable; -import java.util.List; - import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Component; import org.springframework.web.context.WebApplicationContext; +import java.io.Serializable; @Component @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) @Data public class Session implements Serializable { - private static final long serialVersionUID = 2695379118371574037L; - - private String accountId; - private String accountName; - private String basePath; - private String envelopeId; - private String templateName; - private List envelopeDocuments; - private String permissionProfileId; - private String permissionProfileName; - private AuthType authTypeSelected = AuthType.AGC; - private boolean refreshToken = false; + + public String accountId; + public String accountName; + public String basePath; + + public Session() { + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public String getBasePath() { + return basePath; + } + + public void setBasePath(String basePath) { + this.basePath = basePath; + } } diff --git a/src/main/java/com/docusign/model/User.java b/src/main/java/com/docusign/model/User.java index 5fa6b558..b2fdb4e3 100644 --- a/src/main/java/com/docusign/model/User.java +++ b/src/main/java/com/docusign/model/User.java @@ -1,22 +1,35 @@ package com.docusign.model; import lombok.Data; - -import java.io.Serializable; - import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Component; import org.springframework.web.context.WebApplicationContext; +import java.io.Serializable; @Component @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) @Data public class User implements Serializable { - private static final long serialVersionUID = 8959083682339598499L; - private String name; - private String accessToken; + public String name; + public String accessToken; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } } diff --git a/src/main/java/com/docusign/security/OAuthProperties.java b/src/main/java/com/docusign/security/OAuthProperties.java deleted file mode 100644 index 0f8b0d77..00000000 --- a/src/main/java/com/docusign/security/OAuthProperties.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.docusign.security; - -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2SsoProperties; - -@Getter -@Setter -public class OAuthProperties extends OAuth2SsoProperties { - - private String redirectUrl; - - public String getLoginPath() { - return super.getLoginPath() + redirectUrl; - } -} diff --git a/src/main/java/com/docusign/security/jwt/JWTAuthorizationCodeResourceDetails.java b/src/main/java/com/docusign/security/jwt/JWTAuthorizationCodeResourceDetails.java deleted file mode 100644 index 541c0a53..00000000 --- a/src/main/java/com/docusign/security/jwt/JWTAuthorizationCodeResourceDetails.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.docusign.security.jwt; - -import com.docusign.esign.client.ApiClient; -import com.docusign.exception.LauncherException; -import java.io.IOException; -import lombok.Getter; -import lombok.Setter; -import org.springframework.core.io.FileSystemResource; -import org.springframework.security.oauth2.client.token.grant.redirect.AbstractRedirectResourceDetails; -import org.springframework.util.FileCopyUtils; - -@Getter -@Setter -public class JWTAuthorizationCodeResourceDetails extends AbstractRedirectResourceDetails { - - private String privateKeyPath; - private String impersonatedUserGuid; - private String baseUrl; - - private FileSystemResource rsaPrivateKey; - private final ApiClient apiClient = new ApiClient(); - - public byte[] getRsaBytes() throws LauncherException { - if (rsaPrivateKey == null) { - rsaPrivateKey = new FileSystemResource(privateKeyPath); - } - try { - return FileCopyUtils.copyToByteArray(rsaPrivateKey.getInputStream()); - } catch (IOException e) { - throw new LauncherException("Error reading rsa file. Check configuration"); - } - } - - public ApiClient getApiClient() { - return apiClient; - } -} diff --git a/src/main/java/com/docusign/security/jwt/JWTOAuth2RestTemplate.java b/src/main/java/com/docusign/security/jwt/JWTOAuth2RestTemplate.java deleted file mode 100644 index a0dd6793..00000000 --- a/src/main/java/com/docusign/security/jwt/JWTOAuth2RestTemplate.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.docusign.security.jwt; - -import com.docusign.esign.client.ApiClient; -import com.docusign.esign.client.ApiException; -import com.docusign.esign.client.auth.OAuth; -import com.docusign.esign.client.auth.OAuth.OAuthToken; -import com.docusign.exception.LauncherException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.oauth2.client.OAuth2ClientContext; -import org.springframework.security.oauth2.client.OAuth2RestTemplate; -import org.springframework.security.oauth2.client.http.AccessTokenRequiredException; -import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; -import org.springframework.security.oauth2.common.OAuth2AccessToken; - -public class JWTOAuth2RestTemplate extends OAuth2RestTemplate { - - private static final Logger logger = LoggerFactory.getLogger(JWTOAuth2RestTemplate.class); - private static final long TOKEN_EXPIRATION_IN_SECONDS = 3600; - - private final JWTAuthorizationCodeResourceDetails resource; - private final OAuth2ClientContext context; - private ApiClient apiClient; - - public JWTOAuth2RestTemplate(JWTAuthorizationCodeResourceDetails resource, OAuth2ClientContext context) { - super(resource, context); - this.resource = resource; - this.context = context; - apiClient = resource.getApiClient(); - } - - @Override - public OAuth2AccessToken getAccessToken() throws UserRedirectRequiredException { - OAuth2AccessToken accessToken = context.getAccessToken(); - - if (accessToken == null || accessToken.isExpired()) { - try { - accessToken = accessToken(); - context.setAccessToken(accessToken); - } catch (Exception e) { - context.setAccessToken(null); - throw e; - } - } - return accessToken; - } - - private OAuth2AccessToken accessToken() { - logger.info("Fetching an access token via JWT grant..."); - List scopes = new ArrayList<>(); - // Only signature scope is needed. Impersonation scope is implied. - scopes.add(OAuth.Scope_SIGNATURE); - byte[] privateKeyBytes; - try { - privateKeyBytes = resource.getRsaBytes(); - } catch (LauncherException e) { - throw new AccessTokenRequiredException(e.getMessage(), resource); - } - apiClient.setOAuthBasePath(resource.getBaseUrl()); - OAuthToken oAuthToken; - try { - oAuthToken = apiClient.requestJWTUserToken( - resource.getClientId(), - resource.getImpersonatedUserGuid(), - scopes, - privateKeyBytes, - TOKEN_EXPIRATION_IN_SECONDS); - } catch (IOException | ApiException e) { - throw new AccessTokenRequiredException(e.getMessage(), resource); - } - apiClient.setAccessToken(oAuthToken.getAccessToken(), oAuthToken.getExpiresIn()); - logger.info("Done. Continuing..."); - return convert(oAuthToken); - } - - private static OAuth2AccessToken convert(OAuthToken oAuthToken) { - DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(oAuthToken.getAccessToken()); - accessToken.setExpiration(new Date(System.currentTimeMillis() + (oAuthToken.getExpiresIn() * 1000))); - accessToken.setTokenType(oAuthToken.getTokenType()); - return accessToken; - } - -} diff --git a/src/main/java/com/docusign/security/jwt/JWTUserInfoTokenService.java b/src/main/java/com/docusign/security/jwt/JWTUserInfoTokenService.java deleted file mode 100644 index dc684c72..00000000 --- a/src/main/java/com/docusign/security/jwt/JWTUserInfoTokenService.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.docusign.security.jwt; - -import com.docusign.esign.client.ApiClient; -import com.docusign.esign.client.ApiException; -import com.docusign.esign.client.auth.OAuth.Account; -import com.docusign.esign.client.auth.OAuth.UserInfo; -import com.docusign.utils.AccountsConverter; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; - -public class JWTUserInfoTokenService implements ResourceServerTokenServices { - - private static final String AUTHORITIES = "ROLE_USER"; - - private final JWTAuthorizationCodeResourceDetails resource; - private ApiClient apiClient; - - public JWTUserInfoTokenService(JWTAuthorizationCodeResourceDetails resource) { - this.resource = resource; - apiClient = resource.getApiClient(); - } - - @Override - public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { - UserInfo userInfo; - try { - userInfo = apiClient.getUserInfo(apiClient.getAccessToken()); - } catch (ApiException e) { - throw new AuthenticationServiceException("Cannot authenticate", e); - } - return convert(userInfo, resource.getClientId()); - } - - @Override - public OAuth2AccessToken readAccessToken(String accessToken) { - throw new UnsupportedOperationException("Not supported: read access token"); - } - - private static OAuth2Authentication convert(UserInfo userInfo, String clientId) { - List accounts = userInfo.getAccounts(); - Object principal = userInfo.getName() == null ? "unknown" : userInfo.getName(); - List authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(AUTHORITIES); - OAuth2Request request = new OAuth2Request(null, clientId, null, true, null, - null, null, null, null); - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken( - principal, "N/A", authorities); - Map details = new LinkedHashMap<>(); - List> accountsAsMap = accounts.stream() - .map(AccountsConverter::convert) - .collect(Collectors.toList()); - details.put("accounts", accountsAsMap); - token.setDetails(details); - return new OAuth2Authentication(request, token); - } -} diff --git a/src/main/java/com/docusign/utils/AccountsConverter.java b/src/main/java/com/docusign/utils/AccountsConverter.java deleted file mode 100644 index b01c5a98..00000000 --- a/src/main/java/com/docusign/utils/AccountsConverter.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.docusign.utils; - -import com.docusign.esign.client.auth.OAuth; -import java.util.LinkedHashMap; -import java.util.Map; - -public final class AccountsConverter { - - public static Map convert(OAuth.Account account) { - Map res = new LinkedHashMap<>(); - res.put("account_id", account.getAccountId()); - res.put("is_default", account.getIsDefault()); - res.put("account_name", account.getAccountName()); - res.put("base_uri", account.getBaseUri()); - return res; - } - - public static OAuth.Account convert(Map account) { - OAuth.Account res = new OAuth.Account(); - res.setAccountId((String) account.get("account_id")); - res.setIsDefault(String.valueOf(account.get("is_default"))); - res.setAccountName((String) account.get("account_name")); - res.setBaseUri((String) account.get("base_uri")); - return res; - } -} diff --git a/src/main/resources/World_Wide_Corp_salary.docx b/src/main/resources/World_Wide_Corp_salary.docx deleted file mode 100644 index e47e2944..00000000 Binary files a/src/main/resources/World_Wide_Corp_salary.docx and /dev/null differ diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e8834828..3036f7df 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,51 +1,28 @@ -DS_SIGNER_EMAIL={SIGNER_EMAIL} -DS_SIGNER_NAME={SIGNER_NAME} -# Set if you want a specific DocuSign AccountId, If null, the user's default account will be used. -DS_TARGET_ACCOUNT_ID={IMPERSONATED_USER_GUID} -# The client id (Integrator Key) must include a Redirect URI of /login appended -# to the DS_APP_URL. Eg http://localhost:8080/login +# The client id is the same as the DocuSign Integration Key +security.oauth2.client.client-id={CLIENT_ID} +security.oauth2.client.client-secret={CLIENT_SECRET} + +DS_SIGNER_EMAIL={USER_EMAIL} +DS_SIGNER_NAME={USER_FULLNAME} +# The client id (Integration Key) must include a Redirect URI +# of /login appended to the DS_APP_URL. +# Eg http://localhost:8080/login DS_APP_URL=http://localhost:8080 +# Silent authentication allowed: +security.oauth2.client.user-authorization-uri=https://account-d.docusign.com/oauth/auth +# Uncomment this line (and comment one above) to not allow silent login: +#security.oauth2.client.user-authorization-uri=https://account-d.docusign.com/oauth/auth?prompt=login +security.oauth2.client.access-token-uri=https://account-d.docusign.com/oauth/token +security.oauth2.resource.user-info-uri=https://account-d.docusign.com/oauth/userinfo + # Optional settings for a payment gateway Gateway_Account_Id={DS_PAYMENT_GATEWAY_ID} -Gateway_Name={DS_PAYMENT_GATEWAY_NAME} -Gateway_Display_Name={DS_PAYMENT_GATEWAY_DISPLAY_NAME} +Gateway_Name=stripe +Gateway_Display_Name=Stripe -spring.jmx.enabled=false -# this option is needed for preventing annoying warning related to missing JAXB -# dependecies which had been excluded from the Java 11 -server.tomcat.additional-tld-skip-patterns=*jaxb*.jar -server.servlet.session.tracking-modes=cookie -server.servlet.session.persistent=false -spring.session.store-type=none +security.oauth2.client.scope=signature +security.basic.enabled=false server.error.whitelabel.enabled=false -server.error.include-stacktrace=always - -spring.mvc.view.prefix=/WEB-INF/templates/views/ -spring.mvc.view.suffix=.jsp - -# location of the source code of examples -com.docusign.github.example-uri=https://github.com/docusign/eg-03-java-auth-code-grant/blob/master/src/main/java/ -com.docusign.documentation-path= - -#JWT Grant client: -jwt.grant.client.client-id={JWT_API_ACCOUNT_ID} -jwt.grant.client.impersonated-user-guid={JWT_USER_IMPERSONATED_GUID} -jwt.grant.client.base-url=account-d.docusign.com -jwt.grant.client.scope=signature impersonation -jwt.grant.client.private-key-path=src/main/resources/private.key -#To distinguish authentication. Add to redirect urls -jwt.grant.sso.redirect-url=&type=jwt - -#Authorization Code Grant (acg) client: -authorization.code.grant.client.client-id={INTEGRATION_ID} -authorization.code.grant.client.client-secret={INTEGRATION_SECRET} -authorization.code.grant.client.user-authorization-uri=https://account-d.docusign.com/oauth/auth?prompt=login -authorization.code.grant.client.access-token-uri=https://account-d.docusign.com/oauth/token -authorization.code.grant.client.scope=signature -#To distinguish authentication. Add to redirect urls -authorization.code.grant.sso.redirect-url=&type=acg - -#User info -common.client.resource.user-info-uri=https://account-d.docusign.com/oauth/userinfo +server.error.include-stacktrace=always \ No newline at end of file diff --git a/src/main/resources/public/assets/css.css b/src/main/resources/public/assets/css.css index 40080201..eb59c905 100644 --- a/src/main/resources/public/assets/css.css +++ b/src/main/resources/public/assets/css.css @@ -67,16 +67,7 @@ pre.json-display { color: olive; } -table, th, td { - background-color: #f4f4fb; - padding-left: 7px; - padding-right: 7px; - caption-side: top; -} -th { - border-bottom: 1px solid black; -} /* From http://tobiasahlin.com/spinkit/ */ diff --git a/src/main/resources/templates/candy-bonbon.ftl b/src/main/resources/templates/candy-bonbon.ftl deleted file mode 100644 index d10ecaed..00000000 --- a/src/main/resources/templates/candy-bonbon.ftl +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - -

World Wide Corp

-

Order Processing Division

-

Ordered by ${args.signerName}

-

Email: ${args.signerEmail}

-

Copy to: ${args.ccName}, ${args.ccEmail}

-

- Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie. - Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée. - Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice. - Donut jujubes oat cake jelly-o. Dessert bear claw chocolate cake gummies lollipop sugar plum ice - cream gummies cheesecake.

- -

Agreed: **signature_1**

- -" \ No newline at end of file diff --git a/src/main/resources/templates/candy-bonbon2.ftl b/src/main/resources/templates/candy-bonbon2.ftl deleted file mode 100644 index 2758a607..00000000 --- a/src/main/resources/templates/candy-bonbon2.ftl +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - -

World Wide Corp

-

Order Processing Division

-

Ordered by ${args.signerName}

-

Email: ${args.signerEmail}

-

Copy to: ${args.ccName}, ${args.ccEmail}

-

Item: ${args.item}, quantity: ${args.quantity} at market price.

-

- Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie. - Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée. - Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice. - Donut jujubes oat cake jelly-o. Dessert bear claw chocolate cake gummies lollipop sugar plum ice - cream gummies cheesecake.

- -

Agreed: **signature_1**

- -" \ No newline at end of file diff --git a/src/main/resources/templates/order-form.ftl b/src/main/resources/templates/order-form.ftl deleted file mode 100644 index 6f12c57f..00000000 --- a/src/main/resources/templates/order-form.ftl +++ /dev/null @@ -1,64 +0,0 @@ - - - - - Order form - - -

World Wide Corp

-

Order Processing Division

-

Ordered by ${args.signerName}

-

Email: ${args.signerEmail}

-

Copy to: ${args.ccName}, ${args.ccEmail}

-

- Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie. Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée. Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice. Donut jujubes oat cake jelly-o. Dessert bear claw chocolate cake gummies lollipop sugar plum ice cream gummies cheesecake. -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ItemQuantityEachExtended
Harmonica - /l1q/ - $5 - $/l1e/ -
Xylophone - /l2q/ - $150 - $/l2e/ -
Total: - $/l3t/ -
- - - -

Agreed: /sn1/

- - diff --git a/src/main/webapp/WEB-INF/templates/views/error.jsp b/src/main/webapp/WEB-INF/templates/views/error.jsp index 49417655..68fa56c0 100644 --- a/src/main/webapp/WEB-INF/templates/views/error.jsp +++ b/src/main/webapp/WEB-INF/templates/views/error.jsp @@ -1,15 +1,25 @@ <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -

${done.title}. ${done.name}

-

The example "${done.name}" is failed. Error information:

-

Message:
${done.message}

-

Stack trace:

-
    - -
  • ${item.className}.${item.methodName}(${item.fileName}:${item.lineNumber})
  • -
    -
+

Problem: an error occurred

+

Error information:

+ + + +

${status}: ${message} +

+
+ +

+

${error}
+

+

Stack trace

+

+

${trace}
+

+
+
+

Continue

diff --git a/src/main/webapp/WEB-INF/templates/views/pages/ds_must_authenticate.jsp b/src/main/webapp/WEB-INF/templates/views/pages/ds_must_authenticate.jsp index aa857c4b..6583e365 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/ds_must_authenticate.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/ds_must_authenticate.jsp @@ -1,19 +1,13 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +

Please Authenticate with DocuSign

-

Please choose your authentication type

-
- -

- -

-
+

+ Authenticate with DocuSign +


You need to authenticate with DocuSign to continue your request.

+ + diff --git a/src/main/webapp/WEB-INF/templates/views/pages/example_done.jsp b/src/main/webapp/WEB-INF/templates/views/pages/example_done.jsp index 9f1609f7..ca8cc0c0 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/example_done.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/example_done.jsp @@ -1,14 +1,14 @@ <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -

${done.name}

-

${done.message}

+

${h1}

+

${message}

- + - +

-

${done.json}
+
${json}

diff --git a/src/main/webapp/WEB-INF/templates/views/pages/example_done_compare.jsp b/src/main/webapp/WEB-INF/templates/views/pages/example_done_compare.jsp deleted file mode 100644 index d5b5edd7..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/example_done_compare.jsp +++ /dev/null @@ -1,28 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - -

${done.name}

-

${done.message}

- - -
Changed fields
- - - - - - - - - - - - - - -
Property nameOld valueNew value
${item.name}${item.leftValue}${item.rightValue}
-
- -

Continue

- - diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg001.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg001.jsp index 1a689fbe..e4b7f241 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg001.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg001.jsp @@ -1,9 +1,9 @@ <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -

1. Embedded Signing Ceremony

-

This example sends an envelope, and then uses an embedded signing ceremony for the first signer.

-

Embedded signing provides a smoother user experience for the signer: the DocuSign signing ceremony is initiated from +

1. Embedded Signing

+

This example sends an envelope, and then uses Embedded signing for the first signer.

+

Embedded signing provides a smoother user experience for the signer: DocuSign signing is initiated from your website.

Documentation about this example.

@@ -34,7 +34,7 @@ value="${locals.dsConfig.signerName}" required> - + diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg002.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg002.jsp index 6958c6f3..89a09d5c 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg002.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg002.jsp @@ -47,7 +47,7 @@ required> - + diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg003.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg003.jsp index e8dfd42d..9ceb15bd 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg003.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg003.jsp @@ -19,7 +19,7 @@
- +
diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg004.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg004.jsp index 051abe61..48807fbb 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg004.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg004.jsp @@ -31,7 +31,7 @@
- +
@@ -39,7 +39,7 @@ Thank you.

- +
diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg005.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg005.jsp index f0cff895..07852cba 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg005.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg005.jsp @@ -23,7 +23,7 @@
- +
@@ -31,7 +31,7 @@ Thank you.

- +
diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg006.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg006.jsp index a0341084..c2bf9a21 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg006.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg006.jsp @@ -27,7 +27,7 @@
- +
@@ -35,7 +35,7 @@ Thank you.

- +
diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg007.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg007.jsp index 89eed27e..84bec4ab 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg007.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg007.jsp @@ -34,7 +34,7 @@ Thank you.

- +
@@ -43,7 +43,7 @@ Thank you.

- +
@@ -63,7 +63,7 @@ - + diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg008.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg008.jsp index fd249303..141e82ca 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg008.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg008.jsp @@ -28,7 +28,7 @@
- +
diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg009.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg009.jsp index 227dfc8b..b633f5ec 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg009.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg009.jsp @@ -63,7 +63,7 @@ required> - + @@ -71,7 +71,7 @@ Thank you.

- +
diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg010.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg010.jsp index 008fd01c..4ef4f034 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg010.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg010.jsp @@ -47,7 +47,7 @@ required> - + diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg011.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg011.jsp index c67e4034..45c7c5b6 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg011.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg011.jsp @@ -1,7 +1,7 @@ <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -

11. Embedded Sending

+

11. Use embedded sending

An envelope will be created in draft mode. Your browser will then be redirected to the DocuSign web tool where the envelope can be (optionally) updated and then sent.

The envelope includes a pdf, Word, and HTML document. Anchor text @@ -60,7 +60,7 @@ required> - + diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg012.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg012.jsp index 97134c97..cc3c8905 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg012.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg012.jsp @@ -41,7 +41,7 @@ - + diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg013.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg013.jsp index 3739cb36..56bf1b82 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg013.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg013.jsp @@ -1,7 +1,7 @@ <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> -

13. Embedded Signing Ceremony from a template with an added document

+

13. Use embedded signing from a template with an added document

This example sends an envelope based on a template.

In addition to the template's document(s), the example adds an @@ -15,7 +15,7 @@

This example then enables you to sign the envelope using embedded signing.

Embedded signing provides a smoother user experience for a signer who is already logged into your web application since the DocuSign - signing ceremony is initiated from your website.

+ signing is initiated from your website.

@@ -99,7 +99,7 @@ - + @@ -107,7 +107,7 @@ Thank you.

- +
diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg014.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg014.jsp index 37e49588..f2739b35 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg014.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg014.jsp @@ -19,7 +19,7 @@

API method used: - Envelopes::create. + Envelopes::create.

@@ -44,7 +44,7 @@

+ aria-describedby="emailHelp" placeholder="pat@example.com" required The email for the cc recipient must be different from the signer's email.
@@ -57,7 +57,7 @@ -

Problem: please set the DS_PAYMENT_GATEWAY_ID value in the configuration file.

+

Problem: please set the DS_PAYMENT_GATEWAY_ID value in the configuration file.

diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg015.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg015.jsp deleted file mode 100644 index 7f451ab3..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg015.jsp +++ /dev/null @@ -1,51 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - -

15. Get the tab data from an envelope.

- -

Get the tab (field) values from an envelope for all of the envelope's recipients.

- -

- This method is used to read the updated tab values from - the envelope. The method can be used after the envelope is complete or while it is - still in progress. -

- - - - -

Documentation about this example.

-
- -

- API method used: - EnvelopeFormData::get. -

- -

- View source file ${sourceFile} on GitHub. -

- - - -

- The last envelope you created with this example launcher will be queried. - Recommendation: use example 9, then this example, since example 9 includes many tabs of different types. -

- -
- - -
-
- -

Problem: please first create an envelope using example 9.
- Thank you.

- -
- -
-
-
- - diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg016.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg016.jsp deleted file mode 100644 index aaa47d98..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg016.jsp +++ /dev/null @@ -1,56 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - -

16. Set tab values for an envelope.

- -

- This example creates an example with both read-only tabs (fields) and tabs that can - be updated by the recipient. -

-

The example also sets custom metadata in the envelope via the envelope custom fields feature.

- - - - -

Documentation about this example.

-
- -

- API method used: - EnvelopeFormData::get. -

- -

- View source file ${sourceFile} on GitHub. -

- - -
-
- - - We'll never share your email with anyone else. -
-
- - -
-
- - - The email for the cc recipient must be different from the signer's email. -
-
- - -
- - -
- - - diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg017.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg017.jsp deleted file mode 100644 index 50aeba3f..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg017.jsp +++ /dev/null @@ -1,64 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - - -

17. Set template tab values

-

- This example sets the value of a template's tabs. It includes setting - radio button and checkbox tabs. -

-

The example also sets custom metadata in the envelope via the envelope custom fields feature.

- - -

Documentation about this example.

-
- -

API method used: - Envelopes::create. -

-

- View source file ${sourceFile} on GitHub. -

- - - -

The template you created via example 8 will be used.

-
-
- - - We'll never share your email with anyone else. -
-
- - -
-
- - - The email for the cc recipient must be different from the signer's email. -
-
- - -
- - -
- -
- -

Problem: please first create the template using example 8.
- Thank you.

- -
- -
-
-
- - diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg018.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg018.jsp deleted file mode 100644 index 0a90c072..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg018.jsp +++ /dev/null @@ -1,52 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - -

18. Get the custom field data for an envelope

-

- Get the data values associated with the envelope itself. The custom data fields enable you to - add additional meta-data to the envelope. The custom data fields can be set by the Sender - via the DocuSign web tool, or can be set programmatically. The data can be included in the - envelope's certificate of completion. -

- -

- This method is used to read the custom field values from - an envelope. -

- - -

Documentation about this example.

-
- - -

- API method used: - EnvelopeCustomFields::list. -

-

- View source file ${sourceFile} on GitHub. -

- - - -

- The last envelope you created with this example launcher will be queried. - Recommendation: use example 9, then this example, since example 9 includes many tabs of different types. -

-
- - -
-
- -

Problem: please first create an envelope using example 9.
- Thank you.

- -
- -
-
-
- - - diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg019.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg019.jsp index e0955e80..43a6dc6d 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg019.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg019.jsp @@ -45,7 +45,7 @@ value="${locals.dsConfig.signerName}" required>
- + diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg020.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg020.jsp index c0daa432..6ba63d61 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg020.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg020.jsp @@ -17,6 +17,11 @@
+
+ + +
+ - +
diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg021.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg021.jsp index 85007e4d..5b61baff 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg021.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg021.jsp @@ -17,6 +17,13 @@
+ +
+ + +
+
- +
diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg022.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg022.jsp index 667e08c9..fb73b2b2 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg022.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg022.jsp @@ -8,7 +8,7 @@

API method used: - Envelopes::create. + Envelopes::create.

diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg023.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg023.jsp deleted file mode 100644 index cc628383..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg023.jsp +++ /dev/null @@ -1,45 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - -

23. Requiring Id Verification for a Recipient

- -

- The envelope includes a pdf document. Anchor text - (AutoPlace) - is used to position the signing fields in the documents. -

-

- This is a general example of creating and sending an envelope (a signing request) using ID Verification for Recipient Authentication. -

- - -

Documentation about this example.

-
- -

- API method used: - Envelopes::create. -

- -

- View source file ${sourceFile} on GitHub. -

- -
-
- - - We'll never share your email with anyone else. -
-
- - -
- - -
- - diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg024.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg024.jsp deleted file mode 100644 index dbd066d9..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg024.jsp +++ /dev/null @@ -1,34 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - -

24. ${title}.

- -

-Permission profiles are collections of account settings that determine the behavior and actions -available to the user groups to which they're applied. This code example demonstrates how to -create a permission profile with the eSignature REST API. -

- - -

Documentation about this example.

-
- -

API method used: - AccountPermissionProfiles::create. -

- -

- View source file ${sourceFile} on GitHub. -

- - -
-
- - -
- - -
- - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg025.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg025.jsp deleted file mode 100644 index 1383dad5..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg025.jsp +++ /dev/null @@ -1,48 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - -

25. ${title}.

- -

- Permission profiles are collections of account settings that determine the behavior and actions - available to the user groups to which they're applied. This code example demonstrates how to - the permission profile for the group with the eSignature REST API. -

- - -

Documentation about this example.

-
- -

API method used: - Groups::update. -

- -

- View source file ${sourceFile} on GitHub. -

- - -
-
- - -
- -
- - -
- - - -
- - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg026.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg026.jsp deleted file mode 100644 index e71c0566..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg026.jsp +++ /dev/null @@ -1,66 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - -

26. ${title}.

- -

-This code example demonstrates how to edit individual permission settings on a permissions profile -with the eSignature REST API. -

- - -

Documentation about this example.

-
- -

API method used: - AccountPermissionProfiles::update. -

- -

- View source file ${sourceFile} on GitHub. -

- - -
-
- - -
- -<%--
--%> -<%-- --%> -<%-- --%> -<%--
--%> -<%-- --%> -<%--

There is no account role settings is defined

--%> -<%--
--%> -<%-- --%> -<%-- --%> -<%-- --%> -<%-- --%> -<%-- --%> -<%-- --%> -<%-- --%> -<%-- --%> -<%--
${property.name}${property.leftValue}
--%> - - -
- - - - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg027.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg027.jsp deleted file mode 100644 index c427e350..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg027.jsp +++ /dev/null @@ -1,39 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - -

27. ${title}.

- -

- Permission profiles are collections of account settings that determine the behavior and actions - available to the user groups to which they're applied. This code example demonstrates how to - delete a permission profile with the eSignature REST API. -

- - -

Documentation about this example.

-
- -

API method used: - AccountPermissionProfiles::update. -

- -

- View source file ${sourceFile} on GitHub. -

- - -
-
- - -
- - - -
- - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg028.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg028.jsp deleted file mode 100644 index 131aed33..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg028.jsp +++ /dev/null @@ -1,50 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - -

28. Creating a brand.

- -

- The brand includes a Brand Name Configure Brands. -

-

- DocuSign enables you to customize the eSignature sending and signing experience with your own - branding elements-logo, colors, and more-stored as a collection of settings called a brand in - your DocuSign account. This code example demonstrates how to create a brand with the - eSignature REST API that you subsequently apply to a DocuSign envelope. -

- - -

Documentation about this example.

-
- -

API method used: - AccountBrands::create. -

- -

- View source file ${sourceFile} on GitHub. -

- - -
-

- New brand -

-
- - -
-
- - -
- - -
- - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg029.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg029.jsp deleted file mode 100644 index 00924592..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg029.jsp +++ /dev/null @@ -1,74 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> - - -

29. Applying a brand to an envelope

-

- The envelope includes a pdf document. Anchor text (AutoPlace) - is used to position the signing fields in the documents. -

-

- DocuSign enables you to customize the eSignature sending and signing experience with your own - branding elements-logo, colors, and more-stored as a collection of settings called a brand in - your DocuSign account. This code example demonstrates how to create a brand with the eSignature - REST API that you subsequently apply to a DocuSign envelope. -

- -

- Documentation about - this example. -

-
- - -

- API method used: Envelopes::create. -

-

- View source file ${sourceFile} - on GitHub. -

- - - -
-
- We'll never - share your email with anyone else. -
-
- -
-
- - - - There are no brands - created in your account. Create ones at the 'Create brand' page. - - -
- - -
-
- -

Problem: please first create the brand(s) using example 24.
Thank you.

-
- -
-
-
- - diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg030.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg030.jsp deleted file mode 100644 index 8f816b41..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg030.jsp +++ /dev/null @@ -1,102 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> - - -

30. Applying a brand to a template

-

This code example demonstrates how to apply a brand to an template.

-

- The envelope is defined by the template. The signer and cc recipient - name and email are used to fill in the template's roles. -

- -

- This example demonstrates a common pattern for DocuSign integrations: envelopes will be sent - programmatically, based on a template. If the template definition needs to be updated, the - DocuSign web tool can be used to easily update the template, thus avoiding the need to make - software changes. -

- -

- Documentation about this example. -

-
- - -

- API method used: Envelopes::create. -

-

- View source file ${sourceFile} on GitHub. -

- - - -

The template you created via example 8 and brands created via - example 24 will be used.

-
-
- We'll never - share your email with anyone else. -
-
- -
-
- - The email - and/or name for the cc recipient must be different from the signer. -
-
- -
-
- -
-
- - -
- - -
-
- - -

Problem: please create the template using example 8.

-
- -

Problem: please create the brand using example 24.

-
- -
- -
-
- -
- -
-
-
-
- - diff --git a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg031.jsp b/src/main/webapp/WEB-INF/templates/views/pages/examples/eg031.jsp deleted file mode 100644 index 654ef489..00000000 --- a/src/main/webapp/WEB-INF/templates/views/pages/examples/eg031.jsp +++ /dev/null @@ -1,87 +0,0 @@ -<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> - - -

31. ${title}.

- -

- This code example demonstrates how to send envelopes in bulk to multiple recipients with the - eSignature API. To accomplish the task, you will first create a bulk send recipients list, - and then create an envelope. From there, you will combine the envelope and bulk list to - initiate bulk send. Method BulkSend::createBulkSendList creates a bulk send list that you can - use to send an envelope to up to 1,000 recipients at once. -

- - -

Documentation about this example.

-
- -

API methods used: - EnvelopeRecipients::create, - Envelopes::create, - BulkEnvelopes::get, - EnvelopeCustomFields::create, - BulkSend::createBulkSendList - EnvelopeRecipients::create. -

- -

- View source file ${sourceFile} on GitHub. -

- - -
-
- - - We'll never share your email with anyone else. -
-
- - -
-
- - - The email and/or name for the cc recipient must be different - from the signer. - -
-
- - -
- -
- - - We'll never share your email with anyone else. -
-
- - -
-
- - - The email and/or name for the cc recipient must be different - from the signer. - -
-
- - -
- - - -
- - \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/templates/views/pages/index.jsp b/src/main/webapp/WEB-INF/templates/views/pages/index.jsp index a99c03c3..ed61e247 100644 --- a/src/main/webapp/WEB-INF/templates/views/pages/index.jsp +++ b/src/main/webapp/WEB-INF/templates/views/pages/index.jsp @@ -5,16 +5,16 @@

Welcome!

-

Welcome to the DocuSign Java Example Launcher - using OAuth2.

+

Welcome to the DocuSign Node.JS examples with + OAuth Authorization Code Grant.

Welcome

-

This launcher both demonstrates use of common OAuth2 grant flows and multiple usage examples from the DocuSign eSignature REST API.

+

This launcher both demonstrates use of the OAuth Authorization Code Grant flow and includes multiple usage examples for the DocuSign eSignature REST API.

-

Documentation on using OAuth Authorization Code Grant from a Java application.

+

Documentation on using OAuth Authorization Code Grant from a Node Express application.

Basic Examples

@@ -24,24 +24,24 @@ With embedded signing, the DocuSign signing ceremony is initiated from your website.

API methods used: - Envelopes::create and - EnvelopeViews::createRecipient. + Envelopes::create and + EnvelopeViews::createRecipient.

2. Send an envelope with a remote (email) signer and cc recipient

The envelope includes a pdf, Word, and HTML document. Anchor text - (AutoPlace) + (AutoPlace) is used to position the signing fields in the documents.

API method used: - Envelopes::create. + Envelopes::create.

3. List envelopes in the user's account

List the envelopes created in the last 30 days.

API method used: - Envelopes::listStatusChanges. + Envelopes::listStatusChanges.

4. Get an envelope's basic information and status

@@ -50,43 +50,43 @@ envelope, its documents, recipients, etc.

API method used: - Envelopes::get. + Envelopes::get.

5. List an envelope's recipients and their status

List the envelope's recipients, including their current status.

API method used: - EnvelopeRecipients::list. + EnvelopeRecipients::list.

6. List an envelope's documents

-

List the envelope's documents. A Certificate of Completion document +

List the envelope's documents. A Certificate of Completion document is also associated with every envelope.

API method used: - EnvelopeDocuments::list. + EnvelopeDocuments::list.

7. Download a document from an envelope

An envelope's documents can be downloaded one by one or as a complete set.

API method used: - EnvelopeDocuments::get. + EnvelopeDocuments::get.

8. Create a template

-

Create a template with two roles, signer and cc.

+

Create a template with two roles, signer and cc.

API methods used: - Templates::list, - Templates::create. + Templates::list, + Templates::create.

9. Send an envelope using a template

The envelope is defined by the template. - The signer and cc recipient name and email are used to fill in the template's roles.

+ The signer and cc recipient name and email are used to fill in the template's roles.

API method used: - Envelopes::create. + Envelopes::create.

10. Send an envelope using binary document transfer

@@ -96,7 +96,7 @@ Binary transfer is not yet supported by the SDK.

API method used: - Envelopes::create. + Envelopes::create.

11. Embedded sending

@@ -105,144 +105,73 @@ to the envelope before it is sent.

API methods used: - Envelopes::create, - EnvelopeViews::createSender. + Envelopes::create, + EnvelopeViews::createSender.

12. Embedded DocuSign web tool

Redirect the user to the DocuSign web tool.

API method used: - EnvelopeViews::createConsole. + EnvelopeViews::createConsole.

13. Embedded Signing Ceremony from a template with an added document

This example sends an envelope based on a template.

In addition to the template's document(s), the example adds an additional document to the envelope by using the - Composite Templates + Composite Templates feature.

API methods used: - Envelopes::create and - EnvelopeViews::createRecipient. + Envelopes::create and + EnvelopeViews::createRecipient.

14. Send an envelope with an order form and payment field

Anchor text - (AutoPlace) + (AutoPlace) is used to position the fields in the documents.

API method used: - Envelopes::create. + Envelopes::create.

- - -

Tabs Examples

-

15. Get the tab data from an envelope

-

This example retrieves the tab (field) values from an envelope.

-

- API method used: - EnvelopeFormData::get. +

15. Get the envelope tab data

+

Coming Soon

-

16. Set tab values for an envelope

-

This example sets the tab (field) values for an envelope including tabs that can and cannot be changed by the signer.

-

- API method used: - Envelopes::create and - EnvelopeViews::createRecipient. +

16. Set envelope tab values

+

Coming Soon

-

17. Set template tab values

-

This example sets the tab (field) values for a template being used by an envelope.

-

- API method used: - Envelopes::create and - EnvelopeViews::createRecipient. +

17. Set template tab values

+

Coming Soon

-

18. List envelope custom metadata field values

-

This example lists the envelope's custom metadata field values.

-

- API method used: - EnvelopeCustomFields::list. +

18. Get the envelope custom field data (metadata)

+

Coming Soon

19. Send an envelope with Access Code Authentication

-

This example sends and envelope that requires an access-code for the purpose of multi-factor authentication.

API method used: - Envelopes::create. + Envelopes::create.

20. Send an envelope with SMS Authentication

-

This example sends and envelope that requires entering in a six digit code from an text message for the purpose of multi-factor authentication.

API method used: - Envelopes::create. + Envelopes::create.

21. Send an envelope with Phone Authentication

-

This example sends and envelope that requires entering in a voice-based response code for the purpose of multi-factor authentication.

-

API method used: - Envelopes::create. -

-

22. Send an envelope with Knowledge-Based Authentication

-

This example sends and envelope that requires passing a Public records check to validate identity for the purpose of multi-factor authentication.

API method used: - Envelopes::create. -

- -

23. Send an envelope with ID Verification Authentication

-

Submit an envelope that requires verification of a government issued identity.

-

- API method used: Envelopes::create.

- -

24. Creating a Permission Profile

-

- Coming Soon -

- -

25. Setting a permission profile

-

This example allows you to set a permission profile on an existing user group.

-

API method used: - Groups::update. -

- -

26. Modify a Permission Profile

-

- Coming Soon -

- -

27. Deleting a permission profile

-

This example lists all available permissions profiles and allows you to delete any without associated users. Please note that you cannot remove "Everyone" nor "Administrator" permission profiles.

-

API method used: - AccountPermissionProfiles::create. -

-

28. Create a brand

-

This example will create an account brand that can be used to apply customization to your envelopes such as your own logo, colors, and text elements.

-

API method used: - AccountBrands::create. -

- -

29. Applying a brand to an envelope

-

This example will show you how to create an envelope using any of the created brands on your account.

-

API method used: - Envelopes::create. -

- -

30. Applying a brand to a template

-

This example will show you how to create an envelope using a brand

+

22. Send an envelope with Knowledge-Based Authentication

API method used: - Envelopes::create. + Envelopes::create.

-

31. Bulk sending envelopes to multiple recipients

-

- Coming Soon -

diff --git a/src/main/webapp/WEB-INF/templates/views/partials/foot.jsp b/src/main/webapp/WEB-INF/templates/views/partials/foot.jsp index bbe98422..ef38fba3 100644 --- a/src/main/webapp/WEB-INF/templates/views/partials/foot.jsp +++ b/src/main/webapp/WEB-INF/templates/views/partials/foot.jsp @@ -11,6 +11,7 @@ + diff --git a/src/main/webapp/WEB-INF/templates/views/partials/head.jsp b/src/main/webapp/WEB-INF/templates/views/partials/head.jsp index 349c038b..ceed0e00 100644 --- a/src/main/webapp/WEB-INF/templates/views/partials/head.jsp +++ b/src/main/webapp/WEB-INF/templates/views/partials/head.jsp @@ -40,7 +40,7 @@
  • - Login (current)
  • diff --git a/src/test/java/com/docusign/HelloControllerTest.java b/src/test/java/com/docusign/HelloControllerTest.java new file mode 100644 index 00000000..d08781d5 --- /dev/null +++ b/src/test/java/com/docusign/HelloControllerTest.java @@ -0,0 +1,47 @@ +package com.docusign; + +import com.jayway.restassured.RestAssured; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.IntegrationTest; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +import static com.jayway.restassured.RestAssured.given; +import static com.jayway.restassured.RestAssured.when; +import static org.hamcrest.CoreMatchers.is; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = App.class) +@WebAppConfiguration +@IntegrationTest({"server.port:0", + "spring.datasource.url:jdbc:h2:mem:eg-03-java-auth-code-grant;DB_CLOSE_ON_EXIT=FALSE"}) +public class HelloControllerTest { + @Value("${local.server.port}") + int port; + + @Before + public void setUp() throws Exception { + RestAssured.port = port; + } + + @Test + public void testHello() throws Exception { + when().get("/").then() + .body(is("Hello World!")); + } + + @Test + public void testCalc() throws Exception { + given().param("left", 100) + .param("right", 200) + .get("/calc") + .then() + .body("left", is(100)) + .body("right", is(200)) + .body("answer", is(300)); + } +} \ No newline at end of file diff --git a/src/test/resources/.gitkeep b/src/test/resources/.gitkeep new file mode 100644 index 00000000..e69de29b