diff --git a/.github/workflows/rubyonrails.yml b/.github/workflows/rubyonrails.yml new file mode 100644 index 0000000..6dcb601 --- /dev/null +++ b/.github/workflows/rubyonrails.yml @@ -0,0 +1,47 @@ +# This workflow uses actions that are not certified by GitHub. They are +# provided by a third-party and are governed by separate terms of service, +# privacy policy, and support documentation. +# +# This workflow will install a prebuilt Ruby version, install dependencies, and +# run tests and linters. +name: "Ruby on Rails CI" +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: false + ruby-version: 3.1.2 + + - name: Update rubygems + run: gem update --system + + - name: Install dependencies + run: bundle install + + - name: Run linter + run: bundle exec rubocop --parallel + + - name: Run tests + run: | + gem install docusign_esign + gem install docusign_click + ruby test/run_tests.rb + env: + CLIENT_ID: ${{ secrets.CLIENT_ID }} + USER_ID: ${{ secrets.USER_ID }} + SIGNER_EMAIL: ${{ secrets.SIGNER_EMAIL }} + SIGNER_NAME: ${{ secrets.SIGNER_NAME }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..acc84a5 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,93 @@ +AllCops: + Exclude: + - '**/db/**/*' + - '**/config/**/*' + - '**/test/**/*' + - '**/bin/**/*' + - '**/*test*' + - '**/vendor/**/*' + +Layout/EndOfLine: + Enabled: false + +Layout/LeadingCommentSpace: + Enabled: false + +Layout/LineLength: + Enabled: false + +Style/ClassAndModuleChildren: + Enabled: false + +Style/Documentation: + Enabled: false + +Lint/EnsureReturn: + Enabled: false + +Style/HashSyntax: + Enabled: false + +Style/StringLiterals: + EnforcedStyle: single_quotes + +Lint/RescueException: + Enabled: false + +Lint/UnreachableCode: + Enabled: false + +Metrics/AbcSize: + Enabled: false + +Metrics/BlockLength: + Exclude: + - 'config/routes.rb' + +Metrics/ClassLength: + Enabled: false + +Metrics/CyclomaticComplexity: + Enabled: false + +Metrics/MethodLength: + Enabled: false + +Metrics/PerceivedComplexity: + Enabled: false + +Naming/AccessorMethodName: + Enabled: false + +Naming/MethodParameterName: + Enabled: false + +Naming/VariableName: + Enabled: false + +Naming/VariableNumber: + Enabled: false + +Style/FrozenStringLiteralComment: + Enabled: false + +Style/GlobalStdStream: + Enabled: false + +Style/GlobalVars: + Enabled: false + +Style/MixinUsage: + Exclude: + - 'bin/setup' + - 'bin/update' + +Style/RedundantFetchBlock: + Exclude: + - 'config/puma.rb' + - 'quick_acg/config/puma.rb' + +Style/SpecialGlobalVars: + Exclude: + - 'bin/bundle' + - 'quick_acg/bin/bundle' diff --git a/.ruby-version b/.ruby-version index 5588ae8..ef538c2 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.1 \ No newline at end of file +3.1.2 diff --git a/Gemfile b/Gemfile index e44be3a..db883af 100644 --- a/Gemfile +++ b/Gemfile @@ -1,20 +1,20 @@ - # frozen_string_literal: true +# frozen_string_literal: true source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } -ruby '2.7.2' +ruby '~>3.1.2' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '~> 6.0.3.5' +gem 'rails', '~> 7.2.2.1' # Use sqlite3 as the database for Active Record -gem 'sqlite3', '~> 1.4.2' +gem 'sqlite3', '~> 2.1.1' # Use Puma as the app server -gem 'puma', '~> 4.3.3' +gem 'puma', '~> 6.6.0' # Use SCSS for stylesheets gem 'sass-rails', '~> 6.0.0' # Use Uglifier as compressor for JavaScript assets -gem 'uglifier', '~> 4.2.0' +gem 'uglifier', '~> 4.2.1' # See https://github.com/rails/execjs#readme for more supported runtimes # gem 'mini_racer', platforms: :ruby @@ -23,7 +23,7 @@ gem 'coffee-rails', '~> 5.0.0' # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks gem 'turbolinks', '~> 5.2.1' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder -gem 'jbuilder', '~> 2.10.0' +gem 'jbuilder', '~> 2.13.0' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 4.0' # Use ActiveModel has_secure_password @@ -36,38 +36,48 @@ gem 'jbuilder', '~> 2.10.0' # gem 'capistrano-rails', group: :development # Reduces boot times through caching; required in config/boot.rb -gem 'bootsnap', '>= 1.1.0', '< 1.4.2', require: false +if RUBY_PLATFORM =~ /mswin/ + gem 'bootsnap', '>= 1.1.0', '< 1.4.2', require: false +else + gem 'bootsnap', '~> 1.7.3', require: false +end group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console - gem 'byebug', '~> 11.1.1', platforms: %i[mri mingw x64_mingw] + gem 'byebug', '~> 12.0.0', platforms: %i[mri mingw x64_mingw] end group :development do # Access an interactive console on exception pages or by calling 'console' anywhere in the code. - gem 'listen', '~> 3.2.1' - gem 'web-console', '~> 4.0.1' + gem 'listen', '~> 3.9.0' + gem 'web-console', '~> 4.2.1' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring - gem 'pry-nav', '~> 0.3.0' - gem 'pry-rails', '~> 0.3.9' - gem 'spring', '~> 2.1.0' - gem 'spring-watcher-listen', '~> 2.0.1' + gem 'pry-nav', '~> 1.0.0' + gem 'pry-rails', '~> 0.3.11' + gem 'rubocop', '~> 1.75.8', require: false + gem 'spring', '~> 4.3.0' + gem 'spring-watcher-listen', '~> 2.1.0' end group :test do # Adds support for Capybara system testing and selenium driver - gem 'capybara', '~> 3.31.0' - gem 'selenium-webdriver', '~> 3.142.7' + gem 'capybara', '~> 3.40.0' + gem 'selenium-webdriver', '~> 4.25.0' # Easy installation and use of chromedriver to run system tests with Chrome gem 'chromedriver-helper', '~> 2.1.1' + gem 'test-unit' end -gem 'docusign_esign', '~> 3.8.0.rc1' -gem 'docusign_rooms', '~> 1.1.0.rc1' -gem 'docusign_click', '~> 1.0.0' -gem 'omniauth-oauth2', '~> 1.7.1' +gem 'docusign_admin', '~> 2.0.0' +gem 'docusign_click', '~> 1.4.0' +gem 'docusign_esign', '~> 5.1.0' +gem 'docusign_monitor', '~> 1.2.0' +gem 'docusign_rooms', '~> 1.3.0' +gem 'docusign_webforms', '~> 2.1.0' +gem 'omniauth-oauth2', '~> 1.8.0' gem 'omniauth-rails_csrf_protection' # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', '~> 1.2019.3', platforms: %i[mingw mswin x64_mingw jruby] -gem 'wdm', '>= 0.1.0', platforms: %i[mingw mswin x64_mingw] +gem 'matrix', '~> 0.4.2' +gem 'tzinfo-data', '~> 1.2025.2' +gem 'wdm', '>= 0.2.0', platforms: %i[mingw mswin x64_mingw] diff --git a/Gemfile.lock b/Gemfile.lock index a9a9ab2..a7129b6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,79 +1,99 @@ GEM remote: https://rubygems.org/ specs: - actioncable (6.0.3.5) - actionpack (= 6.0.3.5) + actioncable (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.0.3.5) - actionpack (= 6.0.3.5) - activejob (= 6.0.3.5) - activerecord (= 6.0.3.5) - activestorage (= 6.0.3.5) - activesupport (= 6.0.3.5) - mail (>= 2.7.1) - actionmailer (6.0.3.5) - actionpack (= 6.0.3.5) - actionview (= 6.0.3.5) - activejob (= 6.0.3.5) - mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 2.0) - actionpack (6.0.3.5) - actionview (= 6.0.3.5) - activesupport (= 6.0.3.5) - rack (~> 2.0, >= 2.0.8) + zeitwerk (~> 2.6) + actionmailbox (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) + actionmailer (7.2.2.1) + actionpack (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (7.2.2.1) + actionview (= 7.2.2.1) + activesupport (= 7.2.2.1) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4, < 3.2) + rack-session (>= 1.0.1) rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.0.3.5) - actionpack (= 6.0.3.5) - activerecord (= 6.0.3.5) - activestorage (= 6.0.3.5) - activesupport (= 6.0.3.5) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (7.2.2.1) + actionpack (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (6.0.3.5) - activesupport (= 6.0.3.5) + actionview (7.2.2.1) + activesupport (= 7.2.2.1) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (6.0.3.5) - activesupport (= 6.0.3.5) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.2.2.1) + activesupport (= 7.2.2.1) globalid (>= 0.3.6) - activemodel (6.0.3.5) - activesupport (= 6.0.3.5) - activerecord (6.0.3.5) - activemodel (= 6.0.3.5) - activesupport (= 6.0.3.5) - activestorage (6.0.3.5) - actionpack (= 6.0.3.5) - activejob (= 6.0.3.5) - activerecord (= 6.0.3.5) - marcel (~> 0.3.1) - activesupport (6.0.3.5) - concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - zeitwerk (~> 2.2, >= 2.2.2) - addressable (2.7.0) - public_suffix (>= 2.0.2, < 5.0) + activemodel (7.2.2.1) + activesupport (= 7.2.2.1) + activerecord (7.2.2.1) + activemodel (= 7.2.2.1) + activesupport (= 7.2.2.1) + timeout (>= 0.4.0) + activestorage (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activesupport (= 7.2.2.1) + marcel (~> 1.0) + activesupport (7.2.2.1) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) archive-zip (0.12.0) io-like (~> 0.3.0) + ast (2.4.3) + base64 (0.2.0) + benchmark (0.4.0) + bigdecimal (3.1.9) bindex (0.8.1) - bootsnap (1.4.1) + bootsnap (1.7.7) msgpack (~> 1.0) - builder (3.2.4) - byebug (11.1.3) - capybara (3.31.0) + builder (3.3.0) + byebug (12.0.0) + capybara (3.40.0) addressable + matrix mini_mime (>= 0.1.3) - nokogiri (~> 1.8) + nokogiri (~> 1.11) rack (>= 1.6.0) rack-test (>= 0.6.3) - regexp_parser (~> 1.5) + regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - childprocess (3.0.0) chromedriver-helper (2.1.1) archive-zip (~> 0.10) nokogiri (~> 1.8) @@ -85,220 +105,327 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.1.8) + concurrent-ruby (1.3.5) + connection_pool (2.5.0) crass (1.0.6) - docusign_click (1.0.0) + date (3.4.1) + docusign_admin (2.0.0) + addressable (~> 2.7, >= 2.7.0) + json (~> 2.1, >= 2.1.0) + jwt (~> 2.2, >= 2.2.1) + typhoeus (~> 1.0, >= 1.0.1) + docusign_click (1.4.0) + addressable (~> 2.7, >= 2.7.0) + json (~> 2.1, >= 2.1.0) + jwt (~> 2.2, >= 2.2.1) + typhoeus (~> 1.0, >= 1.0.1) + docusign_esign (5.1.0) addressable (~> 2.7, >= 2.7.0) json (~> 2.1, >= 2.1.0) jwt (~> 2.2, >= 2.2.1) typhoeus (~> 1.0, >= 1.0.1) - docusign_esign (3.8.0.rc1) + docusign_monitor (1.2.0) + addressable (~> 2.7, >= 2.7.0) + json (~> 2.1, >= 2.1.0) + jwt (~> 2.2, >= 2.2.1) + typhoeus (~> 1.0, >= 1.0.1) + docusign_rooms (1.3.0) + addressable (~> 2.7, >= 2.7.0) json (~> 2.1, >= 2.1.0) jwt (~> 2.2, >= 2.2.1) typhoeus (~> 1.0, >= 1.0.1) - docusign_rooms (1.1.0.rc1) + docusign_webforms (2.1.0) + addressable (~> 2.7, >= 2.7.0) json (~> 2.1, >= 2.1.0) jwt (~> 2.2, >= 2.2.1) typhoeus (~> 1.0, >= 1.0.1) - erubi (1.10.0) - ethon (0.12.0) - ffi (>= 1.3.0) - execjs (2.7.0) - faraday (1.3.0) - faraday-net_http (~> 1.0) - multipart-post (>= 1.2, < 3) - ruby2_keywords - faraday-net_http (1.0.1) - ffi (1.15.0) - ffi (1.15.0-x64-mingw32) - globalid (0.4.2) - activesupport (>= 4.2.0) - hashie (4.1.0) - i18n (1.8.9) + drb (2.2.1) + erubi (1.13.1) + ethon (0.16.0) + ffi (>= 1.15.0) + execjs (2.10.0) + faraday (2.12.2) + faraday-net_http (>= 2.0, < 3.5) + json + logger + faraday-net_http (3.4.0) + net-http (>= 0.5.0) + ffi (1.16.3) + ffi (1.16.3-x64-mingw-ucrt) + globalid (1.2.1) + activesupport (>= 6.1) + hashie (5.0.0) + i18n (1.14.7) concurrent-ruby (~> 1.0) + io-console (0.8.0) io-like (0.3.1) - jbuilder (2.10.2) + irb (1.15.1) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jbuilder (2.13.0) + actionview (>= 5.0.0) activesupport (>= 5.0.0) - json (2.5.1) - jwt (2.2.2) - listen (3.2.1) + json (2.9.1) + jwt (2.10.1) + base64 + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - loofah (2.9.0) + logger (1.6.5) + loofah (2.24.0) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) + nokogiri (>= 1.12.0) + mail (2.8.1) mini_mime (>= 0.1.1) - marcel (0.3.3) - mimemagic (~> 0.3.2) - method_source (0.9.2) - mimemagic (0.3.5) - mini_mime (1.0.2) - mini_portile2 (2.5.0) - minitest (5.14.4) - msgpack (1.4.2) - multi_json (1.15.0) - multi_xml (0.6.0) - multipart-post (2.1.1) - nio4r (2.5.7) - nokogiri (1.11.2) - mini_portile2 (~> 2.5.0) + net-imap + net-pop + net-smtp + marcel (1.0.4) + matrix (0.4.2) + method_source (1.1.0) + mini_mime (1.1.5) + minitest (5.25.4) + msgpack (1.7.5) + multi_xml (0.7.1) + bigdecimal (~> 3.1) + net-http (0.6.0) + uri + net-imap (0.5.5) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.0) + net-protocol + nio4r (2.7.4) + nokogiri (1.18.2-x64-mingw-ucrt) racc (~> 1.4) - nokogiri (1.11.2-x64-mingw32) + nokogiri (1.18.2-x86_64-linux-gnu) racc (~> 1.4) - oauth2 (1.4.7) - faraday (>= 0.8, < 2.0) + oauth2 (2.0.9) + faraday (>= 0.17.3, < 3.0) jwt (>= 1.0, < 3.0) - multi_json (~> 1.3) multi_xml (~> 0.5) - rack (>= 1.2, < 3) - omniauth (2.0.3) + rack (>= 1.2, < 4) + snaky_hash (~> 2.0) + version_gem (~> 1.1) + omniauth (2.1.2) hashie (>= 3.4.6) - rack (>= 1.6.2, < 3) + rack (>= 2.2.3) rack-protection - omniauth-oauth2 (1.7.1) - oauth2 (~> 1.4) - omniauth (>= 1.9, < 3) - omniauth-rails_csrf_protection (1.0.0) + omniauth-oauth2 (1.8.0) + oauth2 (>= 1.4, < 3) + omniauth (~> 2.0) + omniauth-rails_csrf_protection (1.0.2) actionpack (>= 4.2) omniauth (~> 2.0) - pry (0.12.2) - coderay (~> 1.1.0) - method_source (~> 0.9.0) - pry-nav (0.3.0) - pry (>= 0.9.10, < 0.13.0) - pry-rails (0.3.9) - pry (>= 0.10.4) - public_suffix (4.0.6) - puma (4.3.7) + parallel (1.27.0) + parser (3.3.8.0) + ast (~> 2.4.1) + racc + power_assert (2.0.5) + pp (0.6.2) + prettyprint + prettyprint (0.2.0) + prism (1.4.0) + pry (0.14.2) + coderay (~> 1.1) + method_source (~> 1.0) + pry-nav (1.0.0) + pry (>= 0.9.10, < 0.15) + pry-rails (0.3.11) + pry (>= 0.13.0) + psych (5.2.3) + date + stringio + public_suffix (6.0.1) + puma (6.6.0) nio4r (~> 2.0) - racc (1.5.2) - rack (2.2.3) - rack-protection (2.1.0) - rack - rack-test (1.1.0) - rack (>= 1.0, < 3) - rails (6.0.3.5) - actioncable (= 6.0.3.5) - actionmailbox (= 6.0.3.5) - actionmailer (= 6.0.3.5) - actionpack (= 6.0.3.5) - actiontext (= 6.0.3.5) - actionview (= 6.0.3.5) - activejob (= 6.0.3.5) - activemodel (= 6.0.3.5) - activerecord (= 6.0.3.5) - activestorage (= 6.0.3.5) - activesupport (= 6.0.3.5) - bundler (>= 1.3.0) - railties (= 6.0.3.5) - sprockets-rails (>= 2.0.0) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + racc (1.8.1) + rack (3.1.9) + rack-protection (4.1.1) + base64 (>= 0.1.0) + logger (>= 1.6.0) + rack (>= 3.0.0, < 4) + rack-session (2.1.0) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rack-test (2.2.0) + rack (>= 1.3) + rackup (2.2.1) + rack (>= 3) + rails (7.2.2.1) + actioncable (= 7.2.2.1) + actionmailbox (= 7.2.2.1) + actionmailer (= 7.2.2.1) + actionpack (= 7.2.2.1) + actiontext (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activemodel (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + bundler (>= 1.15.0) + railties (= 7.2.2.1) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.3.0) - loofah (~> 2.3) - railties (6.0.3.5) - actionpack (= 6.0.3.5) - activesupport (= 6.0.3.5) - method_source - rake (>= 0.8.7) - thor (>= 0.20.3, < 2.0) - rake (13.0.3) - rb-fsevent (0.10.4) - rb-inotify (0.10.1) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + rainbow (3.1.1) + rake (13.2.1) + rb-fsevent (0.11.2) + rb-inotify (0.11.1) ffi (~> 1.0) - regexp_parser (1.8.2) - ruby2_keywords (0.0.4) - rubyzip (2.3.0) + rdoc (6.11.0) + psych (>= 4.0.0) + regexp_parser (2.10.0) + reline (0.6.0) + io-console (~> 0.5) + rexml (3.4.0) + rubocop (1.75.8) + json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.44.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.44.1) + parser (>= 3.3.7.2) + prism (~> 1.4) + ruby-progressbar (1.13.0) + rubyzip (2.4.1) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) sassc (2.4.0) ffi (~> 1.9) - sassc (2.4.0-x64-mingw32) - ffi (~> 1.9) sassc-rails (2.1.2) railties (>= 4.0.0) sassc (>= 2.0) sprockets (> 3.0) sprockets-rails tilt - selenium-webdriver (3.142.7) - childprocess (>= 0.5, < 4.0) - rubyzip (>= 1.2.2) - spring (2.1.1) - spring-watcher-listen (2.0.1) + securerandom (0.4.1) + selenium-webdriver (4.25.0) + base64 (~> 0.2) + logger (~> 1.4) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2, < 3.0) + websocket (~> 1.0) + snaky_hash (2.0.1) + hashie + version_gem (~> 1.1, >= 1.1.1) + spring (4.3.0) + spring-watcher-listen (2.1.0) listen (>= 2.7, < 4.0) - spring (>= 1.2, < 3.0) - sprockets (4.0.2) + spring (>= 4) + sprockets (4.2.1) concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.2.2) - actionpack (>= 4.0) - activesupport (>= 4.0) + rack (>= 2.2.4, < 4) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) - sqlite3 (1.4.2) - thor (1.1.0) - thread_safe (0.3.6) - tilt (2.0.10) + sqlite3 (2.1.1-x64-mingw-ucrt) + sqlite3 (2.1.1-x86_64-linux-gnu) + stringio (3.1.2) + test-unit (3.6.7) + power_assert + thor (1.3.2) + tilt (2.6.0) + timeout (0.4.3) turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) - typhoeus (1.4.0) + typhoeus (1.4.1) ethon (>= 0.9.0) - tzinfo (1.2.9) - thread_safe (~> 0.1) - tzinfo-data (1.2019.3) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + tzinfo-data (1.2025.2) tzinfo (>= 1.0.0) - uglifier (4.2.0) + uglifier (4.2.1) execjs (>= 0.3.0, < 3) - wdm (0.1.1) - web-console (4.0.4) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + uri (1.0.2) + useragent (0.16.11) + version_gem (1.1.4) + wdm (0.2.0) + web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - websocket-driver (0.7.3) + websocket (1.2.11) + websocket-driver (0.7.7) + base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.4.2) + zeitwerk (2.6.18) PLATFORMS - ruby - x64-mingw32 + x64-mingw-ucrt + x86_64-linux DEPENDENCIES - bootsnap (>= 1.1.0, < 1.4.2) - byebug (~> 11.1.1) - capybara (~> 3.31.0) + bootsnap (~> 1.7.3) + byebug (~> 12.0.0) + capybara (~> 3.40.0) chromedriver-helper (~> 2.1.1) coffee-rails (~> 5.0.0) - docusign_click (~> 1.0.0) - docusign_esign (~> 3.8.0.rc1) - docusign_rooms (~> 1.1.0.rc1) - jbuilder (~> 2.10.0) - listen (~> 3.2.1) - omniauth-oauth2 (~> 1.7.1) + docusign_admin (~> 2.0.0) + docusign_click (~> 1.4.0) + docusign_esign (~> 5.1.0) + docusign_monitor (~> 1.2.0) + docusign_rooms (~> 1.3.0) + docusign_webforms (~> 2.1.0) + jbuilder (~> 2.13.0) + listen (~> 3.9.0) + matrix (~> 0.4.2) + omniauth-oauth2 (~> 1.8.0) omniauth-rails_csrf_protection - pry-nav (~> 0.3.0) - pry-rails (~> 0.3.9) - puma (~> 4.3.3) - rails (~> 6.0.3.5) + pry-nav (~> 1.0.0) + pry-rails (~> 0.3.11) + puma (~> 6.6.0) + rails (~> 7.2.2.1) + rubocop (~> 1.75.8) sass-rails (~> 6.0.0) - selenium-webdriver (~> 3.142.7) - spring (~> 2.1.0) - spring-watcher-listen (~> 2.0.1) - sqlite3 (~> 1.4.2) + selenium-webdriver (~> 4.25.0) + spring (~> 4.3.0) + spring-watcher-listen (~> 2.1.0) + sqlite3 (~> 2.1.1) + test-unit turbolinks (~> 5.2.1) - tzinfo-data (~> 1.2019.3) - uglifier (~> 4.2.0) - wdm (>= 0.1.0) - web-console (~> 4.0.1) + tzinfo-data (~> 1.2025.2) + uglifier (~> 4.2.1) + wdm (>= 0.2.0) + web-console (~> 4.2.1) RUBY VERSION - ruby 2.7.2p137 + ruby 3.1.2p20 BUNDLED WITH - 2.1.4 + 2.5.22 diff --git a/LICENSE b/LICENSE index 390a206..652162e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2018 DocuSign, Inc. (https://www.docusign.com) +Copyright (c) 2024 Docusign, Inc. (https://www.docusign.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/PAYMENTS_INSTALLATION.md b/PAYMENTS_INSTALLATION.md index bfd85dc..311a410 100644 --- a/PAYMENTS_INSTALLATION.md +++ b/PAYMENTS_INSTALLATION.md @@ -1,24 +1,21 @@ -# Configuring a DocuSign payments gateway +# Configure a payment gateway -DocuSign offers built-in connections to multiple payment -gateways. The payments example uses a demo account via the Stripe -gateway service. +Docusign offers built-in connections to multiple payment gateways. The payments code example uses a developer account via the Stripe gateway service. -## Creating the payments gateway account -1. Login to demo.docusign.net and go to the Admin Tool. -1. On the Integrations / Payments screen, click Stripe. -1. For development, you can skip the Stripe account application - by using the `Skip this account form` link: +## Create a Stripe payment gateway + +1. Select the Stripe button on the [**Payments**](https://admindemo.docusign.com/authenticate?goTo=payments) page in your developer account. + +1. For development, you can skip the Stripe account application by using the **Skip this account form** link at the top of the page.
![Skipping the Stripe account form](docs/stripe_skip_account_form_link.png) -1. Next, the Admin Tool will show that an enabled Stripe - payment gateway account has been associated with your - DocuSign Developer account. -1. Configure the example launcher with the gateway account id shown in the Admin tool. + + An enabled Stripe payment gateway is now associated with your Docusign developer account and is shown under **Payment Gateway**. + +1. Save the **Gateway Account ID** GUID to the code example launcher configuration file. + ## Additional documentation -See the -[Managing Payment Gateways](https://support.docusign.com/en/guides/managing-payment-gateways) -documentation. - +* [Managing Payment Gateways](https://support.docusign.com/en/guides/managing-payment-gateways) +* [How to send a request for payment](https://developers.docusign.com/docs/esign-rest-api/how-to/request-a-payment) diff --git a/README.md b/README.md index 7930541..ca83b49 100644 --- a/README.md +++ b/README.md @@ -1,297 +1,180 @@ # Ruby Launcher Code Examples -### Github repo: https://github.com/docusign/code-examples-ruby +> +>### PLEASE! Share your feedback in a [two-question survey](https://docs.google.com/forms/d/e/1FAIpQLScPa74hwhJwi7XWDDj4-XZVOQTF9jJWgbIFEpulXokCqYWT4A/viewform?usp=pp_url&entry.680551577=Ruby). +> +> +### GitHub repo: [code-examples-ruby](./README.md) -To switch between API code examples, modify the `examples_API` setting at the end of the configuration file. Set only one API type to `true` and set the remaining to `false`. +If you downloaded this project using the [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) tool, it may be configured in one of three ways: -If none of the API types are set to `true`, the DocuSign eSignature REST API code examples will be shown. If multiple API types are set to `true`, only the first will be shown. +* **[JWT Grant remote signing example](#jwt-grant-remote-signing-example)**–demonstrates how to implement JSON Web Token authentication. It includes a single remote signing workflow. +* **[Authorization Code Grant embedded signing example](#authorization-code-grant-embedded-signing-example)**–demonstrates how to implement Authorization Code Grant authentication. It includes a single embedded signing workflow. +* **[Multiple code examples, Authorization Code Grant and JWT Grant](#installation-steps)**–includes the full range of examples and authentication types. -Before you can make any API calls using [JWT Grant](https://developers.docusign.com/platform/auth/jwt/), you must get your user’s consent for your app to impersonate them. To do this, the `impersonation` scope is added when requesting a JSON Web Token. +***Installation and running instructions vary depending on the configuration. Follow the link that matches your project type to get started.*** + +This GitHub repo includes code examples for the [Web Forms API](https://developers.docusign.com/docs/web-forms-api/), [Docusign Admin API](https://developers.docusign.com/docs/admin-api/), [Click API](https://developers.docusign.com/docs/click-api/), [eSignature REST API](https://developers.docusign.com/docs/esign-rest-api/), [Monitor API](https://developers.docusign.com/docs/monitor-api/), and [Rooms API](https://developers.docusign.com/docs/rooms-api/). ## Introduction -This repo is a Ruby on Rails application. +This repo is a Ruby on Rails application that supports the following authentication workflows: + +* Authentication with Docusign via [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode). +When the token expires, the user is asked to re-authenticate. The refresh token is not used. +* Authentication with Docusign via [JSON Web Token (JWT) Grant](https://developers.docusign.com/platform/auth/jwt/). +When the token expires, it updates automatically. ## eSignature API -For more information about the scopes used for obtaining authorization to use the eSignature API, see the [Required Scopes section](https://developers.docusign.com/docs/esign-rest-api/esign101/auth). - -1. **Use embedded signing.** - [Source.](app/services/e_sign/eg001_embedded_signing_service.rb) - This example sends an envelope, and then uses embedded signing for the first signer. - With embedded signing, the DocuSign signing is initiated from your website. -1. **Request a signature by email (Remote Signing).** - [Source.](app/services/e_sign/eg002_signing_via_email_service.rb) - 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.](app/services/e_sign/eg003_list_envelopes_service.rb) - The envelopes' current status is included. -1. **Get an envelope's basic information.** - [Source.](app/services/e_sign/eg004_envelope_info_service.rb) - The example lists the basic information about an envelope, including its overall status. -1. **List an envelope's recipients** - [Source.](./app/controllers/e_sign/eg005_envelope_recipients_controller.rb) - Includes current recipient status. -1. **List an envelope's documents.** - [Source.](app/services/e_sign/eg006_envelope_docs_service.rb) -1. **Download an envelope's documents.** - [Source.](app/services/e_sign/eg007_envelope_get_doc_service.rb) - The example can download individual - documents, the documents concatenated together, or a zip file of the documents. -1. **Programmatically create a template.** - [Source.](app/services/e_sign/eg008_create_template_service.rb) -1. **Request a signature by email using a template.** - The example creates an envelope using a template and sets the initial values for some of its tabs (fields). - [Source.](app/services/e_sign/eg009_use_template_service.rb) -1. **Send an envelope and upload its documents with multpart binary transfer.** - [Source.](app/services/e_sign/eg010_send_binary_docs_service.rb) - Binary transfer is 33% more efficient than using Base64 encoding. -1. **Use embedded sending.** - [Source.](app/services/e_sign/eg011_embedded_sending_service.rb) - Embeds the DocuSign web tool (NDSE) in your web app to finalize or update - the envelope and documents before they are sent. -1. **Embedded DocuSign web tool (NDSE).** - [Source.](app/services/e_sign/eg012_embedded_console_service.rb) -1. **Use embedded signing from a template with an added document.** - [Source.](app/services/e_sign/eg013_add_doc_to_template_service.rb) - 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.](app/services/e_sign/eg014_collect_payment_service.rb) - -1. **Get the envelope tab data.** - Retrieve the tab (field) values for all of the envelope's recipients. - [Source.](app/services/e_sign/eg015_get_envelope_tab_data_service.rb) -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.](app/services/e_sign/eg016_set_envelope_tab_data_service.rb) -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.](app/services/e_sign/eg017_set_template_tab_values_service.rb) -1. **Get the envelope custom field data (metadata).** - The example retrieves the custom metadata (custom data fields) stored with the envelope. - [Source.](app/services/e_sign/eg018_get_envelope_custom_field_data_service.rb) -1. **Requiring an Access Code for a Recipient** - [Source.](app/services/e_sign/eg019_access_code_authentication_service.rb) - This example sends an envelope that requires an access-code for the purpose of multi-factor authentication. -1. **Requiring SMS authentication for a recipient** - [Source.](app/services/e_sign/eg020_sms_authentication_service.rb) - This example sends an 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.](app/services/e_sign/eg021_phone_authentication_service.rb) - This example sends an 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.](app/services/e_sign/eg022_kba_authentication_service.rb) - This example sends an 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.](app/services/e_sign/eg023_idv_authentication_service.rb) - This example sends an envelope that requires the recipient to upload a government issued id. - -1. **Creating a permission profile** - [Source.](app/services/e_sign/eg024_permission_create_service.rb) - 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.](app/services/e_sign/eg025_permissions_set_user_group_service.rb) - 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.](app/services/e_sign/eg026_permissions_change_single_setting_service.rb) - 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.](app/services/e_sign/eg027_permissions_delete_service.rb) - 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. - You cannot delete the Everyone nor the Administrator profile types as those are defaults. - -1. **Creating a brand** - [Source.](app/services/e_sign/eg028_brands_creating_service.rb) - 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.](app/services/e_sign/eg029_brands_apply_to_envelope_service.rb) - 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. - -1. **Applying a brand to a template** - [Source.](app/services/e_sign/eg030_brands_apply_to_template_service.rb) - 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.](app/services/e_sign/eg031_bulk_sending_envelopes_service.rb) - This example creates and sends a bulk envelope by generating a bulk recipient list and initiating a bulk send. - -1. **Pausing a signature workflow** - [Source.](app/services/e_sign/eg032_pauses_signature_workflow_service.rb) - This example demonstrates how to create an envelope where the workflow is paused before the envelope is sent to a second recipient. - -1. **Unpausing a signature workflow** - [Source.](app/services/e_sign/eg033_unpauses_signature_workflow_service.rb) - This example demonstrates how to resume an envelope workflow that has been paused. - -1. **Using conditional recipients** - [Source.](app/services/e_sign/eg034_use_conditional_recipients_service.rb) - This example demonstrates how to create an envelope where the workflow is routed to different recipients based on the value of a transaction. - -1. **Request a signature by SMS delivery** - [Source.](app/services/e_sign/eg035_sms_delivery_service.rb) - This code example demonstrates how to send a signature request via an SMS message using the [Envelopes: create](https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/create) method. +For more information about the scopes used for obtaining authorization to use the eSignature API, see [Required scopes](https://developers.docusign.com/docs/esign-rest-api/esign101/auth#required-scopes). + +For a list of code examples that use the eSignature API, see the [How-to guides overview](https://developers.docusign.com/docs/esign-rest-api/how-to/) on the Docusign Developer Center. ## Rooms API -**Note:** To use the Rooms API, you must also [create a Rooms developer account](https://developers.docusign.com/docs/rooms-api/rooms101/create-account). -For more information about the scopes used for obtaining authorization to use the Rooms API, see the [Required Scopes section](https://developers.docusign.com/docs/rooms-api/rooms101/auth/). - -1. **Create a room with data.** - [Source.](./app/services/room_api/eg001_create_room_with_data_service.rb) - This example creates a new room in your DocuSign Rooms account to be used for a transaction. -1. **Create a room from a template.** - [Source.](./app/services/room_api/eg002_create_room_with_template_service.rb) - This example creates a new room using a template. -1. **Export data from a room.** - [Source.](./app/services/room_api/eg003_export_data_from_room_service.rb) - This example exports all the available data from a specific room in your DocuSign Rooms account. -1. **Add forms to a room.** - [Source.](./app/services/room_api/eg004_add_forms_to_room_service.rb) - This example adds a standard real estate related form to a specific room in your DocuSign Rooms account. -1. **Search for rooms with filters.** - [Source.](./app/services/room_api/eg005_get_rooms_with_filters_service.rb) - This example searches for rooms in your DocuSign Rooms account using a specific filter. -1. **Get an external form fillable session.** - [Source.](./app/services/room_api/eg006_create_an_external_form_fill_session_service.rb) - This example creates an external form that can be filled using DocuSign for a specific room in your DocuSign Rooms account. -1. **Creating a form group.** - [Source.](./app/services/room_api/eg007_create_form_group_service.rb) - This example creates a new form group with the name given in the name property of the request body. -1. **Grant office access to a form group.** - [Source.](./app/services/room_api/eg008_grant_office_access_to_form_group_service.rb) - This example assigns an office to a form group for your DocuSign Rooms. -1. **Assign a form to a form group.** - [Source.](./app/services/room_api/eg009_assign_form_to_form_group_service.rb) - This example assigns a form to a form group for your DocuSign Rooms. +**Note:** To use the Rooms API, you must also [create your Rooms developer account](https://developers.docusign.com/docs/rooms-api/rooms101/create-account). Examples 4 and 6 require that you have the Docusign Forms feature enabled in your Rooms for Real Estate account. +For more information about the scopes used for obtaining authorization to use the Rooms API, see [Required scopes](https://developers.docusign.com/docs/rooms-api/rooms101/auth/). + +For a list of code examples that use the Rooms API, see the [How-to guides overview](https://developers.docusign.com/docs/rooms-api/how-to/) on the Docusign Developer Center. ## Click API +For more information about the scopes used for obtaining authorization to use the Click API, see [Required scopes](https://developers.docusign.com/docs/click-api/click101/auth/#required-scopes) + +For a list of code examples that use the Click API, see the [How-to guides overview](https://developers.docusign.com/docs/click-api/how-to/) on the Docusign Developer Center. + + +## Monitor API + +**Note:** To use the Monitor API, you must also [enable Docusign Monitor for your organization](https://developers.docusign.com/docs/monitor-api/how-to/enable-monitor/). -For more information about the scopes used for obtaining authorization to use the Clickwrap API, see the [Required Scopes section](https://developers.docusign.com/docs/click-api/click101/auth). - -1. **Creating a clickwrap.** -[Source.](./app/services/clickwrap/eg001_create_clickwrap_service.rb) -This example demonstrates how to use the Click API to create a clickwrap that you can embed in your website or app. -1. **Activate a clickwrap.** -[Source.](./app/services/clickwrap/eg002_activate_clickwrap_service.rb) -This example demonstrates how to use the Click API to activate a new clickwrap that you have already created. -1. **Creating a new clickwrap version.** -[Source.](./app/services/clickwrap/eg003_create_new_clickwrap_version_service.rb) -This example demonstrates how to use the Click API to create a new version of a clickwrap. -1. **Getting a list of clickwraps.** -[Source.](./app/services/clickwrap/eg004_list_clickwraps_service.rb) -This example demonstrates how to use the Click API to get a list of clickwraps associated with a specific DocuSign user. -1. **Getting clickwrap responses.** -[Source.](./app/services/clickwrap/eg005_clickwrap_responses_service.rb) -This example demonstrates how to use the Click API to get a list of clickwraps associated with a specific DocuSign user. +For information about the scopes used for obtaining authorization to use the Monitor API, see the [scopes section](https://developers.docusign.com/docs/monitor-api/monitor101/auth/). +For a list of code examples that use the Monitor API, see the [How-to guides overview](https://developers.docusign.com/docs/monitor-api/how-to/) on the Docusign Developer Center. + + +## Admin API + +**Note:** To use the Admin API, you must [create an organization](https://support.docusign.com/en/guides/org-admin-guide-create-org) in your Docusign developer account. Also, to run the Docusign CLM code example, [CLM must be enabled for your organization](https://support.docusign.com/en/articles/DocuSign-and-SpringCM). + +For information about the scopes used for obtaining authorization to use the Admin API, see the [scopes section](https://developers.docusign.com/docs/admin-api/admin101/auth/). + +For a list of code examples that use the Admin API, see the [How-to guides overview](https://developers.docusign.com/docs/admin-api/how-to/) on the Docusign Developer Center. ## Installation ### Prerequisites -**Note:** If you downloaded this code using [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) from the DocuSign Developer Center, skip items 1 and 2 below as they were automatically performed for you. +**Note:** If you downloaded this code using [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) from the Docusign Developer Center, skip items 1 and 2 as they were automatically performed for you. + +1. A free [Docusign developer account](https://www.docusign.com/developers/sandbox); create one if you don't already have one. +1. A Docusign app and integration key that is configured for authentication to use either [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode/) or [JWT Grant](https://developers.docusign.com/platform/auth/jwt/). -1. A free [DocuSign developer account](https://go.docusign.com/o/sandbox/); create one if you don't already have one. -1. A DocuSign app and integration key that is configured for authentication to use either [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode/) or [JWT Grant](https://developers.docusign.com/platform/auth/jwt/). + This [video](https://www.youtube.com/watch?v=eiRI4fe5HgM) demonstrates how to obtain an integration key. - This [video](https://www.youtube.com/watch?v=eiRI4fe5HgM) demonstrates how to obtain an integration key. + To use [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode/), you will need an integration key and a secret key. See [Installation steps](#installation-steps) for details. - To use [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode/), you will need an integration key and a secret key. See [Installation steps](#installation-steps) for details. + To use [JWT Grant](https://developers.docusign.com/platform/auth/jwt/), you will need an integration key, an RSA key pair, and the User ID GUID of the impersonated user. See [Installation steps for JWT Grant authentication](#installation-steps-for-jwt-grant-authentication) for details. - To use [JWT Grant](https://developers.docusign.com/platform/auth/jwt/), you will need an integration key, an RSA key pair, and the API Username GUID of the impersonated user. See [Installation steps for JWT Grant authentication](#installation-steps-for-jwt-grant-authentication) for details. + For both authentication flows: - For both authentication flows: - - If you use this launcher on your own workstation, the integration key must include a redirect URI of + If you use this launcher on your own workstation, the integration key must include a redirect URI of http://localhost:3000/auth/docusign/callback - If you host this launcher on a remote web server, set your redirect URI as - + If you host this launcher on a remote web server, set your redirect URI as + {base_url}/auth/docusign/callback - - where {base_url} is the URL for the web app. - + + where {base_url} is the URL for the web app. + 1. [Ruby version 2.7.2](https://www.ruby-lang.org/en/downloads/) or later 1. Update the Gemfile to use later versions of Ruby. 1. Windows x64 only: - 1. Ensure that your Ruby folder is appended with -x64, e.g. **Ruby27-x64** - 2. Install Curl for Ruby: [Download libcurl.dll](https://curl.haxx.se/windows/) - Save **libcurl-x64.dll** as **libcurl.dll** + 1. Ensure that your Ruby folder is appended with **-x64**, e.g. **Ruby27-x64** + 2. Install Curl for Ruby: [Download libcurl.dll](https://curl.haxx.se/windows/) + Save **libcurl-x64.dll** as **libcurl.dll** Place **libcurl.dll** in your Ruby folder, e.g. **C:\Ruby27-x64\bin** ### Installation steps -**Note:** If you downloaded this code using [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) from the DocuSign Developer Center, skip step 4 as it was automatically performed for you. +**Note:** If you downloaded this code using [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) from the Docusign Developer Center, skip step 4 as it was automatically performed for you. -1. Extract the Quickstart ZIP file or download or clone the code-examples-ruby repository. -1. In your command-line environment, switch to the folder: - `cd ` or `cd code-examples-ruby` -1. Install the dependencies: `bundle install` +1. Extract the Quickstart ZIP file, or download or clone the code-examples-ruby repository. +1. In your command-line environment, switch to the folder: + `cd ` or `cd code-examples-ruby` +1. To install dependencies, run: `bundler install` 1. To configure the launcher for [Authorization Code Grant](https://developers.docusign.com/platform/auth/authcode/) authentication, create a copy of the file config/appsettings.example.yml and save the copy as config/appsettings.yml. 1. Add your integration key. On the [Apps and Keys](https://admindemo.docusign.com/authenticate?goTo=apiIntegratorKey) page, under **Apps and Integration Keys**, choose the app to use, then select **Actions** > **Edit**. Under **General Info**, copy the **Integration Key** GUID and save it in appsettings.yml as your `integration_key`. 1. Generate a secret key, if you don’t already have one. Under **Authentication**, select **+ ADD SECRET KEY**. Copy the secret key and save it in appsettings.yml as your `integration_secret`. - 1. Add the launcher’s redirect URI. Under **Additional settings**, select **+ ADD URI**, and set a redirect URI of http://localhost:3000/auth/docusign/callback. Select **SAVE**. - 1. Set a name and email address for the signer. In appsettings.yml, save an email address as `signer_email` and a name as `signer_name`. + 1. Add the launcher’s redirect URI. Under **Additional settings**, select **+ ADD URI**, and set a redirect URI of http://localhost:3000/auth/docusign/callback. Select **SAVE**. + 1. Set a name and email address for the signer. In appsettings.yml, save an email address as `signer_email` and a name as `signer_name`. **Note:** Protect your personal information. Please make sure that appsettings.yml will not be stored in your source code repository. 1. Run the launcher: `rails s` -1. Open a browser to http://localhost:3000/auth/docusign +1. Open a browser to http://localhost:3000 ### Installation steps for JWT Grant authentication -**Note:** If you downloaded this code using [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) from the DocuSign Developer Center, skip step 4 as it was automatically performed for you. +**Note:** If you downloaded this code using [Quickstart](https://developers.docusign.com/docs/esign-rest-api/quickstart/) from the Docusign Developer Center, skip step 4 as it was automatically performed for you. Also, in order to select JSON Web Token authentication in the launcher, in config/appsettings.yml, change `quickstart` to `false`. 1. Extract the Quickstart ZIP file or download or clone the code-examples-ruby repository. -1. In your command-line environment, switch to the folder: - `cd ` or `cd code-examples-ruby` -1. Install the dependencies: `bundle install` +1. In your command-line environment, switch to the folder: + `cd ` or `cd code-examples-ruby` +1. Install the dependencies: `bundler install` 1. To configure the launcher for [JWT Grant](https://developers.docusign.com/platform/auth/jwt/) authentication, create a copy of the file config/appsettings.example.yml and save the copy as config/appsettings.yml. - 1. Add your API Username. On the [Apps and Keys](https://admindemo.docusign.com/authenticate?goTo=apiIntegratorKey) page, under **My Account Information**, copy the **API Username** GUID and save it in appsettings.yml as your `impersonated_user_guid`. + 1. Add your User ID. On the [Apps and Keys](https://admindemo.docusign.com/authenticate?goTo=apiIntegratorKey) page, under **My Account Information**, copy the **User ID** GUID and save it in appsettings.yml as your `impersonated_user_guid`. 1. Add your integration key. On the [Apps and Keys](https://admindemo.docusign.com/authenticate?goTo=apiIntegratorKey) page, under **Apps and Integration Keys**, choose the app to use, then select **Actions** > **Edit**. Under **General Info**, copy the **Integration Key** GUID and save it in appsettings.yml as your `jwt_integration_key`. 1. Generate an RSA key pair, if you don’t already have one. Under **Authentication**, select **+ GENERATE RSA**. Copy the private key and save it in a new file named config/docusign_private_key.txt. - 1. Add the launcher’s redirect URI. Under **Additional settings**, select **+ ADD URI**, and set a redirect URI of http://localhost:3000/auth/docusign/callback. Select **SAVE**. - 1. Set a name and email address for the signer. In appsettings.yml, save an email address as `signer_email` and a name as `signer_name`. + 1. Add the launcher’s redirect URI. Under **Additional settings**, select **+ ADD URI**, and set a redirect URI of http://localhost:3000/auth/docusign/callback. Select **SAVE**. + 1. Set a name and email address for the signer. In appsettings.yml, save an email address as `signer_email` and a name as `signer_name`. **Note:** Protect your personal information. Please make sure that appsettings.yml will not be stored in your source code repository. 1. Run the launcher: `rails s` -1. Open a browser to http://localhost:3000/auth/docusign +1. Open a browser to http://localhost:3000 1. If it is your first time using the app, grant consent by selecting **Accept**. On the black navigation bar, select **Logout**, then **Login**. -1. From the picklist, select **JSON Web Token (JWT) grant** > **Authenticate with DocuSign**. +1. From the picklist, select **JSON Web Token (JWT) grant** > **Authenticate with Docusign**. 1. Select your desired code example. +## JWT grant remote signing and Authorization Code Grant embedded signing projects +See [Docusign Quickstart overview](https://developers.docusign.com/docs/esign-rest-api/quickstart/overview/) on the Docusign Developer Center for more information on how to run the JWT grant remote signing project and the Authorization Code Grant embedded signing project. + +### Authorization Code Grant embedded signing example: +Run in Git Bash: +``` +$ cd /quick_acg +$ bundler install +$ rails s +``` + +Open a browser to http://localhost:3000 + +### JWT grant remote signing example: +Run in Windows Command Prompt (CMD): +``` +$ cd jwt_console_project +$ ruby jwt_console.rb +``` ### Troubleshooting Windows SSL issue When using the Ruby launcher on a Windows machine you may get the following error: **SSL peer certificate or SSH remote key was not OK** -This error occurs because you’re attempting to use the Ruby launcher with a self-signed certificate or without SSL/HTTP security. The API calls from Ruby SDKs are using a built-in Curl tool that is enforcing the SSL requirement. You can disable this security check to run the launcher in an insecure manner on your developer machine. +This error occurs because you’re attempting to use the Ruby launcher with a self-signed certificate or without SSL/HTTP security. The API calls from Ruby SDKs are using a built-in Curl tool that is enforcing the SSL requirement. You can disable this security check to run the launcher in an insecure manner on your developer machine. ```diff -- It is highly recommended that you don’t disable this security check -- in a production environment or in your integration. -- This method is offered here solely as a means to enable you to +- It is highly recommended that you don’t disable this security check +- in a production environment or in your integration. +- This method is offered here solely as a means to enable you to - develop quickly by lowering the security bar on your local machine. ``` Find the root folder for your Ruby gems (in this case, a 64-bit version of Ruby 2.7.0): C:\Ruby27-x64\lib\ruby\gems\2.7.0\gems\ -Find the relevant DocuSign Ruby SDK you are using. The name always starts with “docusign”; for instance, DocuSign Click SDK version 1.0.0: +Find the relevant Docusign Ruby SDK you are using. The name always starts with “docusign”; for instance, Docusign Click SDK version 1.0.0: C:\Ruby27-x64\lib\ruby\gems\2.7.0\gems\docusign_click-1.0.0\lib\docusign_click @@ -304,13 +187,23 @@ Modify the following two lines in the **configuration.rb** file, replacing `true Once this is complete, you can run your Ruby on Rails application again and you should be able to make API calls on your localhost. +### Troubleshooting macOS SSL issue +When using the Ruby launcher on OSX you may get the following error: + +``` +Faraday::SSLError (SSL_connect returned=1 errno=0 state=error: certificate verify failed (self signed certificate in certificate chain)) +``` +Please update SSL certificates if rvm is your version manager. Or check [other steps for different scenarios](https://gemfury.com/help/could-not-verify-ssl-certificate/#updating-ssl-certificates). +``` +$ rvm osx-ssl-certs status all +$ rvm osx-ssl-certs update all +``` ## Payments code example To use the payments code example, create a test payment gateway on the [**Payments**](https://admindemo.docusign.com/authenticate?goTo=payments) page in your developer account. See [Configure a payment gateway](./PAYMENTS_INSTALLATION.md) for details. Once you've created a payment gateway, save the **Gateway Account ID** GUID to appsettings.yml. - ## License and additional information ### License diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico deleted file mode 100644 index b3b2dc4..0000000 Binary files a/app/assets/images/favicon.ico and /dev/null differ diff --git a/app/assets/images/favicon.png b/app/assets/images/favicon.png new file mode 100644 index 0000000..fefff55 Binary files /dev/null and b/app/assets/images/favicon.png differ diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js new file mode 100644 index 0000000..c539995 --- /dev/null +++ b/app/assets/javascripts/search.js @@ -0,0 +1,295 @@ +const DS_SEARCH = (function () { + const API_TYPES = { + ESIGNATURE: "esignature", + MONITOR: "monitor", + CLICK: "click", + ROOMS: "rooms", + ADMIN: "admin", + CONNECT: "connect", + WEBFORMS: "webforms", + NOTARY: "notary", + CONNECTED_FIELDS: "connectedfields" + } + + const processJSONData = function () { + const json_raw = $("#api_json_data").text(); + const json = json_raw ? JSON.parse(json_raw) : false; + + return json; + } + + const processCFR11Value = function () { + const cfr11_data = $("#cfr11_data"); + return cfr11_data.text(); + } + + function shouldAppendExample(element, example, cfrPart11) { + const isEsignature = element.Name.toLowerCase() === API_TYPES.ESIGNATURE.toLowerCase(); + const isCFREnabledForAll = example.CFREnabled === "AllAccounts"; + const isCFREnabledForCFROnly = cfrPart11 === "enabled" && example.CFREnabled === "CFROnly"; + const isCFREnabledForNonCFR = cfrPart11 !== "enabled" && example.CFREnabled === "NonCFR"; + + return !isEsignature || isCFREnabledForAll || isCFREnabledForCFROnly || isCFREnabledForNonCFR; + } + + function checkIfExampleMatches(example, matches) { + const name = example.ExampleName; + const description = example.ExampleDescription; + + let pathNames = []; + if (example.LinksToAPIMethod) { + pathNames = example.LinksToAPIMethod.map((a) => a.PathName); + } + + for (let i = 0; i < matches.length; i++) { + if ( + name === matches[i].value || + description === matches[i].value || + pathNames && pathNames.indexOf(matches[i].value) > -1 + ) { + return true; + } + } + + return false; + } + + function clearNonMatchingExamples(examples, matches) { + for (let i = examples.length - 1; i >= 0; i--) { + if (!checkIfExampleMatches(examples[i], matches)) { + examples.splice(i, 1); + } + } + } + + function clearResultsAfterMatching(api, matches) { + const groups = api.Groups; + + for (let i = groups.length - 1; i >= 0; i--) { + const group = groups[i]; + clearNonMatchingExamples(group.Examples, matches); + + if (group.Examples.length === 0) { + groups.splice(i, 1); + } + } + } + + const findCodeExamplesByKeywords = function(json, pattern) { + const options = { + isCaseSensitive: false, + minMatchCharLength: pattern.length, + threshold: -0.0, + includeMatches: true, + ignoreLocation: true, + useExtendedSearch: true, + keys: [ + "Groups.Examples.ExampleName", + "Groups.Examples.ExampleDescription", + "Groups.Examples.LinksToAPIMethod.PathName", + ], + }; + + const clearJSON = JSON.stringify(json).replace(/<\/?[^>]+(>|$)/g, ""); + const fuse = new Fuse(JSON.parse(clearJSON), options); + + const searchResults = fuse.search(JSON.stringify(pattern)); + + searchResults.forEach((searchResult) => + clearResultsAfterMatching(searchResult.item, searchResult.matches) + ); + + return searchResults; + } + + const getExamplesByAPIType = function (apiType, codeExamples) { + let codeExamplesByAPI = codeExamples.find( + (x) => x.Name.toLowerCase() === apiType + ); + + if (codeExamplesByAPI != null) { + return [codeExamplesByAPI]; + } else { + return null; + } + }; + + const getEnteredAPIType = function (inputValue) { + const inputLength = inputValue.length; + + for (const key in API_TYPES) { + if (Object.hasOwnProperty.call(API_TYPES, key)) { + const apiType = API_TYPES[key]; + const comparedValue = apiType.substr(0, inputLength); + + if (inputValue === comparedValue) { + return apiType; + } + } + } + + return null; + }; + + function getLinkForApiType(apiName) { + switch (apiName) { + case API_TYPES.ADMIN: + return "aeg"; + case API_TYPES.CLICK: + return "ceg"; + case API_TYPES.ROOMS: + return "reg"; + case API_TYPES.MONITOR: + return "meg"; + case API_TYPES.ESIGNATURE: + return "eeg"; + case API_TYPES.CONNECT: + return "cneg"; + case API_TYPES.WEBFORMS: + return "weg"; + case API_TYPES.NOTARY: + return "neg"; + case API_TYPES.CONNECTED_FIELDS: + return "feg"; + } + } + + const addCodeExampleToHomepage = function (codeExamples) { + var cfrPart11 = processCFR11Value(); + + codeExamples.forEach((element) => { + let linkToCodeExample = getLinkForApiType(element.Name.toLowerCase()); + + element.Groups.forEach((group) => { + $("#filtered_code_examples").append("

" + group.Name + "

"); + + group.Examples.forEach((example) => { + if ( + !example.SkipForLanguages || + !example.SkipForLanguages.toLowerCase().includes("ruby") + ) { + if (shouldAppendExample(element, example, cfrPart11)) + { + $("#filtered_code_examples").append( + "

" + + "" + + example.ExampleName + + "

" + ); + + $("#filtered_code_examples").append( + "

" + example.ExampleDescription + "

" + ); + + $("#filtered_code_examples").append("

"); + const links = example.LinksToAPIMethod || []; + if (links.length > 0) { + if (links.length == 1) { + $("#filtered_code_examples").append( + processJSONData().SupportingTexts.APIMethodUsed + ); + } else { + $("#filtered_code_examples").append( + processJSONData().SupportingTexts.APIMethodUsedPlural + ); + } + + links.forEach((link, index) => { + $("#filtered_code_examples").append(`${link.PathName}`); + + if (index + 1 === links.length) { + $("#filtered_code_examples").append(""); + } else if (index + 1 === links.length - 1) { + $("#filtered_code_examples").append(" and "); + } else { + $("#filtered_code_examples").append(", "); + } + }) + + } + + $("#filtered_code_examples").append("

"); + } + } + }); + }); + }); + }; + + const textCouldNotBeFound = function () { + $("#filtered_code_examples").append( + processJSONData().SupportingTexts.SearchFailed + ); + }; + + return { + processJSONData, + getEnteredAPIType, + getExamplesByAPIType, + findCodeExamplesByKeywords, + textCouldNotBeFound, + addCodeExampleToHomepage, + shouldAppendExample, + }; +})(); + +const input = document.getElementById("code_example_search"); +const log = document.getElementById("values"); + +input.addEventListener("input", updateValue); + +function updateValue(esearchPattern) { + document.getElementById("filtered_code_examples").innerHTML = ""; + + const inputValue = esearchPattern.target.value.toLowerCase(); + const json = DS_SEARCH.processJSONData().APIs; + + if (inputValue === "") { + DS_SEARCH.addCodeExampleToHomepage(json); + } else { + const apiType = DS_SEARCH.getEnteredAPIType(inputValue); + + if (apiType !== null) { + const adminExamples = DS_SEARCH.getExamplesByAPIType(apiType, json); + DS_SEARCH.addCodeExampleToHomepage(adminExamples); + } else { + const result = DS_SEARCH.findCodeExamplesByKeywords(json, inputValue); + if (result.length < 1) { + DS_SEARCH.textCouldNotBeFound(); + } else { + result.forEach((x) => { + const api = json.filter((api) => { + return api.Name === x.item.Name; + })[0]; + + x.item.Groups.forEach((group, groupIndex) => { + const unfilteredGroup = api.Groups.filter((apiGroup) => { + return apiGroup.Name === group.Name; + })[0]; + + group.Examples.forEach((example, index) => { + const clearedExamples = unfilteredGroup.Examples.filter((apiExample) => { + return apiExample.ExampleNumber === example.ExampleNumber; + }) + x.item.Groups[groupIndex].Examples[index] = clearedExamples[0]; + }); + }); + + DS_SEARCH.addCodeExampleToHomepage([x.item]); + }); + } + } + } +} diff --git a/app/controllers/admin_api/aeg001_create_user_controller.rb b/app/controllers/admin_api/aeg001_create_user_controller.rb new file mode 100644 index 0000000..76eaf0f --- /dev/null +++ b/app/controllers/admin_api/aeg001_create_user_controller.rb @@ -0,0 +1,67 @@ +class AdminApi::Aeg001CreateUserController < EgController + include ApiCreator + before_action -> { check_auth('Admin') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 1, 'Admin') } + + def create + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + organization_id: session['organization_id'] + } + #ds-snippet-start:Admin1Step5 + user_data = { + user_name: param_gsub(params['user_name']), + first_name: param_gsub(params['first_name']), + last_name: param_gsub(params['last_name']), + email: param_gsub(params['email']), + auto_activate_memberships: true, + accounts: [ + { + id: args[:account_id], + permission_profile: { + id: param_gsub(params['permission_profile_id']) + }, + groups: [ + { + id: param_gsub(params['group_id']) + } + ] + } + ] + } + #ds-snippet-end:Admin1Step5 + + begin + results = AdminApi::Eg001CreateUserService.new(args, user_data).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_Admin::ApiError => e + handle_error(e) + end + end + + def get + super + session[:organization_id] = AdminApi::GetDataService.new(session).get_organization_id if session[:organization_id].nil? + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'] + } + + #ds-snippet-start:Admin1Step3 + accounts_api = create_account_api(args) + @permission_profiles = accounts_api.list_permissions(args[:account_id]).permission_profiles + #ds-snippet-end:Admin1Step3 + + #ds-snippet-start:Admin1Step4 + groups_api = create_group_api(args) + @groups = groups_api.list_groups(args[:account_id]).groups + #ds-snippet-end:Admin1Step4 + end +end diff --git a/app/controllers/admin_api/aeg002_create_active_clm_esign_user_controller.rb b/app/controllers/admin_api/aeg002_create_active_clm_esign_user_controller.rb new file mode 100644 index 0000000..dce0b8e --- /dev/null +++ b/app/controllers/admin_api/aeg002_create_active_clm_esign_user_controller.rb @@ -0,0 +1,51 @@ +class AdminApi::Aeg002CreateActiveClmEsignUserController < EgController + before_action -> { check_auth('Admin') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 2, 'Admin') } + + def create + args = { + user_name: params[:user_name], + first_name: params[:first_name], + last_name: params[:last_name], + email: params[:email], + clm_permission_profile_id: params[:clm_permission_profile_id], + esign_permission_profile_id: params[:esign_permission_profile_id], + clm_product_id: params[:clm_product_id], + esign_product_id: params[:esign_product_id], + ds_group_id: params[:ds_group_id], + account_id: session[:ds_account_id], + organization_id: session['organization_id'], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token] + } + + results = AdminApi::Eg002CreateActiveClmEsignUserService.new(args).worker + + session[:clm_email] = params[:email] + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + + render 'ds_common/example_done' + rescue DocuSign_Admin::ApiError => e + handle_error(e) + end + + def get + super + session[:organization_id] = AdminApi::GetDataService.new(session).get_organization_id if session[:organization_id].nil? + product_permission_profiles = AdminApi::GetDataService.new(session).get_product_permission_profiles + + product_permission_profiles.each do |product_permission_profile| + if product_permission_profile['product_name'] == 'CLM' + @clm_permission_profiles = product_permission_profile['permission_profiles'] + @clm_product_id = product_permission_profile['product_id'] + else + @esign_permission_profiles = product_permission_profile['permission_profiles'] + @esign_product_id = product_permission_profile['product_id'] + end + end + @ds_groups = AdminApi::GetDataService.new(session).get_ds_groups + end +end diff --git a/app/controllers/admin_api/aeg003_bulk_export_user_data_controller.rb b/app/controllers/admin_api/aeg003_bulk_export_user_data_controller.rb new file mode 100644 index 0000000..d79423a --- /dev/null +++ b/app/controllers/admin_api/aeg003_bulk_export_user_data_controller.rb @@ -0,0 +1,33 @@ +class AdminApi::Aeg003BulkExportUserDataController < EgController + before_action -> { check_auth('Admin') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 3, 'Admin') } + + def create + session[:organization_id] = AdminApi::GetDataService.new(session).get_organization_id if session[:organization_id].nil? + + file_path = File.expand_path(File.join(File.dirname(__FILE__), '../../../data/exportedUserData.csv')) + + begin + request_body = { + type: 'organization_memberships_export' + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + organization_id: session['organization_id'], + file_path: file_path, + request_body: request_body + } + + results = AdminApi::Eg003BulkExportUserDataService.new(args).worker + + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], file_path) + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_Admin::ApiError => e + handle_error(e) + end + end +end diff --git a/app/controllers/admin_api/aeg004_import_user_controller.rb b/app/controllers/admin_api/aeg004_import_user_controller.rb new file mode 100644 index 0000000..1d3e3d2 --- /dev/null +++ b/app/controllers/admin_api/aeg004_import_user_controller.rb @@ -0,0 +1,51 @@ +class AdminApi::Aeg004ImportUserController < EgController + before_action -> { check_auth('Admin') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 4, 'Admin') } + + def create + session[:organization_id] = AdminApi::GetDataService.new(session).get_organization_id if session[:organization_id].nil? + + file_path = File.expand_path(File.join(File.dirname(__FILE__), '../../../data/userData.csv')) + + begin + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + organization_id: session['organization_id'], + csv_file_path: file_path + } + + results = AdminApi::Eg004ImportUserService.new(args).worker + session[:import_id] = results.id + + @title = @example['ExampleName'] + @check_status = true, + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_Admin::ApiError => e + handle_error(e) + end + end + + def check_status + session[:organization_id] = AdminApi::GetDataService.new(session).get_organization_id if session[:organization_id].nil? + + begin + import_id = session[:import_id] + results = AdminApi::GetDataService.new(session).check_import_status(import_id) + + @status = results.status + @title = @example['ExampleName'] + @message = @example['AdditionalPage'][0]['ResultsPageText'] + @json = results.to_json.to_json + render 'admin_api/aeg004_import_user/get_status.html.erb' + rescue DocuSign_Admin::ApiError => e + error = JSON.parse e.response_body + @error_code = e.code + @error_message = error['error_description'] + render 'ds_common/error' + end + end +end diff --git a/app/controllers/admin_api/aeg005_audit_users_controller.rb b/app/controllers/admin_api/aeg005_audit_users_controller.rb new file mode 100644 index 0000000..4b65c56 --- /dev/null +++ b/app/controllers/admin_api/aeg005_audit_users_controller.rb @@ -0,0 +1,27 @@ +class AdminApi::Aeg005AuditUsersController < EgController + before_action -> { check_auth('Admin') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 5, 'Admin') } + + def create + session[:organization_id] = AdminApi::GetDataService.new(session).get_organization_id if session[:organization_id].nil? + + begin + args = { + account_id: session[:ds_account_id], + organization_id: session['organization_id'], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token] + } + + results = AdminApi::Eg005AuditUsersService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + + render 'ds_common/example_done' + rescue DocuSign_Admin::ApiError => e + handle_error(e) + end + end +end diff --git a/app/controllers/admin_api/aeg006_get_user_profile_by_email_controller.rb b/app/controllers/admin_api/aeg006_get_user_profile_by_email_controller.rb new file mode 100644 index 0000000..0af11c3 --- /dev/null +++ b/app/controllers/admin_api/aeg006_get_user_profile_by_email_controller.rb @@ -0,0 +1,24 @@ +class AdminApi::Aeg006GetUserProfileByEmailController < EgController + include ApiCreator + before_action -> { check_auth('Admin') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 6, 'Admin') } + + def create + session[:organization_id] = AdminApi::GetDataService.new(session).get_organization_id if session[:organization_id].nil? + + args = { + access_token: session['ds_access_token'], + organization_id: session['organization_id'], + email: param_gsub(params['email']) + } + + results = AdminApi::Eg006GetUserProfileByEmailService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_Admin::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/admin_api/aeg007_get_user_profile_by_user_id_controller.rb b/app/controllers/admin_api/aeg007_get_user_profile_by_user_id_controller.rb new file mode 100644 index 0000000..d21f01e --- /dev/null +++ b/app/controllers/admin_api/aeg007_get_user_profile_by_user_id_controller.rb @@ -0,0 +1,24 @@ +class AdminApi::Aeg007GetUserProfileByUserIdController < EgController + include ApiCreator + before_action -> { check_auth('Admin') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 7, 'Admin') } + + def create + session[:organization_id] = AdminApi::GetDataService.new(session).get_organization_id if session[:organization_id].nil? + + args = { + access_token: session['ds_access_token'], + organization_id: session['organization_id'], + user_id: param_gsub(params['user_id']) + } + + results = AdminApi::Eg007GetUserProfileByUserIdService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_Admin::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/admin_api/aeg008_update_user_product_permission_profile_controller.rb b/app/controllers/admin_api/aeg008_update_user_product_permission_profile_controller.rb new file mode 100644 index 0000000..e5fef15 --- /dev/null +++ b/app/controllers/admin_api/aeg008_update_user_product_permission_profile_controller.rb @@ -0,0 +1,82 @@ +class AdminApi::Aeg008UpdateUserProductPermissionProfileController < EgController + before_action -> { check_auth('Admin') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 8, 'Admin') } + + def create + clm_email = session[:clm_email] + get_data_service = AdminApi::GetDataService.new(session) + + unless clm_email && get_data_service.check_user_exists_by_email(clm_email) + @email_ok = false + return + end + + begin + product_permission_profiles = get_data_service.get_product_permission_profiles + product_id = params[:product_id] + permission_profile_id = nil + + product_permission_profiles.each do |profile| + next unless product_id == profile['product_id'] + + permission_profile_id = if profile['product_name'] == 'CLM' + params[:clm_permission_profile_id] + else + params[:esign_permission_profile_id] + end + end + + args = { + email: clm_email, + permission_profile_id: permission_profile_id, + product_id: product_id, + account_id: session[:ds_account_id], + organization_id: session['organization_id'], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token] + } + + results = AdminApi::Eg008UpdateUserProductPermissionProfileService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + + render 'ds_common/example_done' + rescue DocuSign_Admin::ApiError => e + handle_error(e) + end + end + + def get + super + get_data_service = AdminApi::GetDataService.new(session) + + session[:organization_id] = get_data_service.get_organization_id if session[:organization_id].nil? + + clm_email = session[:clm_email] + + unless clm_email && get_data_service.check_user_exists_by_email(clm_email) + @email_ok = false + return + end + + product_permission_profiles = get_data_service.get_product_permission_profiles + + product_permission_profiles.each do |product_permission_profile| + if product_permission_profile['product_name'] == 'CLM' + @clm_permission_profiles = product_permission_profile['permission_profiles'] + @clm_product_id = product_permission_profile['product_id'] + else + @esign_permission_profiles = product_permission_profile['permission_profiles'] + @esign_product_id = product_permission_profile['product_id'] + end + end + product_list = [] + product_list.push({ 'product_id' => @clm_product_id, 'product_name' => 'CLM' }) + product_list.push({ 'product_id' => @esign_product_id, 'product_name' => 'eSignature' }) + @product_list = product_list + @email_ok = true + @email = clm_email + end +end diff --git a/app/controllers/admin_api/aeg009_delete_user_product_permission_profile_controller.rb b/app/controllers/admin_api/aeg009_delete_user_product_permission_profile_controller.rb new file mode 100644 index 0000000..e5fb322 --- /dev/null +++ b/app/controllers/admin_api/aeg009_delete_user_product_permission_profile_controller.rb @@ -0,0 +1,83 @@ +class AdminApi::Aeg009DeleteUserProductPermissionProfileController < EgController + before_action -> { check_auth('Admin') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 9, 'Admin') } + + def create + clm_email = session[:clm_email] + get_data_service = AdminApi::GetDataService.new(session) + + unless clm_email && get_data_service.check_user_exists_by_email(clm_email) + @email_ok = false + return + end + + begin + args = { + email: clm_email, + product_id: params[:product_id], + account_id: session[:ds_account_id], + organization_id: session['organization_id'], + access_token: session[:ds_access_token] + } + + results = AdminApi::Eg009DeleteUserProductPermissionProfileService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + + render 'ds_common/example_done' + rescue DocuSign_Admin::ApiError => e + handle_error(e) + end + end + + def get + super + get_data_service = AdminApi::GetDataService.new(session) + + session[:organization_id] = get_data_service.get_organization_id if session[:organization_id].nil? + + clm_email = session[:clm_email] + + unless clm_email && get_data_service.check_user_exists_by_email(clm_email) + @email_ok = false + return + end + + args = { + email: clm_email, + account_id: session[:ds_account_id], + organization_id: session['organization_id'], + access_token: session[:ds_access_token] + } + + product_permission_profiles = AdminApi::Eg009DeleteUserProductPermissionProfileService.new(args).get_permission_profiles_by_email + permission_profile_list = [] + clm_product_id = nil + clm_permission_profile_name = nil + esign_product_id = nil + esign_permission_profile_name = nil + + product_permission_profiles.each do |product_permission_profile| + permission_profiles = product_permission_profile['permission_profiles'] + permission_profiles.each do |permission_profile| + if product_permission_profile['product_name'] == 'CLM' + clm_permission_profile_name = permission_profile['permission_profile_name'] + clm_product_id = product_permission_profile['product_id'] + else + esign_permission_profile_name = permission_profile['permission_profile_name'] + esign_product_id = product_permission_profile['product_id'] + end + end + end + + permission_profile_list.push({ 'product_id' => clm_product_id, 'permission_name' => "CLM - #{clm_permission_profile_name}" }) if clm_product_id + + permission_profile_list.push({ 'product_id' => esign_product_id, 'permission_name' => "eSignature - #{esign_permission_profile_name}" }) if esign_product_id + + @permission_profile_list = permission_profile_list + @email_ok = true + @email = clm_email + end +end diff --git a/app/controllers/admin_api/aeg010_delete_user_data_from_organization_controller.rb b/app/controllers/admin_api/aeg010_delete_user_data_from_organization_controller.rb new file mode 100644 index 0000000..3ac5113 --- /dev/null +++ b/app/controllers/admin_api/aeg010_delete_user_data_from_organization_controller.rb @@ -0,0 +1,23 @@ +class AdminApi::Aeg010DeleteUserDataFromOrganizationController < EgController + before_action -> { check_auth('Admin') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 10, 'Admin') } + + def create + session[:organization_id] = AdminApi::GetDataService.new(session).get_organization_id if session[:organization_id].nil? + + args = { + access_token: session['ds_access_token'], + email: param_gsub(params['email']), + organization_id: session['organization_id'] + } + + results = AdminApi::Eg010DeleteUserDataFromOrganizationService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_Admin::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/admin_api/aeg011_delete_user_data_from_account_controller.rb b/app/controllers/admin_api/aeg011_delete_user_data_from_account_controller.rb new file mode 100644 index 0000000..1dd5273 --- /dev/null +++ b/app/controllers/admin_api/aeg011_delete_user_data_from_account_controller.rb @@ -0,0 +1,21 @@ +class AdminApi::Aeg011DeleteUserDataFromAccountController < EgController + before_action -> { check_auth('Admin') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 11, 'Admin') } + + def create + args = { + account_id: session['ds_account_id'], + access_token: session['ds_access_token'], + user_id: param_gsub(params['user_id']) + } + + results = AdminApi::Eg011DeleteUserDataFromAccountService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_Admin::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/admin_api/aeg012_clone_account_controller.rb b/app/controllers/admin_api/aeg012_clone_account_controller.rb new file mode 100644 index 0000000..80a7baf --- /dev/null +++ b/app/controllers/admin_api/aeg012_clone_account_controller.rb @@ -0,0 +1,35 @@ +class AdminApi::Aeg012CloneAccountController < EgController + before_action -> { check_auth('Admin') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 12, 'Admin') } + + def create + args = { + access_token: session['ds_access_token'], + organization_id: session['organization_id'], + source_account_id: param_gsub(params['source_account_id']), + target_account_name: param_gsub(params['target_account_name']), + target_account_first_name: param_gsub(params['target_account_first_name']), + target_account_last_name: param_gsub(params['target_account_last_name']), + target_account_email: param_gsub(params['target_account_email']) + } + + results = AdminApi::Eg012CloneAccountService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_Admin::ApiError => e + handle_error(e) + end + + def get + super + session[:organization_id] = AdminApi::GetDataService.new(session).get_organization_id if session[:organization_id].nil? + args = { + access_token: session['ds_access_token'], + organization_id: session['organization_id'] + } + @accounts = AdminApi::Eg012CloneAccountService.new(args).get_account.asset_group_accounts + end +end diff --git a/app/controllers/admin_api/aeg013_create_account_controller.rb b/app/controllers/admin_api/aeg013_create_account_controller.rb new file mode 100644 index 0000000..c3bd95c --- /dev/null +++ b/app/controllers/admin_api/aeg013_create_account_controller.rb @@ -0,0 +1,38 @@ +class AdminApi::Aeg013CreateAccountController < EgController + before_action -> { check_auth('Admin') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 13, 'Admin') } + + def create + args = { + access_token: session['ds_access_token'], + organization_id: session['organization_id'], + subscription_id: session['subscription_id'], + plan_id: session['plan_id'], + email: param_gsub(params['email']), + first_name: param_gsub(params['first_name']), + last_name: param_gsub(params['last_name']) + } + + results = AdminApi::Eg013CreateAccountService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_Admin::ApiError => e + handle_error(e) + end + + def get + super + session[:organization_id] = AdminApi::GetDataService.new(session).get_organization_id if session[:organization_id].nil? + args = { + access_token: session['ds_access_token'], + organization_id: session['organization_id'] + } + plan_items = AdminApi::Eg013CreateAccountService.new(args).get_organization_plan_items + print plan_items + session['subscription_id'] = plan_items[0].subscription_id + session['plan_id'] = plan_items[0].plan_id + end +end diff --git a/app/controllers/clickwrap/ceg001_create_clickwrap_controller.rb b/app/controllers/clickwrap/ceg001_create_clickwrap_controller.rb new file mode 100644 index 0000000..83b3ce9 --- /dev/null +++ b/app/controllers/clickwrap/ceg001_create_clickwrap_controller.rb @@ -0,0 +1,24 @@ +class Clickwrap::Ceg001CreateClickwrapController < EgController + before_action -> { check_auth('Click') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 1, 'Click') } + + def create + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + doc_pdf: File.join('data', Rails.configuration.doc_terms_pdf), + clickwrap_name: param_gsub(params[:clickwrapName]) + } + + results = Clickwrap::Eg001CreateClickwrapService.new(args).worker + + session[:clickwrap_id] = results.clickwrap_id + session[:clickwrap_name] = results.clickwrap_name + + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results.clickwrap_name) + @json = results.to_json.to_json + render 'ds_common/example_done' + end +end diff --git a/app/controllers/clickwrap/ceg002_activate_clickwrap_controller.rb b/app/controllers/clickwrap/ceg002_activate_clickwrap_controller.rb new file mode 100644 index 0000000..38664f3 --- /dev/null +++ b/app/controllers/clickwrap/ceg002_activate_clickwrap_controller.rb @@ -0,0 +1,24 @@ +class Clickwrap::Ceg002ActivateClickwrapController < EgController + before_action -> { check_auth('Click') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 2, 'Click') } + + def create + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + clickwrap_id: params[:clickwrapId] + } + + Clickwrap::Eg002ActivateClickwrapService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + render 'ds_common/example_done' + end + + def get + statuses = %w[inactive draft] + @clickwraps = Clickwrap::Eg002ActivateClickwrapService.new(session).get_inactive_clickwraps(statuses).as_json + end +end diff --git a/app/controllers/clickwrap/ceg003_create_new_clickwrap_version_controller.rb b/app/controllers/clickwrap/ceg003_create_new_clickwrap_version_controller.rb new file mode 100644 index 0000000..73f5f6a --- /dev/null +++ b/app/controllers/clickwrap/ceg003_create_new_clickwrap_version_controller.rb @@ -0,0 +1,20 @@ +class Clickwrap::Ceg003CreateNewClickwrapVersionController < EgController + before_action -> { check_auth('Click') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 3, 'Click') } + + def create + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + clickwrap_id: session[:clickwrap_id], + clickwrap_name: session[:clickwrap_name] + } + + results = Clickwrap::Eg003CreateNewClickwrapVersionService.new(args).worker + puts results.to_json.to_json + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results.version_number, results.clickwrap_name) + render 'ds_common/example_done' + end +end diff --git a/app/controllers/clickwrap/ceg004_list_clickwraps_controller.rb b/app/controllers/clickwrap/ceg004_list_clickwraps_controller.rb new file mode 100644 index 0000000..a734dc0 --- /dev/null +++ b/app/controllers/clickwrap/ceg004_list_clickwraps_controller.rb @@ -0,0 +1,19 @@ +class Clickwrap::Ceg004ListClickwrapsController < EgController + before_action -> { check_auth('Click') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 4, 'Click') } + + def create + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token] + } + + results = Clickwrap::Eg004ListClickwrapsService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + render 'ds_common/example_done' + end +end diff --git a/app/controllers/clickwrap/ceg005_clickwrap_responses_controller.rb b/app/controllers/clickwrap/ceg005_clickwrap_responses_controller.rb new file mode 100644 index 0000000..cf2f3a9 --- /dev/null +++ b/app/controllers/clickwrap/ceg005_clickwrap_responses_controller.rb @@ -0,0 +1,21 @@ +class Clickwrap::Ceg005ClickwrapResponsesController < EgController + before_action -> { check_auth('Click') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 5, 'Click') } + + def create + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + clickwrap_id: session[:clickwrap_id], + client_user_id: param_gsub(params[:client_user_id]) + } + + results = Clickwrap::Eg005ClickwrapResponsesService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + render 'ds_common/example_done' + end +end diff --git a/app/controllers/clickwrap/ceg006_embed_clickwrap_controller.rb b/app/controllers/clickwrap/ceg006_embed_clickwrap_controller.rb new file mode 100644 index 0000000..3d739a6 --- /dev/null +++ b/app/controllers/clickwrap/ceg006_embed_clickwrap_controller.rb @@ -0,0 +1,38 @@ +class Clickwrap::Ceg006EmbedClickwrapController < EgController + before_action -> { check_auth('Click') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 6, 'Click') } + + def create + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + clickwrap_id: params[:clickwrapId], + full_name: params[:fullName], + email: params[:email], + company: params[:company], + title: params[:title], + date: params[:date] + } + + results = Clickwrap::Eg006EmbedClickwrapService.new(args).worker + + if results.nil? + @error_code = '200' + @error_message = 'The email address was already used to agree to this elastic template. Provide a different email address if you want to view the agreement and agree to it.' + render 'ds_common/error' + else + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText']) + @agreementUrl = results['agreementUrl'] + render 'clickwrap/ceg006_embed_clickwrap/results' + end + rescue DocuSign_Click::ApiError => e + handle_error(e) + end + + def get + @clickwraps = Clickwrap::Eg006EmbedClickwrapService.new(session).get_active_clickwraps + @inactive_clickwraps = Clickwrap::Eg006EmbedClickwrapService.new(session).get_inactive_clickwraps + end +end diff --git a/app/controllers/clickwrap/eg001_create_clickwrap_controller.rb b/app/controllers/clickwrap/eg001_create_clickwrap_controller.rb deleted file mode 100644 index 454396e..0000000 --- a/app/controllers/clickwrap/eg001_create_clickwrap_controller.rb +++ /dev/null @@ -1,27 +0,0 @@ -class Clickwrap::Eg001CreateClickwrapController < EgController - before_action :check_auth - - def create - results = Clickwrap::Eg001CreateClickwrapService.new(session, request).call - - session[:clickwrap_id] = results.clickwrap_id - session[:clickwrap_name] = results.clickwrap_name - - @title = 'Creating a new clickwrap' - @h1 = 'Creating a new clickwrap' - @message = "The clickwrap #{results.clickwrap_name} has been created!" - @json = results.to_json.to_json - render 'ds_common/example_done' - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/clickwrap/eg002_activate_clickwrap_controller.rb b/app/controllers/clickwrap/eg002_activate_clickwrap_controller.rb deleted file mode 100644 index 239733f..0000000 --- a/app/controllers/clickwrap/eg002_activate_clickwrap_controller.rb +++ /dev/null @@ -1,23 +0,0 @@ -class Clickwrap::Eg002ActivateClickwrapController < EgController - before_action :check_auth - - def create - results = Clickwrap::Eg002ActivateClickwrapService.new(session).call - - @title = 'Activating a new clickwrap' - @h1 = 'Activating a new clickwrap' - @message = "The clickwrap #{results.clickwrap_name} has been activated" - render 'ds_common/example_done' - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/clickwrap/eg003_create_new_clickwrap_version_controller.rb b/app/controllers/clickwrap/eg003_create_new_clickwrap_version_controller.rb deleted file mode 100644 index 456078b..0000000 --- a/app/controllers/clickwrap/eg003_create_new_clickwrap_version_controller.rb +++ /dev/null @@ -1,23 +0,0 @@ -class Clickwrap::Eg003CreateNewClickwrapVersionController < EgController - before_action :check_auth - - def create - results = Clickwrap::Eg003CreateNewClickwrapVersionService.new(session).call - puts results.to_json.to_json - @title = 'Creating a new clickwrap version' - @h1 = 'Creating a new clickwrap version' - @message = "Version #{results.version_number} of clickwrap #{results.clickwrap_name} has been created" - render 'ds_common/example_done' - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/clickwrap/eg004_list_clickwraps_controller.rb b/app/controllers/clickwrap/eg004_list_clickwraps_controller.rb deleted file mode 100644 index d345013..0000000 --- a/app/controllers/clickwrap/eg004_list_clickwraps_controller.rb +++ /dev/null @@ -1,24 +0,0 @@ -class Clickwrap::Eg004ListClickwrapsController < EgController - before_action :check_auth - - def create - results = Clickwrap::Eg004ListClickwrapsService.new(session, request).call - - @title = 'List clickwraps results' - @h1 = 'List clickwraps results' - @message = "Results from the ClickWraps::getClickwraps method:" - @json = results.to_json.to_json - render 'ds_common/example_done' - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/clickwrap/eg005_clickwrap_responses_controller.rb b/app/controllers/clickwrap/eg005_clickwrap_responses_controller.rb deleted file mode 100644 index c639807..0000000 --- a/app/controllers/clickwrap/eg005_clickwrap_responses_controller.rb +++ /dev/null @@ -1,24 +0,0 @@ -class Clickwrap::Eg005ClickwrapResponsesController < EgController - before_action :check_auth - - def create - results = Clickwrap::Eg005ClickwrapResponsesService.new(session, request).call - - @title = 'Getting clickwrap responses' - @h1 = 'Getting clickwrap responses' - @message = "Results from the ClickWraps::getClickwrapAgreements method:" - @json = results.to_json.to_json - render 'ds_common/example_done' - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/connect/cneg001_validate_webhook_message_controller.rb b/app/controllers/connect/cneg001_validate_webhook_message_controller.rb new file mode 100644 index 0000000..9da1522 --- /dev/null +++ b/app/controllers/connect/cneg001_validate_webhook_message_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require_relative '../../services/utils' + +class Connect::Cneg001ValidateWebhookMessageController < EgController + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 1, 'Connect') } + + def create + args = { + secret: params['secret'], + payload: params['payload'] + } + results = Connect::Eg001ValidateWebhookMessageService.new(args).worker + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/connected_fields/feg001_set_connected_fields_controller.rb b/app/controllers/connected_fields/feg001_set_connected_fields_controller.rb new file mode 100644 index 0000000..954dec8 --- /dev/null +++ b/app/controllers/connected_fields/feg001_set_connected_fields_controller.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require_relative '../../services/utils' + +class ConnectedFields::Feg001SetConnectedFieldsController < EgController + before_action -> { check_auth('ConnectedFields') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 1, 'ConnectedFields') } + + def get + super + args = { + account_id: session['ds_account_id'], + access_token: session['ds_access_token'], + extensions_base_path: 'https://api-d.docusign.com' + } + + example_service = ConnectedFields::Eg001SetConnectedFieldsService.new(args) + @apps = example_service.get_tab_groups + + return unless @apps.nil? || @apps.empty? + + additional_page_data = example['AdditionalPage'].find { |p| p['Name'] == 'no_verification_app' } + + @title = @example['ExampleName'] + @message = additional_page_data['ResultsPageText'] + render 'ds_common/example_done' + end + + def create + envelope_args = { + signer_email: param_gsub(params['signerEmail']), + signer_name: param_gsub(params['signerName']), + doc_pdf: File.join('data', Rails.application.config.doc_pdf) + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + extensions_base_path: 'https://api-d.docusign.com', + access_token: session['ds_access_token'], + selected_app_id: param_gsub(params['appId']), + envelope_args: envelope_args + } + + example_service = ConnectedFields::Eg001SetConnectedFieldsService.new(args) + apps = example_service.get_tab_groups + selected_app = apps.find { |app| app['appId'] == args[:selected_app_id] } + + results = example_service.send_envelope selected_app + session[:envelope_id] = results['envelope_id'] + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results['envelope_id']) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/ds_common_controller.rb b/app/controllers/ds_common_controller.rb index 9d366c4..dc350a3 100644 --- a/app/controllers/ds_common_controller.rb +++ b/app/controllers/ds_common_controller.rb @@ -6,64 +6,122 @@ class DsCommonController < ApplicationController def index @show_doc = Rails.application.config.documentation - if Rails.configuration.examples_API['Rooms'] == true - render 'room_api/index' - elsif Rails.configuration.examples_API['Click'] == true - render 'clickwrap/index' + handle_redirects + end + + def handle_redirects + if Rails.configuration.quickstart + @manifest = Utils::ManifestUtils.new.get_manifest(Rails.configuration.example_manifest_url) + + if session[:quickstarted].nil? + session[:api] = 'eSignature' + session[:quickstarted] = true + redirect_to '/auth/docusign' + elsif session[:been_here].nil? + return redirect_to '/auth/docusign' if session[:ds_access_token].nil? || session[:ds_base_path].nil? + + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + session[:status_cfr] = 'enabled' + @status_cfr = session[:status_cfr] + redirect_to '/eeg041' + else + redirect_to '/eeg001' + end + else + render_examples + end + elsif check_token + if !session[:api] || session[:api] == 'eSignature' + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + session[:status_cfr] = 'enabled' if enableCFR == 'enabled' + end + render_examples else - @show_doc = Rails.application.config.documentation - if Rails.configuration.quickstart == true && session[:been_here].nil? - redirect_to '/eg001' + render_examples + end + end + + def render_examples + if check_token && !session[:status_cfr] && (!session[:api] || session[:api] == 'eSignature') + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + @status_cfr = 'enabled' + session[:status_cfr] = 'enabled' end + else + @status_cfr = session[:status_cfr] end + load_manifest end def ds_return # To break out of the Quickstart loop an example has been completed session[:been_here] = true - @title = 'Return from DocuSign' + @title = 'Return from Docusign' @event = request.params['event'] @state = request.params['state'] @envelope_id = request.params['envelopeId'] end def ds_must_authenticate - if Rails.configuration.quickstart == "true" - redirect_to('auth/docusign') - end - @title = 'Authenticate with DocuSign' + load_manifest + + jwt_auth if session[:api] == 'Monitor' + redirect_to '/auth/docusign' if Rails.configuration.quickstart && session[:been_here].nil? + @title = 'Authenticate with Docusign' @show_doc = Rails.application.config.documentation - if params[:auth] == 'grand-auth' - redirect_to('/auth/docusign') - elsif params[:auth] == 'jwt-auth' - if JwtAuth::JwtCreator.new(session).check_jwt_token - if session[:eg] - url = "/" + session[:eg] - else - url = root_path - end - else - url = JwtAuth::JwtCreator.consent_url + case params[:auth] + when 'grand-auth' + redirect_to '/auth/docusign' + when 'jwt-auth' + jwt_auth + end + end + + def jwt_auth + if JwtAuth::JwtCreator.new(session).check_jwt_token + url = if session[:eg] + "/#{session[:eg]}" + else + root_path + end + else + session['omniauth.state'] = SecureRandom.hex + url = JwtAuth::JwtCreator.consent_url(session['omniauth.state'], session['api']) redirect_to root_path if session[:token].present? - end - if Rails.configuration.examples_API['Rooms'] == true - configuration = DocuSign_Rooms::Configuration.new - api_client = DocuSign_Rooms::ApiClient.new(configuration) - elsif Rails.configuration.examples_API['Click'] == true - configuration = DocuSign_Click::Configuration.new - api_client = DocuSign_Click::ApiClient.new configuration - end - resp = ::JwtAuth::JwtCreator.new(session).check_jwt_token - if resp.is_a? String - redirect_to resp - end - redirect_to url end + case session[:api] + when 'Rooms' + configuration = DocuSign_Rooms::Configuration.new + DocuSign_Rooms::ApiClient.new(configuration) + when 'Click' + configuration = DocuSign_Click::Configuration.new + DocuSign_Click::ApiClient.new configuration + when 'Admin' + configuration = DocuSign_Admin::Configuration.new + DocuSign_Admin::ApiClient.new configuration + end + resp = ::JwtAuth::JwtCreator.new(session).check_jwt_token + redirect_to resp if resp.is_a? String + redirect_to url end def example_done; end def error; end + private + + def load_manifest + @manifest = Utils::ManifestUtils.new.get_manifest(Rails.configuration.example_manifest_url) + end + + def check_token(buffer_in_min = 10) + buffer = buffer_in_min * 60 + expires_at = session[:ds_expires_at] + remaining_duration = expires_at.nil? ? 0 : expires_at - buffer.seconds.from_now.to_i + remaining_duration.positive? + end end diff --git a/app/controllers/e_sign/eeg002_signing_via_email_controller.rb b/app/controllers/e_sign/eeg002_signing_via_email_controller.rb new file mode 100644 index 0000000..a606a2c --- /dev/null +++ b/app/controllers/e_sign/eeg002_signing_via_email_controller.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require_relative '../../services/utils' + +class ESign::Eeg002SigningViaEmailController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 2, 'eSignature') } + + def create + envelope_args = { + signer_email: param_gsub(params['signerEmail']), + signer_name: param_gsub(params['signerName']), + cc_email: param_gsub(params['ccEmail']), + cc_name: param_gsub(params['ccName']), + status: 'sent', + doc_docx: File.join('data', Rails.application.config.doc_docx), + doc_pdf: File.join('data', Rails.application.config.doc_pdf) + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + results = ESign::Eg002SigningViaEmailService.new(args).worker + session[:envelope_id] = results['envelope_id'] + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results['envelope_id']) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg003_list_envelopes_controller.rb b/app/controllers/e_sign/eeg003_list_envelopes_controller.rb new file mode 100644 index 0000000..459a242 --- /dev/null +++ b/app/controllers/e_sign/eeg003_list_envelopes_controller.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class ESign::Eeg003ListEnvelopesController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 3, 'eSignature') } + + def create + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token] + } + results = ESign::Eg003ListEnvelopesService.new(args).worker + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + render 'ds_common/example_done' + end +end diff --git a/app/controllers/e_sign/eeg004_envelope_info_controller.rb b/app/controllers/e_sign/eeg004_envelope_info_controller.rb new file mode 100644 index 0000000..ea10ea1 --- /dev/null +++ b/app/controllers/e_sign/eeg004_envelope_info_controller.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class ESign::Eeg004EnvelopeInfoController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 4, 'eSignature') } + + def create + envelope_id = session[:envelope_id] + + if envelope_id + begin + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_id: envelope_id + } + results = ESign::Eg004EnvelopeInfoService.new(args).worker + # results is an object that implements ArrayAccess. Convert to a regular array: + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + elsif !envelope_id + @title = @example['ExampleName'] + @envelope_ok = false + end + end +end diff --git a/app/controllers/e_sign/eeg005_envelope_recipients_controller.rb b/app/controllers/e_sign/eeg005_envelope_recipients_controller.rb new file mode 100644 index 0000000..a5d962d --- /dev/null +++ b/app/controllers/e_sign/eeg005_envelope_recipients_controller.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class ESign::Eeg005EnvelopeRecipientsController < EgController + include ApiCreator + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 5, 'eSignature') } + skip_before_action :set_meta + + def create + envelope_id = session[:envelope_id] || nil + + if envelope_id + # 2. Call the worker method + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_id: envelope_id + } + + begin + results = ESign::Eg005EnvelopeRecipientsService.new(args).worker + @title = @example['ExampleName'] + @message = 'Results from the EnvelopesRecipients::list method:' + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + elsif !envelope_id + @title = @example['ExampleName'] + @envelope_ok = false + end + end +end diff --git a/app/controllers/e_sign/eeg006_envelope_docs_controller.rb b/app/controllers/e_sign/eeg006_envelope_docs_controller.rb new file mode 100644 index 0000000..4eb859d --- /dev/null +++ b/app/controllers/e_sign/eeg006_envelope_docs_controller.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +class ESign::Eeg006EnvelopeDocsController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 6, 'eSignature') } + + def create + envelope_id = session[:envelope_id] + + if envelope_id + begin + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_id: envelope_id + } + + standart_doc_items = [ + { name: 'Combined', type: 'content', document_id: 'combined' }, + { name: 'Zip archive', type: 'zip', document_id: 'archive' }, + { name: 'PDF Portfolio', type: 'content', document_id: 'portfolio' } + ] + + results = ESign::Eg006EnvelopeDocsService.new(args).worker + # The certificate of completion is named "summary" + # We give it a better name below. + envelope_doc_items = results.envelope_documents.map do |doc| + new = if doc.document_id == 'certificate' + { document_id: doc.document_id, name: 'Certificate of completion', type: doc.type } + else + { document_id: doc.document_id, name: doc.name, type: doc.type } + end + new + end + + documents = standart_doc_items + envelope_doc_items + envelope_documents = { envelope_id: args[:envelope_id], documents: documents } + session[:envelope_documents] = envelope_documents + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + elsif !envelope_id + @title = @example['ExampleName'] + @envelope_ok = false + end + end +end diff --git a/app/controllers/e_sign/eeg007_envelope_get_doc_controller.rb b/app/controllers/e_sign/eeg007_envelope_get_doc_controller.rb new file mode 100644 index 0000000..59303a8 --- /dev/null +++ b/app/controllers/e_sign/eeg007_envelope_get_doc_controller.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +class ESign::Eeg007EnvelopeGetDocController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 7, 'eSignature') } + + def create + envelope_id = session[:envelope_id] + envelope_documents = session[:envelope_documents] + if envelope_id && envelope_documents + begin + document_id = param_gsub(params['docSelect']) + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_id: envelope_id, + document_id: document_id, + envelope_documents: envelope_documents + } + results = ESign::Eg007EnvelopeGetDocService.new(args).worker + send_data results['data'], filename: results['doc_name'], + content_type: results['mime_type'], + disposition: "attachment; filename=\"#{results['doc_name']}\"" + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + elsif !envelope_id || !envelope_documents + @title = @example['ExampleName'] + @envelope_ok = false + @documents_ok = false + end + end +end diff --git a/app/controllers/e_sign/eeg008_create_template_controller.rb b/app/controllers/e_sign/eeg008_create_template_controller.rb new file mode 100644 index 0000000..4a38528 --- /dev/null +++ b/app/controllers/e_sign/eeg008_create_template_controller.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class ESign::Eeg008CreateTemplateController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 8, 'eSignature') } + + def create + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + template_name: 'Example Signer and CC template v2' + } + results = ESign::Eg008CreateTemplateService.new(args).worker + session[:template_id] = results[:template_id] + session[:workflow_template_id] = results[:template_id] + msg = if results.fetch(:created_new_template) + 'The template has been created!' + else + 'Done. The template already existed in your account.' + end + @title = @example['ExampleName'] + @message = "#{msg} #{format_string(@example['ResultsPageText'], results[:template_name], results[:template_id])}" + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg009_use_template_controller.rb b/app/controllers/e_sign/eeg009_use_template_controller.rb new file mode 100644 index 0000000..9e4f625 --- /dev/null +++ b/app/controllers/e_sign/eeg009_use_template_controller.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +class ESign::Eeg009UseTemplateController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 9, 'eSignature') } + + def create + template_id = session[:template_id] + + if template_id + begin + envelope_args = { + signer_email: params['signerEmail'], + signer_name: params['signerName'], + cc_email: params['ccEmail'], + cc_name: params['ccName'], + template_id: template_id + } + + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + + results = ESign::Eg009UseTemplateService.new(args).worker + + session[:envelope_id] = results[:envelope_id] + # results is an object that implements ArrayAccess. Convert to a regular array: + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results[:envelope_id]) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + elsif !template_id + @title = @example['ExampleName'] + @template_ok = false + end + end +end diff --git a/app/controllers/e_sign/eeg010_send_binary_docs_controller.rb b/app/controllers/e_sign/eeg010_send_binary_docs_controller.rb new file mode 100644 index 0000000..4039f64 --- /dev/null +++ b/app/controllers/e_sign/eeg010_send_binary_docs_controller.rb @@ -0,0 +1,36 @@ +class ESign::Eeg010SendBinaryDocsController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 10, 'eSignature') } + + def create + envelope_args = { + # Validation: Delete any non-usual characters + signer_email: param_gsub(params['signerEmail']), + signer_name: param_gsub(params['signerName']), + cc_email: param_gsub(params['ccEmail']), + cc_name: param_gsub(params['ccName']) + } + + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + + results = ESign::Eg010SendBinaryDocsService.new(args).worker + session[:envelope_id] = results['envelope_id'] + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results['envelope_id']) + render 'ds_common/example_done' + rescue Net::HTTPError => e + if !e.response.nil? + json_response = JSON.parse e.response + @error_code = json_response['errorCode'] + @error_message = json_response['message'] + else + @error_code = 'API request problem' + @error_message = e.to_s + end + end +end diff --git a/app/controllers/e_sign/eeg011_embedded_sending_controller.rb b/app/controllers/e_sign/eeg011_embedded_sending_controller.rb new file mode 100644 index 0000000..ad1f9ac --- /dev/null +++ b/app/controllers/e_sign/eeg011_embedded_sending_controller.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class ESign::Eeg011EmbeddedSendingController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 11, 'eSignature') } + + def create + envelope_args = { + signer_email: param_gsub(params['signerEmail']), + signer_name: param_gsub(params['signerName']), + cc_email: param_gsub(params['ccEmail']), + cc_name: param_gsub(params['ccName']), + status: 'created' + } + + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + starting_view: param_gsub(params['starting_view']), + envelope_args: envelope_args, + ds_return_url: "#{Rails.application.config.app_url}/ds_common-return" + } + + results = ESign::Eg011EmbeddedSendingService.new(args).worker + redirect_to results['redirect_url'] + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg012_embedded_console_controller.rb b/app/controllers/e_sign/eeg012_embedded_console_controller.rb new file mode 100644 index 0000000..b2dc4cf --- /dev/null +++ b/app/controllers/e_sign/eeg012_embedded_console_controller.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class ESign::Eeg012EmbeddedConsoleController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 12, 'eSignature') } + + def create + envelope_id = session[:envelope_id] + + begin + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_id: envelope_id, + starting_view: params['starting_view'], + ds_return_url: "#{Rails.application.config.app_url}/ds_common-return" + } + + results = ESign::Eg012EmbeddedConsoleService.new(args).worker + redirect_to results['redirect_url'] + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + end +end diff --git a/app/controllers/e_sign/eeg013_add_doc_to_template_controller.rb b/app/controllers/e_sign/eeg013_add_doc_to_template_controller.rb new file mode 100644 index 0000000..7bc2333 --- /dev/null +++ b/app/controllers/e_sign/eeg013_add_doc_to_template_controller.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +class ESign::Eeg013AddDocToTemplateController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 13, 'eSignature') } + + def create + template_id = session[:template_id] + + if template_id + # 2. Call the worker method + # More data validation would be a good idea here + # Strip anything other than characters listed + begin + envelope_args = { + signer_email: param_gsub(params['signerEmail']), + signer_name: param_gsub(params['signerName']), + cc_email: param_gsub(params['ccEmail']), + cc_name: param_gsub(params['ccName']), + item: param_gsub(params['item']), + quantity: param_gsub(params['quantity']).to_i, + signer_client_id: 1000, + template_id: template_id, + ds_return_url: "#{Rails.application.config.app_url}/ds_common-return" + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + results = ESign::Eg013AddDocToTemplateService.new(args).worker + # Save for use by other examples + # which need an envelopeId + session[:envelope_id] = results[:envelope_id] + # Redirect the user to the embedded signing + # Don't use an iFrame! + # State can be stored/recovered using the framework's session or a + # query parameter on the returnUrl + redirect_to results[:redirect_url] + rescue DocuSign_eSign::ApiError => e + error = JSON.parse e.response_body + @error_code = error['errorCode'] + @error_message = error['message'] + render 'ds_common/error' + end + elsif !template_id + @title = @example['ExampleName'] + @template_ok = false + end + end +end diff --git a/app/controllers/e_sign/eeg014_collect_payment_controller.rb b/app/controllers/e_sign/eeg014_collect_payment_controller.rb new file mode 100644 index 0000000..f3dd68b --- /dev/null +++ b/app/controllers/e_sign/eeg014_collect_payment_controller.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +class ESign::Eeg014CollectPaymentController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 14, 'eSignature') } + + def create + envelope_args = { + signer_email: param_gsub(params['signerEmail']), + signer_name: param_gsub(params['signerName']), + cc_email: param_gsub(params['ccEmail']), + cc_name: param_gsub(params['ccName']), + gateway_account_id: Rails.application.config.gateway_account_id, + gateway_name: Rails.application.config.gateway_name, + gateway_display_name: Rails.application.config.gateway_display_name + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + + results = ESign::Eg014CollectPaymentService.new(args).worker + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results[:envelope_id]) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + + def get + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + session[:status_cfr] = 'enabled' + @title = 'Not CFR Part 11 compatible' + @error_information = @manifest['SupportingTexts']['CFRError'] + render 'ds_common/error' + end + super + end +end diff --git a/app/controllers/e_sign/eeg015_get_envelope_tab_data_controller.rb b/app/controllers/e_sign/eeg015_get_envelope_tab_data_controller.rb new file mode 100644 index 0000000..f47566b --- /dev/null +++ b/app/controllers/e_sign/eeg015_get_envelope_tab_data_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class ESign::Eeg015GetEnvelopeTabDataController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 15, 'eSignature') } + + def create + envelope_id = session[:envelope_id] + + args = { + access_token: session['ds_access_token'], + base_path: session['ds_base_path'], + account_id: session['ds_account_id'], + envelope_id: envelope_id + } + + results = ESign::Eg015GetEnvelopeTabDataService.new(args).worker + @title = @example['ExampleName'] + @message = 'Results from the EnvelopeFormData::get method:' + @json = results.to_json.to_json + render 'ds_common/example_done' + end +end diff --git a/app/controllers/e_sign/eeg016_set_envelope_tab_data_controller.rb b/app/controllers/e_sign/eeg016_set_envelope_tab_data_controller.rb new file mode 100644 index 0000000..bdcb013 --- /dev/null +++ b/app/controllers/e_sign/eeg016_set_envelope_tab_data_controller.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class ESign::Eeg016SetEnvelopeTabDataController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 16, 'eSignature') } + + def create + args = { + # Validation: Delete any non-usual characters + signer_email: params['signerEmail'].gsub(/([^\w\-.+@, ])+/, ''), + signer_name: params['signerName'].gsub(/([^\w\-., ])+/, ''), + access_token: session['ds_access_token'], + base_path: session['ds_base_path'], + account_id: session['ds_account_id'] + } + + begin + results = ESign::Eg016SetEnvelopeTabDataService.new(args).worker + + # Save for future use within the example launcher + session[:envelope_id] = results[:envelope_id] + + redirect_to results[:url] + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + end +end diff --git a/app/controllers/e_sign/eeg017_set_template_tab_values_controller.rb b/app/controllers/e_sign/eeg017_set_template_tab_values_controller.rb new file mode 100644 index 0000000..e024a70 --- /dev/null +++ b/app/controllers/e_sign/eeg017_set_template_tab_values_controller.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class ESign::Eeg017SetTemplateTabValuesController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 17, 'eSignature') } + + def create + template_id = session[:template_id] + + if template_id + envelope_args = { + signer_email: params['signerEmail'], + signer_name: params['signerName'], + cc_email: params['ccEmail'], + cc_name: params['ccName'], + ds_ping_url: Rails.application.config.app_url, + template_id: template_id + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + begin + redirect_url = ESign::Eg017SetTemplateTabValuesService.new(args).worker + redirect_to redirect_url + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + elsif !template_id + @title = @example['ExampleName'] + @template_ok = false + end + end +end diff --git a/app/controllers/e_sign/eeg018_get_envelope_custom_field_data_controller.rb b/app/controllers/e_sign/eeg018_get_envelope_custom_field_data_controller.rb new file mode 100644 index 0000000..cd05b28 --- /dev/null +++ b/app/controllers/e_sign/eeg018_get_envelope_custom_field_data_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class ESign::Eeg018GetEnvelopeCustomFieldDataController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 18, 'eSignature') } + + def create + envelope_id = session[:envelope_id] + + args = { + access_token: session['ds_access_token'], + base_path: session['ds_base_path'], + account_id: session['ds_account_id'], + envelope_id: envelope_id + } + + results = ESign::Eg018GetEnvelopeCustomFieldDataService.new(args).worker + @title = @example['ExampleName'] + @message = 'Results from the Envelopes::listStatusChanges method:' + @json = results.to_json.to_json + render 'ds_common/example_done' + end +end diff --git a/app/controllers/e_sign/eeg019_access_code_authentication_controller.rb b/app/controllers/e_sign/eeg019_access_code_authentication_controller.rb new file mode 100644 index 0000000..eb02c89 --- /dev/null +++ b/app/controllers/e_sign/eeg019_access_code_authentication_controller.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class ESign::Eeg019AccessCodeAuthenticationController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 19, 'eSignature') } + + def create + # ***DS.snippet.0.start + envelope_args = { + signer_email: param_gsub(params['signerEmail']), + signer_name: param_gsub(params['signerName']), + accessCode: params['accessCode'], + status: 'sent' + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + + results = ESign::Eg019AccessCodeAuthenticationService.new(args).worker + session[:envelope_id] = results.envelope_id + + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results.envelope_id) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + # ***DS.snippet.0.end +end diff --git a/app/controllers/e_sign/eeg020_phone_authentication_controller.rb b/app/controllers/e_sign/eeg020_phone_authentication_controller.rb new file mode 100644 index 0000000..92aa261 --- /dev/null +++ b/app/controllers/e_sign/eeg020_phone_authentication_controller.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +class ESign::Eeg020PhoneAuthenticationController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 20, 'eSignature') } + + def create + envelope_args = { + signer_email: param_gsub(params['signer_email']), + signer_name: param_gsub(params['signer_name']), + country_code: param_gsub(params['country_code']), + phone_number: param_gsub(params['phone_number']), + status: 'sent' + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + + if Rails.application.config.signer_email == envelope_args[:signer_email] + @error_code = 400 + @error_message = @manifest['SupportingTexts']['IdenticalEmailsNotAllowedErrorMessage'] + return render 'ds_common/error' + end + + phone_auth_service = ESign::Eg020PhoneAuthenticationService.new(args) + + # Retrieve the workflow id + workflow_id = phone_auth_service.get_workflow + session[:workflow_id] = workflow_id + + results = phone_auth_service.worker(workflow_id) + + if results.to_s == 'phone_auth_not_enabled' + @error_code = 'IDENTITY_WORKFLOW_INVALID_ID' + @error_message = 'The identity workflow ID specified is not valid.' + @error_information = @example['CustomErrorTexts'][0]['ErrorMessage'] + render 'ds_common/error' + else + session[:envelope_id] = results.envelope_id + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results.envelope_id) + render 'ds_common/example_done' + end + end + + def get + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + session[:status_cfr] = 'enabled' + @title = 'Not CFR Part 11 compatible' + @error_information = @manifest['SupportingTexts']['CFRError'] + render 'ds_common/error' + end + super + end +end diff --git a/app/controllers/e_sign/eeg022_kba_authentication_controller.rb b/app/controllers/e_sign/eeg022_kba_authentication_controller.rb new file mode 100644 index 0000000..3601b79 --- /dev/null +++ b/app/controllers/e_sign/eeg022_kba_authentication_controller.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class ESign::Eeg022KbaAuthenticationController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 22, 'eSignature') } + + def create + envelope_args = { + signer_email: param_gsub(params['signerEmail']), + signer_name: param_gsub(params['signerName']), + status: 'sent' + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + + if Rails.application.config.signer_email == envelope_args[:signer_email] + @error_code = 400 + @error_message = @manifest['SupportingTexts']['IdenticalEmailsNotAllowedErrorMessage'] + return render 'ds_common/error' + end + + results = ESign::Eg022KbaAuthenticationService.new(args).worker + session[:envelope_id] = results.envelope_id + + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results.envelope_id) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg023_idv_authentication_controller.rb b/app/controllers/e_sign/eeg023_idv_authentication_controller.rb new file mode 100644 index 0000000..ccc50c2 --- /dev/null +++ b/app/controllers/e_sign/eeg023_idv_authentication_controller.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +class ESign::Eeg023IdvAuthenticationController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 23, 'eSignature') } + + def create + envelope_args = { + signer_email: param_gsub(params['signerEmail']), + signer_name: param_gsub(params['signerName']), + status: 'sent' + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + + if Rails.application.config.signer_email == envelope_args[:signer_email] + @error_code = 400 + @error_message = @manifest['SupportingTexts']['IdenticalEmailsNotAllowedErrorMessage'] + return render 'ds_common/error' + end + + results = ESign::Eg023IdvAuthenticationService.new(args).worker + + if results.to_s == 'idv_not_enabled' + @error_code = 'IDENTITY_WORKFLOW_INVALID_ID' + @error_message = 'The identity workflow ID specified is not valid.' + @error_information = @example['CustomErrorTexts'][0]['ErrorMessage'] + render 'ds_common/error' + else + session[:envelope_id] = results.envelope_id + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results.envelope_id) + render 'ds_common/example_done' + end + rescue DocuSign_eSign::ApiError => e + error = JSON.parse e.response_body + @error_code = error['errorCode'] + @error_message = error['message'] + render 'ds_common/error' + end + + # ***DS.snippet.0.end +end diff --git a/app/controllers/e_sign/eeg024_permission_create_controller.rb b/app/controllers/e_sign/eeg024_permission_create_controller.rb new file mode 100644 index 0000000..d477569 --- /dev/null +++ b/app/controllers/e_sign/eeg024_permission_create_controller.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class ESign::Eeg024PermissionCreateController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 24, 'eSignature') } + + def create + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + permission_profile_name: params[:permission_profile_name] + } + + results = ESign::Eg024PermissionCreateService.new(args).worker + # Step 4. a) Call the eSignature API + # b) Display the JSON response + @title = @example['ExampleName'] + @message = 'Permission profile was created' + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg025_permissions_set_user_group_controller.rb b/app/controllers/e_sign/eeg025_permissions_set_user_group_controller.rb new file mode 100644 index 0000000..505dd15 --- /dev/null +++ b/app/controllers/e_sign/eeg025_permissions_set_user_group_controller.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +class ESign::Eeg025PermissionsSetUserGroupController < EgController + include ApiCreator + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 25, 'eSignature') } + + def get + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'] + } + accounts_api = create_account_api(args) + permissions = accounts_api.list_permissions(args[:account_id], DocuSign_eSign::ListPermissionsOptions.default) + @permissions_lists = permissions.permission_profiles + # Get a user group + group_api = create_group_api(args) + @group_lists = group_api.list_groups(args[:account_id], DocuSign_eSign::ListGroupsOptions.default) + super + end + + def create + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + permission_profile_id: params[:lists], + group_id: params[:group_lists] + } + + results = ESign::Eg025PermissionsSetUserGroupService.new(args).worker + # Step 4. a) Call the eSignature API + # b) Display the JSON response + @title = @example['ExampleName'] + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg026_permissions_change_single_setting_controller.rb b/app/controllers/e_sign/eeg026_permissions_change_single_setting_controller.rb new file mode 100644 index 0000000..9929a49 --- /dev/null +++ b/app/controllers/e_sign/eeg026_permissions_change_single_setting_controller.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +class ESign::Eeg026PermissionsChangeSingleSettingController < EgController + include ApiCreator + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 26, 'eSignature') } + + def get + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'] + } + accounts_api = create_account_api(args) + permissions = accounts_api.list_permissions(args[:account_id], DocuSign_eSign::ListPermissionsOptions.default) + @permissions_lists = permissions.permission_profiles + super + end + + def create + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + permission_profile_id: params[:lists] + } + + results = ESign::Eg026PermissionsChangeSingleSettingService.new(args).worker + # Step 4. a) Call the eSignature API + # b) Display the JSON response + @title = @example['ExampleName'] + @message = 'Existing permission profile was changed' + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg027_permissions_delete_controller.rb b/app/controllers/e_sign/eeg027_permissions_delete_controller.rb new file mode 100644 index 0000000..95291b4 --- /dev/null +++ b/app/controllers/e_sign/eeg027_permissions_delete_controller.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +class ESign::Eeg027PermissionsDeleteController < EgController + include ApiCreator + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 27, 'eSignature') } + + def get + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'] + } + accounts_api = create_account_api(args) + permissions = accounts_api.list_permissions(args[:account_id], DocuSign_eSign::ListPermissionsOptions.default) + @permissions_lists = permissions.permission_profiles + super + end + + def create + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + permission_profile_id: params[:lists] + } + + ESign::Eg027PermissionsDeleteService.new(args).worker + # Step 4. a) Call the eSignature API + # b) Display the JSON response + @title = @example['ExampleName'] + @message = "Permission profile #{params[:lists]} was deleted" + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg028_brands_creating_controller.rb b/app/controllers/e_sign/eeg028_brands_creating_controller.rb new file mode 100644 index 0000000..6de5bec --- /dev/null +++ b/app/controllers/e_sign/eeg028_brands_creating_controller.rb @@ -0,0 +1,25 @@ +class ESign::Eeg028BrandsCreatingController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 28, 'eSignature') } + + def create + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + brandName: params[:brandName], + defaultBrandLanguage: params[:defaultBrandLanguage] + } + + results = ESign::Eg028BrandsCreatingService.new(args).worker + # Step 4. a) Call the eSignature API + # b) Display the JSON response + brand_id = results.brands[0].brand_id + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], brand_id) + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg029_brands_apply_to_envelope_controller.rb b/app/controllers/e_sign/eeg029_brands_apply_to_envelope_controller.rb new file mode 100644 index 0000000..5b13850 --- /dev/null +++ b/app/controllers/e_sign/eeg029_brands_apply_to_envelope_controller.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +class ESign::Eeg029BrandsApplyToEnvelopeController < EgController + include ApiCreator + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 29, 'eSignature') } + + def get + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'] + } + accounts_api = create_account_api(args) + brand_lists = accounts_api.list_brands(args[:account_id], DocuSign_eSign::ListBrandsOptions.default) + @brand_names = brand_lists.brands + super + end + + def create + envelope_args = { + signer_email: param_gsub(params[:signerEmail]), + signer_name: param_gsub(params[:signerName]), + brand_id: params[:brands], + status: 'sent' + + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + + results = ESign::Eg029BrandsApplyToEnvelopeService.new(args).worker + session[:envelope_id] = results.envelope_id + + # Step 4. a) Call the eSignature API + # b) Display the JSON response + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results.envelope_id) + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg030_brands_apply_to_template_controller.rb b/app/controllers/e_sign/eeg030_brands_apply_to_template_controller.rb new file mode 100644 index 0000000..0bdbc01 --- /dev/null +++ b/app/controllers/e_sign/eeg030_brands_apply_to_template_controller.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: truebrand_lists = accounts_api.list_brands(args[:account_id], options = DocuSign_eSign::ListBrandsOptions.default) + +class ESign::Eeg030BrandsApplyToTemplateController < EgController + include ApiCreator + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 30, 'eSignature') } + + def get + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'] + } + accounts_api = create_account_api(args) + brand_lists = accounts_api.list_brands(args[:account_id], DocuSign_eSign::ListBrandsOptions.default) + @brand_names = brand_lists.brands + super + end + + def create + template_id = session[:template_id] + + if template_id + begin + envelope_args = { + signer_email: param_gsub(params[:signerEmail]), + signer_name: param_gsub(params[:signerName]), + cc_email: param_gsub(params[:ccEmail]), + cc_name: param_gsub(params[:ccName]), + brand_id: params[:brands], + template_id: template_id, + status: 'sent' + + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + + results = ESign::Eg030BrandsApplyToTemplateService.new(args).worker + session[:envelope_id] = results.envelope_id + + # Step 4. a) Call the eSignature API + # b) Display the JSON response + # brand_id = results.brands[0].brand_id + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results.envelope_id) + @json = results.to_json.to_json + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + elsif !template_id + @title = @example['ExampleName'] + @template_ok = false + end + end +end diff --git a/app/controllers/e_sign/eeg031_bulk_sending_envelopes_controller.rb b/app/controllers/e_sign/eeg031_bulk_sending_envelopes_controller.rb new file mode 100644 index 0000000..a4b4e84 --- /dev/null +++ b/app/controllers/e_sign/eeg031_bulk_sending_envelopes_controller.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +class ESign::Eeg031BulkSendingEnvelopesController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 31, 'eSignature') } + + def create + signers = { + signer_email: param_gsub(params['signerEmail1']), + signer_name: param_gsub(params['signerName1']), + cc_email: param_gsub(params['ccEmail1']), + cc_name: param_gsub(params['ccName1']), + status: 'created', + + signer_email1: param_gsub(params['signerEmail2']), + signer_name1: param_gsub(params['signerName2']), + cc_email1: param_gsub(params['ccEmail2']), + cc_name1: param_gsub(params['ccName2']) + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'] + } + + results = ESign::Eg031BulkSendingEnvelopesService.new(args, signers).worker + # Step 4. a) Call the eSignature API + # b) Display the JSON response + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg032_pauses_signature_workflow_controller.rb b/app/controllers/e_sign/eeg032_pauses_signature_workflow_controller.rb new file mode 100644 index 0000000..cf7e25c --- /dev/null +++ b/app/controllers/e_sign/eeg032_pauses_signature_workflow_controller.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +class ESign::Eeg032PausesSignatureWorkflowController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 32, 'eSignature') } + + def create + signers = { + signerEmail1: param_gsub(params['signerEmail1']), + signerName1: param_gsub(params['signerName1']), + signerEmail2: param_gsub(params['signerEmail2']), + signerName2: param_gsub(params['signerName2']) + } + args = { + accountId: session['ds_account_id'], + basePath: session['ds_base_path'], + accessToken: session['ds_access_token'], + status: 'sent' + } + + results = ESign::Eg032PausesSignatureWorkflowService.new(args, signers).worker + + @envelop_id = results.to_hash[:envelopeId].to_s + session[:envelope_id] = @envelop_id + + render 'e_sign/eeg032_pauses_signature_workflow/return' + end + + def get + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + session[:status_cfr] = 'enabled' + @title = 'Not CFR Part 11 compatible' + @error_information = @manifest['SupportingTexts']['CFRError'] + render 'ds_common/error' + end + super + end +end diff --git a/app/controllers/e_sign/eeg033_unpauses_signature_workflow_controller.rb b/app/controllers/e_sign/eeg033_unpauses_signature_workflow_controller.rb new file mode 100644 index 0000000..001a3bc --- /dev/null +++ b/app/controllers/e_sign/eeg033_unpauses_signature_workflow_controller.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class ESign::Eeg033UnpausesSignatureWorkflowController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 33, 'eSignature') } + + def update + args = { + accountId: session['ds_account_id'], + basePath: session['ds_base_path'], + accessToken: session['ds_access_token'], + envelopeId: session['envelope_id'], + status: 'in_progress' + } + + results = ESign::Eg033UnpausesSignatureWorkflowService.new(args).worker + + @envelop_id = results.to_hash[:envelopeId].to_s + render 'e_sign/eeg033_unpauses_signature_workflow/return' + end + + def get + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + session[:status_cfr] = 'enabled' + @title = 'Not CFR Part 11 compatible' + @error_information = @manifest['SupportingTexts']['CFRError'] + render 'ds_common/error' + end + super + end +end diff --git a/app/controllers/e_sign/eeg034_use_conditional_recipients_controller.rb b/app/controllers/e_sign/eeg034_use_conditional_recipients_controller.rb new file mode 100644 index 0000000..ab7e5a2 --- /dev/null +++ b/app/controllers/e_sign/eeg034_use_conditional_recipients_controller.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +class ESign::Eeg034UseConditionalRecipientsController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 34, 'eSignature') } + + def create + signers = { + signerEmail1: param_gsub(params['signerEmail1']), + signerName1: param_gsub(params['signerName1']), + + signerEmailNotChecked: param_gsub(params['signerEmailNotChecked']), + signerNameNotChecked: param_gsub(params['signerNameNotChecked']), + + signerEmailChecked: param_gsub(params['signerEmailChecked']), + signerNameChecked: param_gsub(params['signerNameChecked']) + } + + args = { + accountId: session['ds_account_id'], + basePath: session['ds_base_path'], + accessToken: session['ds_access_token'] + } + + results = ESign::Eg034UseConditionalRecipientsService.new(args, signers).worker + @envelop_id = results.to_hash[:envelopeId].to_s + render 'e_sign/eeg034_use_conditional_recipients/return' + rescue DocuSign_eSign::ApiError => e + error = JSON.parse e.response_body + @error_code = error['errorCode'] + if error['errorCode']['WORKFLOW_UPDATE_RECIPIENTROUTING_NOT_ALLOWED'] + @error_message = @example['CustomErrorTexts'][0]['ErrorMessage'] + @error_information = @example['CustomErrorTexts'][0]['ErrorMessage'] + else + @error_message = error['message'] + end + render 'ds_common/error' + end + + def get + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + session[:status_cfr] = 'enabled' + @title = 'Not CFR Part 11 compatible' + @error_information = @manifest['SupportingTexts']['CFRError'] + render 'ds_common/error' + end + super + end +end diff --git a/app/controllers/e_sign/eeg035_scheduled_sending_controller.rb b/app/controllers/e_sign/eeg035_scheduled_sending_controller.rb new file mode 100644 index 0000000..ee6a766 --- /dev/null +++ b/app/controllers/e_sign/eeg035_scheduled_sending_controller.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class ESign::Eeg035ScheduledSendingController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 35, 'eSignature') } + + def create + envelope_args = { + signer_email: param_gsub(params['signer_email']), + signer_name: param_gsub(params['signer_name']), + resume_date: param_gsub(params['resume_date']), + status: 'sent' + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + results = ESign::Eg035ScheduledSendingService.new(args).worker + session[:envelope_id] = results['envelope_id'] + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results['envelope_id']) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg036_delayed_routing_controller.rb b/app/controllers/e_sign/eeg036_delayed_routing_controller.rb new file mode 100644 index 0000000..e863437 --- /dev/null +++ b/app/controllers/e_sign/eeg036_delayed_routing_controller.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +class ESign::Eeg036DelayedRoutingController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 36, 'eSignature') } + + def create + envelope_args = { + signer1_email: param_gsub(params['signer1Email']), + signer1_name: param_gsub(params['signer1Name']), + signer2_email: param_gsub(params['signer2Email']), + signer2_name: param_gsub(params['signer2Name']), + delay: param_gsub(params['delay']), + status: 'sent' + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + results = ESign::Eg036DelayedRoutingService.new(args).worker + session[:envelope_id] = results['envelope_id'] + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results['envelope_id']) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + + def get + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + session[:status_cfr] = 'enabled' + @title = 'Not CFR Part 11 compatible' + @error_information = @manifest['SupportingTexts']['CFRError'] + render 'ds_common/error' + end + super + end +end diff --git a/app/controllers/e_sign/eeg037_sms_delivery_controller.rb b/app/controllers/e_sign/eeg037_sms_delivery_controller.rb new file mode 100644 index 0000000..dc82fa9 --- /dev/null +++ b/app/controllers/e_sign/eeg037_sms_delivery_controller.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class ESign::Eeg037SmsDeliveryController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 37, 'eSignature') } + + def create + envelope_args = { + delivery_method: param_gsub(params['delivery_method']), + signer_name: param_gsub(params['signer_name']), + cc_name: param_gsub(params['cc_name']), + cc_phone_number: param_gsub(params['cc_phone_number']), + cc_country_code: param_gsub(params['cc_country_code']), + phone_number: param_gsub(params['phone_number']), + country_code: param_gsub(params['country_code']), + status: 'sent' + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + + results = ESign::Eg037SmsDeliveryService.new(args).worker + session[:envelope_id] = results['envelope_id'] + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results['envelope_id']) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + + def get + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + session[:status_cfr] = 'enabled' + @title = 'Not CFR Part 11 compatible' + @error_information = @manifest['SupportingTexts']['CFRError'] + render 'ds_common/error' + end + super + end +end diff --git a/app/controllers/e_sign/eeg038_responsive_signing_controller.rb b/app/controllers/e_sign/eeg038_responsive_signing_controller.rb new file mode 100644 index 0000000..cd05084 --- /dev/null +++ b/app/controllers/e_sign/eeg038_responsive_signing_controller.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class ESign::Eeg038ResponsiveSigningController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 38, 'eSignature') } + + def create + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + signer_email: param_gsub(params[:signerEmail]), + signer_name: param_gsub(params[:signerName]), + cc_email: param_gsub(params[:ccEmail]), + cc_name: param_gsub(params[:ccName]), + ds_ping_url: Rails.application.config.app_url, + signer_client_id: 1000, + doc_file: 'data/order_form.html' + } + + redirect_url = ESign::Eg038ResponsiveSigningService.new(args).worker + redirect_to redirect_url + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg039_signing_in_person_controller.rb b/app/controllers/e_sign/eeg039_signing_in_person_controller.rb new file mode 100644 index 0000000..d6e2222 --- /dev/null +++ b/app/controllers/e_sign/eeg039_signing_in_person_controller.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +class ESign::Eeg039SigningInPersonController < EgController + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 39, 'eSignature') } + + def create + minimum_buffer_min = 10 + token_ok = check_token(minimum_buffer_min) + unless token_ok + flash[:messages] = 'Sorry, you need to re-authenticate.' + # We could store the parameters of the requested operation + # so it could be restarted automatically. + # But since it should be rare to have a token issue here, + # we'll make the user re-enter the form data after + # authentication. + return redirect_to '/ds/mustAuthenticate' + end + + access_token = session[:ds_access_token] + base_path = session[:ds_base_path] + email = ESign::GetDataService.new(access_token, base_path).get_current_user_email + name = ESign::GetDataService.new(access_token, base_path).get_current_user_name + + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + host_email: param_gsub(email), + host_name: param_gsub(name), + signer_name: param_gsub(params[:signer_name]), + ds_ping_url: Rails.application.config.app_url, + pdf_filename: 'data/World_Wide_Corp_lorem.pdf' + } + + redirect_url = ESign::Eg039SigningInPersonService.new(args).worker + redirect_to redirect_url + end + + def get + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + session[:status_cfr] = 'enabled' + @title = 'Not CFR Part 11 compatible' + @error_information = @manifest['SupportingTexts']['CFRError'] + render 'ds_common/error' + end + super + end +end diff --git a/app/controllers/e_sign/eeg040_set_document_visibility_controller.rb b/app/controllers/e_sign/eeg040_set_document_visibility_controller.rb new file mode 100644 index 0000000..0e75106 --- /dev/null +++ b/app/controllers/e_sign/eeg040_set_document_visibility_controller.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +class ESign::Eeg040SetDocumentVisibilityController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 40, 'eSignature') } + + def create + envelope_args = { + signer1_email: param_gsub(params['signer1Email']), + signer1_name: param_gsub(params['signer1Name']), + signer2_email: param_gsub(params['signer2Email']), + signer2_name: param_gsub(params['signer2Name']), + cc_email: param_gsub(params['ccEmail']), + cc_name: param_gsub(params['ccName']), + status: 'sent', + doc_docx: File.join('data', Rails.application.config.doc_docx), + doc_pdf: File.join('data', Rails.application.config.doc_pdf) + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + results = ESign::Eg040SetDocumentVisibilityService.new(args).worker + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results['envelope_id']) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + error = JSON.parse e.response_body + + if error['errorCode'] == 'ACCOUNT_LACKS_PERMISSIONS' + @error_information = @example['CustomErrorTexts'][0]['ErrorMessage'] + + @error_code = error['errorCode'] + @error_message = error['error_description'] || error['message'] + + return render 'ds_common/error' + end + + handle_error(e) + end + + def get + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + session[:status_cfr] = 'enabled' + @title = 'Not CFR Part 11 compatible' + @error_information = @manifest['SupportingTexts']['CFRError'] + render 'ds_common/error' + end + super + end +end diff --git a/app/controllers/e_sign/eeg041_cfr_embedded_signing_controller.rb b/app/controllers/e_sign/eeg041_cfr_embedded_signing_controller.rb new file mode 100644 index 0000000..99357c6 --- /dev/null +++ b/app/controllers/e_sign/eeg041_cfr_embedded_signing_controller.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require_relative '../../services/utils' + +class ESign::Eeg041CfrEmbeddedSigningController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 41, 'eSignature') } + + def create + pdf_file_path = 'data/World_Wide_Corp_lorem.pdf' + + pdf_file_path = '../data/World_Wide_Corp_lorem.pdf' unless File.exist?(pdf_file_path) + + envelope_args = { + signer_email: param_gsub(params['signerEmail']), + signer_name: param_gsub(params['signerName']), + country_code: param_gsub(params['countryCode']), + phone_number: param_gsub(params['phoneNumber']), + signer_client_id: 1000, + ds_return_url: "#{Rails.application.config.app_url}/ds_common-return", + ds_ping_url: "#{Rails.application.config.app_url}/", + pdf_filename: pdf_file_path + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + redirect_url = ESign::Eg041CfrEmbeddedSigningService.new(args).worker + + if redirect_url.to_s == 'invalid_workflow_id' + @error_code = 'IDENTITY_WORKFLOW_INVALID_ID' + @error_message = 'The identity workflow ID specified is not valid.' + render 'ds_common/error' + else + redirect_to redirect_url + end + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + + def get + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR != 'enabled' + @title = 'Must use a CFR Part 11 enabled account' + @error_information = 'This example requires a CFR Part 11 account. Please return to the homepage to run one of the examples that is compatible or authenticate with a different account.' + render 'ds_common/error' + end + super + end +end diff --git a/app/controllers/e_sign/eeg042_document_generation_controller.rb b/app/controllers/e_sign/eeg042_document_generation_controller.rb new file mode 100644 index 0000000..3a12f3f --- /dev/null +++ b/app/controllers/e_sign/eeg042_document_generation_controller.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require_relative '../../services/utils' + +class ESign::Eeg042DocumentGenerationController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 42, 'eSignature') } + + def create + envelope_args = { + candidate_email: param_gsub(params['candidate_email']), + candidate_name: param_gsub(params['candidate_name']), + manager_name: param_gsub(params['manager_name']), + job_title: param_gsub(params['job_title']), + salary: param_gsub(params['salary']), + rsus: param_gsub(params['rsus']), + start_date: param_gsub(params['start_date']), + doc_file: File.join('data', Rails.application.config.offer_letter_dynamic_table) + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + results = ESign::Eg042DocumentGenerationService.new(args).worker + session[:envelope_id] = results['envelope_id'] + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results['envelope_id']) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg043_shared_access_controller.rb b/app/controllers/e_sign/eeg043_shared_access_controller.rb new file mode 100644 index 0000000..35ce36b --- /dev/null +++ b/app/controllers/e_sign/eeg043_shared_access_controller.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +require_relative '../../services/utils' + +class ESign::Eeg043SharedAccessController < EgController + before_action :authenticate_agent, only: [:list_envelopes] + before_action -> { check_auth('eSignature') }, except: %i[reauthenticate list_envelopes] + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 43, 'eSignature') } + + def create_agent + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + email: params['email'], + user_name: param_gsub(params['user_name']), + activation: param_gsub(params['activation']) + } + results = ESign::Eg043SharedAccessService.new(args).create_agent + session[:agent_user_id] = results.user_id + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + @redirect_url = '/eeg043auth' + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + + def create_authorization + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + agent_user_id: session['agent_user_id'] + } + args[:user_id] = Utils::DocuSignUtils.new.get_user_id args + session[:principal_user_id] = args[:user_id] + + ESign::Eg043SharedAccessService.new(args).create_authorization + + additional_page_data = @example['AdditionalPage'].find { |p| p['Name'] == 'authenticate_as_agent' } + @title = 'Authenticate as the agent' + @message = additional_page_data['ResultsPageText'] + @redirect_url = '/eeg043reauthenticate' + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + error = JSON.parse e.response_body + if error['errorCode'] == 'USER_NOT_FOUND' + additional_page_data = @example['AdditionalPage'].find { |p| p['Name'] == 'user_not_found' } + @title = 'Authenticate as the agent' + @message = additional_page_data['ResultsPageText'] + @redirect_url = '/eeg043auth' + return render 'ds_common/example_done' + end + handle_error(e) + end + + def reauthenticate + logout + redirect_to '/eeg043envelopes' + end + + def list_envelopes + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + user_id: session[:principal_user_id] + } + results = ESign::Eg043SharedAccessService.new(args).get_envelopes + + if results.result_set_size.to_i.positive? + additional_page_data = @example['AdditionalPage'].find { |p| p['Name'] == 'list_status_successful' } + @title = "Principal's envelopes visible in the agent's Shared Access UI" + @json = results.to_json.to_json + else + additional_page_data = @example['AdditionalPage'].find { |p| p['Name'] == 'list_status_unsuccessful' } + @title = "No envelopes in the principal user's account" + end + + @message = additional_page_data['ResultsPageText'] + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + + private + + def logout + session.delete :ds_access_token + session.delete :ds_account_id + session.delete :ds_expires_at + session.delete 'omniauth.state' + session.delete 'omniauth.params' + session.delete 'omniauth.origin' + end + + def authenticate_agent + minimum_buffer_min = 10 + token_ok = check_token(minimum_buffer_min) + return if token_ok + + session[:eg] = 'eeg043envelopes' + redirect_to '/ds/mustAuthenticate' + end +end diff --git a/app/controllers/e_sign/eeg044_focused_view_controller.rb b/app/controllers/e_sign/eeg044_focused_view_controller.rb new file mode 100644 index 0000000..a4f391c --- /dev/null +++ b/app/controllers/e_sign/eeg044_focused_view_controller.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class ESign::Eeg044FocusedViewController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 44) } + + def create + pdf_file_path = 'data/World_Wide_Corp_lorem.pdf' + + pdf_file_path = '../data/World_Wide_Corp_lorem.pdf' unless File.exist?(pdf_file_path) + + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + signer_email: param_gsub(params[:signerEmail]), + signer_name: param_gsub(params[:signerName]), + ds_ping_url: Rails.application.config.app_url, + signer_client_id: 1000, + pdf_filename: pdf_file_path + } + + @integration_key = Rails.application.config.integration_key + @url = ESign::Eg044FocusedViewService.new(args).worker + render 'e_sign/eeg044_focused_view/embed' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/e_sign/eeg045_delete_restore_envelope_controller.rb b/app/controllers/e_sign/eeg045_delete_restore_envelope_controller.rb new file mode 100644 index 0000000..dfb3dd7 --- /dev/null +++ b/app/controllers/e_sign/eeg045_delete_restore_envelope_controller.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +require_relative '../../services/utils' + +class ESign::Eeg045DeleteRestoreEnvelopeController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 45, 'eSignature') } + + DELETE_FOLDER_ID = 'recyclebin' + + def delete_envelope + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_id: param_gsub(params['envelope_id']), + delete_folder_id: DELETE_FOLDER_ID + } + + delete_restore_envelope_service = ESign::Eg045DeleteRestoreEnvelopeService.new + + delete_restore_envelope_service.delete_envelope args + + session[:envelope_id] = args[:envelope_id] + additional_page_data = @example['AdditionalPage'].find { |p| p['Name'] == 'envelope_is_deleted' } + @title = @example['ExampleName'] + @message = format_string(additional_page_data['ResultsPageText'], args[:envelope_id]) + @redirect_url = "/#{session[:eg]}restore" + + render 'ds_common/example_done' + end + + def restore_envelope + folder_name = param_gsub(params['folder_name']) + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_id: param_gsub(session[:envelope_id]), + from_folder_id: DELETE_FOLDER_ID + } + + delete_restore_envelope_service = ESign::Eg045DeleteRestoreEnvelopeService.new + + folders = delete_restore_envelope_service.get_folders args + args[:folder_id] = Utils::DocuSignUtils.new.get_folder_id_by_name(folders.folders, folder_name) + + if args[:folder_id].nil? || args[:folder_id].empty? + additional_page_data = @example['AdditionalPage'].find { |p| p['Name'] == 'folder_does_not_exist' } + @title = @example['ExampleName'] + @message = format_string(additional_page_data['ResultsPageText'], folder_name) + @redirect_url = "/#{session[:eg]}restore" + + return render 'ds_common/example_done' + end + + delete_restore_envelope_service.move_envelope_to_folder args + + session[:envelope_id] = args[:envelope_id] + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], session[:envelope_id], args[:folder_id], folder_name) + render 'ds_common/example_done' + end + + def get_delete_envelope + get + @envelope_id = session[:envelope_id] + @submit_button_text = @manifest['SupportingTexts']['HelpingTexts']['SubmitButtonDeleteText'] + + render 'e_sign/eeg045_delete_restore_envelope/delete' + end + + def get_restore_envelope + return redirect_to "/#{session[:eg]}" if session[:envelope_id].nil? + + get + @envelope_id = session[:envelope_id] + @submit_button_text = @manifest['SupportingTexts']['HelpingTexts']['SubmitButtonRestoreText'] + + render 'e_sign/eeg045_delete_restore_envelope/restore' + end +end diff --git a/app/controllers/e_sign/eeg046_multiple_delivery_controller.rb b/app/controllers/e_sign/eeg046_multiple_delivery_controller.rb new file mode 100644 index 0000000..6d391b8 --- /dev/null +++ b/app/controllers/e_sign/eeg046_multiple_delivery_controller.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +class ESign::Eeg046MultipleDeliveryController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 46, 'eSignature') } + + def create + envelope_args = { + delivery_method: param_gsub(params['delivery_method']), + signer_name: param_gsub(params['signer_name']), + signer_email: param_gsub(params['signer_email']), + cc_name: param_gsub(params['cc_name']), + cc_email: param_gsub(params['cc_email']), + cc_phone_number: param_gsub(params['cc_phone_number']), + cc_country_code: param_gsub(params['cc_country_code']), + phone_number: param_gsub(params['phone_number']), + country_code: param_gsub(params['country_code']), + status: 'sent' + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + + results = ESign::Eg046MultipleDeliveryService.new(args).worker + session[:envelope_id] = results['envelope_id'] + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results['envelope_id']) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + error = JSON.parse e.response_body + error_message = error['error_description'] || error['message'] || error['error'] + + if error_message.include?('ACCOUNT_LACKS_PERMISSIONS') + @error_code = 'ACCOUNT_LACKS_PERMISSIONS' + @error_message = @example['CustomErrorTexts'][0]['ErrorMessage'] + return render 'ds_common/error' + end + + handle_error(e) + end + + def get + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + session[:status_cfr] = 'enabled' + @title = 'Not CFR Part 11 compatible' + @error_information = @manifest['SupportingTexts']['CFRError'] + render 'ds_common/error' + end + super + end +end diff --git a/app/controllers/e_sign/eg001_embedded_signing_controller.rb b/app/controllers/e_sign/eg001_embedded_signing_controller.rb deleted file mode 100644 index 3b00793..0000000 --- a/app/controllers/e_sign/eg001_embedded_signing_controller.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg001EmbeddedSigningController < EgController - def create - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation - # so it could be restarted automatically. - # But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after - # authentication. - redirect_to '/ds/mustAuthenticate' - end - redirect_url = ESign::Eg001EmbeddedSigningService.new(session, request).call - redirect_to redirect_url - end - - def get - session[:been_here] = true - super - end -end diff --git a/app/controllers/e_sign/eg002_signing_via_email_controller.rb b/app/controllers/e_sign/eg002_signing_via_email_controller.rb deleted file mode 100644 index 09d215f..0000000 --- a/app/controllers/e_sign/eg002_signing_via_email_controller.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg002SigningViaEmailController < EgController - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg002SigningViaEmailService.new(session, request, 'sent').call - session[:envelope_id] = results['envelope_id'] - @title = 'Envelope sent' - @h1 = 'Envelope sent' - @message = "The envelope has been created and sent!
Envelope ID #{results['envelope_id']}." - render 'ds_common/example_done' - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation - # so it could be restarted automatically. - # But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after - # authentication. - redirect_to '/' - end - end -end diff --git a/app/controllers/e_sign/eg003_list_envelopes_controller.rb b/app/controllers/e_sign/eg003_list_envelopes_controller.rb deleted file mode 100644 index 235c808..0000000 --- a/app/controllers/e_sign/eg003_list_envelopes_controller.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg003ListEnvelopesController < EgController - def create - minimum_buffer_min = 3 - token_ok = check_token(minimum_buffer_min) - if token_ok - results = ESign::Eg003ListEnvelopesService.new(session).call - @h1 = 'List envelopes results' - @message = 'Results from the Envelopes::listStatusChanges method:' - @json = results.to_json.to_json - render 'ds_common/example_done' - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically. - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication. - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg004_envelope_info_controller.rb b/app/controllers/e_sign/eg004_envelope_info_controller.rb deleted file mode 100644 index f598872..0000000 --- a/app/controllers/e_sign/eg004_envelope_info_controller.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg004EnvelopeInfoController < EgController - def create - minimum_buffer_min = 3 - envelope_id = session[:envelope_id] - token_ok = check_token(minimum_buffer_min) - - if token_ok && envelope_id - begin - results = ESign::Eg004EnvelopeInfoService.new(session, envelope_id).call - # results is an object that implements ArrayAccess. Convert to a regular array: - @title = 'Envelope status results' - @h1 = 'Envelope status results' - @message = 'Results from the Envelopes::get method:' - @json = results.to_json.to_json - render 'ds_common/example_done' - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - elsif !token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation - # so it could be restarted automatically. - # But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after - # authentication. - redirect_to '/ds/mustAuthenticate' - elsif !envelope_id - @title = 'Envelope information' - @envelope_ok = false - end - end -end diff --git a/app/controllers/e_sign/eg005_envelope_recipients_controller.rb b/app/controllers/e_sign/eg005_envelope_recipients_controller.rb deleted file mode 100644 index 2737183..0000000 --- a/app/controllers/e_sign/eg005_envelope_recipients_controller.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg005EnvelopeRecipientsController < EgController - include ApiCreator - skip_before_action :set_meta - - def create - minimum_buffer_min = 3 - envelope_id = session[:envelope_id] || nil - token_ok = check_token(minimum_buffer_min) - - if token_ok && envelope_id - # 2. Call the worker method - args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_id: envelope_id - } - - begin - results = worker args - @title = 'Envelope recipients results' - @h1 = 'List the envelope\'s recipients and their status' - @message = 'Results from the EnvelopesRecipients::list method:' - @json = results.to_json.to_json - render 'ds_common/example_done' - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - elsif !token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation - # so it could be restarted automatically. - # But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after - # authentication. - redirect_to '/ds/mustAuthenticate' - elsif !envelope_id - @title = 'Envelope recipient information' - @envelope_ok = false - end - end - - # ***DS.snippet.0.start - def worker(args) - # Step 1. List envelope recipients - # Exceptions will be caught by the calling function - envelope_api = create_envelope_api(args) - results = envelope_api.list_recipients args[:account_id], args[:envelope_id] - results - end - # ***DS.snippet.0.end -end diff --git a/app/controllers/e_sign/eg006_envelope_docs_controller.rb b/app/controllers/e_sign/eg006_envelope_docs_controller.rb deleted file mode 100644 index c2869f1..0000000 --- a/app/controllers/e_sign/eg006_envelope_docs_controller.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg006EnvelopeDocsController < EgController - def create - minimum_buffer_min = 3 - envelope_id = session[:envelope_id] - token_ok = check_token(minimum_buffer_min) - - if token_ok && envelope_id - begin - results = ESign::Eg006EnvelopeDocsService.new(request, session, envelope_id).call - - @title = 'Envelope documents list' - @h1 = 'List the envelope\'s documents' - @message = 'Results from the EnvelopeDocuments::list method:' - @json = results.to_json.to_json - render 'ds_common/example_done' - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - elsif !token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation - # so it could be restarted automatically. - # But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after - # authentication. - redirect_to '/ds/mustAuthenticate' - elsif !envelope_id - @title = 'Envelope recipient information' - @envelope_ok = false - end - end -end diff --git a/app/controllers/e_sign/eg007_envelope_get_doc_controller.rb b/app/controllers/e_sign/eg007_envelope_get_doc_controller.rb deleted file mode 100644 index d66b235..0000000 --- a/app/controllers/e_sign/eg007_envelope_get_doc_controller.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg007EnvelopeGetDocController < EgController - def create - minimum_buffer_min = 3 - envelope_id = session[:envelope_id] - envelope_documents = session[:envelope_documents] - token_ok = check_token(minimum_buffer_min) - if token_ok && envelope_id && envelope_documents - begin - results = ESign::Eg007EnvelopeGetDocService.new(request, session, envelope_id, envelope_documents).call - send_data results['data'], filename: results['doc_name'], - content_type: results['mime_type'], - disposition: "attachment; filename=\"#{results['doc_name']}\"" - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - elsif !token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation - # so it could be restarted automatically. - # But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after - # authentication. - redirect_to '/ds/mustAuthenticate' - elsif !envelope_id || !envelope_documents - @title = 'Download an envelope\'s document' - @envelope_ok = false - @documents_ok = false - end - end -end diff --git a/app/controllers/e_sign/eg008_create_template_controller.rb b/app/controllers/e_sign/eg008_create_template_controller.rb deleted file mode 100644 index e2962b9..0000000 --- a/app/controllers/e_sign/eg008_create_template_controller.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg008CreateTemplateController < EgController - def create - minimum_buffer_min = 3 - token_ok = check_token(minimum_buffer_min) - - if token_ok - begin - results = ESign::Eg008CreateTemplateService.new(session).call - msg = if results.fetch(:created_new_template) - 'The template has been created!' - else - 'Done. The template already existed in your account.' - end - @title = 'Template results' - @h1 = 'Template results' - @message = "#{msg}
Template name: #{results[:template_name]}, - ID #{results[:template_id]}." - render 'ds_common/example_done' - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - end - end -end diff --git a/app/controllers/e_sign/eg009_use_template_controller.rb b/app/controllers/e_sign/eg009_use_template_controller.rb deleted file mode 100644 index 89be3c5..0000000 --- a/app/controllers/e_sign/eg009_use_template_controller.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg009UseTemplateController < EgController - def create - minimum_buffer_min = 3 - template_id = session[:template_id] - token_ok = check_token(minimum_buffer_min) - - if token_ok && template_id - begin - results = ESign::Eg009UseTemplateService.new(request, session, template_id).call - # results is an object that implements ArrayAccess. Convert to a regular array: - @title = 'Envelope sent' - @h1 = 'Envelope sent' - @message = "The envelope has been created and sent!
Envelope ID #{results[:envelope_id]}." - render 'ds_common/example_done' - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - elsif !token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation - # so it could be restarted automatically. - # But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after - # authentication. - redirect_to '/ds/mustAuthenticate' - elsif !template_id - @title = 'Use a template to send an envelope' - @template_ok = false - end - end -end diff --git a/app/controllers/e_sign/eg010_send_binary_docs_controller.rb b/app/controllers/e_sign/eg010_send_binary_docs_controller.rb deleted file mode 100644 index e9a70bf..0000000 --- a/app/controllers/e_sign/eg010_send_binary_docs_controller.rb +++ /dev/null @@ -1,32 +0,0 @@ -class ESign::Eg010SendBinaryDocsController < EgController - def create - minimum_buffer_min = 3 - if check_token minimum_buffer_min - begin - results = ESign::Eg010SendBinaryDocsService.new(request, session).call - @title = 'Envelope sent' - @h1 = 'Envelope sent' - @message = "The envelope has been created and sent!
Envelope ID #{results['envelope_id']}." - render 'ds_common/example_done' - rescue Net::HTTPError => e - if !e.response.nil? - json_response = JSON.parse e.response - @error_code = json_response['errorCode'] - @error_message = json_response['message'] - else - @error_code = 'API request problem' - @error_message = e.to_s - end - end - else - flash[:message] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation - # so it could be restarted automatically. - # But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after - # authentication. - session['eg'] = eg_name - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg011_embedded_sending_controller.rb b/app/controllers/e_sign/eg011_embedded_sending_controller.rb deleted file mode 100644 index 2dce77c..0000000 --- a/app/controllers/e_sign/eg011_embedded_sending_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg011EmbeddedSendingController < EgController - def create - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - - if token_ok - begin - results = ESign::Eg011EmbeddedSendingService.new(request, session).call - redirect_to results['redirect_url'] - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - elsif !token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation - # so it could be restarted automatically. - # But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after - # authentication. - redirect_to '/ds/mustAuthenticate' - elsif !template_id - @title = 'Use a template to send an envelope' - @template_ok = false - end - end -end diff --git a/app/controllers/e_sign/eg012_embedded_console_controller.rb b/app/controllers/e_sign/eg012_embedded_console_controller.rb deleted file mode 100644 index cc9ac72..0000000 --- a/app/controllers/e_sign/eg012_embedded_console_controller.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg012EmbeddedConsoleController < EgController - def create - minimum_buffer_min = 3 - envelope_id = session[:envelope_id] - token_ok = check_token(minimum_buffer_min) - - if token_ok - begin - results = ESign::Eg012EmbeddedConsoleService.new(session, envelope_id, request).call - redirect_to results['redirect_url'] - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation - # so it could be restarted automatically. - # But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after - # authentication. - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg013_add_doc_to_template_controller.rb b/app/controllers/e_sign/eg013_add_doc_to_template_controller.rb deleted file mode 100644 index 5816582..0000000 --- a/app/controllers/e_sign/eg013_add_doc_to_template_controller.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg013AddDocToTemplateController < EgController - def create - minimum_buffer_min = 3 - template_id = session[:template_id] - token_ok = check_token(minimum_buffer_min) - - if token_ok && template_id - # 2. Call the worker method - # More data validation would be a good idea here - # Strip anything other than characters listed - begin - results = ESign::Eg013AddDocToTemplateService.new(request, session, template_id).call - # which need an envelopeId - # Redirect the user to the embedded signing - # Don't use an iFrame! - # State can be stored/recovered using the framework's session or a - # query parameter on the returnUrl - redirect_to results[:redirect_url] - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - elsif !token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation - # so it could be restarted automatically. - # But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after - # authentication. - redirect_to '/ds/mustAuthenticate' - elsif !template_id - @title = 'Use embedded signing from template and extra doc', - @template_ok = false - end - end -end diff --git a/app/controllers/e_sign/eg014_collect_payment_controller.rb b/app/controllers/e_sign/eg014_collect_payment_controller.rb deleted file mode 100644 index 154e9f2..0000000 --- a/app/controllers/e_sign/eg014_collect_payment_controller.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg014CollectPaymentController < EgController - def create - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - - if token_ok - begin - results = ESign::Eg014CollectPaymentService.new(request, session).call - @title = 'Envelope sent' - @h1 = 'Envelope sent' - @message = "The order form envelope has been created and sent!
- Envelope ID #{results[:envelope_id]}" - render 'ds_common/example_done' - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation - # so it could be restarted automatically. - # But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after - # authentication. - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg015_get_envelope_tab_data_controller.rb b/app/controllers/e_sign/eg015_get_envelope_tab_data_controller.rb deleted file mode 100644 index 9f6b6e2..0000000 --- a/app/controllers/e_sign/eg015_get_envelope_tab_data_controller.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg015GetEnvelopeTabDataController < EgController - def create - minimum_buffer_min = 3 - envelope_id = session[:envelope_id] - token_ok = check_token(minimum_buffer_min) - if token_ok - results = ESign::Eg015GetEnvelopeTabDataService.new(envelope_id, session).call - @h1 = 'List envelopes results' - @message = 'Results from the Envelopes::listStatusChanges method:' - @json = results.to_json.to_json - render 'ds_common/example_done' - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg016_set_envelope_tab_data_controller.rb b/app/controllers/e_sign/eg016_set_envelope_tab_data_controller.rb deleted file mode 100644 index 20b6533..0000000 --- a/app/controllers/e_sign/eg016_set_envelope_tab_data_controller.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg016SetEnvelopeTabDataController < EgController - def create - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - redirect_url = ESign::Eg016SetEnvelopeTabDataService.new(request, session).call - redirect_to redirect_url - end -end diff --git a/app/controllers/e_sign/eg017_set_template_tab_values_controller.rb b/app/controllers/e_sign/eg017_set_template_tab_values_controller.rb deleted file mode 100644 index 778c38e..0000000 --- a/app/controllers/e_sign/eg017_set_template_tab_values_controller.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg017SetTemplateTabValuesController < EgController - def create - minimum_buffer_min = 3 - template_id = session[:template_id] - token_ok = check_token(minimum_buffer_min) - if token_ok && template_id - redirect_url = ESign::Eg017SetTemplateTabValuesService.new(request, session, template_id).call - redirect_to redirect_url - elsif !token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - elsif !template_id - @title = 'Use a template to send an envelope' - @template_ok = false - end - end -end diff --git a/app/controllers/e_sign/eg018_get_envelope_custom_field_data_controller.rb b/app/controllers/e_sign/eg018_get_envelope_custom_field_data_controller.rb deleted file mode 100644 index 0122b70..0000000 --- a/app/controllers/e_sign/eg018_get_envelope_custom_field_data_controller.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg018GetEnvelopeCustomFieldDataController < EgController - def create - minimum_buffer_min = 3 - envelope_id = session[:envelope_id] - token_ok = check_token(minimum_buffer_min) - if token_ok - results = ESign::Eg018GetEnvelopeCustomFieldDataService.new(session, envelope_id).call - @h1 = 'List envelopes results' - @message = 'Results from the Envelopes::listStatusChanges method:' - @json = results.to_json.to_json - render 'ds_common/example_done' - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg019_access_code_authentication_controller.rb b/app/controllers/e_sign/eg019_access_code_authentication_controller.rb deleted file mode 100644 index 88e8114..0000000 --- a/app/controllers/e_sign/eg019_access_code_authentication_controller.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg019AccessCodeAuthenticationController < EgController - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - # ***DS.snippet.0.start - results = ESign::Eg019AccessCodeAuthenticationService.new(request, session).call - @title = 'Envelope sent' - @h1 = 'Envelope sent' - @message = "The envelope has been created and sent!
Envelope ID #{results.envelope_id}." - render 'ds_common/example_done' - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/' - end - end - # ***DS.snippet.0.end -end diff --git a/app/controllers/e_sign/eg020_sms_authentication_controller.rb b/app/controllers/e_sign/eg020_sms_authentication_controller.rb deleted file mode 100644 index 19eaaae..0000000 --- a/app/controllers/e_sign/eg020_sms_authentication_controller.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg020SmsAuthenticationController < EgController - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg020SmsAuthenticationService.new(request, session).call - @title = 'Envelope sent' - @h1 = 'Envelope sent' - @message = "The envelope has been created and sent!
Envelope ID #{results.envelope_id}." - render 'ds_common/example_done' - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/' - end - end - # ***DS.snippet.0.end -end diff --git a/app/controllers/e_sign/eg021_phone_authentication_controller.rb b/app/controllers/e_sign/eg021_phone_authentication_controller.rb deleted file mode 100644 index 023a588..0000000 --- a/app/controllers/e_sign/eg021_phone_authentication_controller.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg021PhoneAuthenticationController < EgController - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg021PhoneAuthenticationService.new(request, session).call - session[:envelope_id] = results.envelope_id - @title = 'Envelope sent' - @h1 = 'Envelope sent' - @message = "The envelope has been created and sent!
Envelope ID #{results.envelope_id}." - render 'ds_common/example_done' - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/' - end - end - - # ***DS.snippet.0.end -end diff --git a/app/controllers/e_sign/eg022_kba_authentication_controller.rb b/app/controllers/e_sign/eg022_kba_authentication_controller.rb deleted file mode 100644 index 5433e48..0000000 --- a/app/controllers/e_sign/eg022_kba_authentication_controller.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg022KbaAuthenticationController < EgController - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg022KbaAuthenticationService.new(request, session).call - @title = 'Envelope sent' - @h1 = 'Envelope sent' - @message = "The envelope has been created and sent!
Envelope ID #{results.envelope_id}." - render 'ds_common/example_done' - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/' - end - end -end diff --git a/app/controllers/e_sign/eg023_idv_authentication_controller.rb b/app/controllers/e_sign/eg023_idv_authentication_controller.rb deleted file mode 100644 index ec62cde..0000000 --- a/app/controllers/e_sign/eg023_idv_authentication_controller.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg023IdvAuthenticationController < EgController - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg023IdvAuthenticationService.new(request, session).call - if results.to_s == 'needs_idv_activated' - @title = 'Error' - @h1 = 'Error' - @message = 'Please activate IDV on your account to use this example.' - render 'ds_common/example_done' - - else - @title = 'Envelope sent' - @h1 = 'Envelope sent' - @message = "The envelope has been created and sent!
Envelope ID #{results.envelope_id}." - render 'ds_common/example_done' - end - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/' - end - end - - # ***DS.snippet.0.end -end diff --git a/app/controllers/e_sign/eg024_permission_create_controller.rb b/app/controllers/e_sign/eg024_permission_create_controller.rb deleted file mode 100644 index f94f1b1..0000000 --- a/app/controllers/e_sign/eg024_permission_create_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg024PermissionCreateController < EgController - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg024PermissionCreateService.new(session, request).call - # Step 4. a) Call the eSignature API - # b) Display the JSON response - @title = 'Creating a permission profile' - @h1 = 'Creating a permission profile' - @message = "Permission profile was created" - @json = results.to_json.to_json - render 'ds_common/example_done' - - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/' - end - end -end diff --git a/app/controllers/e_sign/eg025_permissions_set_user_group_controller.rb b/app/controllers/e_sign/eg025_permissions_set_user_group_controller.rb deleted file mode 100644 index 12d3a8f..0000000 --- a/app/controllers/e_sign/eg025_permissions_set_user_group_controller.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg025PermissionsSetUserGroupController < EgController - include ApiCreator - before_action :check_auth - - def get - args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'] - } - accounts_api = create_account_api(args) - permissions = accounts_api.list_permissions(args[:account_id], options = DocuSign_eSign::ListPermissionsOptions.default) - @permissions_lists = permissions.permission_profiles - # Get a user group - group_api = create_group_api(args) - @group_lists = group_api.list_groups(args[:account_id], options = DocuSign_eSign::ListGroupsOptions.default) - super - end - - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg025PermissionsSetUserGroupService.new(session, request).call - # Step 4. a) Call the eSignature API - # b) Display the JSON response - @title = 'Setting a permission profile' - @h1 = 'Setting a permission profile' - @json = results.to_json.to_json - render 'ds_common/example_done' - - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg026_permissions_change_single_setting_controller.rb b/app/controllers/e_sign/eg026_permissions_change_single_setting_controller.rb deleted file mode 100644 index 90aa89e..0000000 --- a/app/controllers/e_sign/eg026_permissions_change_single_setting_controller.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg026PermissionsChangeSingleSettingController < EgController - include ApiCreator - before_action :check_auth - - def get - args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'] - } - accounts_api = create_account_api(args) - permissions = accounts_api.list_permissions(args[:account_id], options = DocuSign_eSign::ListPermissionsOptions.default) - @permissions_lists = permissions.permission_profiles - super - end - - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg026PermissionsChangeSingleSettingService.new(session, request).call - # Step 4. a) Call the eSignature API - # b) Display the JSON response - @title = 'Updating individual permission settings' - @h1 = 'Updating individual permission settings' - @message = "Existing permission profile was changed" - @json = results.to_json.to_json - render 'ds_common/example_done' - - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/' - end - end - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg027_permissions_delete_controller.rb b/app/controllers/e_sign/eg027_permissions_delete_controller.rb deleted file mode 100644 index b7ddc0b..0000000 --- a/app/controllers/e_sign/eg027_permissions_delete_controller.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg027PermissionsDeleteController < EgController - include ApiCreator - before_action :check_auth - def get - args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'] - } - accounts_api = create_account_api(args) - permissions = accounts_api.list_permissions(args[:account_id], options = DocuSign_eSign::ListPermissionsOptions.default) - @permissions_lists = permissions.permission_profiles - super - end - - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg027PermissionsDeleteService.new(session, request).call - # Step 4. a) Call the eSignature API - # b) Display the JSON response - @title = 'Permission profile from an account was deleted' - @h1 = 'Permission profile from an account was deleted' - @message = "Permission profile #{request.params[:lists]} was deleted" - render 'ds_common/example_done' - - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/' - end - end - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg028_brands_creating_controller.rb b/app/controllers/e_sign/eg028_brands_creating_controller.rb deleted file mode 100644 index 5328933..0000000 --- a/app/controllers/e_sign/eg028_brands_creating_controller.rb +++ /dev/null @@ -1,30 +0,0 @@ -class ESign::Eg028BrandsCreatingController < EgController - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg028BrandsCreatingService.new(session, request).call - # Step 4. a) Call the eSignature API - # b) Display the JSON response - brand_id = results.brands[0].brand_id - @title = 'Brand creating' - @h1 = 'Brand creating' - @message = "The Brand has been created!
Brand ID: #{brand_id}." - @json = results.to_json.to_json - render 'ds_common/example_done' - - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/' - end - end -end diff --git a/app/controllers/e_sign/eg029_brands_apply_to_envelope_controller.rb b/app/controllers/e_sign/eg029_brands_apply_to_envelope_controller.rb deleted file mode 100644 index 6d38fc6..0000000 --- a/app/controllers/e_sign/eg029_brands_apply_to_envelope_controller.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg029BrandsApplyToEnvelopeController < EgController - include ApiCreator - before_action :check_auth - - def get - args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'] - } - accounts_api = create_account_api(args) - brand_lists = accounts_api.list_brands(args[:account_id], options = DocuSign_eSign::ListBrandsOptions.default) - @brand_names = brand_lists.brands - super - end - - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg029BrandsApplyToEnvelopeService.new(session, request).call - # Step 4. a) Call the eSignature API - # b) Display the JSON response - @title = 'Applying a Brand to an envelope' - @h1 = 'Applying a Brand to an envelope' - @message = "The envelope has been created and sent!
Envelope ID #{results.envelope_id}." - @json = results.to_json.to_json - render 'ds_common/example_done' - - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/' - end - end - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg030_brands_apply_to_template_controller.rb b/app/controllers/e_sign/eg030_brands_apply_to_template_controller.rb deleted file mode 100644 index 4c3df30..0000000 --- a/app/controllers/e_sign/eg030_brands_apply_to_template_controller.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: truebrand_lists = accounts_api.list_brands(args[:account_id], options = DocuSign_eSign::ListBrandsOptions.default) - -class ESign::Eg030BrandsApplyToTemplateController < EgController - include ApiCreator - before_action :check_auth - - def get - args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'] - } - accounts_api = create_account_api(args) - brand_lists = accounts_api.list_brands(args[:account_id], options = DocuSign_eSign::ListBrandsOptions.default) - @brand_names = brand_lists.brands - # get the template lists - template_api = create_template_api(args) - template_lists = template_api.list_templates(args[:account_id], options = DocuSign_eSign::ListTemplatesOptions.default) - @templates = template_lists.envelope_templates - super - end - - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg030BrandsApplyToTemplateService.new(session, request).call - # Step 4. a) Call the eSignature API - # b) Display the JSON response - # brand_id = results.brands[0].brand_id - @title = 'Applying a brand to an envelope using a template' - @h1 = 'Applying a brand to an envelope using a template' - @message = "The envelope has been created and sent!
Envelope ID #{results.envelope_id}." - @json = results.to_json.to_json - render 'ds_common/example_done' - - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/' - end - end - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg031_bulk_sending_envelopes_controller.rb b/app/controllers/e_sign/eg031_bulk_sending_envelopes_controller.rb deleted file mode 100644 index b9ebf41..0000000 --- a/app/controllers/e_sign/eg031_bulk_sending_envelopes_controller.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg031BulkSendingEnvelopesController < EgController - - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg031BulkSendingEnvelopesService.new(request, session).call - # Step 4. a) Call the eSignature API - # b) Display the JSON response - @title = 'Bulk sent' - @h1 = 'Bulk send envelope was successfully performed!' - @message = "Bulk request queued to #{results.queued} user lists." - @json = results.to_json.to_json - - render 'ds_common/example_done' - - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted - # automatically. But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg032_pauses_signature_workflow_controller.rb b/app/controllers/e_sign/eg032_pauses_signature_workflow_controller.rb deleted file mode 100644 index 3b7e16f..0000000 --- a/app/controllers/e_sign/eg032_pauses_signature_workflow_controller.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg032PausesSignatureWorkflowController < EgController - before_action :check_auth - - def create - results = ESign::Eg032PausesSignatureWorkflowService.new(session, request).call - - @envelop_id = results.to_hash[:envelopeId].to_s - session[:envelope_id] = @envelop_id - - render 'e_sign/eg032_pauses_signature_workflow/return' - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg033_unpauses_signature_workflow_controller.rb b/app/controllers/e_sign/eg033_unpauses_signature_workflow_controller.rb deleted file mode 100644 index d9497db..0000000 --- a/app/controllers/e_sign/eg033_unpauses_signature_workflow_controller.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg033UnpausesSignatureWorkflowController < EgController - before_action :check_auth - - def update - results = ESign::Eg033UnpausesSignatureWorkflowService.new(session).call - - @envelop_id = results.to_hash[:envelopeId].to_s - render 'e_sign/eg033_unpauses_signature_workflow/return' - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg034_use_conditional_recipients_controller.rb b/app/controllers/e_sign/eg034_use_conditional_recipients_controller.rb deleted file mode 100644 index 870cea4..0000000 --- a/app/controllers/e_sign/eg034_use_conditional_recipients_controller.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg034UseConditionalRecipientsController < EgController - before_action :check_auth - - def create - begin - results = ESign::Eg034UseConditionalRecipientsService.new(session, request).call - @envelop_id = results.to_hash[:envelopeId].to_s - render 'e_sign/eg034_use_conditional_recipients/return' - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - if error['errorCode']["WORKFLOW_UPDATE_RECIPIENTROUTING_NOT_ALLOWED"] - @error_message = "Update to the workflow with recipient routing is not allowed for your account!" - @error_information = "Please contact with our support team to resolve this issue." - else - @error_message = error['message'] - end - render 'ds_common/error' - end - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/e_sign/eg035_sms_delivery_controller.rb b/app/controllers/e_sign/eg035_sms_delivery_controller.rb deleted file mode 100644 index 957e5e8..0000000 --- a/app/controllers/e_sign/eg035_sms_delivery_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg035SmsDeliveryController < EgController - def create - minimum_buffer_min = 3 - if check_token(minimum_buffer_min) - begin - results = ESign::Eg035SmsDeliveryService.new(session, request, 'sent').call - session[:envelope_id] = results['envelope_id'] - @title = 'Envelope sent' - @h1 = 'Envelope sent' - @message = "The envelope has been created and sent!
Envelope ID #{results['envelope_id']}." - render 'ds_common/example_done' - rescue DocuSign_eSign::ApiError => e - error = JSON.parse e.response_body - @error_code = error['errorCode'] - @error_message = error['message'] - render 'ds_common/error' - end - else - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation - # so it could be restarted automatically. - # But since it should be rare to have a token issue here, - # we'll make the user re-enter the form data after - # authentication. - redirect_to '/' - end - end - end - \ No newline at end of file diff --git a/app/controllers/eeg001_embedded_signing_controller.rb b/app/controllers/eeg001_embedded_signing_controller.rb new file mode 100644 index 0000000..5b2b093 --- /dev/null +++ b/app/controllers/eeg001_embedded_signing_controller.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +class Eeg001EmbeddedSigningController < EgController + before_action -> { check_auth('eSignature') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 1) } + + def create + pdf_file_path = 'data/World_Wide_Corp_lorem.pdf' + + pdf_file_path = '../data/World_Wide_Corp_lorem.pdf' unless File.exist?(pdf_file_path) + + args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + signer_email: param_gsub(params[:signerEmail]), + signer_name: param_gsub(params[:signerName]), + ds_ping_url: Rails.application.config.app_url, + signer_client_id: 1000, + pdf_filename: pdf_file_path + } + + redirect_url = Eg001EmbeddedSigningService.new(args).worker + redirect_to redirect_url + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + + def get + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + session[:status_cfr] = 'enabled' + @title = 'Not CFR Part 11 compatible' + @error_information = @manifest['SupportingTexts']['CFRError'] + render 'ds_common/error' + end + session[:been_here] = true + super + end +end diff --git a/app/controllers/eg_controller.rb b/app/controllers/eg_controller.rb index c3295bc..d53d4ae 100644 --- a/app/controllers/eg_controller.rb +++ b/app/controllers/eg_controller.rb @@ -1,68 +1,110 @@ -# frozen_string_literal: true - -class EgController < ApplicationController - skip_before_action :verify_authenticity_token - before_action :eg_name, :set_eg, :set_meta - - def file_name - "#{controller_path}_service.rb" - end - - def eg_name - controller_name.to(4) - end - - def set_eg - session[:eg] = controller_name.to(4) - end - - def get - @messages = '' - - # to have the user authenticate or re-authenticate. - @token_ok = check_token - @config = Rails.application.config - if @token_ok - # addSpecialAttributes(model) - @envelope_ok = session[:envelope_id].present? - @documents_ok = session[:envelope_documents].present? - @document_options = session.fetch(:envelope_documents, {})['documents'] - @gateway_ok = @config.gateway_account_id.try(:length) > 25 - @template_ok = session[:template_id].present? - @documentation = "#{@config.documentation}#{eg_name}" #= Config.documentation + EgName - @show_doc = @config.documentation - else - redirect_to '/ds/mustAuthenticate' - end - end - - def set_meta - - @source_file = file_name.to_s - @source_url = "#{Rails.application.config.github_example_url}#{@source_file}" - end - - private - - def check_token(buffer_in_min = 10) - buffer = buffer_in_min * 60 - expires_at = session[:ds_expires_at] - remaining_duration = expires_at.nil? ? 0 : expires_at - buffer.seconds.from_now.to_i - if expires_at.nil? - Rails.logger.info "==> Token expiration is not available: fetching token" - elsif remaining_duration.negative? - Rails.logger.debug "==> Token is about to expire in #{time_in_words(remaining_duration)} at: #{Time.at(expires_at)}: fetching token" - else - Rails.logger.debug "==> Token is OK for #{time_in_words(remaining_duration)} at: #{Time.at(expires_at)}" - end - remaining_duration > 0 - end - - def time_in_words(duration) - "#{Object.new.extend(ActionView::Helpers::DateHelper).distance_of_time_in_words(duration)}#{duration.negative? ? ' ago' : ''}" - end - - def create_source_path - # code here - end -end +# frozen_string_literal: true + +class EgController < ApplicationController + skip_before_action :verify_authenticity_token + before_action :eg_name, :set_eg, :set_meta, :ensure_manifest + + def file_name + "#{controller_path}_service.rb" + end + + def eg_name + controller_name.to(4) + end + + def set_eg + session[:eg] = controller_name.split('_', 2).first + end + + def get + @messages = '' + + # to have the user authenticate or re-authenticate. + @token_ok = check_token + @config = Rails.application.config + if @token_ok || controller_name.include?('cneg') + # addSpecialAttributes(model) + @envelope_ok = session[:envelope_id].present? + @documents_ok = session[:envelope_documents].present? + @document_options = session.fetch(:envelope_documents, {})['documents'] + @gateway_ok = @config.gateway_account_id.try(:length) > 25 + @template_ok = session[:template_id].present? + @documentation = "#{@config.documentation}#{eg_name}" #= Config.documentation + EgName + @show_doc = @config.documentation + else + redirect_to '/ds/mustAuthenticate' + end + end + + def set_meta + @source_file = file_name.to_s + #remove extra character that doesn't exist in service file + index = @source_file.index('/') + @source_file = index.nil? ? @source_file.sub(/^.*?eg/, 'eg') : @source_file.sub(%r{/.+?eg}, '/eg') + @source_url = "#{Rails.application.config.github_example_url}#{@source_file}" + end + + def check_token(buffer_in_min = 10) + buffer = buffer_in_min * 60 + expires_at = session[:ds_expires_at] + remaining_duration = expires_at.nil? ? 0 : expires_at - buffer.seconds.from_now.to_i + if expires_at.nil? + Rails.logger.info '==> Token expiration is not available: fetching token' + elsif remaining_duration.negative? + Rails.logger.debug "==> Token is about to expire in #{time_in_words(remaining_duration)} at: #{Time.at(expires_at)}: fetching token" + else + Rails.logger.debug "==> Token is OK for #{time_in_words(remaining_duration)} at: #{Time.at(expires_at)}" + end + remaining_duration.positive? + end + + private + + def time_in_words(duration) + "#{Object.new.extend(ActionView::Helpers::DateHelper).distance_of_time_in_words(duration)}#{duration.negative? ? ' ago' : ''}" + end + + def param_gsub(parameter) + parameter.gsub(/([^\w \-@.,])+/, '') + end + + def check_auth(api) + # if not authorized for same API type example or + # if it is an attempt to authorize from home page + # then user will be redirected to login page + unless (session[:api] == api) || ((api == 'eSignature') && !session[:api]) + session[:api] = api + params[:auth] = 'jwt-auth' if api == 'Monitor' + + return redirect_to '/ds/mustAuthenticate' + end + + minimum_buffer_min = 10 + token_ok = check_token(minimum_buffer_min) + return if token_ok + + flash[:messages] = 'Sorry, you need to re-authenticate.' + # We could store the parameters of the requested operation so it could be restarted automatically + # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication + redirect_to '/ds/mustAuthenticate' + end + + def handle_error(e) + error = JSON.parse e.response_body + @error_code = e.code || error['errorCode'] + @error_message = error['error_description'] || error['message'] || error['error'] + render 'ds_common/error' + end + + def create_source_path + # code here + end + + def ensure_manifest + @manifest = Utils::ManifestUtils.new.get_manifest(Rails.configuration.example_manifest_url) + end + + def format_string(string, *args) + string.gsub(/\{(\d+)\}/) { args[::Regexp.last_match(1).to_i] } + end +end diff --git a/app/controllers/monitor_api/meg001_get_monitoring_dataset_controller.rb b/app/controllers/monitor_api/meg001_get_monitoring_dataset_controller.rb new file mode 100644 index 0000000..e96cad3 --- /dev/null +++ b/app/controllers/monitor_api/meg001_get_monitoring_dataset_controller.rb @@ -0,0 +1,25 @@ +class MonitorApi::Meg001GetMonitoringDatasetController < EgController + before_action -> { check_auth('Monitor') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 1, 'Monitor') } + + def create + args = { + access_token: session[:ds_access_token], + data_set_name: 'monitor', + version: '2.0' + } + + results = MonitorApi::Eg001GetMonitoringDatasetService.new(args).worker + + @title = @example['ExampleName'] + + if results != 'Monitor not enabled' + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + else + @message = "You do not have Monitor enabled for your account, follow How to enable Monitor for your account to get it enabled." + end + + render 'ds_common/example_done' + end +end diff --git a/app/controllers/notary/neg004_send_with_third_party_notary_controller.rb b/app/controllers/notary/neg004_send_with_third_party_notary_controller.rb new file mode 100644 index 0000000..e49259a --- /dev/null +++ b/app/controllers/notary/neg004_send_with_third_party_notary_controller.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require_relative '../../services/utils' + +class Notary::Neg004SendWithThirdPartyNotaryController < EgController + before_action -> { check_auth('Notary') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 4, 'Notary') } + + def create + envelope_args = { + signer_email: param_gsub(params['signerEmail']), + signer_name: param_gsub(params['signerName']), + doc_path: File.join('data', Rails.application.config.doc_docx) + } + args = { + account_id: session['ds_account_id'], + base_path: session['ds_base_path'], + access_token: session['ds_access_token'], + envelope_args: envelope_args + } + results = Notary::Eg004SendWithThirdPartyNotaryService.new(args).worker + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results['envelope_id']) + render 'ds_common/example_done' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end +end diff --git a/app/controllers/room_api/eg001_create_room_with_data_controller.rb b/app/controllers/room_api/eg001_create_room_with_data_controller.rb deleted file mode 100644 index 91bbaa6..0000000 --- a/app/controllers/room_api/eg001_create_room_with_data_controller.rb +++ /dev/null @@ -1,27 +0,0 @@ -class RoomApi::Eg001CreateRoomWithDataController < EgController - before_action :check_auth - - def create - results = RoomApi::Eg001CreateRoomWithDataService.new(session, request).call - - @title = "The room was successfully created" - @h1 = "The room was successfully created" - @message = "The room was created! Room ID: #{results.room_id}, Name: #{results.name}" - @json = results.to_json.to_json - - render 'ds_common/example_done' - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/room_api/eg002_create_room_with_template_controller.rb b/app/controllers/room_api/eg002_create_room_with_template_controller.rb deleted file mode 100644 index 27bcaac..0000000 --- a/app/controllers/room_api/eg002_create_room_with_template_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -class RoomApi::Eg002CreateRoomWithTemplateController < EgController - before_action :check_auth - - def create - results = RoomApi::Eg002CreateRoomWithTemplateService.new(session, request).call - - @title = "The room was successfully created" - @h1 = "The room was successfully created" - @message = "The room was created! Room ID: #{results.room_id}, Name: #{results.name}" - @json = results.to_json.to_json - - render 'ds_common/example_done' - end - - def get - @templates = RoomApi::GetDataService.new(session).get_templates - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/room_api/eg003_export_data_from_room_controller.rb b/app/controllers/room_api/eg003_export_data_from_room_controller.rb deleted file mode 100644 index 29bcbf9..0000000 --- a/app/controllers/room_api/eg003_export_data_from_room_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -class RoomApi::Eg003ExportDataFromRoomController < EgController - before_action :check_auth - - def create - results = RoomApi::Eg003ExportDataFromRoomService.new(session, request).call - - @title = "The room data was successfully exported" - @h1 = "The room data was successfully exported" - @message = "Results from the Rooms::GetRoomFieldData method:" - @json = results.to_json.to_json - - render 'ds_common/example_done' - end - - def get - @rooms = RoomApi::GetDataService.new(session).get_rooms - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/room_api/eg004_add_forms_to_room_controller.rb b/app/controllers/room_api/eg004_add_forms_to_room_controller.rb deleted file mode 100644 index bd70bdd..0000000 --- a/app/controllers/room_api/eg004_add_forms_to_room_controller.rb +++ /dev/null @@ -1,32 +0,0 @@ -class RoomApi::Eg004AddFormsToRoomController < EgController - before_action :check_auth - - def create - results = RoomApi::Eg004AddFormsToRoomService.new(session, request).call - - @title = "The form was successfully added to a room" - @h1 = "The form was successfully added to a room" - @message = "Results from the Rooms: AddFormToRoom method:" - @json = results.to_json.to_json - - render 'ds_common/example_done' - end - - def get - @rooms = RoomApi::GetDataService.new(session).get_rooms - @form_libraries = RoomApi::GetDataService.new(session).get_form_libraries - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/room_api/eg005_get_rooms_with_filters_controller.rb b/app/controllers/room_api/eg005_get_rooms_with_filters_controller.rb deleted file mode 100644 index 55679e4..0000000 --- a/app/controllers/room_api/eg005_get_rooms_with_filters_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -class RoomApi::Eg005GetRoomsWithFiltersController < EgController - before_action :check_auth - - def create - results = RoomApi::Eg005GetRoomsWithFiltersService.new(session, request).call - - @title = "The rooms with filters were loaded" - @h1 = "The rooms with filters were loaded" - @message = "Results from the Rooms: GetRooms method:" - @json = results.to_json.to_json - - render 'ds_common/example_done' - end - - def get - @rooms = RoomApi::GetDataService.new(session).get_rooms - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/room_api/eg006_create_an_external_form_fill_session_controller.rb b/app/controllers/room_api/eg006_create_an_external_form_fill_session_controller.rb deleted file mode 100644 index 3653789..0000000 --- a/app/controllers/room_api/eg006_create_an_external_form_fill_session_controller.rb +++ /dev/null @@ -1,35 +0,0 @@ -class RoomApi::Eg006CreateAnExternalFormFillSessionController < EgController - before_action :check_auth - - def create - results = RoomApi::Eg006CreateAnExternalFormFillSessionService.new(session, request).call - - @title = "External form fill session was successfully created" - @h1 = "External form fill session was successfully created" - @message = "To fill the form navigate the following URL: Fill the form" - @json = results.to_json.to_json - - render 'ds_common/example_done' - end - - def get_rooms - @rooms = RoomApi::GetDataService.new(session).get_rooms - end - - def get_forms - @form_libraries = RoomApi::GetDataService.new(session, params['roomId']).get_forms_from_room - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/room_api/eg007_create_form_group_controller.rb b/app/controllers/room_api/eg007_create_form_group_controller.rb deleted file mode 100644 index 5990003..0000000 --- a/app/controllers/room_api/eg007_create_form_group_controller.rb +++ /dev/null @@ -1,27 +0,0 @@ -class RoomApi::Eg007CreateFormGroupController < EgController - before_action :check_auth - - def create - results = RoomApi::Eg007CreateFormGroupService.new(session, request).call - - @title = "The form group was successfully created" - @h1 = "The form group was successfully created" - @message = "The form group was created! form group ID: #{results.form_group_id}, Name: #{results.name}" - @json = results.to_json.to_json - - render 'ds_common/example_done' - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/room_api/eg008_grant_office_access_to_form_group_controller.rb b/app/controllers/room_api/eg008_grant_office_access_to_form_group_controller.rb deleted file mode 100644 index d7eedee..0000000 --- a/app/controllers/room_api/eg008_grant_office_access_to_form_group_controller.rb +++ /dev/null @@ -1,43 +0,0 @@ -class RoomApi::Eg008GrantOfficeAccessToFormGroupController < EgController - before_action :check_auth - - def create - results = RoomApi::Eg008GrantOfficeAccessToFormGroupService.new(session, request).call - result = results.to_json.to_json - if result['exception'] - @error_code = results[:exception] - @error_message = "Office may already have access to form group." - render 'ds_common/error' - else - @title = "Granted office access to form group" - @h1 = "Granted office access to form group" - @message = "office access has been granted for the form group." - render 'ds_common/example_done' - end - - end - - def get - super - # Step 3 start - @offices = RoomApi::GetDataService.new(session).get_offices - # Step 3 end - - # Step 4 start - @form_groups = RoomApi::GetDataService.new(session).get_form_groups - # Step 4 end - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/room_api/eg009_assign_form_to_form_group_controller.rb b/app/controllers/room_api/eg009_assign_form_to_form_group_controller.rb deleted file mode 100644 index 4ecde46..0000000 --- a/app/controllers/room_api/eg009_assign_form_to_form_group_controller.rb +++ /dev/null @@ -1,43 +0,0 @@ -class RoomApi::Eg009AssignFormToFormGroupController < EgController - before_action :check_auth - - def create - results = RoomApi::Eg009AssignFormToFormGroupService.new(session, request).call - result = results.to_json.to_json - if result['exception'] - @error_code = results[:exception] - @error_message = "Form may already be assigned to form group." - render 'ds_common/error' - else - @title = "Assigned form to form group" - @h1 = "Assigned form to form group" - @message = "Form has been assigned to the selected form group." - - render 'ds_common/example_done' - end - end - - def get - super - # Step 3 start - @forms = RoomApi::GetDataService.new(session).get_form_libraries - # Step 3 end - - # Step 4 start - @form_groups = RoomApi::GetDataService.new(session).get_form_groups - # Step 4 end - end - - private - - def check_auth - minimum_buffer_min = 10 - token_ok = check_token(minimum_buffer_min) - unless token_ok - flash[:messages] = 'Sorry, you need to re-authenticate.' - # We could store the parameters of the requested operation so it could be restarted automatically - # But since it should be rare to have a token issue here, we'll make the user re-enter the form data after authentication - redirect_to '/ds/mustAuthenticate' - end - end -end diff --git a/app/controllers/room_api/reg001_create_room_with_data_controller.rb b/app/controllers/room_api/reg001_create_room_with_data_controller.rb new file mode 100644 index 0000000..fa99f71 --- /dev/null +++ b/app/controllers/room_api/reg001_create_room_with_data_controller.rb @@ -0,0 +1,23 @@ +class RoomApi::Reg001CreateRoomWithDataController < EgController + before_action -> { check_auth('Rooms') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 1, 'Rooms') } + + def create + args = { + room_name: params[:roomName], + office_id: RoomApi::GetDataService.new(session).get_offices[0]['officeId'], + role_id: RoomApi::GetDataService.new(session).get_default_admin_role_id, + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token] + } + + results = RoomApi::Eg001CreateRoomWithDataService.new(args).worker + + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results.room_id, results.name) + @json = results.to_json.to_json + + render 'ds_common/example_done' + end +end diff --git a/app/controllers/room_api/reg002_create_room_with_template_controller.rb b/app/controllers/room_api/reg002_create_room_with_template_controller.rb new file mode 100644 index 0000000..ac5e264 --- /dev/null +++ b/app/controllers/room_api/reg002_create_room_with_template_controller.rb @@ -0,0 +1,30 @@ +class RoomApi::Reg002CreateRoomWithTemplateController < EgController + before_action -> { check_auth('Rooms') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 2, 'Rooms') } + + def create + args = { + room_name: params[:roomName], + office_id: RoomApi::GetDataService.new(session).get_offices[0]['officeId'], + role_id: RoomApi::GetDataService.new(session).get_default_admin_role_id, + template_id: params['templateId'], + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token] + } + + results = RoomApi::Eg002CreateRoomWithTemplateService.new(args).worker + + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results.room_id, results.name) + @json = results.to_json.to_json + + render 'ds_common/example_done' + end + + def get + #ds-snippet-start:Rooms2Step3 + @templates = RoomApi::GetDataService.new(session).get_templates + #ds-snippet-end:Rooms2Step3 + end +end diff --git a/app/controllers/room_api/reg003_export_data_from_room_controller.rb b/app/controllers/room_api/reg003_export_data_from_room_controller.rb new file mode 100644 index 0000000..e4a36a2 --- /dev/null +++ b/app/controllers/room_api/reg003_export_data_from_room_controller.rb @@ -0,0 +1,25 @@ +class RoomApi::Reg003ExportDataFromRoomController < EgController + before_action -> { check_auth('Rooms') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 3, 'Rooms') } + + def create + args = { + room_id: params['roomId'], + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token] + } + + results = RoomApi::Eg003ExportDataFromRoomService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + + render 'ds_common/example_done' + end + + def get + @rooms = RoomApi::GetDataService.new(session).get_rooms + end +end diff --git a/app/controllers/room_api/reg004_add_forms_to_room_controller.rb b/app/controllers/room_api/reg004_add_forms_to_room_controller.rb new file mode 100644 index 0000000..4c5a2b3 --- /dev/null +++ b/app/controllers/room_api/reg004_add_forms_to_room_controller.rb @@ -0,0 +1,27 @@ +class RoomApi::Reg004AddFormsToRoomController < EgController + before_action -> { check_auth('Rooms') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 4, 'Rooms') } + + def create + args = { + form_id: params['formId'], + room_id: params['roomId'], + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token] + } + + results = RoomApi::Eg004AddFormsToRoomService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + + render 'ds_common/example_done' + end + + def get + @rooms = RoomApi::GetDataService.new(session).get_rooms + @form_libraries = RoomApi::GetDataService.new(session).get_form_libraries + end +end diff --git a/app/controllers/room_api/reg005_get_rooms_with_filters_controller.rb b/app/controllers/room_api/reg005_get_rooms_with_filters_controller.rb new file mode 100644 index 0000000..35bb503 --- /dev/null +++ b/app/controllers/room_api/reg005_get_rooms_with_filters_controller.rb @@ -0,0 +1,26 @@ +class RoomApi::Reg005GetRoomsWithFiltersController < EgController + before_action -> { check_auth('Rooms') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 5, 'Rooms') } + + def create + args = { + date_from: params[:date_from], + date_to: params[:date_to], + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token] + } + + results = RoomApi::Eg005GetRoomsWithFiltersService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json.to_json + + render 'ds_common/example_done' + end + + def get + @rooms = RoomApi::GetDataService.new(session).get_rooms + end +end diff --git a/app/controllers/room_api/reg006_create_an_external_form_fill_session_controller.rb b/app/controllers/room_api/reg006_create_an_external_form_fill_session_controller.rb new file mode 100644 index 0000000..691dea0 --- /dev/null +++ b/app/controllers/room_api/reg006_create_an_external_form_fill_session_controller.rb @@ -0,0 +1,35 @@ +class RoomApi::Reg006CreateAnExternalFormFillSessionController < EgController + before_action -> { check_auth('Rooms') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 6, 'Rooms') } + + def create + args = { + form_id: params['formId'], + room_id: params['roomId'], + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + allowed_host: request.host_with_port + } + begin + results = RoomApi::Eg006CreateAnExternalFormFillSessionService.new(args).worker + + @title = @example['ExampleName'] + @message = @example['ResultsPageText'] + @json = results.to_json + @url = results.url + + render 'room_api/reg006_create_an_external_form_fill_session/results' + rescue DocuSign_Rooms::ApiError => e + handle_error(e) + end + end + + def get_rooms + @rooms = RoomApi::GetDataService.new(session).get_rooms + end + + def get_forms + @form_libraries = RoomApi::GetDataService.new(session, params['roomId']).get_forms_from_room + end +end diff --git a/app/controllers/room_api/reg007_create_form_group_controller.rb b/app/controllers/room_api/reg007_create_form_group_controller.rb new file mode 100644 index 0000000..4ae34d6 --- /dev/null +++ b/app/controllers/room_api/reg007_create_form_group_controller.rb @@ -0,0 +1,20 @@ +class RoomApi::Reg007CreateFormGroupController < EgController + before_action -> { check_auth('Rooms') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 7, 'Rooms') } + + def create + args = { + group_name: params[:group_name], + account_id: session[:ds_account_id], + access_token: session[:ds_access_token] + } + + results = RoomApi::Eg007CreateFormGroupService.new(args).worker + + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results.name) + @json = results.to_json.to_json + + render 'ds_common/example_done' + end +end diff --git a/app/controllers/room_api/reg008_grant_office_access_to_form_group_controller.rb b/app/controllers/room_api/reg008_grant_office_access_to_form_group_controller.rb new file mode 100644 index 0000000..83a57c6 --- /dev/null +++ b/app/controllers/room_api/reg008_grant_office_access_to_form_group_controller.rb @@ -0,0 +1,36 @@ +class RoomApi::Reg008GrantOfficeAccessToFormGroupController < EgController + before_action -> { check_auth('Rooms') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 8, 'Rooms') } + + def create + args = { + office_id: params[:office_id], + form_group_id: params[:form_group_id], + account_id: session[:ds_account_id], + access_token: session[:ds_access_token] + } + + results = RoomApi::Eg008GrantOfficeAccessToFormGroupService.new(args).worker + result = results.to_json.to_json + if result['exception'] + @error_code = results[:exception] + @error_message = 'Office may already have access to form group.' + render 'ds_common/error' + else + @title = @example['ExampleName'] + @message = 'office access has been granted for the form group.' + render 'ds_common/example_done' + end + end + + def get + super + # Step 3 start + @offices = RoomApi::GetDataService.new(session).get_offices + # Step 3 end + + # Step 4 start + @form_groups = RoomApi::GetDataService.new(session).get_form_groups + # Step 4 end + end +end diff --git a/app/controllers/room_api/reg009_assign_form_to_form_group_controller.rb b/app/controllers/room_api/reg009_assign_form_to_form_group_controller.rb new file mode 100644 index 0000000..9b36f1d --- /dev/null +++ b/app/controllers/room_api/reg009_assign_form_to_form_group_controller.rb @@ -0,0 +1,37 @@ +class RoomApi::Reg009AssignFormToFormGroupController < EgController + before_action -> { check_auth('Rooms') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 9, 'Rooms') } + + def create + args = { + form_id: params[:form_id], + form_group_id: params[:form_group_id], + account_id: session[:ds_account_id], + access_token: session[:ds_access_token] + } + + results = RoomApi::Eg009AssignFormToFormGroupService.new(args).worker + result = results.to_json.to_json + if result['exception'] + @error_code = results[:exception] + @error_message = 'Form may already be assigned to form group.' + render 'ds_common/error' + else + @title = @example['ExampleName'] + @message = 'Form has been assigned to the selected form group.' + + render 'ds_common/example_done' + end + end + + def get + super + #ds-snippet-start:Rooms9Step3 + @forms = RoomApi::GetDataService.new(session).get_form_libraries + #ds-snippet-end:Rooms9Step3 + + #ds-snippet-start:Rooms9Step4 + @form_groups = RoomApi::GetDataService.new(session).get_form_groups + #ds-snippet-end:Rooms9Step4 + end +end diff --git a/app/controllers/session_controller.rb b/app/controllers/session_controller.rb index 151349d..39d51bc 100644 --- a/app/controllers/session_controller.rb +++ b/app/controllers/session_controller.rb @@ -1,76 +1,89 @@ -# frozen_string_literal: true - -class SessionController < ApplicationController - # GET /auth/:provider/callback - def create - if session[:eg] - redirect_url = "/" + session[:eg] - else - redirect_url = root_path - end - - # reset the session - internal_destroy - - Rails.logger.debug "\n==> DocuSign callback Authentication response:\n#{auth_hash.to_yaml}\n" - Rails.logger.info "==> Login: New token for admin user which will expire at: #{Time.at(auth_hash.credentials['expires_at'])}" - store_auth_hash_from_docusign_callback - redirect_to redirect_url - end - - # GET /ds/logout - def destroy - internal_destroy - redirect_to root_path - end - - # GET /auth/failure - def omniauth_failure - error_msg = "OmniAuth authentication failure message: #{params[:message]} for strategy: #{params[:strategy]} and HTTP_REFERER: #{params[:origin]}" - Rails.logger.warn "\n==> #{error_msg}" - flash[:notice] = error_msg - redirect_to root_path - end - - def show - Rails.logger.debug "==> Session:\n#{session.to_h.to_yaml}" - render json: session.to_json - end - - protected - - def internal_destroy - session.delete :ds_expires_at - session.delete :ds_user_name - session.delete :ds_access_token - session.delete :ds_account_id - session.delete :ds_account_name - session.delete :ds_base_path - session.delete 'omniauth.state' - session.delete 'omniauth.params' - session.delete 'omniauth.origin' - session.delete :envelope_id - session.delete :envelope_documents - session.delete :template_id - session.delete :eg - end - - def store_auth_hash_from_docusign_callback - session[:ds_expires_at] = auth_hash.credentials['expires_at'] - session[:ds_user_name] = auth_hash.info.name - session[:ds_access_token] = auth_hash.credentials.token - session[:ds_account_id] = auth_hash.extra.account_id - session[:ds_account_name] = auth_hash.extra.account_name - session[:ds_base_path] = auth_hash.extra.base_uri - end - - # returns hash with key structure of: - # - provider - # - uid - # - info: [name, email, first_name, last_name] - # - credentials: [token, refresh_token, expires_at, expires] - # - extra: [sub, account_id, account_name, base_uri] - def auth_hash - @auth_hash ||= request.env['omniauth.auth'] - end -end +# frozen_string_literal: true + +class SessionController < ApplicationController + # GET /auth/:provider/callback + def create + redirect_url = if session[:eg] + "/#{session[:eg]}" + else + root_path + end + + # reset the session + internal_destroy + + Rails.logger.debug "\n==> Docusign callback Authentication response:\n#{auth_hash.to_yaml}\n" + Rails.logger.info "==> Login: New token for admin user which will expire at: #{Time.at(auth_hash.credentials['expires_at'])}" + store_auth_hash_from_docusign_callback + redirect_to redirect_url + end + + # GET /ds/logout + def destroy + internal_destroy + redirect_to root_path + end + + # def switch_api + # internal_destroy + # end + + # GET /auth/failure + def omniauth_failure + unless session[:pkce_failed] + Rails.logger.warn "PKCE Auth failed \n" + session[:pkce_failed] = true + return redirect_to '/auth/docusign' + end + + error_msg = "OmniAuth authentication failure message: #{params[:message]} for strategy: #{params[:strategy]} and HTTP_REFERER: #{params[:origin]}" + Rails.logger.warn "\n==> #{error_msg}" + flash[:notice] = error_msg + redirect_to root_path + end + + def show + Rails.logger.debug "==> Session:\n#{session.to_h.to_yaml}" + render json: session.to_json + end + + protected + + def internal_destroy + session.delete :ds_expires_at + session.delete :ds_user_name + session.delete :ds_access_token + session.delete :ds_account_id + session.delete :ds_account_name + session.delete :ds_base_path + session.delete 'omniauth.state' + session.delete 'omniauth.params' + session.delete 'omniauth.origin' + session.delete :envelope_id + session.delete :envelope_documents + session.delete :template_id + session.delete :eg + session.delete :manifest + session.delete :status_cfr + session.delete :is_workflow_published + end + + def store_auth_hash_from_docusign_callback + session[:ds_expires_at] = auth_hash.credentials['expires_at'] + session[:ds_user_name] = auth_hash.info.name + session[:ds_access_token] = auth_hash.credentials.token + session[:ds_account_id] = auth_hash.extra.account_id + session[:ds_account_name] = auth_hash.extra.account_name + session[:ds_base_path] = auth_hash.extra.base_uri + end + + # returns hash with key structure of: + # - provider + # - uid + # - info: [name, email, first_name, last_name] + # - credentials: [token, refresh_token, expires_at, expires] + # - extra: [sub, account_id, account_name, base_uri] + def auth_hash + @auth_hash ||= request.env['omniauth.auth'] + end +end diff --git a/app/controllers/webforms/weg001_create_instance_controller.rb b/app/controllers/webforms/weg001_create_instance_controller.rb new file mode 100644 index 0000000..8899dab --- /dev/null +++ b/app/controllers/webforms/weg001_create_instance_controller.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +class Webforms::Weg001CreateInstanceController < EgController + before_action -> { check_auth('WebForms') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 1, 'WebForms') } + + def create_web_form_template + args = { + template_name: 'Web Form Example Template', + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token] + } + + begin + web_form_template_id = Webforms::Eg001CreateInstanceService.new(args).create_web_form_template + Utils::FileUtils.new.replace_template_id(File.join('data', Rails.application.config.web_form_config_file), web_form_template_id) + session[:web_form_template_id] = web_form_template_id + + redirect_to '/weg001webForm' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + end + + def create_web_form_instance + args = { + form_name: 'Web Form Example Template', + client_user_id: '1234-5678-abcd-ijkl', + account_id: session[:ds_account_id], + base_path: Rails.application.config.webforms_host, + access_token: session[:ds_access_token] + } + create_instance_service = Webforms::Eg001CreateInstanceService.new(args) + web_forms = create_instance_service.list_web_forms + results = create_instance_service.create_web_form_instance web_forms.items.first.id + + @integration_key = Rails.application.config.integration_key + @form_url = results.form_url + @instance_token = results.instance_token + render 'webforms/weg001_create_instance/web_form_embed' + end + + def get + additional_page = @example['AdditionalPage'].find { |p| p['Name'] == 'create_web_form_template' } + @example['ExampleDescription'] = additional_page['ResultsPageText'] + + render 'webforms/weg001_create_instance/get' + end + + def get_web_form_create_view + redirect_to '/weg001' if session[:web_form_template_id].nil? + + additional_page = @example['AdditionalPage'].find { |p| p['Name'] == 'create_web_form' } + @title = @example['ExampleName'] + @description = format_string(additional_page['ResultsPageText'], 'data') + + render 'webforms/weg001_create_instance/web_form_create' + end +end diff --git a/app/controllers/webforms/weg002_create_remote_instance_controller.rb b/app/controllers/webforms/weg002_create_remote_instance_controller.rb new file mode 100644 index 0000000..92c57b3 --- /dev/null +++ b/app/controllers/webforms/weg002_create_remote_instance_controller.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +class Webforms::Weg002CreateRemoteInstanceController < EgController + before_action -> { check_auth('WebForms') } + before_action -> { @example = Utils::ManifestUtils.new.get_example(@manifest, 2, 'WebForms') } + + def create_web_form_template + args = { + template_name: 'Web Form Example Template', + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token] + } + + begin + web_form_template_id = Webforms::Eg002CreateRemoteInstanceService.new(args).create_web_form_template + Utils::FileUtils.new.replace_template_id(File.join('data', Rails.application.config.web_form_config_file), web_form_template_id) + session[:web_form_template_id] = web_form_template_id + + redirect_to '/weg002webForm' + rescue DocuSign_eSign::ApiError => e + handle_error(e) + end + end + + def create_web_form_instance + args = { + form_name: 'Web Form Example Template', + account_id: session[:ds_account_id], + base_path: Rails.application.config.webforms_host, + signer_name: Rails.application.config.signer_name, + signer_email: Rails.application.config.signer_email, + access_token: session[:ds_access_token] + } + create_remote_instance_service = Webforms::Eg002CreateRemoteInstanceService.new(args) + web_forms = create_remote_instance_service.list_web_forms + + if web_forms.items.nil? || web_forms.items.empty? + @error_code = '404' + @error_message = @example['CustomErrorTexts'][0]['ErrorMessage'] + return render 'ds_common/error' + end + results = create_remote_instance_service.create_web_form_instance web_forms.items.first.id + + @title = @example['ExampleName'] + @message = format_string(@example['ResultsPageText'], results.envelopes[0].id, results.id) + render 'ds_common/example_done' + end + + def get_web_form_create_view + redirect_to '/weg002' if session[:web_form_template_id].nil? + + additional_page = @example['AdditionalPage'].find { |p| p['Name'] == 'create_web_form' } + @title = @example['ExampleName'] + @description = format_string(additional_page['ResultsPageText'], 'data') + + render 'webforms/weg002_create_remote_instance/web_form_create' + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 15b06f0..9a905c5 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,4 +1,17 @@ # frozen_string_literal: true module ApplicationHelper + require 'json/ext' + + def format_string(string, *args) + string.gsub(/\{(\d+)\}/) { args[::Regexp.last_match(1).to_i] } + end + + def to_json(hash) + hash.to_json + end + + def example_available?(example) + !example['SkipForLanguages'] or !example['SkipForLanguages'].include? 'ruby' + end end diff --git a/app/helpers/eg024_brands_creating_helper.rb b/app/helpers/eg024_brands_creating_helper.rb index 8f00128..df0fc09 100644 --- a/app/helpers/eg024_brands_creating_helper.rb +++ b/app/helpers/eg024_brands_creating_helper.rb @@ -1,13 +1,13 @@ module Eg024BrandsCreatingHelper def language_list - languages = [ ['Arabic', 'ar'],[ 'Armenian', 'hy'] , ['Bahasa Indonesia', 'id' ], ['Bahasa Malay', 'ms'], ['Bulgarian', 'bg'], - ['Chinese Simplified', 'zh_CN'], ['Chinese Traditional', 'zh_TW'], ['Croatian', 'hr'], ['Czech', 'cs'], - ['Danish', 'da'], ['Dutch', 'nl'], ['English UK', 'en_GB'], ['English US', 'en' ], ['Estonian', 'et'], ['Farsi', 'fa'], - ['Hindi', 'hi'], ['Hungarian', 'hu'], ['Italian', 'it'], ['Japanese', 'ja'], ['Korean', 'ko'], ['Latvian', 'lv'], - ['Lithuanian', 'lt'], ['Norwegian', 'no'], ['Polish', 'pl'], ['Portuguese', 'pt'], ['Portuguese Brasil', 'pt_BR'], ['Romanian', 'ro'], - ['Russian', 'ru'], ['Serbian', 'sr'], ['Slovak', 'sk'], ['Slovenian', 'sl'], ['Spanish', 'es'], ['Spanish Latin America', 'es_MX'], - ['Swedish', 'sv'], ['Thai', 'th'], ['Turkish', 'tr'], ['Ukranian', 'uk'], ['Vietnamese', 'vi'] ] - array = languages.map{ |key, value| [key, value] } + languages = [%w[Arabic ar], %w[Armenian hy], ['Bahasa Indonesia', 'id'], ['Bahasa Malay', 'ms'], %w[Bulgarian bg], + ['Chinese Simplified', 'zh_CN'], ['Chinese Traditional', 'zh_TW'], %w[Croatian hr], %w[Czech cs], + %w[Danish da], %w[Dutch nl], ['English UK', 'en_GB'], ['English US', 'en'], %w[Estonian et], %w[Farsi fa], + %w[Hindi hi], %w[Hungarian hu], %w[Italian it], %w[Japanese ja], %w[Korean ko], %w[Latvian lv], + %w[Lithuanian lt], %w[Norwegian no], %w[Polish pl], %w[Portuguese pt], ['Portuguese Brasil', 'pt_BR'], %w[Romanian ro], + %w[Russian ru], %w[Serbian sr], %w[Slovak sk], %w[Slovenian sl], %w[Spanish es], ['Spanish Latin America', 'es_MX'], + %w[Swedish sv], %w[Thai th], %w[Turkish tr], %w[Ukranian uk], %w[Vietnamese vi]] + array = languages.map { |key, value| [key, value] } options_for_select(array) end end diff --git a/app/services/admin_api/eg001_create_user_service.rb b/app/services/admin_api/eg001_create_user_service.rb new file mode 100644 index 0000000..7201cc8 --- /dev/null +++ b/app/services/admin_api/eg001_create_user_service.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class AdminApi::Eg001CreateUserService + attr_reader :args, :user_data + + def initialize(args, user_data) + @args = args + @user_data = user_data + end + + def worker + #ds-snippet-start:Admin1Step2 + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Admin1Step2 + + #ds-snippet-start:Admin1Step6 + users_api = DocuSign_Admin::UsersApi.new(api_client) + results, _status, headers = users_api.create_user_with_http_info(args[:organization_id], user_data) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin1Step6 + + results + end +end diff --git a/app/services/admin_api/eg002_create_active_clm_esign_user_service.rb b/app/services/admin_api/eg002_create_active_clm_esign_user_service.rb new file mode 100644 index 0000000..499e238 --- /dev/null +++ b/app/services/admin_api/eg002_create_active_clm_esign_user_service.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +class AdminApi::Eg002CreateActiveClmEsignUserService + attr_reader :args + + def initialize(args) + @args = args + end + + def worker + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + + #ds-snippet-start:Admin2Step6 + users_api = DocuSign_Admin::UsersApi.new(api_client) + results, _status, headers = users_api.add_or_update_user_with_http_info(args[:organization_id], args[:account_id], body(args)) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin2Step6 + + results + end + + private + + #ds-snippet-start:Admin2Step5 + def body(args) + { + user_name: args[:user_name], + first_name: args[:first_name], + last_name: args[:last_name], + email: args[:email], + auto_activate_memberships: true, + product_permission_profiles: [ + { + permission_profile_id: args[:esign_permission_profile_id], + product_id: args[:esign_product_id] + }, + { + permission_profile_id: args[:clm_permission_profile_id], + product_id: args[:clm_product_id] + } + ], + ds_groups: [ + { + ds_group_id: args[:ds_group_id] + } + ] + } + end + #ds-snippet-end:Admin2Step5 +end diff --git a/app/services/admin_api/eg003_bulk_export_user_data_service.rb b/app/services/admin_api/eg003_bulk_export_user_data_service.rb new file mode 100644 index 0000000..679e049 --- /dev/null +++ b/app/services/admin_api/eg003_bulk_export_user_data_service.rb @@ -0,0 +1,91 @@ +class AdminApi::Eg003BulkExportUserDataService + attr_reader :bulk_exports_api, :args + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:Admin3Step2 + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Admin3Step2 + + #ds-snippet-start:Admin3Step3 + @bulk_exports_api = DocuSign_Admin::BulkExportsApi.new(api_client) + response, _status, headers = bulk_exports_api.create_user_list_export_with_http_info(args[:organization_id], args[:request_body]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin3Step3 + + #ds-snippet-start:Admin3Step4 + retry_count = 5 + while retry_count >= 0 + if response.results + get_exported_user_data(args, response.id) + break + else + retry_count -= 1 + sleep(5) + response, _status, headers = bulk_exports_api.get_user_list_export_with_http_info(args[:organization_id], response.id) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + end + end + #ds-snippet-end:Admin3Step4 + + response + end + + private + + #ds-snippet-start:Admin3Step5 + def get_exported_user_data(args, export_id) + bulk_export_response, _status, headers = bulk_exports_api.get_user_list_export_with_http_info(args[:organization_id], export_id) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + data_url = bulk_export_response.results[0].url + + uri = URI(data_url) + + req = Net::HTTP::Get.new(uri) + req['Authorization'] = "Bearer #{args[:access_token]}" + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + + http.request(req) do |response| + File.open(args[:file_path], 'w') do |file| + response.read_body do |chunk| + file.write(chunk) + end + end + end + end + #ds-snippet-end:Admin3Step5 +end diff --git a/app/services/admin_api/eg004_import_user_service.rb b/app/services/admin_api/eg004_import_user_service.rb new file mode 100644 index 0000000..d9f0c7e --- /dev/null +++ b/app/services/admin_api/eg004_import_user_service.rb @@ -0,0 +1,36 @@ +class AdminApi::Eg004ImportUserService + attr_reader :args + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:Admin4Step2 + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Admin4Step2 + + #ds-snippet-start:Admin4Step3 + csv_file_data = File.open(args[:csv_file_path]).read + csv_file_data = csv_file_data.gsub('{account_id}', args[:account_id]) + + @bulk_imports_api = DocuSign_Admin::BulkImportsApi.new(api_client) + results, _status, headers = @bulk_imports_api.create_bulk_import_add_users_request_with_http_info(args[:organization_id], csv_file_data) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin4Step3 + + results + end +end diff --git a/app/services/admin_api/eg005_audit_users_service.rb b/app/services/admin_api/eg005_audit_users_service.rb new file mode 100644 index 0000000..7afab0c --- /dev/null +++ b/app/services/admin_api/eg005_audit_users_service.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +class AdminApi::Eg005AuditUsersService + attr_reader :args + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:Admin5Step2 + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Admin5Step2 + + #ds-snippet-start:Admin5Step3 + options = DocuSign_Admin::GetUsersOptions.new + options.account_id = args[:account_id] + options.last_modified_since = (Date.today - 10).strftime('%Y/%m/%d') + + users_api = DocuSign_Admin::UsersApi.new(api_client) + modified_users, _status, headers = users_api.get_users_with_http_info(args[:organization_id], options) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin5Step3 + + #ds-snippet-start:Admin5Step5 + results = [] + modified_users.as_json['users'].each do |user| + #ds-snippet-end:Admin5Step5 + #ds-snippet-start:Admin5Step4 + userProfilesOptions = DocuSign_Admin::GetUserProfilesOptions.new + userProfilesOptions.email = user['email'] + #ds-snippet-end:Admin5Step4 + #ds-snippet-start:Admin5Step5 + result, _status, headers = users_api.get_user_profiles_with_http_info(args[:organization_id], userProfilesOptions) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + results.push(result) + end + #ds-snippet-end:Admin5Step5 + + results + end +end diff --git a/app/services/admin_api/eg006_get_user_profile_by_email_service.rb b/app/services/admin_api/eg006_get_user_profile_by_email_service.rb new file mode 100644 index 0000000..8fdd40c --- /dev/null +++ b/app/services/admin_api/eg006_get_user_profile_by_email_service.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +class AdminApi::Eg006GetUserProfileByEmailService + attr_reader :args + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:Admin6Step2 + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Admin6Step2 + + #ds-snippet-start:Admin6Step3 + users_api = DocuSign_Admin::UsersApi.new(api_client) + + options = DocuSign_Admin::GetUserDSProfilesByEmailOptions.new + options.email = args[:email] + results, _status, headers = users_api.get_user_ds_profiles_by_email_with_http_info(args[:organization_id], options) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin6Step3 + + results + end +end diff --git a/app/services/admin_api/eg007_get_user_profile_by_user_id_service.rb b/app/services/admin_api/eg007_get_user_profile_by_user_id_service.rb new file mode 100644 index 0000000..cb4f237 --- /dev/null +++ b/app/services/admin_api/eg007_get_user_profile_by_user_id_service.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class AdminApi::Eg007GetUserProfileByUserIdService + attr_reader :args + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:Admin7Step2 + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Admin7Step2 + + #ds-snippet-start:Admin7Step3 + users_api = DocuSign_Admin::UsersApi.new(api_client) + results, _status, headers = users_api.get_user_ds_profile_with_http_info(args[:organization_id], args[:user_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin7Step3 + + results + end +end diff --git a/app/services/admin_api/eg008_update_user_product_permission_profile_service.rb b/app/services/admin_api/eg008_update_user_product_permission_profile_service.rb new file mode 100644 index 0000000..60d726a --- /dev/null +++ b/app/services/admin_api/eg008_update_user_product_permission_profile_service.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +class AdminApi::Eg008UpdateUserProductPermissionProfileService + attr_reader :args + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:Admin8Step2 + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Admin8Step2 + + #ds-snippet-start:Admin8Step3 + product_permission_profile = DocuSign_Admin::ProductPermissionProfileRequest.new({ 'permission_profile_id' => args[:permission_profile_id], 'product_id' => args[:product_id] }) + user_product_permission_profile_request = DocuSign_Admin::UserProductPermissionProfilesRequest.new({ 'email' => args[:email], 'product_permission_profiles' => [product_permission_profile] }) + #ds-snippet-end:Admin8Step3 + + #ds-snippet-start:Admin8Step4 + product_permission_profiles_api = DocuSign_Admin::ProductPermissionProfilesApi.new(api_client) + results, _status, headers = product_permission_profiles_api.add_user_product_permission_profiles_by_email_with_http_info(args[:organization_id], args[:account_id], user_product_permission_profile_request) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin8Step4 + + results + end +end diff --git a/app/services/admin_api/eg009_delete_user_product_permission_profile_service.rb b/app/services/admin_api/eg009_delete_user_product_permission_profile_service.rb new file mode 100644 index 0000000..75a9b75 --- /dev/null +++ b/app/services/admin_api/eg009_delete_user_product_permission_profile_service.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +class AdminApi::Eg009DeleteUserProductPermissionProfileService + attr_reader :args + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:Admin9Step2 + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Admin9Step2 + + #ds-snippet-start:Admin9Step4 + user_product_profile_delete_request = DocuSign_Admin::UserProductProfileDeleteRequest.new({ 'user_email' => args[:email], 'product_ids' => [args[:product_id]] }) + #ds-snippet-end:Admin9Step4 + + #ds-snippet-start:Admin9Step5 + product_permission_profiles_api = DocuSign_Admin::ProductPermissionProfilesApi.new(api_client) + results, _status, headers = product_permission_profiles_api.remove_user_product_permission_with_http_info(args[:organization_id], args[:account_id], user_product_profile_delete_request) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin9Step5 + + results + end + + def get_permission_profiles_by_email + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + + #ds-snippet-start:Admin9Step3 + product_permission_profiles_api = DocuSign_Admin::ProductPermissionProfilesApi.new(api_client) + + options = DocuSign_Admin::GetUserProductPermissionProfilesByEmailOptions.new + options.email = args[:email] + + product_permission_profiles, _status, headers = product_permission_profiles_api.get_user_product_permission_profiles_by_email_with_http_info(args[:organization_id], args[:account_id], options) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + product_permission_profiles.as_json['product_permission_profiles'] + #ds-snippet-end:Admin9Step3 + end +end diff --git a/app/services/admin_api/eg010_delete_user_data_from_organization_service.rb b/app/services/admin_api/eg010_delete_user_data_from_organization_service.rb new file mode 100644 index 0000000..52c0515 --- /dev/null +++ b/app/services/admin_api/eg010_delete_user_data_from_organization_service.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +class AdminApi::Eg010DeleteUserDataFromOrganizationService + attr_reader :args + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:Admin10Step2 + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Admin10Step2 + + # Get user + users_api = DocuSign_Admin::UsersApi.new(api_client) + + options = DocuSign_Admin::GetUserDSProfilesByEmailOptions.new + options.email = args[:email] + result, _status, headers = users_api.get_user_ds_profiles_by_email_with_http_info(args[:organization_id], options) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + user = result.users[0] + + #ds-snippet-start:Admin10Step3 + organizations_api = DocuSign_Admin::OrganizationsApi.new(api_client) + user_data_redaction_request = DocuSign_Admin::IndividualUserDataRedactionRequest.new( + user_id: user.id, + memberships: [ + DocuSign_Admin::MembershipDataRedactionRequest.new( + account_id: user.memberships[0].account_id + ) + ] + ) + #ds-snippet-end:Admin10Step3 + + #ds-snippet-start:Admin10Step4 + results, _status, headers = organizations_api.redact_individual_user_data_with_http_info(args[:organization_id], user_data_redaction_request) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin10Step4 + + results + end +end diff --git a/app/services/admin_api/eg011_delete_user_data_from_account_service.rb b/app/services/admin_api/eg011_delete_user_data_from_account_service.rb new file mode 100644 index 0000000..4001765 --- /dev/null +++ b/app/services/admin_api/eg011_delete_user_data_from_account_service.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +class AdminApi::Eg011DeleteUserDataFromAccountService + attr_reader :args + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:Admin11Step2 + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Admin11Step2 + + #ds-snippet-start:Admin11Step3 + accounts_api = DocuSign_Admin::AccountsApi.new(api_client) + membership_redaction_request = DocuSign_Admin::IndividualMembershipDataRedactionRequest.new( + user_id: args[:user_id] + ) + #ds-snippet-end:Admin11Step3 + + #ds-snippet-start:Admin11Step4 + results, _status, headers = accounts_api.redact_individual_membership_data_with_http_info(args[:account_id], membership_redaction_request) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin11Step4 + + results + end +end diff --git a/app/services/admin_api/eg012_clone_account_service.rb b/app/services/admin_api/eg012_clone_account_service.rb new file mode 100644 index 0000000..3b87af7 --- /dev/null +++ b/app/services/admin_api/eg012_clone_account_service.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +class AdminApi::Eg012CloneAccountService + attr_reader :args + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:Admin12Step2 + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Admin12Step2 + + #ds-snippet-start:Admin12Step4 + source_account = DocuSign_Admin::AssetGroupAccountCloneSourceAccount.new + source_account.id = args[:source_account_id] + + target_account_admin = DocuSign_Admin::AssetGroupAccountCloneTargetAccountAdmin.new + target_account_admin.first_name = args[:target_account_first_name] + target_account_admin.last_name = args[:target_account_last_name] + target_account_admin.email = args[:target_account_email] + + target_account = DocuSign_Admin::AssetGroupAccountCloneTargetAccount.new + target_account.name = args[:target_account_name] + target_account.admin = target_account_admin + target_account.country_code = 'US' + + account_data = DocuSign_Admin::AssetGroupAccountClone.new + account_data.source_account = source_account + account_data.target_account = target_account + #ds-snippet-end:Admin12Step4 + + #ds-snippet-start:Admin12Step5 + asset_group_api = DocuSign_Admin::ProvisionAssetGroupApi.new(api_client) + results, _status, headers = asset_group_api.clone_asset_group_account_with_http_info(args[:organization_id], account_data) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin12Step5 + + results + end + + def get_account + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + + #ds-snippet-start:Admin12Step3 + asset_group_api = DocuSign_Admin::ProvisionAssetGroupApi.new(api_client) + options = DocuSign_Admin::GetAssetGroupAccountsOptions.new + options.compliant = true + results, _status, headers = asset_group_api.get_asset_group_accounts_with_http_info(args[:organization_id], options) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin12Step3 + + results + end +end diff --git a/app/services/admin_api/eg013_create_account_service.rb b/app/services/admin_api/eg013_create_account_service.rb new file mode 100644 index 0000000..5439555 --- /dev/null +++ b/app/services/admin_api/eg013_create_account_service.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +class AdminApi::Eg013CreateAccountService + attr_reader :args + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:Admin13Step2 + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Admin13Step2 + + #ds-snippet-start:Admin13Step4 + target_account_admin = DocuSign_Admin::SubAccountCreateRequestSubAccountCreationTargetAccountAdmin.new + target_account_admin.first_name = args[:first_name] + target_account_admin.last_name = args[:last_name] + target_account_admin.email = args[:email] + target_account_admin.locale = 'en' + + target_account = DocuSign_Admin::SubAccountCreateRequestSubAccountCreationTargetAccountDetails.new + target_account.name = 'CreatedThroughAPI' + target_account.admin = target_account_admin + target_account.country_code = 'US' + + subscription_details = DocuSign_Admin::SubAccountCreateRequestSubAccountCreationSubscription.new + subscription_details.id = args[:subscription_id] + subscription_details.plan_id = args[:plan_id] + subscription_details.modules = [] + + account_data = DocuSign_Admin::SubAccountCreateRequest.new + account_data.subscription_details = subscription_details + account_data.target_account = target_account + #ds-snippet-end:Admin13Step4 + + #ds-snippet-start:Admin13Step5 + asset_group_api = DocuSign_Admin::ProvisionAssetGroupApi.new(api_client) + results, _status, headers = asset_group_api.create_asset_group_account_with_http_info(args[:organization_id], account_data) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin13Step5 + + results + end + + def get_organization_plan_items + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + + api_client = DocuSign_Admin::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + + #ds-snippet-start:Admin13Step3 + asset_group_api = DocuSign_Admin::ProvisionAssetGroupApi.new(api_client) + results, _status, headers = asset_group_api.get_organization_plan_items_with_http_info(args[:organization_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Admin13Step3 + + results + end +end diff --git a/app/services/admin_api/get_data_service.rb b/app/services/admin_api/get_data_service.rb new file mode 100644 index 0000000..dd7b326 --- /dev/null +++ b/app/services/admin_api/get_data_service.rb @@ -0,0 +1,69 @@ +class AdminApi::GetDataService + attr_reader :args + + def initialize(session, _options = {}) + @args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + organization_id: session[:organization_id] + } + end + + def get_product_permission_profiles + worker + + #ds-snippet-start:Admin2Step3 + product_permission_profiles_api = DocuSign_Admin::ProductPermissionProfilesApi.new(@api_client) + product_permission_profiles = product_permission_profiles_api.get_product_permission_profiles(args[:organization_id], args[:account_id]) + product_permission_profiles.as_json['product_permission_profiles'] + #ds-snippet-end:Admin2Step3 + end + + def get_ds_groups + worker + + #ds-snippet-start:Admin2Step4 + ds_groups_api = DocuSign_Admin::DSGroupsApi.new(@api_client) + ds_groups = ds_groups_api.get_ds_groups(args[:organization_id], args[:account_id]) + ds_groups.as_json['ds_groups'] + #ds-snippet-end:Admin2Step4 + end + + def get_organization_id + worker + accounts_api = DocuSign_Admin::AccountsApi.new(@api_client) + accounts_api.get_organizations.organizations[0].as_json['id'] + end + + def check_import_status(import_id) + worker + #ds-snippet-start:Admin4Step4 + bulk_imports_api = DocuSign_Admin::BulkImportsApi.new(@api_client) + bulk_imports_api.get_bulk_user_import_request(args[:organization_id], import_id) + #ds-snippet-end:Admin4Step4 + end + + def check_user_exists_by_email(email) + worker + users_api = DocuSign_Admin::UsersApi.new(@api_client) + options = DocuSign_Admin::GetUsersOptions.new + options.email = email + response = users_api.get_users(args[:organization_id], options) + + return false if response.users.empty? || response.users[0].user_status == 'closed' + + true + end + + private + + def worker + #ds-snippet-start:AdminRubyStep2 + configuration = DocuSign_Admin::Configuration.new + configuration.host = Rails.configuration.admin_host + @api_client = DocuSign_Admin::ApiClient.new(configuration) + @api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:AdminRubyStep2 + end +end diff --git a/app/services/api_creator.rb b/app/services/api_creator.rb index 3cc5b29..16850e2 100644 --- a/app/services/api_creator.rb +++ b/app/services/api_creator.rb @@ -1,13 +1,10 @@ # frozen_string_literal: true module ApiCreator - def create_initial_api_client(host: nil, debugging: false) - configuration = DocuSign_eSign::Configuration.new - - # https://github.com/docusign/docusign-ruby-client/blob/master/lib/docusign_esign/configuration.rb#L55-L60 + def create_initial_api_client(host: nil, client_module: DocuSign_eSign, debugging: false) + configuration = client_module::Configuration.new configuration.debugging = debugging - - api_client = DocuSign_eSign::ApiClient.new(configuration) + api_client = client_module::ApiClient.new(configuration) api_client.set_oauth_base_path(host) api_client end @@ -22,7 +19,7 @@ def create_account_api(args) api_client.default_headers['Authorization'] = "Bearer #{args[:access_token]}" # Construct your request body - accounts_api = DocuSign_eSign::AccountsApi.new api_client + DocuSign_eSign::AccountsApi.new api_client end def create_template_api(args) @@ -30,18 +27,18 @@ def create_template_api(args) configuration.host = args[:base_path] api_client = DocuSign_eSign::ApiClient.new configuration api_client.default_headers['Authorization'] = "Bearer #{args[:access_token]}" - templates_api = DocuSign_eSign::TemplatesApi.new api_client + DocuSign_eSign::TemplatesApi.new api_client end def create_envelope_api(args) # Obtain your OAuth token - # Step 2 start + #ds-snippet-start:eSignRubyStep2 configuration = DocuSign_eSign::Configuration.new configuration.host = args[:base_path] api_client = DocuSign_eSign::ApiClient.new configuration api_client.default_headers['Authorization'] = "Bearer #{args[:access_token]}" - # Step 2 end + #ds-snippet-end:eSignRubyStep2 DocuSign_eSign::EnvelopesApi.new api_client end @@ -50,6 +47,14 @@ def create_group_api(args) configuration.host = args[:base_path] api_client = DocuSign_eSign::ApiClient.new configuration api_client.default_headers['Authorization'] = "Bearer #{args[:access_token]}" - group_api = DocuSign_eSign::GroupsApi.new api_client + DocuSign_eSign::GroupsApi.new api_client + end + + def create_folders_api(args) + configuration = DocuSign_eSign::Configuration.new + configuration.host = args[:base_path] + api_client = DocuSign_eSign::ApiClient.new configuration + api_client.default_headers['Authorization'] = "Bearer #{args[:access_token]}" + DocuSign_eSign::FoldersApi.new api_client end end diff --git a/app/services/clickwrap/eg001_create_clickwrap_service.rb b/app/services/clickwrap/eg001_create_clickwrap_service.rb index d9a116b..794c28b 100644 --- a/app/services/clickwrap/eg001_create_clickwrap_service.rb +++ b/app/services/clickwrap/eg001_create_clickwrap_service.rb @@ -3,24 +3,22 @@ class Clickwrap::Eg001CreateClickwrapService attr_reader :args - def initialize(session, request) - @args = { - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token], - clickwrap_name: request[:clickwrapName] - } + def initialize(args) + @args = args end - def call + def worker # Step 2. Construct your API headers + #ds-snippet-start:Click1Step2 configuration = DocuSign_Click::Configuration.new configuration.host = args[:base_path] api_client = DocuSign_Click::ApiClient.new configuration api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Click1Step2 # Step 3. Construct the request body + #ds-snippet-start:Click1Step3 # Create the display settings display_settings = DocuSign_Click::DisplaySettings.new( consentButtonText: 'I Agree', @@ -29,7 +27,6 @@ def call format: 'modal', hasAccept: true, mustRead: true, - mustView: true, requireAccept: true, size: 'medium', documentDisplay: 'document' @@ -37,8 +34,7 @@ def call # Read file from a local directory # The reads could raise an exception if the file is not available! - doc_pdf = Rails.configuration.doc_terms_pdf - doc_b64 = Base64.encode64(File.binread(File.join('data', doc_pdf))) + doc_b64 = Base64.encode64(File.binread(args[:doc_pdf])) # Create the document model. documents = [ @@ -57,9 +53,23 @@ def call name: args[:clickwrap_name], requireReacceptance: true ) + #ds-snippet-end:Click1Step3 # Step 4. Call the Click API + #ds-snippet-start:Click1Step4 account_api = DocuSign_Click::AccountsApi.new(api_client) - account_api.create_clickwrap(args[:account_id], clickwrap_request) + results, _status, headers = account_api.create_clickwrap_with_http_info(args[:account_id], clickwrap_request) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Click1Step4 + + results end end diff --git a/app/services/clickwrap/eg002_activate_clickwrap_service.rb b/app/services/clickwrap/eg002_activate_clickwrap_service.rb index 954c8e7..ba8908e 100644 --- a/app/services/clickwrap/eg002_activate_clickwrap_service.rb +++ b/app/services/clickwrap/eg002_activate_clickwrap_service.rb @@ -3,34 +3,79 @@ class Clickwrap::Eg002ActivateClickwrapService attr_reader :args - def initialize(session) - @args = { - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token], - clickwrap_id: session[:clickwrap_id] - } + def initialize(args) + @args = args end - def call + def worker # Step 2. Construct your API headers + #ds-snippet-start:Click2Step2 configuration = DocuSign_Click::Configuration.new configuration.host = args[:base_path] api_client = DocuSign_Click::ApiClient.new configuration api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Click2Step2 # Step 3. Construct the request body # Create a clickwrap request model + #ds-snippet-start:Click2Step3 clickwrap_request = DocuSign_Click::ClickwrapRequest.new(status: 'active') + #ds-snippet-end:Click2Step3 # Step 4. Call the Click API + #ds-snippet-start:Click2Step4 accounts_api = DocuSign_Click::AccountsApi.new(api_client) - response = accounts_api.update_clickwrap_version( + results, _status, headers = accounts_api.update_clickwrap_version_with_http_info( args[:account_id], args[:clickwrap_id], 1, clickwrap_request ) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Click2Step4 + + results + end + + def get_inactive_clickwraps(statuses) + configuration = DocuSign_Click::Configuration.new + configuration.host = args[:ds_base_path] + + api_client = DocuSign_Click::ApiClient.new configuration + api_client.set_default_header('Authorization', "Bearer #{args[:ds_access_token]}") + + accounts_api = DocuSign_Click::AccountsApi.new(api_client) + + clickwraps = [] + statuses.each do |status| + options = DocuSign_Click::GetClickwrapsOptions.new + options.status = status + results, _status, headers = accounts_api.get_clickwraps_with_http_info( + args[:ds_account_id], + options + ) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + clickwraps.concat results.clickwraps + end + + clickwraps end end diff --git a/app/services/clickwrap/eg003_create_new_clickwrap_version_service.rb b/app/services/clickwrap/eg003_create_new_clickwrap_version_service.rb index 7ea4540..d30498f 100644 --- a/app/services/clickwrap/eg003_create_new_clickwrap_version_service.rb +++ b/app/services/clickwrap/eg003_create_new_clickwrap_version_service.rb @@ -1,35 +1,31 @@ # frozen_string_literal: true class Clickwrap::Eg003CreateNewClickwrapVersionService - attr :args + attr_reader :args - def initialize(session) - @args = { - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token], - clickwrap_id: session[:clickwrap_id], - clickwrap_name: session[:clickwrap_name] - } + def initialize(args) + @args = args end - def call + def worker # Step 2. Construct your API headers + #ds-snippet-start:Click3Step2 configuration = DocuSign_Click::Configuration.new configuration.host = args[:base_path] api_client = DocuSign_Click::ApiClient.new configuration api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Click3Step2 # Step 3. Construct the request body # Create a display settings model + #ds-snippet-start:Click3Step3 display_settings = DocuSign_Click::DisplaySettings.new( consentButtonText: 'I Agree', displayName: "#{args[:clickwrap_name]} v2", downloadable: false, format: 'modal', mustRead: true, - mustView: false, requireAccept: false, documentDisplay: 'document', sendToEmail: false @@ -56,13 +52,27 @@ def call requireReacceptance: true, status: 'active' ) + #ds-snippet-end:Click3Step3 # Step 4. Call the Click API + #ds-snippet-start:Click3Step4 accounts_api = DocuSign_Click::AccountsApi.new(api_client) - accounts_api.create_clickwrap_version( + results, _status, headers = accounts_api.create_clickwrap_version_with_http_info( args[:account_id], args[:clickwrap_id], clickwrap_request ) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Click4Step4 + + results end end diff --git a/app/services/clickwrap/eg004_list_clickwraps_service.rb b/app/services/clickwrap/eg004_list_clickwraps_service.rb index c66d8a5..b833054 100644 --- a/app/services/clickwrap/eg004_list_clickwraps_service.rb +++ b/app/services/clickwrap/eg004_list_clickwraps_service.rb @@ -3,26 +3,37 @@ class Clickwrap::Eg004ListClickwrapsService attr_reader :args - def initialize(session, _request) - @args = { - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token] - } + def initialize(args) + @args = args end - def call + def worker # Step 2. Construct your API headers + #ds-snippet-start:Click4Step2 configuration = DocuSign_Click::Configuration.new configuration.host = args[:base_path] api_client = DocuSign_Click::ApiClient.new configuration api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Click4Step2 # Step 3. Call the Click API + #ds-snippet-start:Click4Step3 accounts_api = DocuSign_Click::AccountsApi.new(api_client) - accounts_api.get_clickwraps( + results, _status, headers = accounts_api.get_clickwraps_with_http_info( args[:account_id] ) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Click4Step3 + + results end end diff --git a/app/services/clickwrap/eg005_clickwrap_responses_service.rb b/app/services/clickwrap/eg005_clickwrap_responses_service.rb index 079d0ae..6f46aa3 100644 --- a/app/services/clickwrap/eg005_clickwrap_responses_service.rb +++ b/app/services/clickwrap/eg005_clickwrap_responses_service.rb @@ -3,25 +3,22 @@ class Clickwrap::Eg005ClickwrapResponsesService attr_reader :args - def initialize(session, request) - @args = { - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token], - clickwrap_id: session[:clickwrap_id], - client_user_id: request[:client_user_id] - } + def initialize(args) + @args = args end - def call + def worker # Step 2. Construct your API headers + #ds-snippet-start:Click5Step2 configuration = DocuSign_Click::Configuration.new configuration.host = args[:base_path] api_client = DocuSign_Click::ApiClient.new configuration api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Click5Step2 # Step 3. Call the Click API + #ds-snippet-start:Click5Step3 # Set Clickwrap Agreements options agreements = DocuSign_Click::GetClickwrapAgreementsOptions.new agreements.client_user_id = args[:client_user_id] @@ -29,10 +26,22 @@ def call # Get clickwrap responses using SDK accounts_api = DocuSign_Click::AccountsApi.new(api_client) - accounts_api.get_clickwrap_agreements( + results, _status, headers = accounts_api.get_clickwrap_agreements_with_http_info( args[:account_id], args[:clickwrap_id], agreements ) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Click5Step3 + + results end end diff --git a/app/services/clickwrap/eg006_embed_clickwrap_service.rb b/app/services/clickwrap/eg006_embed_clickwrap_service.rb new file mode 100644 index 0000000..2a99f13 --- /dev/null +++ b/app/services/clickwrap/eg006_embed_clickwrap_service.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +class Clickwrap::Eg006EmbedClickwrapService + attr_reader :args + + def initialize(args) + @args = args + end + + def worker + # Step 2. Construct your API headers + #ds-snippet-start:Click6Step2 + configuration = DocuSign_Click::Configuration.new + configuration.host = args[:base_path] + + api_client = DocuSign_Click::ApiClient.new configuration + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + api_client.config.debugging = true + #ds-snippet-end:Click6Step2 + + #ds-snippet-start:Click6Step3 + document_data = { + 'fullName' => args[:full_name], + 'email' => args[:email], + 'company' => args[:company], + 'title' => args[:title], + 'date' => args[:date] + } + + userAgreementRequest = DocuSign_Click::UserAgreementRequest.new({ + clientUserId: args[:email], + documentData: document_data + }) + + #ds-snippet-end:Click6Step3 + #ds-snippet-start:Click6Step4 + accounts_api = DocuSign_Click::AccountsApi.new(api_client) + + response, _status, headers = accounts_api.create_has_agreed_with_http_info(args[:account_id], args[:clickwrap_id], userAgreementRequest) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + response.as_json + #ds-snippet-end:Click6Step4 + end + + def get_active_clickwraps + configuration = DocuSign_Click::Configuration.new + configuration.host = args[:ds_base_path] + + api_client = DocuSign_Click::ApiClient.new configuration + api_client.set_default_header('Authorization', "Bearer #{args[:ds_access_token]}") + + accounts_api = DocuSign_Click::AccountsApi.new(api_client) + + options = DocuSign_Click::GetClickwrapsOptions.new + options.status = 'active' + + results, _status, headers = accounts_api.get_clickwraps_with_http_info( + args[:ds_account_id], + options + ) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + puts results.as_json['clickwraps'] + results.as_json['clickwraps'] + end + + def get_inactive_clickwraps + configuration = DocuSign_Click::Configuration.new + configuration.host = args[:ds_base_path] + + api_client = DocuSign_Click::ApiClient.new configuration + api_client.set_default_header('Authorization', "Bearer #{args[:ds_access_token]}") + + accounts_api = DocuSign_Click::AccountsApi.new(api_client) + + options = DocuSign_Click::GetClickwrapsOptions.new + options.status = 'inactive' + + results, _status, headers = accounts_api.get_clickwraps_with_http_info( + args[:ds_account_id], + options + ) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + results.as_json['clickwraps'] + end +end diff --git a/app/services/connect/eg001_validate_webhook_message_service.rb b/app/services/connect/eg001_validate_webhook_message_service.rb new file mode 100644 index 0000000..fbc6688 --- /dev/null +++ b/app/services/connect/eg001_validate_webhook_message_service.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'openssl' +require 'base64' + +class Connect::Eg001ValidateWebhookMessageService + attr_reader :args + + def initialize(args) + @args = args + end + + #ds-snippet-start:Connect1Step1 + def worker + digest = OpenSSL::Digest.new('sha256') + hashBytes = OpenSSL::HMAC.digest(digest, args[:secret], args[:payload]) + Base64.encode64(hashBytes) + end + + def hash_valid? + hash = worker(args[:secret], args[:payload]) + OpenSSL.secure_compare(hash.chomp, args[:signature]) + end + #ds-snippet-end:Connect1Step1 +end diff --git a/app/services/connected_fields/eg001_set_connected_fields_service.rb b/app/services/connected_fields/eg001_set_connected_fields_service.rb new file mode 100644 index 0000000..bc22b9a --- /dev/null +++ b/app/services/connected_fields/eg001_set_connected_fields_service.rb @@ -0,0 +1,174 @@ +# frozen_string_literal: true + +class ConnectedFields::Eg001SetConnectedFieldsService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def get_tab_groups + #ds-snippet-start:ConnectedFields1Step2 + headers = { + 'Authorization' => "Bearer #{args[:access_token]}", + 'Accept' => 'application/json', + 'Content-Type' => 'application/json' + } + #ds-snippet-end:ConnectedFields1Step2 + + #ds-snippet-start:ConnectedFields1Step3 + url = URI("#{args[:extensions_base_path]}/v1/accounts/#{args[:account_id]}/connected-fields/tab-groups") + response = Net::HTTP.get_response(url, headers) + response_data = JSON.parse(response.body) + + filtered_apps = response_data.select do |app| + app['tabs']&.any? do |tab| + (tab['extensionData']&.dig('actionContract')&.include? 'Verify') || + (tab['tabLabel']&.include? 'connecteddata') + end + end + + # return unique apps + filtered_apps.uniq { |app| app['appId'] } + #ds-snippet-end:ConnectedFields1Step3 + end + + #ds-snippet-start:ConnectedFields1Step4 + def extract_verification_data(selected_app_id, tab) + extension_data = tab['extensionData'] + + { + app_id: selected_app_id, + extension_group_id: extension_data['extensionGroupId'], + publisher_name: extension_data['publisherName'], + application_name: extension_data['applicationName'], + action_name: extension_data['actionName'], + action_input_key: extension_data['actionInputKey'], + action_contract: extension_data['actionContract'], + extension_name: extension_data['extensionName'], + extension_contract: extension_data['extensionContract'], + required_for_extension: extension_data['requiredForExtension'], + tab_label: tab['tabLabel'], + connection_key: extension_data['connectionInstances']&.dig(0, 'connectionKey') || '', + connection_value: extension_data['connectionInstances']&.dig(0, 'connectionValue') || '' + } + end + #ds-snippet-end:ConnectedFields1Step4 + + def send_envelope(app) + # Create the envelope definition + #ds-snippet-start:ConnectedFields1Step6 + envelope = make_envelope args[:envelope_args], app + + # Call Docusign to create the envelope + envelope_api = create_envelope_api(args) + + results = envelope_api.create_envelope args[:account_id], envelope + + { 'envelope_id' => results.envelope_id } + #ds-snippet-end:ConnectedFields1Step6 + end + + private + + #ds-snippet-start:ConnectedFields1Step5 + def make_envelope(envelope_args, app) + doc = DocuSign_eSign::Document.new + doc.document_base64 = Base64.encode64(File.binread(envelope_args[:doc_pdf])) + doc.name = 'Lorem Ipsum' + doc.file_extension = 'pdf' + doc.document_id = '1' + + # The order in the docs array determines the order in the envelope + # Create a signer recipient to sign the document, identified by name and email + # We're setting the parameters via the object creation + signer = DocuSign_eSign::Signer.new + signer.email = envelope_args[:signer_email] + signer.name = envelope_args[:signer_name] + signer.recipient_id = 1 + + # The Docusign platform searches throughout your envelope's documents for matching + # anchor strings. So the sign_here_2 tab will be used in both document 2 and 3 + # since they use the same anchor string for their "signer 1" tabs. + sign_here = DocuSign_eSign::SignHere.new + sign_here.anchor_string = '/sn1/' + sign_here.anchor_units = 'pixels' + sign_here.anchor_x_offset = '20' + sign_here.anchor_y_offset = '10' + + text_tabs = [] + app['tabs'].each do |tab| + next if tab['tabLabel'].include?('SuggestionInput') + + verification_data = extract_verification_data(app['appId'], tab) + text_tabs.push(text_tab(verification_data, text_tabs.length)) + end + + tabs = DocuSign_eSign::Tabs.new + tabs.sign_here_tabs = [sign_here] + tabs.text_tabs = text_tabs + signer.tabs = tabs + + recipients = DocuSign_eSign::Recipients.new + recipients.signers = [signer] + + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + envelope_definition.email_subject = 'Please sign this document' + envelope_definition.documents = [doc] + envelope_definition.recipients = recipients + envelope_definition.status = 'sent' + envelope_definition + end + + def extension_data(verification_data) + { + extensionGroupId: verification_data[:extension_group_id], + publisherName: verification_data[:publisher_name], + applicationId: verification_data[:app_id], + applicationName: verification_data[:application_name], + actionName: verification_data[:action_name], + actionContract: verification_data[:action_contract], + extensionName: verification_data[:extension_name], + extensionContract: verification_data[:extension_contract], + requiredForExtension: verification_data[:required_for_extension], + actionInputKey: verification_data[:action_input_key], + extensionPolicy: 'MustVerifyToSign', + connectionInstances: [ + { + connectionKey: verification_data[:connection_key], + connectionValue: verification_data[:connection_value] + } + ] + } + end + + def text_tab(verification_data, text_tabs_count) + { + requireInitialOnSharedChange: false, + requireAll: false, + name: verification_data[:application_name], + required: true, + locked: false, + disableAutoSize: false, + maxLength: 4000, + tabLabel: verification_data[:tab_label], + font: 'lucidaconsole', + fontColor: 'black', + fontSize: 'size9', + documentId: '1', + recipientId: '1', + pageNumber: '1', + xPosition: (70 + 100 * (text_tabs_count / 10)).to_s, + yPosition: (560 + 20 * (text_tabs_count % 10)).to_s, + width: '84', + height: '22', + templateRequired: false, + tabType: 'text', + tooltip: verification_data[:action_input_key], + extensionData: extension_data(verification_data) + } + end + #ds-snippet-end:ConnectedFields1Step5 +end diff --git a/app/services/e_sign/eg002_signing_via_email_service.rb b/app/services/e_sign/eg002_signing_via_email_service.rb index 2e5f827..8c7f447 100644 --- a/app/services/e_sign/eg002_signing_via_email_service.rb +++ b/app/services/e_sign/eg002_signing_via_email_service.rb @@ -1,44 +1,42 @@ # frozen_string_literal: true class ESign::Eg002SigningViaEmailService + attr_reader :args + include ApiCreator - attr_reader :args, :envelope_args - - def initialize(session, request, status) - @envelope_args = { - signer_email: request.params['signerEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params['signerName'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_email: request.params['ccEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_name: request.params['ccName'].gsub(/([^\w \-\@\.\,])+/, ''), - status: status - } - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_args: @envelope_args - } - end - def call - worker + def initialize(args) + @args = args end - private - + #ds-snippet-start:eSign2Step3 def worker - # 1. Create the envelope request object - envelope_definition = make_envelope - # 2. Call Envelopes::create API method + # Create the envelope request object + envelope_definition = make_envelope args[:envelope_args] + # Call Envelopes::create API method # Exceptions will be caught by the calling function envelope_api = create_envelope_api(args) - results = envelope_api.create_envelope args[:account_id], envelope_definition + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + envelope_id = results.envelope_id { 'envelope_id' => envelope_id } end + #ds-snippet-end:eSign2Step3 + + private - def make_envelope + #ds-snippet-start:eSign2Step2 + def make_envelope(envelope_args) # document 1 (HTML) has tag **signature_1** # document 2 (DOCX) has tag /sn1/ # document 3 (PDF) has tag /sn1/ @@ -55,31 +53,29 @@ def make_envelope envelope_definition.email_subject = 'Please sign this document set' # Add the documents - doc1_b64 = Base64.encode64(create_document1) + doc1_b64 = Base64.encode64(create_document1(envelope_args)) # Read files 2 and 3 from a local directory # The reads could raise an exception if the file is not available! - doc_docx = Rails.application.config.doc_docx - doc2_b64 = Base64.encode64(File.binread(File.join('data', doc_docx))) - doc_pdf = Rails.application.config.doc_pdf - doc3_b64 = Base64.encode64(File.binread(File.join('data', doc_pdf))) + doc2_b64 = Base64.encode64(File.binread(envelope_args[:doc_docx])) + doc3_b64 = Base64.encode64(File.binread(envelope_args[:doc_pdf])) # Create the document models document1 = DocuSign_eSign::Document.new( - # Create the DocuSign document object + # Create the Docusign document object documentBase64: doc1_b64, name: 'Order acknowledgement', # Can be different from actual file name fileExtension: 'html', # Many different document types are accepted documentId: '1' # A label used to reference the doc ) document2 = DocuSign_eSign::Document.new( - # Create the DocuSign document object + # Create the Docusign document object documentBase64: doc2_b64, name: 'Battle Plan', # Can be different from actual file name fileExtension: 'docx', # Many different document types are accepted documentId: '2' # A label used to reference the do ) document3 = DocuSign_eSign::Document.new( - # Create the DocuSign document object + # Create the Docusign document object documentBase64: doc3_b64, name: 'Lorem Ipsum', # Can be different from actual file name fileExtension: 'pdf', # Many different document types are accepted @@ -109,7 +105,7 @@ def make_envelope # 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 + # The Docusign platform searches throughout your envelope's documents for matching # anchor strings. So the sign_here_2 tab will be used in both document 2 and 3 # since they use the same anchor string for their "signer 1" tabs. sign_here1 = DocuSign_eSign::SignHere.new( @@ -127,9 +123,9 @@ def make_envelope ) # Add the tabs model (including the sign_here tabs) to the signer # The Tabs object takes arrays of the different field/tab types - signer1_tabs = DocuSign_eSign::Tabs.new ({ - signHereTabs: [sign_here1, sign_here2] - }) + signer1_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here1, sign_here2] + }) signer1.tabs = signer1_tabs @@ -145,7 +141,7 @@ def make_envelope envelope_definition end - def create_document1 + def create_document1(args) " @@ -169,4 +165,5 @@ def create_document1 " end -end \ No newline at end of file + #ds-snippet-end:eSign2Step2 +end diff --git a/app/services/e_sign/eg003_list_envelopes_service.rb b/app/services/e_sign/eg003_list_envelopes_service.rb index 4d658a1..7911fd5 100644 --- a/app/services/e_sign/eg003_list_envelopes_service.rb +++ b/app/services/e_sign/eg003_list_envelopes_service.rb @@ -1,29 +1,39 @@ # frozen_string_literal: true class ESign::Eg003ListEnvelopesService - include ApiCreator attr_reader :args - def initialize(session) - @args = { - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token] - } + include ApiCreator + + def initialize(args) + @args = args end - def call - # Step 1. List the envelopes + def worker + # List the envelopes # The Envelopes::listStatusChanges method has many options # See https://developers.docusign.com/esign-rest-api/reference/Envelopes/Envelopes/listStatusChange# # The list status changes call requires at least a from_date OR # a set of envelopeIds. Here we filter using a from_date. # Here we set the from_date to filter envelopes for the last month # Use ISO 8601 date format + #ds-snippet-start:eSign3Step2 envelope_api = create_envelope_api(args) options = DocuSign_eSign::ListStatusChangesOptions.new - options.from_date = (Date.today - 30).strftime('%Y/%m/%d') + options.from_date = (Date.today - 30).strftime('%Y-%m-%d') # Exceptions will be caught by the calling function - results = envelope_api.list_status_changes args[:account_id], options + results, _status, headers = envelope_api.list_status_changes_with_http_info args[:account_id], options + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign3Step2 + + results end -end \ No newline at end of file +end diff --git a/app/services/e_sign/eg004_envelope_info_service.rb b/app/services/e_sign/eg004_envelope_info_service.rb index 5d9c6d8..c7f343a 100644 --- a/app/services/e_sign/eg004_envelope_info_service.rb +++ b/app/services/e_sign/eg004_envelope_info_service.rb @@ -1,20 +1,29 @@ # frozen_string_literal: true class ESign::Eg004EnvelopeInfoService - include ApiCreator attr_reader :args - def initialize(session, envelope_id) - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_id: envelope_id - } + include ApiCreator + + def initialize(args) + @args = args end - def call + def worker + #ds-snippet-start:eSign4Step2 envelope_api = create_envelope_api(args) - results = envelope_api.get_envelope(args[:account_id], args[:envelope_id]) + results, _status, headers = envelope_api.get_envelope_with_http_info(args[:account_id], args[:envelope_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign4Step2 + + results end end diff --git a/app/services/e_sign/eg005_envelope_recipients_service.rb b/app/services/e_sign/eg005_envelope_recipients_service.rb new file mode 100644 index 0000000..c193deb --- /dev/null +++ b/app/services/e_sign/eg005_envelope_recipients_service.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class ESign::Eg005EnvelopeRecipientsService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:eSign5Step2 + envelope_api = create_envelope_api(args) + results, _status, headers = envelope_api.list_recipients_with_http_info args[:account_id], args[:envelope_id] + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign5Step2 + + results + end +end diff --git a/app/services/e_sign/eg006_envelope_docs_service.rb b/app/services/e_sign/eg006_envelope_docs_service.rb index 49e299b..de7a705 100644 --- a/app/services/e_sign/eg006_envelope_docs_service.rb +++ b/app/services/e_sign/eg006_envelope_docs_service.rb @@ -1,51 +1,29 @@ # frozen_string_literal: true class ESign::Eg006EnvelopeDocsService + attr_reader :args + include ApiCreator - attr_reader :args, :standart_doc_items, :session - def initialize(_request, session, envelope_id) - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_id: envelope_id - } - @session = session - # Save the envelopeId and its list of documents in the session so - # they can be used in example 7 (download a document) - @standart_doc_items = [ - { name: 'Combined', type: 'content', document_id: 'combined' }, - { name: 'Zip archive', type: 'zip', document_id: 'archive' } - ] + def initialize(args) + @args = args end - def call - results = worker - # The certificate of completion is named "summary" - # We give it a better name below. - envelope_doc_items = results.envelope_documents.map do |doc| - if doc.document_id == 'certificate' - new = { document_id: doc.document_id, name: 'Certificate of completion', type: doc.type } - else - new = { document_id: doc.document_id, name: doc.name, type: doc.type } - end - new - end + def worker + #ds-snippet-start:eSign6Step3 + envelope_api = create_envelope_api(args) + results, _status, headers = envelope_api.list_documents_with_http_info args[:account_id], args[:envelope_id] - documents = standart_doc_items + envelope_doc_items - envelope_documents = { envelope_id: args[:envelope_id], documents: documents } - session[:envelope_documents] = envelope_documents - results - end + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] - private + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign6Step3 - # ***DS.snippet.0.start - def worker - envelope_api = create_envelope_api(args) - results = envelope_api.list_documents args[:account_id], args[:envelope_id] results end - # ***DS.snippet.0.end -end \ No newline at end of file +end diff --git a/app/services/e_sign/eg007_envelope_get_doc_service.rb b/app/services/e_sign/eg007_envelope_get_doc_service.rb index 441470c..a28e065 100644 --- a/app/services/e_sign/eg007_envelope_get_doc_service.rb +++ b/app/services/e_sign/eg007_envelope_get_doc_service.rb @@ -1,37 +1,33 @@ # frozen_string_literal: true class ESign::Eg007EnvelopeGetDocService + attr_reader :args + include ApiCreator - attr_reader :args, :document_id - - def initialize(request, session, envelope_id, envelope_documents) - document_id = request.params['docSelect'].gsub(/([^\w \-\@\.\,])+/, '') - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_id: envelope_id, - 'document_id' => document_id, - 'envelope_documents' => envelope_documents - } - end - def call - results = worker + def initialize(args) + @args = args end - private - def worker - # Step 3 start + #ds-snippet-start:eSign7Step3 envelope_api = create_envelope_api(args) - document_id = args['document_id'] + document_id = args[:document_id] - temp_file = envelope_api.get_document args[:account_id], document_id, args[:envelope_id] - # Step 3 end + temp_file, _status, headers = envelope_api.get_document_with_http_info args[:account_id], document_id, args[:envelope_id] + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign7Step3 # Find the matching document information item - doc_item = args['envelope_documents']['documents'].find { |item| item['document_id'] == document_id } + doc_item = args[:envelope_documents]['documents'].find { |item| item['document_id'] == document_id } doc_name = doc_item['name'] has_pdf_suffix = doc_name.upcase.end_with? '.PDF' diff --git a/app/services/e_sign/eg008_create_template_service.rb b/app/services/e_sign/eg008_create_template_service.rb index 319156e..f25065f 100644 --- a/app/services/e_sign/eg008_create_template_service.rb +++ b/app/services/e_sign/eg008_create_template_service.rb @@ -1,45 +1,51 @@ # frozen_string_literal: true class ESign::Eg008CreateTemplateService - include ApiCreator - attr_reader :args, :session, :template_name + attr_reader :args - def initialize(session) - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'] - } - @session = session - end + include ApiCreator - def call - session[:template_id] = false # reset - @template_name = 'Example Signer and CC template' - results = worker - session[:template_id] = results[:template_id] - results + def initialize(args) + @args = args end - private - - # ***DS.snippet.0.start def worker templates_api = create_template_api(args) # Step 1. Does the template exist? Try to look it up by name options = DocuSign_eSign::ListTemplatesOptions.new - options.search_text = template_name - results = templates_api.list_templates(args[:account_id], options) + options.search_text = args[:template_name] + results, _status, headers = templates_api.list_templates_with_http_info(args[:account_id], options) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + created_new_template = false - if results.result_set_size.to_i > 0 + if results.result_set_size.to_i.positive? template_id = results.envelope_templates[0].template_id results_template_name = results.envelope_templates[0].name else # Template not found -- so create it # Step 2 create the template + #ds-snippet-start:eSign8Step3 template_req_object = make_template_req - result = templates_api.create_template(args[:account_id], template_req_object) + result, _status, headers = templates_api.create_template_with_http_info(args[:account_id], template_req_object) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign8Step3 created_new_template = true # Retreive the new template ID @@ -53,6 +59,7 @@ def worker } end + #ds-snippet-start:eSign8Step2 def make_template_req # document 1 is a PDF # @@ -63,12 +70,13 @@ def make_template_req # Read the PDF from the disk # Read files 2 and 3 from a local directory # The reads could raise an exception if the file is not available! + template_name = args[:template_name] doc_file = 'World_Wide_Corp_fields.pdf' base64_file_content = Base64.encode64(File.binread(File.join('data', doc_file))) # Create the document model document = DocuSign_eSign::Document.new({ - # Create the DocuSign document object + # Create the Docusign document object 'documentBase64' => base64_file_content, 'name' => 'Lorem Ipsum', # Can be different from actual file name 'fileExtension' => 'pdf', # Many different document types are accepted @@ -77,9 +85,9 @@ def make_template_req # Create the signer recipient model # Since these are role definitions, no name/email: - signer = DocuSign_eSign::Signer.new ({ - 'roleName' => 'signer', 'recipientId' => '1', 'routingOrder' => '1' - }) + signer = DocuSign_eSign::Signer.new({ + 'roleName' => 'signer', 'recipientId' => '1', 'routingOrder' => '1' + }) # Create a cc recipient to receive a copy of the documents cc = DocuSign_eSign::CarbonCopy.new({ 'roleName' => 'cc', 'recipientId' => '2', 'routingOrder' => '2' @@ -127,11 +135,11 @@ def make_template_req ] ) - number1 = DocuSign_eSign::Number.new( + numerical = DocuSign_eSign::Numerical.new( 'documentId' => '1', 'pageNumber' => '1', 'xPosition' => '163', 'yPosition' => '260', - 'font' => 'helvetica', 'fontSize' => 'size14', - 'tabLabel' => 'numbersOnly', 'width' => '84', 'required' => 'false' + 'font' => 'helvetica', 'fontSize' => 'size14', 'validationType' => 'Currency', + 'tabLabel' => 'numericalCurrency', 'width' => '84', 'required' => 'false' ) radio_group = DocuSign_eSign::RadioGroup.new( 'documentId' => '1', 'groupName' => 'radio1', @@ -161,7 +169,7 @@ def make_template_req 'signHereTabs' => [sign_here], 'checkboxTabs' => [check1, check2, check3, check4], 'listTabs' => [list1], - 'numberTabs' => [number1], + 'numericalTabs' => [numerical], 'radioGroupTabs' => [radio_group], 'textTabs' => [text] ) @@ -172,7 +180,7 @@ def make_template_req ) # Top object: - template_request = DocuSign_eSign::EnvelopeTemplate.new( + DocuSign_eSign::EnvelopeTemplate.new( 'documents' => [document], 'name' => template_name, 'emailSubject' => 'Please sign this document', @@ -182,8 +190,6 @@ def make_template_req ), 'status' => 'created' ) - - template_request end - # ***DS.snippet.0.end -end \ No newline at end of file + #ds-snippet-end:eSign8Step2 +end diff --git a/app/services/e_sign/eg009_use_template_service.rb b/app/services/e_sign/eg009_use_template_service.rb index 3cd81a5..ae01a9e 100644 --- a/app/services/e_sign/eg009_use_template_service.rb +++ b/app/services/e_sign/eg009_use_template_service.rb @@ -1,45 +1,39 @@ # frozen_string_literal: true class ESign::Eg009UseTemplateService - include ApiCreator attr_reader :args - def initialize(request, session, template_id) - envelope_args = { - signer_email: request.params['signerEmail'], - signer_name: request.params['signerName'], - cc_email: request.params['ccEmail'], - cc_name: request.params['ccName'], - template_id: template_id - } - - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_args: envelope_args - } - end + include ApiCreator - def call - results = worker + def initialize(args) + @args = args end - private - - # ***DS.snippet.0.start + #ds-snippet-start:eSign9Step3 def worker envelope_args = args[:envelope_args] - # 1. Create the envelope request object + # Create the envelope request object envelope_definition = make_envelope(envelope_args) - # 2. Call Envelopes::create API method + # Call Envelopes::create API method # Exceptions will be caught by the calling function envelope_api = create_envelope_api(args) - results = envelope_api.create_envelope args[:account_id], envelope_definition + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + envelope_id = results.envelope_id { envelope_id: envelope_id } end + #ds-snippet-end:eSign9Step3 + #ds-snippet-start:eSign9Step2 def make_envelope(args) # Create the envelope definition with the template_id envelope_definition = DocuSign_eSign::EnvelopeDefinition.new({ @@ -83,5 +77,5 @@ def make_envelope(args) envelope_definition.template_roles = [signer, cc] envelope_definition end - # ***DS.snippet.0.end -end \ No newline at end of file + #ds-snippet-end:eSign9Step2 +end diff --git a/app/services/e_sign/eg010_send_binary_docs_service.rb b/app/services/e_sign/eg010_send_binary_docs_service.rb index e1e232d..3dfd6e4 100644 --- a/app/services/e_sign/eg010_send_binary_docs_service.rb +++ b/app/services/e_sign/eg010_send_binary_docs_service.rb @@ -1,39 +1,15 @@ class ESign::Eg010SendBinaryDocsService - attr_reader :args, :envelope_args, :session - - def initialize(request, session) - @envelope_args = { - # Validation: Delete any non-usual characters - signer_email: request.params['signerEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params['signerName'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_email: request.params['ccEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_name: request.params['ccName'].gsub(/([^\w \-\@\.\,])+/, '') - } - - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_args: @envelope_args - } - - @session = session - end + attr_reader :args - def call - results = worker - session[:envelope_id] = results['envelope_id'] - results + def initialize(args) + @args = args end - private - - # ***DS.snippet.0.start def worker envelope_args = args[:envelope_args] - # Step 1. Make the envelope JSON request body - envelopeJSON = make_envelope_json - # Step 2. Gather documents and their headers + # Make the envelope JSON request body + envelopeJSON = make_envelope_json(envelope_args) + # Gather documents and their headers # Read files from a local directory # The reads could raise an exception if the file is not available! config = Rails.application.config @@ -45,7 +21,7 @@ def worker filename: envelopeJSON[:documents][0][:name], document_id: envelopeJSON[:documents][0][:documentId], # See encoding note below for explanation - bytes: create_document1.force_encoding('ASCII-8BIT') }, + bytes: create_document1(envelope_args).force_encoding('ASCII-8BIT') }, { mime: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', filename: envelopeJSON[:documents][1][:name], document_id: envelopeJSON[:documents][1][:documentId], @@ -56,7 +32,7 @@ def worker bytes: doc3_pdf_bytes } ] - # Step 3. Create and send the envelope + # Create and send the envelope crlf = "\r\n" boundary = 'multipartboundary_multipartboundary' hyphens = '--' @@ -104,11 +80,14 @@ def worker buffer << hyphens buffer << crlf + #ds-snippet-start:eSign10Step2 header = { "Accept": 'application/json', "Authorization": "Bearer #{args[:access_token]}" } + #ds-snippet-end:eSign10Step2 # Change your API version in the URL below to v2 for API version 2 + #ds-snippet-start:eSign10Step4 uri = URI.parse("#{args[:base_path]}/restapi/v2.1/accounts/#{args[:account_id]}/envelopes") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = (uri.scheme == 'https') @@ -120,17 +99,15 @@ def worker response = http.request(req) obj = JSON.parse(response.body) - if (response.code.to_i >= 200) && (response.code.to_i < 300) - envelope_id = obj['envelopeId'] - session[:envelope_id] = envelope_id - { 'envelope_id' => envelope_id } - else - raise Net::HTTPError.new(response.code, response.body) - end - end + raise Net::HTTPError.new(response.code, response.body) unless (response.code.to_i >= 200) && (response.code.to_i < 300) + #ds-snippet-end:eSign10Step4 + envelope_id = obj['envelopeId'] + { 'envelope_id' => envelope_id } + end - def make_envelope_json + #ds-snippet-start:eSign10Step3 + def make_envelope_json(envelope_args) # document 1 (HTML) has tag **signature_1** # document 2 (DOCX) has tag /sn1/ # document 3 (PDF) has tag /sn1/ @@ -184,26 +161,26 @@ def make_envelope_json # 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 + # The Docusign platform searches throughout your envelope's documents for matching # anchor strings. So the sign_here_2 tab will be used in both document 2 and 3 # since they use the same anchor string for their "signer 1" tabs. - sign_here1 = DocuSign_eSign::SignHere.new ({ - anchorString: '**signature_1**', - anchorYOffset: '10', - anchorUnits: 'pixels', - anchorXOffset: '20' - }) - - sign_here2 = DocuSign_eSign::SignHere.new ({ - anchorString: '/sn1/', - anchorYOffset: '10', - anchorUnits: 'pixels', - anchorXOffset: '20' - }) + sign_here1 = DocuSign_eSign::SignHere.new({ + anchorString: '**signature_1**', + anchorYOffset: '10', + anchorUnits: 'pixels', + anchorXOffset: '20' + }) + + sign_here2 = DocuSign_eSign::SignHere.new({ + anchorString: '/sn1/', + anchorYOffset: '10', + anchorUnits: 'pixels', + anchorXOffset: '20' + }) # Tabs are set per recipient/signer - signer1_tabs = DocuSign_eSign::Tabs.new ({ - signHereTabs: [sign_here1, sign_here2] - }) + signer1_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here1, sign_here2] + }) signer1.tabs = signer1_tabs @@ -217,7 +194,7 @@ def make_envelope_json env_json end - def create_document1 + def create_document1(args) " @@ -241,5 +218,5 @@ def create_document1 " end - # ***DS.snippet.0.end -end \ No newline at end of file + #ds-snippet-end:eSign10Step3 +end diff --git a/app/services/e_sign/eg011_embedded_sending_service.rb b/app/services/e_sign/eg011_embedded_sending_service.rb index 8c5e91e..089c9f6 100644 --- a/app/services/e_sign/eg011_embedded_sending_service.rb +++ b/app/services/e_sign/eg011_embedded_sending_service.rb @@ -1,43 +1,227 @@ # frozen_string_literal: true class ESign::Eg011EmbeddedSendingService + attr_reader :args + include ApiCreator - attr_reader :args, :request, :session - - def initialize(request, session) - envelope_args = { - signer_email: request.params['signerEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params['signerName'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_email: request.params['ccEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_name: request.params['ccName'].gsub(/([^\w \-\@\.\,])+/, '') - } - - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - starting_view: request.params['starting_view'].gsub(/([^\w \-\@\.\,])+/, ''), - envelope_args: envelope_args, - ds_return_url: "#{Rails.application.config.app_url}/ds_common-return" - } - - @session = session - @request = request + + def initialize(args) + @args = args end - def call - # Step 1. Create the envelope as a draft using eg002's worker + def worker + # Create the envelope as a draft using eg002's worker # Exceptions will be caught by the calling function - results = ESign::Eg002SigningViaEmailService.new(session, request, 'created').call + #ds-snippet-start:eSign11Step2 + results = create_envelope(args) envelope_id = results['envelope_id'] - # Step 2. Create the sender view - view_request = DocuSign_eSign::ReturnUrlRequest.new({ returnUrl: args[:ds_return_url] }) + #ds-snippet-end:eSign11Step2 + + #ds-snippet-start:eSign11Step3 + # Create the sender view + view_request = envelope_view_request(args) envelope_api = create_envelope_api(args) - results = envelope_api.create_sender_view args[:account_id], envelope_id, view_request - # Switch to the Recipients/Documents view if requested by the user in the form - url = results.url - url = url.sub! 'send=1', 'send=0' if args[:starting_view] == 'recipient' + results, _status, headers = envelope_api.create_sender_view_with_http_info args[:account_id], envelope_id, view_request + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + url = results.url { 'envelope_id' => envelope_id, 'redirect_url' => url } + #ds-snippet-end:eSign11Step3 + end + + private + + def create_envelope(args) + envelope_definition = make_envelope args[:envelope_args] + # Exceptions will be caught by the calling function + envelope_api = create_envelope_api(args) + + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + envelope_id = results.envelope_id + { 'envelope_id' => envelope_id } + end + + def make_envelope(envelope_args) + # 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 + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + + envelope_definition.email_subject = 'Please sign this document set' + + # Add the documents + doc1_b64 = Base64.encode64(create_document1(envelope_args)) + # Read files 2 and 3 from a local directory + # The reads could raise an exception if the file is not available! + doc_docx = Rails.application.config.doc_docx + doc2_b64 = Base64.encode64(File.binread(File.join('data', doc_docx))) + doc_pdf = Rails.application.config.doc_pdf + doc3_b64 = Base64.encode64(File.binread(File.join('data', doc_pdf))) + + # Create the document models + document1 = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc1_b64, + name: 'Order acknowledgement', # Can be different from actual file name + fileExtension: 'html', # Many different document types are accepted + documentId: '1' # A label used to reference the doc + ) + document2 = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc2_b64, + name: 'Battle Plan', # Can be different from actual file name + fileExtension: 'docx', # Many different document types are accepted + documentId: '2' # A label used to reference the do + ) + document3 = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc3_b64, + name: 'Lorem Ipsum', # Can be different from actual file name + fileExtension: 'pdf', # Many different document types are accepted + documentId: '3' # A label used to reference the doc + ) + + # The order in the docs array determines the order in the envelope + envelope_definition.documents = [document1, document2, document3] + + # Create the signer recipient model + signer1 = DocuSign_eSign::Signer.new + signer1.email = envelope_args[:signer_email] + signer1.name = envelope_args[:signer_name] + signer1.recipient_id = '1' + signer1.routing_order = '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 + + # Create a cc recipient to receive a copy of the documents + cc1 = DocuSign_eSign::CarbonCopy.new( + email: envelope_args[:cc_email], + name: envelope_args[:cc_name], + routingOrder: '2', + 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 sign_here_2 tab will be used in both document 2 and 3 + # since they use the same anchor string for their "signer 1" tabs. + sign_here1 = DocuSign_eSign::SignHere.new( + anchorString: '**signature_1**', + anchorYOffset: '10', + anchorUnits: 'pixels', + anchorXOffset: '20' + ) + + sign_here2 = DocuSign_eSign::SignHere.new( + anchorString: '/sn1/', + anchorYOffset: '10', + anchorUnits: 'pixels', + anchorXOffset: '20' + ) + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object takes arrays of the different field/tab types + signer1_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here1, sign_here2] + }) + + signer1.tabs = signer1_tabs + + # Add the recipients to the envelope object + recipients = DocuSign_eSign::Recipients.new( + signers: [signer1], + carbonCopies: [cc1] + ) + # Request that the envelope be sent by setting status to "sent". + # To request that the envelope be created as a draft, set status to "created" + envelope_definition.recipients = recipients + envelope_definition.status = envelope_args[:status] + envelope_definition + end + + #ds-snippet-start:eSign11Step3 + def envelope_view_request(args) + DocuSign_eSign::EnvelopeViewRequest.new( + returnUrl: args[:ds_return_url], + viewAccess: 'envelope', + settings: DocuSign_eSign::EnvelopeViewSettings.new( + startingScreen: args[:starting_view], + sendButtonAction: 'send', + showBackButton: 'false', + backButtonAction: 'previousPage', + showHeaderActions: 'false', + showDiscardAction: 'false', + lockToken: '', + recipientSettings: DocuSign_eSign::EnvelopeViewRecipientSettings.new( + showEditRecipients: 'false', + showContactsList: 'false' + ), + documentSettings: DocuSign_eSign::EnvelopeViewDocumentSettings.new( + showEditDocuments: 'false', + showEditDocumentVisibility: 'false', + showEditPages: 'false' + ), + taggerSettings: DocuSign_eSign::EnvelopeViewTaggerSettings.new( + paletteSections: 'default', + paletteDefault: 'custom' + ), + templateSettings: DocuSign_eSign::EnvelopeViewTemplateSettings.new( + showMatchingTemplatesPrompt: 'true' + ) + ) + ) + end + #ds-snippet-end:eSign11Step3 + + def create_document1(args) + " + + + + + + +

World Wide Corp

+

Order Processing Division

+

Ordered by #{args[:signer_name]}

+

Email: #{args[:signer_email]}

+

Copy to: #{args[:cc_name]}, #{args[:cc_email]}

+

+ 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**/

+ + " end -end \ No newline at end of file +end diff --git a/app/services/e_sign/eg012_embedded_console_service.rb b/app/services/e_sign/eg012_embedded_console_service.rb index c5d312e..c18cff5 100644 --- a/app/services/e_sign/eg012_embedded_console_service.rb +++ b/app/services/e_sign/eg012_embedded_console_service.rb @@ -1,43 +1,36 @@ # frozen_string_literal: true class ESign::Eg012EmbeddedConsoleService - include ApiCreator attr_reader :args - def initialize(session, envelope_id, request) - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_id: envelope_id, - starting_view: request.params['starting_view'], - ds_return_url: "#{Rails.application.config.app_url}/ds_common-return" - } - end + include ApiCreator - def call - # Call the worker method - # More data validation would be a good idea here - # Strip anything other than characters listed - results = worker + def initialize(args) + @args = args end - private - - # ***DS.snippet.0.start def worker # Step 1. Create the NDSE view request object # Exceptions will be caught by the calling function + #ds-snippet-start:eSign12Step2 view_request = DocuSign_eSign::ConsoleViewRequest.new({ returnUrl: args[:ds_return_url] }) - if args[:starting_view] == 'envelope' && args[:envelope_id] - view_request.envelope_id = args[:envelope_id] - end + view_request.envelope_id = args[:envelope_id] if args[:starting_view] == 'envelope' && args[:envelope_id] # Step 2. Call the API method envelope_api = create_envelope_api(args) - results = envelope_api.create_console_view args[:account_id], view_request + results, _status, headers = envelope_api.create_console_view_with_http_info args[:account_id], view_request + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + #ds-snippet-end:eSign12Step2 { 'redirect_url' => results.url } end - # ***DS.snippet.0.end -end \ No newline at end of file +end diff --git a/app/services/e_sign/eg013_add_doc_to_template_service.rb b/app/services/e_sign/eg013_add_doc_to_template_service.rb index 85d1dbe..e3c8b73 100644 --- a/app/services/e_sign/eg013_add_doc_to_template_service.rb +++ b/app/services/e_sign/eg013_add_doc_to_template_service.rb @@ -1,39 +1,15 @@ # frozen_string_literal: true class ESign::Eg013AddDocToTemplateService + attr_reader :args + include ApiCreator - attr_reader :args, :envelope_args, :session - - def initialize(request, session, template_id) - @envelope_args = { - signer_email: request.params['signerEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params['signerName'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_email: request.params['ccEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_name: request.params['ccName'].gsub(/([^\w \-\@\.\,])+/, ''), - item: request.params['item'].gsub(/([^\w \-\@\.\,])+/, ''), - quantity: request.params['quantity'].gsub(/([^\w \-\@\.\,])+/, '').to_i, - signer_client_id: 1000, - template_id: template_id, - ds_return_url: "#{Rails.application.config.app_url}/ds_common-return" - } - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_args: envelope_args - } - @session = session - end - def call - results = worker - session[:envelope_id] = results[:envelope_id] # Save for use by other examples - results + def initialize(args) + @args = args end - private - - # ***DS.snippet.0.start + #ds-snippet-start:eSign13Step3 def worker envelope_args = args[:envelope_args] # 1. Create the envelope request object @@ -42,12 +18,24 @@ def worker # Exceptions will be caught by the calling function envelope_api = create_envelope_api(args) - results = envelope_api.create_envelope(args[:account_id], envelope_definition) + results, _status, headers = envelope_api.create_envelope_with_http_info(args[:account_id], envelope_definition) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + envelope_id = results.envelope_id + #ds-snippet-end:eSign13Step3 # 3. Create the Recipient View request object + #ds-snippet-start:eSign13Step4 authentication_method = 'None' # How is this application authenticating # the signer? See the `authenticationMethod' definition - # https://developers.docusign.com/esign-rest-api/reference/Envelopes/EnvelopeViews/createRecipient + # https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopeviews/createrecipient/ recipient_view_request = DocuSign_eSign::RecipientViewRequest.new( authenticationMethod: authentication_method, returnUrl: envelope_args[:ds_return_url], @@ -57,11 +45,25 @@ def worker ) # 4. Obtain the recipient_view_url for the embedded signing # Exceptions will be caught by the calling function - results = envelope_api.create_recipient_view(args[:account_id], - envelope_id, recipient_view_request) + results, _status, headers = envelope_api.create_recipient_view_with_http_info(args[:account_id], + envelope_id, recipient_view_request) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + { envelope_id: envelope_id, redirect_url: results.url } end + #ds-snippet-end:eSign13Step4 + private + + #ds-snippet-start:eSign13Step2 def make_envelope(args) # 1. Create recipients for server template. Note that the Recipients object # is used, not TemplateRole @@ -91,7 +93,7 @@ def make_envelope(args) # Add the roles via an inlineTemplate inlineTemplates: [ DocuSign_eSign::InlineTemplate.new( - 'sequence' => '1', + 'sequence' => '2', 'recipients' => recipients_server_template ) ] @@ -123,7 +125,7 @@ def make_envelope(args) ) # 6. Create the HTML document that will be added to the envelope - doc1_b64 = Base64.encode64(create_document1) + doc1_b64 = Base64.encode64(create_document1(args)) doc1 = DocuSign_eSign::Document.new( documentBase64: doc1_b64, name: 'Appendix 1--Sales order', # Can be different from actual file name @@ -136,21 +138,19 @@ def make_envelope(args) # Add the recipients via an inlineTemplate inlineTemplates: [ DocuSign_eSign::InlineTemplate.new( - sequence: '2', recipients: recipients_added_doc + sequence: '1', recipients: recipients_added_doc ) ], document: doc1 ) # 8. Create the envelope definition with the composited templates - envelope_definition = DocuSign_eSign::EnvelopeDefinition.new( + DocuSign_eSign::EnvelopeDefinition.new( status: 'sent', compositeTemplates: [comp_template1, comp_template2] ) - - envelope_definition end - def create_document1 + def create_document1(args) <<~HEREDOC @@ -176,5 +176,5 @@ def create_document1 HEREDOC end - # ***DS.snippet.0.start -end \ No newline at end of file + #ds-snippet-end:eSign13Step2 +end diff --git a/app/services/e_sign/eg014_collect_payment_service.rb b/app/services/e_sign/eg014_collect_payment_service.rb index 681decc..e529bcc 100644 --- a/app/services/e_sign/eg014_collect_payment_service.rb +++ b/app/services/e_sign/eg014_collect_payment_service.rb @@ -1,44 +1,39 @@ # frozen_string_literal: true class ESign::Eg014CollectPaymentService + attr_reader :args + include ApiCreator - attr_reader :args, :envelope_args - - def initialize(request, session) - @envelope_args = { - signer_email: request.params['signerEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params['signerName'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_email: request.params['ccEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_name: request.params['ccName'].gsub(/([^\w \-\@\.\,])+/, ''), - gateway_account_id: Rails.application.config.gateway_account_id, - gateway_name: Rails.application.config.gateway_name, - gateway_display_name: Rails.application.config.gateway_display_name - } - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_args: @envelope_args - } - end - def call - results = worker args + def initialize(args) + @args = args end - private - - # ***DS.snippet.0.start - def worker(args) - envelope_definition = make_envelope(envelope_args) - # 2. Create and send the envelope + #ds-snippet-start:eSign14Step4 + def worker + envelope_definition = make_envelope(args[:envelope_args]) + # Create and send the envelope # Exceptions will be caught by the calling function envelope_api = create_envelope_api(args) - results = envelope_api.create_envelope args[:account_id], envelope_definition + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + envelope_id = results.envelope_id { envelope_id: envelope_id } end + #ds-snippet-end:eSign14Step4 + + private + #ds-snippet-start:eSign14Step3 def make_envelope(args) # This function creates the envelope definition for the order form # document 1 (html) has multiple tags: @@ -50,16 +45,16 @@ def make_envelope(args) # 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 + # After it is signed, a copy is sent to the cc person # # ################################################################# - # # - # # NOTA BENE: 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 WYSIWYG - # # form designer. - # # + # # + # # NOTA BENE: 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 WYSIWYG + # # form designer. + # # # ################################################################# # @@ -217,5 +212,5 @@ def make_envelope(args) envelope_definition.recipients = recipients envelope_definition end - # ***DS.snippet.0.end -end \ No newline at end of file + #ds-snippet-end:eSign14Step3 +end diff --git a/app/services/e_sign/eg015_get_envelope_tab_data_service.rb b/app/services/e_sign/eg015_get_envelope_tab_data_service.rb index abb5fe7..9dc4e17 100644 --- a/app/services/e_sign/eg015_get_envelope_tab_data_service.rb +++ b/app/services/e_sign/eg015_get_envelope_tab_data_service.rb @@ -1,25 +1,14 @@ # frozen_string_literal: true class ESign::Eg015GetEnvelopeTabDataService + attr_reader :args + include ApiCreator - attr_reader :args, :envelope_id - - def initialize(envelope_id, session) - @args = { - access_token: session['ds_access_token'], - base_path: session['ds_base_path'], - account_id: session['ds_account_id'] - } - @envelope_id = envelope_id - end - def call - results = worker + def initialize(args) + @args = args end - private - - # ***DS.snippet.0.start def worker # Step 3. Call the eSignature REST API # The Envelopes::getEnvelopeFormData method has many options @@ -27,7 +16,19 @@ def worker # The get form data call requires an account ID and an envelope ID # Exceptions will be caught by the calling function - results = create_envelope_api(args).get_form_data args[:account_id], envelope_id + #ds-snippet-start:eSign15Step3 + results, _status, headers = create_envelope_api(args).get_form_data_with_http_info args[:account_id], args[:envelope_id] + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign15Step3 + + results end - # ***DS.snippet.0.end end diff --git a/app/services/e_sign/eg016_set_envelope_tab_data_service.rb b/app/services/e_sign/eg016_set_envelope_tab_data_service.rb index 64bbaef..cdb9ede 100644 --- a/app/services/e_sign/eg016_set_envelope_tab_data_service.rb +++ b/app/services/e_sign/eg016_set_envelope_tab_data_service.rb @@ -1,78 +1,85 @@ # frozen_string_literal: true class ESign::Eg016SetEnvelopeTabDataService + attr_reader :args + include ApiCreator - attr_reader :args, :session - - def initialize(request, session) - @args = { - # Validation: Delete any non-usual characters - signer_email: request.params[:signerEmail].gsub(/([^\w\-.+@, ])+/, ''), - signer_name: request.params[:signerName].gsub(/([^\w\-., ])+/, ''), - access_token: session['ds_access_token'], - base_path: session['ds_base_path'], - account_id: session['ds_account_id'] - } - @session = session - end - def call - worker + def initialize(args) + @args = args end - private - - # ***DS.snippet.0.start def worker ds_ping_url = Rails.application.config.app_url ds_return_url = "#{ds_ping_url}/ds_common-return" signer_client_id = 1000 pdf_filename = 'World_Wide_Corp_salary.docx' - # Step 4. Construct the request body + # Construct the request body envelope = make_envelope(args[:signer_email], args[:signer_name], signer_client_id, pdf_filename) - # Step 5. Call the eSignature REST API - results = create_envelope_api(args).create_envelope args[:account_id], envelope + # Call the eSignature REST API + #ds-snippet-start:eSign16Step4 + results, _status, headers = create_envelope_api(args).create_envelope_with_http_info args[:account_id], envelope + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + envelope_id = results.envelope_id - # Save for future use within the example launcher - session[:envelope_id] = envelope_id + #ds-snippet-end:eSign16Step4 - # Step 6. Create the View Request + # Create the View Request + #ds-snippet-start:eSign16Step5 view_request = make_recipient_view_request(args[:signer_email], args[:signer_name], signer_client_id, ds_return_url, ds_ping_url) + #ds-snippet-end:eSign16Step5 # Call the CreateRecipientView API - results = create_envelope_api(args).create_recipient_view args[:account_id], envelope_id, view_request + results, _status, headers = create_envelope_api(args).create_recipient_view_with_http_info args[:account_id], envelope_id, view_request + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end - # Step 4. Redirect the user to the embedded signing + # Redirect the user to the embedded signing # Don't use an iframe! # State can be stored/recovered using the framework's session or a # query parameter on the return URL (see the makeRecipientViewRequest method) # Redirect to results.url - results.url + { url: results.url, envelope_id: envelope_id } end - def make_recipient_view_request(_signer_email, _signer_name, signer_client_id, ds_return_url, ds_ping_url) + def make_recipient_view_request(signer_email, signer_name, signer_client_id, ds_return_url, ds_ping_url) view_request = DocuSign_eSign::RecipientViewRequest.new # Set the URL where you want the recipient to go once they are done signing; this # 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. It's usually better to use the session mechanism + # to the Docusign signing. It's usually better to use the session mechanism # of your web framework. Query parameters can be changed/spoofed very easily - view_request.return_url = ds_return_url + '?state=123' + view_request.return_url = "#{ds_return_url}?state=123" # How has your app authenticated the user? In addition to your app's authentication, - # you can include authenticate steps from DocuSign; e.g., SMS authentication + # you can include authenticate steps from Docusign; e.g., SMS authentication view_request.authentication_method = 'none' # Recipient information must match embedded recipient info we used to create the envelope - view_request.email = args[:signer_email] - view_request.user_name = args[:signer_name] + view_request.email = signer_email + view_request.user_name = signer_name view_request.client_user_id = signer_client_id - # DocuSign recommends that you redirect to DocuSign for the embedded signing. There are + # Docusign recommends that you redirect to Docusign for the embedded signing. There are # multiple ways to save state. To maintain your application's session, use the pingUrl - # parameter. It causes the DocuSign Signing web page (not the DocuSign server) + # parameter. It causes the Docusign Signing web page (not the Docusign server) # to send pings via AJAX to your app view_request.ping_frequency = '600' # seconds # NOTE: The pings will only be sent if the pingUrl is an HTTPS address @@ -81,7 +88,8 @@ def make_recipient_view_request(_signer_email, _signer_name, signer_client_id, d view_request end - def make_envelope(_signer_email, _signer_name, signer_client_id, pdf_filename) + #ds-snippet-start:eSign16Step3 + def make_envelope(signer_email, signer_name, signer_client_id, pdf_filename) envelope_definition = DocuSign_eSign::EnvelopeDefinition.new envelope_definition.email_subject = 'Please sign this document sent from Ruby SDK' @@ -95,13 +103,12 @@ def make_envelope(_signer_email, _signer_name, signer_client_id, pdf_filename) envelope_definition.documents = [doc1] # Create a signer recipient to sign the document, identified by name and email # We're setting the parameters via the object creation - signer1 = DocuSign_eSign::Signer.new ({ - email: args[:signer_email], name: args[:signer_name], - clientUserId: signer_client_id, recipientId: 1 - }) + signer1 = DocuSign_eSign::Signer.new({ + email: signer_email, name: signer_name, + clientUserId: signer_client_id, recipientId: 1 + }) - # Step 3. Create Tabs and CustomFields - salary = '$123,000' + # Create Tabs and CustomFields sign_here1 = DocuSign_eSign::SignHere.new sign_here1.anchor_string = '/sn1/' @@ -117,7 +124,7 @@ def make_envelope(_signer_email, _signer_name, signer_client_id, pdf_filename) text_legal.font = 'Helvetica' text_legal.font_size = 'size11' text_legal.bold = 'true' - text_legal.value = args[:signer_name] + text_legal.value = signer_name text_legal.locked = 'false' text_legal.tab_id = 'legal_name' text_legal.tab_label = 'Legal name' @@ -126,32 +133,55 @@ def make_envelope(_signer_email, _signer_name, signer_client_id, pdf_filename) text_familiar.anchor_string = '/familiar/' text_familiar.anchor_units = 'pixels' text_familiar.anchor_y_offset = '-9' - text_familiar.anchor_y_offset = '5' + text_familiar.anchor_x_offset = '5' text_familiar.font = 'Helvetica' text_familiar.font_size = 'size11' text_familiar.bold = 'true' - text_familiar.value = args[:signer_name] + text_familiar.value = signer_name text_familiar.locked = 'false' text_familiar.tab_id = 'familiar_name' text_familiar.tab_label = 'Familiar name' - text_salary = DocuSign_eSign::Text.new - text_salary.anchor_string = '/salary/' - text_salary.anchor_units = 'pixels' - text_salary.anchor_y_offset = '-9' - text_salary.anchor_y_offset = '5' - text_salary.font = 'Helvetica' - text_salary.font_size = 'size11' - text_salary.bold = 'true' - text_salary.value = salary - text_salary.locked = 'true' - text_salary.tab_id = 'salary' - text_salary.tab_label = 'Salary' + locale_policy_tab = DocuSign_eSign::LocalePolicyTab.new + locale_policy_tab.culture_name = 'en-US' + locale_policy_tab.currency_code = 'usd' + locale_policy_tab.currency_positive_format = 'csym_1_comma_234_comma_567_period_89' + locale_policy_tab.currency_negative_format = 'minus_csym_1_comma_234_comma_567_period_89' + locale_policy_tab.use_long_currency_format = 'true' + + numerical_salary = DocuSign_eSign::Numerical.new + numerical_salary.page_number = '1' + numerical_salary.document_id = '1' + numerical_salary.x_position = '210' + numerical_salary.y_position = '235' + numerical_salary.height = '20' + numerical_salary.width = '70' + numerical_salary.min_numerical_value = '0' + numerical_salary.max_numerical_value = '1000000' + numerical_salary.validation_type = 'Currency' + numerical_salary.font = 'Helvetica' + numerical_salary.font_size = 'size11' + numerical_salary.bold = 'true' + numerical_salary.tab_id = 'salary' + numerical_salary.tab_label = 'Salary' + numerical_salary.numerical_value = '123000' + numerical_salary.locale_policy = locale_policy_tab + + salary_custom_field = DocuSign_eSign::TextCustomField.new + salary_custom_field.name = 'salary' + salary_custom_field.required = 'false' + salary_custom_field.show = 'true' + salary_custom_field.value = '123000' + + cf = DocuSign_eSign::CustomFields.new + cf.text_custom_fields = [salary_custom_field] + envelope_definition.custom_fields = cf # Tabs are set per recipient / signer tabs = DocuSign_eSign::Tabs.new tabs.sign_here_tabs = [sign_here1] - tabs.text_tabs = [text_legal, text_familiar, text_salary] + tabs.text_tabs = [text_legal, text_familiar] + tabs.numerical_tabs = [numerical_salary] signer1.tabs = tabs # Add the recipients to the envelope object @@ -164,5 +194,5 @@ def make_envelope(_signer_email, _signer_name, signer_client_id, pdf_filename) envelope_definition.status = 'sent' envelope_definition end - # ***DS.snippet.0.end + #ds-snippet-end:eSign16Step3 end diff --git a/app/services/e_sign/eg017_set_template_tab_values_service.rb b/app/services/e_sign/eg017_set_template_tab_values_service.rb index 1734e5c..ac90c23 100644 --- a/app/services/e_sign/eg017_set_template_tab_values_service.rb +++ b/app/services/e_sign/eg017_set_template_tab_values_service.rb @@ -1,169 +1,177 @@ # frozen_string_literal: true class ESign::Eg017SetTemplateTabValuesService -include ApiCreator -attr_reader :args, :envelope_args - -def initialize(request, session, template_id) - @envelope_args = { - signer_email: request.params['signerEmail'], - signer_name: request.params['signerName'], - cc_email: request.params['ccEmail'], - cc_name: request.params['ccName'], - template_id: template_id - } - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_args: @envelope_args - } + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def worker + signer_client_id = 1000 + envelope_args = args[:envelope_args] + ds_ping_url = envelope_args[:ds_ping_url] + ds_return_url = "#{ds_ping_url}/ds_common-return" + + # Step 4. Construct the request body + #ds-snippet-start:eSign17Step4 + envelope_definition = make_envelope(envelope_args) + #ds-snippet-end:eSign17Step4 + + # Step 5. Call the eSignature REST API + #ds-snippet-start:eSign17Step5 + envelope_api = create_envelope_api(args) + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + envelope_id = results.envelope_id + #ds-snippet-end:eSign17Step5 + + # Step 6. Create the View Request + #ds-snippet-start:eSign17Step6 + view_request = make_recipient_view_request(envelope_args[:signer_email], envelope_args[:signer_name], signer_client_id, ds_return_url, ds_ping_url) + + # Call the CreateRecipientView API + results, _status, headers = envelope_api.create_recipient_view_with_http_info args[:account_id], envelope_id, view_request + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + # Redirect the user to the embedded signing + # Don't use an iframe! + # State can be stored/recovered using the framework's session or a + # query parameter on the return URL (see the makeRecipientViewRequest method) + # Redirect to results.url + results.url + #ds-snippet-end:eSign17Step6 + end + + private + + def make_envelope(args) + # Create the envelope definition with the template_id + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new({ + status: 'sent', + templateId: args[:template_id] + }) + # Create the template role elements to connect the signer and cc recipients + # to the template + signer = DocuSign_eSign::TemplateRole.new({ + clientUserId: 1000, + email: args[:signer_email], + name: args[:signer_name], + roleName: 'signer' + }) + # Create a cc template role. + cc = DocuSign_eSign::TemplateRole.new({ + email: args[:cc_email], + name: args[:cc_name], + roleName: 'cc' + }) + + # Step 3. Create Tabs and CustomFields + + # List item + #ds-snippet-start:eSign17Step3 + list1 = DocuSign_eSign::List.new + list1.value = 'Green' + list1.document_id = '1' + list1.page_number = '1' + list1.tab_label = 'list' + + # Checkboxes + check1 = DocuSign_eSign::Checkbox.new + check1.tab_label = 'ckAuthorization' + check1.selected = 'true' + + check3 = DocuSign_eSign::Checkbox.new + check3.tab_label = 'ckAgreement' + check3.selected = 'true' + + radioGroup = DocuSign_eSign::RadioGroup.new + radioGroup.group_name = 'radio1' + radio = DocuSign_eSign::Radio.new + radio.value = 'white' + radio.selected = 'true' + radioGroup.radios = [radio] + + text = DocuSign_eSign::Text.new + text.tab_label = 'text' + text.value = 'Jabberwocky!' + + # We can also add a new tab (field) to the ones already in the template + textExtra = DocuSign_eSign::Text.new + textExtra.document_id = '1' + textExtra.page_number = '1' + textExtra.x_position = '280' + textExtra.y_position = '172' + textExtra.font = 'helvetica' + textExtra.font_size = 'size14' + textExtra.tab_label = 'added text field' + textExtra.height = '23' + textExtra.width = '84' + textExtra.required = 'false' + textExtra.bold = 'true' + textExtra.value = args[:signer_name] + textExtra.locked = 'false' + textExtra.tab_id = 'name' + + # Pull together the existing and new tabs in a Tabs object + tabs = DocuSign_eSign::Tabs.new + tabs.list_tabs = [list1] + tabs.checkbox_tabs = [check1, check3] + tabs.radio_group_tabs = [radioGroup] + tabs.text_tabs = [text, textExtra] + signer.tabs = tabs + + # Add the TemplateRole objects to the envelope object + envelope_definition.template_roles = [signer, cc] + envelope_definition + #ds-snippet-end:eSign17Step3 + end + + def make_recipient_view_request(signer_email, signer_name, signer_client_id, ds_return_url, ds_ping_url) + view_request = DocuSign_eSign::RecipientViewRequest.new + # Set the URL where you want the recipient to go once they are done signing; this + # 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. It's usually better to use the session mechanism + # of your web framework. Query parameters can be changed/spoofed very easily + view_request.return_url = "#{ds_return_url}?state=123" + + # How has your app authenticated the user? In addition to your app's authentication, + # you can include authenticate steps from Docusign; e.g., SMS authentication + view_request.authentication_method = 'none' + + # Recipient information must match embedded recipient info we used to create the envelope + view_request.email = signer_email + view_request.user_name = signer_name + view_request.client_user_id = signer_client_id + + # Docusign recommends that you redirect to Docusign for the embedded signing. There are + # multiple ways to save state. To maintain your application's session, use the pingUrl + # parameter. It causes the Docusign signing web page (not the Docusign server) + # to send pings via AJAX to your app + view_request.ping_frequency = '600' # seconds + # NOTE: The pings will only be sent if the pingUrl is an HTTPS address + view_request.ping_url = ds_ping_url # optional setting + + view_request + end end - -def call - results = worker -end - -private - -# ***DS.snippet.0.start -def worker - ds_ping_url = Rails.application.config.app_url - ds_return_url = "#{ds_ping_url}/ds_common-return" - signer_client_id = 1000 - envelope_args = args[:envelope_args] - - # Step 4. Construct the request body - envelope_definition = make_envelope(envelope_args) - - # Step 5. Call the eSignature REST API - envelope_api = create_envelope_api(args) - results = envelope_api.create_envelope args[:account_id], envelope_definition - envelope_id = results.envelope_id - - - # Step 6. Create the View Request - view_request = make_recipient_view_request(envelope_args[:signer_email], envelope_args[:signer_name], signer_client_id, ds_return_url, ds_ping_url) - - # Call the CreateRecipientView API - results = create_envelope_api(args).create_recipient_view args[:account_id], envelope_id, view_request - - # Redirect the user to the embedded signing - # Don't use an iframe! - # State can be stored/recovered using the framework's session or a - # query parameter on the return URL (see the makeRecipientViewRequest method) - # Redirect to results.url - results.url -end - -def make_envelope(args) - # Create the envelope definition with the template_id - envelope_definition = DocuSign_eSign::EnvelopeDefinition.new({ - status: 'sent', - templateId: args[:template_id] - }) - # Create the template role elements to connect the signer and cc recipients - # to the template - signer = DocuSign_eSign::TemplateRole.new({ - clientUserId: 1000, - email: args[:signer_email], - name: args[:signer_name], - roleName: 'signer' - }) - # Create a cc template role. - cc = DocuSign_eSign::TemplateRole.new({ - email: args[:cc_email], - name: args[:cc_name], - roleName: 'cc' - }) - - # Step 3. Create Tabs and CustomFields - - # List item - list1 = DocuSign_eSign::List.new - list1.value = 'Green' - list1.document_id = '1' - list1.page_number = '1' - list1.tab_label = 'list' - - # Checkboxes - check1 = DocuSign_eSign::Checkbox.new - check1.tab_label = 'ckAuthorization' - check1.selected = 'true' - - check3 = DocuSign_eSign::Checkbox.new - check3.tab_label = 'ckAgreement' - check3.selected = 'true' - - radioGroup = DocuSign_eSign::RadioGroup.new - radioGroup.group_name = 'radio1' - radio = DocuSign_eSign::Radio.new - radio.value = 'white' - radio.selected = 'true' - radioGroup.radios = [radio] - - text = DocuSign_eSign::Text.new - text.tab_label = 'text' - text.value = 'Jabberwocky!' - - # We can also add a new tab (field) to the ones already in the template - textExtra = DocuSign_eSign::Text.new - textExtra.document_id = '1' - textExtra.page_number = '1' - textExtra.x_position = '280' - textExtra.y_position = '172' - textExtra.font = 'helvetica' - textExtra.font_size = 'size14' - textExtra.tab_label = 'added text field' - textExtra.height = '23' - textExtra.width = '84' - textExtra.required = 'false' - textExtra.bold = 'true' - textExtra.value = args[:signer_name] - textExtra.locked = 'false' - textExtra.tab_id = 'name' - - # Pull together the existing and new tabs in a Tabs object - tabs = DocuSign_eSign::Tabs.new - tabs.list_tabs = [list1] - tabs.checkbox_tabs = [check1, check3] - tabs.radio_group_tabs = [radioGroup] - tabs.text_tabs = [text, textExtra] - signer.tabs = tabs - - # Add the TemplateRole objects to the envelope object - envelope_definition.template_roles = [signer, cc] - envelope_definition -end - - -def make_recipient_view_request(signer_email, signer_name, signer_client_id, ds_return_url, ds_ping_url) - view_request = DocuSign_eSign::RecipientViewRequest.new - # Set the URL where you want the recipient to go once they are done signing; this - # 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. It's usually better to use the session mechanism - # of your web framework. Query parameters can be changed/spoofed very easily - view_request.return_url = ds_return_url + '?state=123' - - # How has your app authenticated the user? In addition to your app's authentication, - # you can include authenticate steps from DocuSign; e.g., SMS authentication - view_request.authentication_method = 'none' - - # Recipient information must match embedded recipient info we used to create the envelope - view_request.email = signer_email - view_request.user_name = signer_name - view_request.client_user_id = signer_client_id - - # DocuSign recommends that you redirect to DocuSign for the embedded signing. There are - # multiple ways to save state. To maintain your application's session, use the pingUrl - # parameter. It causes the DocuSign signing web page (not the DocuSign server) - # to send pings via AJAX to your app - view_request.ping_frequency = '600' # seconds - # NOTE: The pings will only be sent if the pingUrl is an HTTPS address - view_request.ping_url = ds_ping_url # optional setting - - view_request -end -# ***DS.snippet.0.end -end \ No newline at end of file diff --git a/app/services/e_sign/eg018_get_envelope_custom_field_data_service.rb b/app/services/e_sign/eg018_get_envelope_custom_field_data_service.rb index ba7fd14..8288193 100644 --- a/app/services/e_sign/eg018_get_envelope_custom_field_data_service.rb +++ b/app/services/e_sign/eg018_get_envelope_custom_field_data_service.rb @@ -1,20 +1,28 @@ # frozen_string_literal: true class ESign::Eg018GetEnvelopeCustomFieldDataService - include ApiCreator attr_reader :args - def initialize(session, envelope_id) - @args = { - access_token: session['ds_access_token'], - base_path: session['ds_base_path'], - account_id: session['ds_account_id'], - envelope_id: envelope_id - } + include ApiCreator + + def initialize(args) + @args = args end - def call - # Step 3. Call the eSignature REST API - results = create_envelope_api(args).list_custom_fields args[:account_id], args[:envelope_id] + def worker + #ds-snippet-start:eSign18Step3 + results, _status, headers = create_envelope_api(args).list_custom_fields_with_http_info args[:account_id], args[:envelope_id] + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign18Step3 + + results end end diff --git a/app/services/e_sign/eg019_access_code_authentication_service.rb b/app/services/e_sign/eg019_access_code_authentication_service.rb index d721df1..d704bce 100644 --- a/app/services/e_sign/eg019_access_code_authentication_service.rb +++ b/app/services/e_sign/eg019_access_code_authentication_service.rb @@ -1,37 +1,26 @@ # frozen_string_literal: true class ESign::Eg019AccessCodeAuthenticationService + attr_reader :args + include ApiCreator - attr_reader :args, :envelope_args, :request, :session - def initialize(request, session) - @envelope_args = { - signer_email: request.params['signerEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params['signerName'].gsub(/([^\w \-\@\.\,])+/, ''), - status: 'sent' - } - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_args: @envelope_args - } - @request = request - @session = session + def initialize(args) + @args = args end - def call - # ***DS.snippet.0.start + def worker envelope_api = create_envelope_api(args) + envelope_args = args[:envelope_args] - # Step 3: Construct your envelope JSON body + #ds-snippet-start:eSign19Step3 envelope_definition = DocuSign_eSign::EnvelopeDefinition.new envelope_definition.email_subject = 'Please sign this document set' # Add the documents and create the document models pdf_filename = 'World_Wide_Corp_lorem.pdf' document1 = DocuSign_eSign::Document.new( - # Create the DocuSign Document object + # Create the Docusign Document object documentBase64: Base64.encode64(File.binread(File.join('data', pdf_filename))), name: 'NDA', # Can be different from actual file name fileExtension: 'pdf', # Many different document types are accepted @@ -46,7 +35,7 @@ def call signer1.name = envelope_args[:signer_name] signer1.recipient_id = '1' signer1.routing_order = '1' - signer1.access_code = request.params['accessCode'] + signer1.access_code = args[:accessCode] sign_here1 = DocuSign_eSign::SignHere.new sign_here1.anchor_string = '/sn1/' @@ -56,9 +45,9 @@ def call # Add the tabs model (including the sign_here tabs) to the signer # The Tabs object takes arrays of the different field/tab types - signer1_tabs = DocuSign_eSign::Tabs.new ({ - signHereTabs: [sign_here1] - }) + signer1_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here1] + }) signer1.tabs = signer1_tabs # Add the recipients to the Envelope object @@ -69,10 +58,21 @@ def call # To request that the envelope be created as a draft, set to "created" envelope_definition.recipients = recipients envelope_definition.status = envelope_args[:status] - # Step 4. Call the eSignature REST API - results = envelope_api.create_envelope(args[:account_id], envelope_definition) - session[:envelope_id] = results.envelope_id + #ds-snippet-end:eSign19Step3 + + #ds-snippet-start:eSign19Step4 + results, _status, headers = envelope_api.create_envelope_with_http_info(args[:account_id], envelope_definition) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign19Step4 + results - # ***DS.snippet.0.end end -end \ No newline at end of file +end diff --git a/app/services/e_sign/eg020_phone_authentication_service.rb b/app/services/e_sign/eg020_phone_authentication_service.rb new file mode 100644 index 0000000..def2877 --- /dev/null +++ b/app/services/e_sign/eg020_phone_authentication_service.rb @@ -0,0 +1,137 @@ +# frozen_string_literal: true + +class ESign::Eg020PhoneAuthenticationService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def worker(workflow_id) + envelope_args = args[:envelope_args] + + return 'phone_auth_not_enabled' if workflow_id.blank? + + # Construct your envelope JSON body + #ds-snippet-start:eSign20Step4 + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + envelope_definition.email_subject = 'Please sign this document set' + + # Add the documents and create the document models + pdf_filename = 'World_Wide_Corp_lorem.pdf' + document1 = DocuSign_eSign::Document.new( + # Create the Docusign Document object + documentBase64: Base64.encode64(File.binread(File.join('data', pdf_filename))), + name: 'Lorem', # Can be different from actual file name + fileExtension: 'pdf', # Many different document types are accepted + documentId: '1' # A label used to reference the doc + ) + + envelope_definition.documents = [document1] + + # Create the signer recipient model + signer1 = DocuSign_eSign::Signer.new + signer1.email = envelope_args[:signer_email] + signer1.name = envelope_args[:signer_name] + signer1.role_name = '' + signer1.note = '' + signer1.status = 'created' + signer1.delivery_method = 'email' + signer1.recipient_id = '1' + signer1.routing_order = '1' + + phone_number = DocuSign_eSign::RecipientIdentityPhoneNumber.new + phone_number.country_code = envelope_args[:country_code] + phone_number.number = envelope_args[:phone_number] + + input_option = DocuSign_eSign::RecipientIdentityInputOption.new + input_option.name = 'phone_number_list' + input_option.value_type = 'PhoneNumberList' + input_option.phone_number_list = [phone_number] + + identity_verification = DocuSign_eSign::RecipientIdentityVerification.new + + identity_verification.workflow_id = workflow_id + identity_verification.input_options = [input_option] + + signer1.identity_verification = identity_verification + + sign_here1 = DocuSign_eSign::SignHere.new + sign_here1.name = 'SignHereTab' + sign_here1.anchor_string = '/sn1/' + sign_here1.anchor_units = 'pixels' + sign_here1.anchor_x_offset = '20' + sign_here1.anchor_y_offset = '10' + + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object wants arrays of the different field/tab types + signer1_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here1] + }) + signer1.tabs = signer1_tabs + + # Add the recipients to the Envelope object + recipients = DocuSign_eSign::Recipients.new( + signers: [signer1] + ) + # 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_definition.recipients = recipients + envelope_definition.status = envelope_args[:status] + #ds-snippet-end:eSign20Step4 + + # Call the eSignature REST API + #ds-snippet-start:eSign20Step5 + envelope_api = create_envelope_api(args) + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign20Step5 + + results + end + + def get_workflow + # Retrieve the workflow id + + #ds-snippet-start:eSign20Step3 + workflow_details = create_account_api(args) + workflow_response, _status, headers = workflow_details.get_account_identity_verification_with_http_info(args[:account_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + # Check that idv authentication is enabled + if workflow_response.identity_verification + phone_auth_workflow = workflow_response.identity_verification.find { |item| item.default_name == 'Phone Authentication' } + if phone_auth_workflow + phone_auth_workflow.workflow_id + else + '' + end + else + '' + end + #ds-snippet-end:eSign20Step3 + rescue DocuSign_eSign::ApiError => e + error = JSON.parse e.response_body + @error_code = e.code + @error_message = error['error_description'] + render 'ds_common/error' + end +end diff --git a/app/services/e_sign/eg020_sms_authentication_service.rb b/app/services/e_sign/eg020_sms_authentication_service.rb deleted file mode 100644 index ae29007..0000000 --- a/app/services/e_sign/eg020_sms_authentication_service.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg020SmsAuthenticationService - include ApiCreator - attr_reader :args, :envelope_args, :request, :session - - def initialize(request, session) - @envelope_args = { - signer_email: request.params['signerEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params['signerName'].gsub(/([^\w \-\@\.\,])+/, ''), - status: 'sent' - } - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_args: @envelope_args - } - @request = request - @session = session - end - - def call - # ***DS.snippet.0.start - envelope_api = create_envelope_api(args) - - # Step 3: Construct your envelope JSON body - envelope_definition = DocuSign_eSign::EnvelopeDefinition.new - envelope_definition.email_subject = 'Please sign this document set' - - # Add the documents and create the document models - pdf_filename = 'World_Wide_Corp_lorem.pdf' - document1 = DocuSign_eSign::Document.new( - # Create the DocuSign Document object - documentBase64: Base64.encode64(File.binread(File.join('data', pdf_filename))), - name: 'NDA', # Can be different from actual file name - fileExtension: 'pdf', # Many different document types are accepted - documentId: '1' # A label used to reference the doc - ) - - envelope_definition.documents = [document1] - - # Create the signer recipient model - signer1 = DocuSign_eSign::Signer.new - signer1.email = envelope_args[:signer_email] - signer1.name = envelope_args[:signer_name] - signer1.recipient_id = '1' - signer1.routing_order = '1' - signer1.require_id_lookup = 'true' - signer1.id_check_configuration_name = 'SMS Auth $' - - sms = DocuSign_eSign::RecipientSMSAuthentication.new - sms.sender_provided_numbers = [request.params[:phoneNumber]] - signer1.sms_authentication = sms - - sign_here1 = DocuSign_eSign::SignHere.new - sign_here1.anchor_string = '/sn1/' - sign_here1.anchor_units = 'pixels' - sign_here1.anchor_x_offset = '20' - sign_here1.anchor_y_offset = '10' - - # Add the tabs model (including the sign_here tabs) to the signer - # The Tabs object wants arrays of the different field/tab types - signer1_tabs = DocuSign_eSign::Tabs.new ({ - signHereTabs: [sign_here1] - }) - signer1.tabs = signer1_tabs - - # Add the recipients to the envelope object - recipients = DocuSign_eSign::Recipients.new( - signers: [signer1] - ) - # Request that the envelope be sent by setting status to "sent" - # To request that the envelope be created as a draft, set status to "created" - envelope_definition.recipients = recipients - envelope_definition.status = envelope_args[:status] - - # Step 4. Call the eSignature REST API - results = envelope_api.create_envelope args[:account_id], envelope_definition - session[:envelope_id] = results.envelope_id - results - # ***DS.snippet.0.end - end -end \ No newline at end of file diff --git a/app/services/e_sign/eg021_phone_authentication_service.rb b/app/services/e_sign/eg021_phone_authentication_service.rb deleted file mode 100644 index 2398dfb..0000000 --- a/app/services/e_sign/eg021_phone_authentication_service.rb +++ /dev/null @@ -1,86 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg021PhoneAuthenticationService - include ApiCreator - attr_reader :args, :envelope_args, :request, :session - - def initialize(request, session) - @envelope_args = { - signer_email: request.params['signerEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params['signerName'].gsub(/([^\w \-\@\.\,])+/, ''), - status: 'sent' - } - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_args: @envelope_args - } - @request = request - @session = session - end - - def call - # ***DS.snippet.0.start - envelope_api = create_envelope_api(args) - - # Step 3: Construct your envelope JSON body - envelope_definition = DocuSign_eSign::EnvelopeDefinition.new - envelope_definition.email_subject = 'Please sign this document set' - - # Add the documents and create the document models - pdf_filename = 'World_Wide_Corp_lorem.pdf' - document1 = DocuSign_eSign::Document.new( - # Create the DocuSign Document object - documentBase64: Base64.encode64(File.binread(File.join('data', pdf_filename))), - name: 'NDA', # Can be different from actual file name - fileExtension: 'pdf', # Many different document types are accepted - documentId: '1' # A label used to reference the doc - ) - - envelope_definition.documents = [document1] - - # Create the signer recipient model - signer1 = DocuSign_eSign::Signer.new - signer1.email = envelope_args[:signer_email] - signer1.name = envelope_args[:signer_name] - signer1.recipient_id = '1' - signer1.routing_order = '1' - - sign_here1 = DocuSign_eSign::SignHere.new - sign_here1.anchor_string = '/sn1/' - sign_here1.anchor_units = 'pixels' - sign_here1.anchor_x_offset = '20' - sign_here1.anchor_y_offset = '10' - - # Add the tabs model (including the sign_here tabs) to the signer - # The Tabs object wants arrays of the different field/tab types - signer1_tabs = DocuSign_eSign::Tabs.new ({ - signHereTabs: [sign_here1] - }) - signer1.tabs = signer1_tabs - - signer1.id_check_configuration_name = 'Phone Auth $' - - phoneAuth = DocuSign_eSign::RecipientPhoneAuthentication.new - phoneAuth.record_voice_print = 'false' - phoneAuth.validate_recip_provided_number = 'false' - phoneAuth.recip_may_provide_number = 'true' - - phoneAuth.sender_provided_numbers = [request.params[:phoneNumber]] - signer1.phone_authentication = phoneAuth - - # Add the recipients to the Envelope object - recipients = DocuSign_eSign::Recipients.new( - signers: [signer1] - ) - # 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_definition.recipients = recipients - envelope_definition.status = envelope_args[:status] - - # Step 4. Call the eSignature REST API - results = envelope_api.create_envelope args[:account_id], envelope_definition - # ***DS.snippet.0.end - end -end \ No newline at end of file diff --git a/app/services/e_sign/eg022_kba_authentication_service.rb b/app/services/e_sign/eg022_kba_authentication_service.rb index 92c21c4..7601615 100644 --- a/app/services/e_sign/eg022_kba_authentication_service.rb +++ b/app/services/e_sign/eg022_kba_authentication_service.rb @@ -1,37 +1,26 @@ # frozen_string_literal: true class ESign::Eg022KbaAuthenticationService + attr_reader :args + include ApiCreator - attr_reader :args, :envelope_args, :request, :session - - def initialize(request, session) - @envelope_args = { - signer_email: request.params['signerEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params['signerName'].gsub(/([^\w \-\@\.\,])+/, ''), - status: 'sent' - } - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_args: @envelope_args - } - @request = request - @session = session + + def initialize(args) + @args = args end - def call - # ***DS.snippet.0.start + def worker envelope_api = create_envelope_api(args) + envelope_args = args[:envelope_args] - # Step 3: Construct your envelope JSON body + #ds-snippet-start:eSign22Step3 envelope_definition = DocuSign_eSign::EnvelopeDefinition.new envelope_definition.email_subject = 'Please sign this document set' # Add the documents and create the document models pdf_filename = 'World_Wide_Corp_lorem.pdf' document1 = DocuSign_eSign::Document.new( - # Create the DocuSign Document object + # Create the Docusign Document object documentBase64: Base64.encode64(File.binread(File.join('data', pdf_filename))), name: 'NDA', # Can be different from actual file name fileExtension: 'pdf', # Many different document types are accepted @@ -67,11 +56,21 @@ def call # To request that the envelope be created as a draft, set to "created" envelope_definition.recipients = recipients envelope_definition.status = envelope_args[:status] + #ds-snippet-end:eSign22Step3 + + #ds-snippet-start:eSign22Step4 + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign22Step4 - # Step 4. Call the eSignature REST API - results = envelope_api.create_envelope args[:account_id], envelope_definition - session[:envelope_id] = results.envelope_id results - # ***DS.snippet.0.end end -end \ No newline at end of file +end diff --git a/app/services/e_sign/eg023_idv_authentication_service.rb b/app/services/e_sign/eg023_idv_authentication_service.rb index 15951dd..2b8d51c 100644 --- a/app/services/e_sign/eg023_idv_authentication_service.rb +++ b/app/services/e_sign/eg023_idv_authentication_service.rb @@ -1,95 +1,110 @@ -# frozen_string_literal: true +# frozen_string_literal: true class ESign::Eg023IdvAuthenticationService + attr_reader :args + include ApiCreator - attr_reader :args, :envelope_args, :request, :session - - def initialize(request, session) - @envelope_args = { - signer_email: request.params['signerEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params['signerName'].gsub(/([^\w \-\@\.\,])+/, ''), - status: 'sent' - } - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_args: @envelope_args - } - @request = request - @session = session + + def initialize(args) + @args = args end - def call - # ***DS.snippet.0.start + def worker + envelope_args = args[:envelope_args] - # Step 3. Obtain your workflow ID + # Obtain your workflow ID + #ds-snippet-start:eSign23Step3 accounts_api = create_account_api(args) - wf_res = accounts_api.get_account_identity_verification args[:account_id] - workflow_id = wf_res.identity_verification[0].workflow_id - - if workflow_id.blank? - "needs_idv_activated" - - else - # Step 4. Construct your envelope JSON body - envelope_definition = DocuSign_eSign::EnvelopeDefinition.new - envelope_definition.email_subject = 'Please sign this document set' - - # Add the documents - pdf_filename = "World_Wide_Corp_lorem.pdf" - - # Create the document models - document1 = DocuSign_eSign::Document.new( - # Create the DocuSign Document object - documentBase64: Base64.encode64(File.binread(File.join('data', pdf_filename))), - name: 'Lorem', # Can be different from the actual file name - fileExtension: 'pdf', # Many different document types are accepted - documentId: '1' # A label used to reference the doc - ) - - envelope_definition.documents = [document1] - - # Create the signer recipient model - signer1 = DocuSign_eSign::Signer.new - signer1.email = envelope_args[:signer_email] - signer1.name = envelope_args[:signer_name] - signer1.recipient_id = '1' - signer1.routing_order = '1' - - sign_here1 = DocuSign_eSign::SignHere.new - sign_here1.anchor_string = '/sn1/' - sign_here1.anchor_units = 'pixels' - sign_here1.anchor_x_offset = '20' - sign_here1.anchor_y_offset = '10' - - # Add the tabs model (including the sign_here tabs) to the signer - # The Tabs object takes arrays of the different field/tab types - signer1_tabs = DocuSign_eSign::Tabs.new ({ - signHereTabs: [sign_here1] - }) - signer1.tabs = signer1_tabs - - wf = DocuSign_eSign::RecipientIdentityVerification.new - wf.workflow_id = workflow_id - signer1.identity_verification = wf - - # Add the recipients to the Envelope object - recipients = DocuSign_eSign::Recipients.new( - signers: [signer1] - ) - # Request that the envelope be sent by setting status to "sent" - # To request that the envelope be created as a draft, set status to "created" - envelope_definition.recipients = recipients - envelope_definition.status = envelope_args[:status] - - # Step 5. Call the eSignature REST API - envelope_api = create_envelope_api(args) - results = envelope_api.create_envelope args[:account_id], envelope_definition - session[:envelope_id] = results.envelope_id - results - - # ***DS.snippet.0.end + workflow_response, _status, headers = accounts_api.get_account_identity_verification_with_http_info args[:account_id] + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + if workflow_response.identity_verification + idv_workflow = workflow_response.identity_verification.find { |item| item.default_name == 'DocuSign ID Verification' } + workflow_id = idv_workflow.workflow_id if idv_workflow + end + ##ds-snippet-end:eSign23Step3 + + return 'idv_not_enabled' if workflow_id.blank? + + puts 'WORKFLOW ID: ' + puts workflow_id + + # Construct your envelope JSON body + #ds-snippet-start:eSign23Step4 + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + envelope_definition.email_subject = 'Please sign this document set' + + # Add the documents + pdf_filename = 'World_Wide_Corp_lorem.pdf' + + # Create the document models + document1 = DocuSign_eSign::Document.new( + # Create the Docusign Document object + documentBase64: Base64.encode64(File.binread(File.join('data', pdf_filename))), + name: 'Lorem', # Can be different from the actual file name + fileExtension: 'pdf', # Many different document types are accepted + documentId: '1' # A label used to reference the doc + ) + + envelope_definition.documents = [document1] + + # Create the signer recipient model + signer1 = DocuSign_eSign::Signer.new + signer1.email = envelope_args[:signer_email] + signer1.name = envelope_args[:signer_name] + signer1.recipient_id = '1' + signer1.routing_order = '1' + + sign_here1 = DocuSign_eSign::SignHere.new + sign_here1.anchor_string = '/sn1/' + sign_here1.anchor_units = 'pixels' + sign_here1.anchor_x_offset = '20' + sign_here1.anchor_y_offset = '10' + + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object takes arrays of the different field/tab types + signer1_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here1] + }) + signer1.tabs = signer1_tabs + + wf = DocuSign_eSign::RecipientIdentityVerification.new + wf.workflow_id = workflow_id + signer1.identity_verification = wf + + # Add the recipients to the Envelope object + recipients = DocuSign_eSign::Recipients.new( + signers: [signer1] + ) + # Request that the envelope be sent by setting status to "sent" + # To request that the envelope be created as a draft, set status to "created" + envelope_definition.recipients = recipients + envelope_definition.status = envelope_args[:status] + #ds-snippet-end:eSign23Step4 + + # Call the eSignature REST API + #ds-snippet-start:eSign23Step5 + envelope_api = create_envelope_api(args) + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" end + #ds-snippet-end:eSign23Step5 + + results end -end \ No newline at end of file +end diff --git a/app/services/e_sign/eg024_permission_create_service.rb b/app/services/e_sign/eg024_permission_create_service.rb index 60e7240..54cbc83 100644 --- a/app/services/e_sign/eg024_permission_create_service.rb +++ b/app/services/e_sign/eg024_permission_create_service.rb @@ -1,58 +1,67 @@ # frozen_string_literal: true class ESign::Eg024PermissionCreateService + attr_reader :args + include ApiCreator - attr_reader :args, :request, :permission_profile_name - def initialize(session, request) - @args = { - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token] - } - @request = request - @permission_profile_name = request.params[:permission_profile_name] + def initialize(args) + @args = args end - def call + def worker + #ds-snippet-start:eSign24Step4 accounts_api = create_account_api(args) + permission_profile_name = args[:permission_profile_name] permission_profile_settings = make_permission_profile_settings - create_permission_account = accounts_api.create_permission_profile(args[:account_id], { permissionProfileName: permission_profile_name, - settings: permission_profile_settings}, - options = DocuSign_eSign::CreatePermissionProfileOptions.default) + results, _status, headers = accounts_api.create_permission_profile_with_http_info(args[:account_id], { permissionProfileName: permission_profile_name, + settings: permission_profile_settings }, + DocuSign_eSign::CreatePermissionProfileOptions.default) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + results + #ds-snippet-end:eSign24Step4 end private + #ds-snippet-start:eSign24Step3 def make_permission_profile_settings - permission_profile = DocuSign_eSign::PermissionProfile.new - pr_settings = ({ + { useNewDocuSignExperienceInterface: 0, - allowBulkSending: "true", - allowEnvelopeSending: "true", - allowSignerAttachments: "true", - allowTaggingInSendAndCorrect:"true", - allowWetSigningOverride:"true", - allowedAddressBookAccess:"personalAndShared", - allowedTemplateAccess: "share", - enableRecipientViewingNotifications:"true", - enableSequentialSigningInterface:"true", - receiveCompletedSelfSignedDocumentsAsEmailLinks:"false", - signingUiVersion:"v2", - useNewSendingInterface:"true", - allowApiAccess:"true", - allowApiAccessToAccount:"true", - allowApiSendingOnBehalfOfOthers:"true", - allowApiSequentialSigning:"true", - enableApiRequestLogging:"true", - allowDocuSignDesktopClient:"false", - allowSendersToSetRecipientEmailLanguage:"true", - allowVaulting:"false", - allowedToBeEnvelopeTransferRecipient: "true", - enableTransactionPointIntegration: "false", - powerFormRole: "admin", - vaultingMode: "none" - }) - permission_profile.settings = pr_settings + allowBulkSending: 'true', + allowEnvelopeSending: 'true', + allowSignerAttachments: 'true', + allowTaggingInSendAndCorrect: 'true', + allowWetSigningOverride: 'true', + allowedAddressBookAccess: 'personalAndShared', + allowedTemplateAccess: 'share', + enableRecipientViewingNotifications: 'true', + enableSequentialSigningInterface: 'true', + receiveCompletedSelfSignedDocumentsAsEmailLinks: 'false', + signingUiVersion: 'v2', + useNewSendingInterface: 'true', + allowApiAccess: 'true', + allowApiAccessToAccount: 'true', + allowApiSendingOnBehalfOfOthers: 'true', + allowApiSequentialSigning: 'true', + enableApiRequestLogging: 'true', + allowDocuSignDesktopClient: 'false', + allowSendersToSetRecipientEmailLanguage: 'true', + allowVaulting: 'false', + allowedToBeEnvelopeTransferRecipient: 'true', + enableTransactionPointIntegration: 'false', + powerFormRole: 'admin', + vaultingMode: 'none' + } end -end \ No newline at end of file + #ds-snippet-end:eSign24Step3 +end diff --git a/app/services/e_sign/eg025_permissions_set_user_group_service.rb b/app/services/e_sign/eg025_permissions_set_user_group_service.rb index 728dd14..3e3dda4 100644 --- a/app/services/e_sign/eg025_permissions_set_user_group_service.rb +++ b/app/services/e_sign/eg025_permissions_set_user_group_service.rb @@ -1,27 +1,36 @@ # frozen_string_literal: true class ESign::Eg025PermissionsSetUserGroupService + attr_reader :args + include ApiCreator - attr_reader :args, :permission_profile_id, :group_id - - def initialize(session, request) - @args = { - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token] - } - @request = request - @permission_profile_id = request.params[:lists] - @group_id = request.params[:group_lists] + + def initialize(args) + @args = args end - def call + def worker group_api = create_group_api(args) - # Step 3: Construct the request body - params = {groups: [{permissionProfileId: permission_profile_id, groupId: group_id}]} - - # Step 4: Call the eSignature REST API - result = group_api.update_groups(args[:account_id], params) + # Construct the request body + #ds-snippet-start:eSign25Step3 + params = { groups: [{ permissionProfileId: args[:permission_profile_id], groupId: args[:group_id] }] } + #ds-snippet-end:eSign25Step3 + + # Call the eSignature REST API + #ds-snippet-start:eSign25Step4 + results, _status, headers = group_api.update_groups_with_http_info(args[:account_id], params) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign25Step4 + + results end -end \ No newline at end of file +end diff --git a/app/services/e_sign/eg026_permissions_change_single_setting_service.rb b/app/services/e_sign/eg026_permissions_change_single_setting_service.rb index 31b0600..7a6cf5a 100644 --- a/app/services/e_sign/eg026_permissions_change_single_setting_service.rb +++ b/app/services/e_sign/eg026_permissions_change_single_setting_service.rb @@ -1,57 +1,69 @@ # frozen_string_literal: true class ESign::Eg026PermissionsChangeSingleSettingService + attr_reader :args + include ApiCreator - attr_reader :args, :request, :permission_profile_id - def initialize(session, request) - @args = { - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token] - } - @request = request - @permission_profile_id = request.params[:lists] + def initialize(args) + @args = args end - def call + def worker + #ds-snippet-start:eSign26Step4 accounts_api = create_account_api(args) permission_profile_settings = make_permission_profile_settings - update_permission_profile = accounts_api.update_permission_profile(args[:account_id], permission_profile_id, - permission_profile_settings, options = DocuSign_eSign::UpdatePermissionProfileOptions.default) + permission_profile_id = args[:permission_profile_id] + results, _status, headers = accounts_api.update_permission_profile_with_http_info(args[:account_id], permission_profile_id, + permission_profile_settings, DocuSign_eSign::UpdatePermissionProfileOptions.default) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign26Step4 + + results end private def make_permission_profile_settings + #ds-snippet-start:eSign26Step3 permission_profile = DocuSign_eSign::PermissionProfile.new - pr_settings = ({ + pr_settings = { useNewDocuSignExperienceInterface: 0, - allowBulkSending: "true", - allowEnvelopeSending: "true", - allowSignerAttachments: "true", - allowTaggingInSendAndCorrect:"true", - allowWetSigningOverride:"true", - allowedAddressBookAccess:"personalAndShared", - allowedTemplateAccess: "share", - enableRecipientViewingNotifications:"true", - enableSequentialSigningInterface:"true", - receiveCompletedSelfSignedDocumentsAsEmailLinks:"false", - signingUiVersion:"v2", - useNewSendingInterface:"true", - allowApiAccess:"true", - allowApiAccessToAccount:"true", - allowApiSendingOnBehalfOfOthers:"true", - allowApiSequentialSigning:"true", - enableApiRequestLogging:"true", - allowDocuSignDesktopClient:"false", - allowSendersToSetRecipientEmailLanguage:"true", - allowVaulting:"false", - allowedToBeEnvelopeTransferRecipient: "true", - enableTransactionPointIntegration: "false", - powerFormRole: "admin", - vaultingMode: "none" - }) + allowBulkSending: 'true', + allowEnvelopeSending: 'true', + allowSignerAttachments: 'true', + allowTaggingInSendAndCorrect: 'true', + allowWetSigningOverride: 'true', + allowedAddressBookAccess: 'personalAndShared', + allowedTemplateAccess: 'share', + enableRecipientViewingNotifications: 'true', + enableSequentialSigningInterface: 'true', + receiveCompletedSelfSignedDocumentsAsEmailLinks: 'false', + signingUiVersion: 'v2', + useNewSendingInterface: 'true', + allowApiAccess: 'true', + allowApiAccessToAccount: 'true', + allowApiSendingOnBehalfOfOthers: 'true', + allowApiSequentialSigning: 'true', + enableApiRequestLogging: 'true', + allowDocuSignDesktopClient: 'false', + allowSendersToSetRecipientEmailLanguage: 'true', + allowVaulting: 'false', + allowedToBeEnvelopeTransferRecipient: 'true', + enableTransactionPointIntegration: 'false', + powerFormRole: 'admin', + vaultingMode: 'none' + } permission_profile.settings = pr_settings + #ds-snippet-end:eSign26Step3 + permission_profile end -end \ No newline at end of file +end diff --git a/app/services/e_sign/eg027_permissions_delete_service.rb b/app/services/e_sign/eg027_permissions_delete_service.rb index c9c2fd3..a3e2924 100644 --- a/app/services/e_sign/eg027_permissions_delete_service.rb +++ b/app/services/e_sign/eg027_permissions_delete_service.rb @@ -1,22 +1,30 @@ # frozen_string_literal: true class ESign::Eg027PermissionsDeleteService + attr_reader :args + include ApiCreator - attr_reader :args, :request, :permission_profile_id - def initialize(session, request) - @args = { - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token] - } - @request = request - @permission_profile_id = request.params[:lists] + def initialize(args) + @args = args end - def call - # Step 3: Call the eSignature REST API + def worker + # Call the eSignature REST API + #ds-snippet-start:eSign27Step3 accounts_api = create_account_api(args) - delete_permission_profile = accounts_api.delete_permission_profile(args[:account_id], permission_profile_id) + results, _status, headers = accounts_api.delete_permission_profile_with_http_info(args[:account_id], args[:permission_profile_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign27Step3 + + results end -end \ No newline at end of file +end diff --git a/app/services/e_sign/eg028_brands_creating_service.rb b/app/services/e_sign/eg028_brands_creating_service.rb index 8f19665..089776d 100644 --- a/app/services/e_sign/eg028_brands_creating_service.rb +++ b/app/services/e_sign/eg028_brands_creating_service.rb @@ -1,20 +1,15 @@ # frozen_string_literal: true class ESign::Eg028BrandsCreatingService + attr_reader :args + include ApiCreator - attr_reader :args, :session, :request - - def initialize(session, request) - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'] - } - @session = session - @request = request + + def initialize(args) + @args = args end - def call + def worker # Step 1. Obtain your OAuth token configuration = DocuSign_eSign::Configuration.new configuration.host = args[:base_path] @@ -24,10 +19,25 @@ def call api_client.default_headers['Authorization'] = "Bearer #{args[:access_token]}" # Step 3: Construct your request body + #ds-snippet-start:eSign28Step3 accounts_api = DocuSign_eSign::AccountsApi.new api_client - params = { brandName: request.params[:brandName], defaultBrandLanguage: request.params[:defaultBrandLanguage] } - + params = { brandName: args[:brandName], defaultBrandLanguage: args[:defaultBrandLanguage] } + #ds-snippet-end:eSign28Step3 + # Step 4: Call the eSignature API - results = accounts_api.create_brand(session['ds_account_id'], params) + #ds-snippet-start:eSign28Step4 + results, _status, headers = accounts_api.create_brand_with_http_info(args[:account_id], params) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign28Step4 + + results end -end \ No newline at end of file +end diff --git a/app/services/e_sign/eg029_brands_apply_to_envelope_service.rb b/app/services/e_sign/eg029_brands_apply_to_envelope_service.rb index 1e59b63..46045c5 100644 --- a/app/services/e_sign/eg029_brands_apply_to_envelope_service.rb +++ b/app/services/e_sign/eg029_brands_apply_to_envelope_service.rb @@ -1,52 +1,53 @@ # frozen_string_literal: true class ESign::Eg029BrandsApplyToEnvelopeService + attr_reader :args + include ApiCreator - attr_reader :envelope_args, :args, :session, :request - - def initialize(session, request) - @envelope_args = { - signer_email: request.params[:signerEmail].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params[:signerName].gsub(/([^\w \-\@\.\,])+/, ''), - status: 'sent' - - } - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'] - } - @session = session - @request = request + + def initialize(args) + @args = args end - def call - # ***DS.snippet.0.start + def worker # Step 1. Obtain your OAuth token # Step 2. Construct your API headers envelope_api = create_envelope_api(args) # Step 3: Construct your envelope JSON body - envelope_definition = make_envelope + #ds-snippet-start:eSign29Step3 + envelope_definition = make_envelope(args[:envelope_args]) + #ds-snippet-end:eSign29Step3 # Step 4. Call the eSignature REST API - results = envelope_api.create_envelope args[:account_id], envelope_definition - session[:envelope_id] = results.envelope_id + #ds-snippet-start:eSign29Step4 + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign29Step4 + results - # ***DS.snippet.0.end end private - def make_envelope + #ds-snippet-start:eSign29Step3 + def make_envelope(envelope_args) envelope_definition = DocuSign_eSign::EnvelopeDefinition.new envelope_definition.email_blurb = 'Sample text for email body' envelope_definition.email_subject = 'Please Sign' envelope_definition.envelope_id_stamping = true - envelope_definition.brand_id = request.params[:brands] + envelope_definition.brand_id = envelope_args[:brands] # Add the documents and create the document models pdf_filename = 'World_Wide_Corp_lorem.pdf' document1 = DocuSign_eSign::Document.new( - # Create the DocuSign Document object + # Create the Docusign Document object documentBase64: Base64.encode64(File.binread(File.join('data', pdf_filename))), name: 'NDA', # Can be different from actual file name fileExtension: 'pdf', # Many different document types are accepted @@ -59,7 +60,7 @@ def make_envelope signer1 = DocuSign_eSign::Signer.new signer1.name = envelope_args[:signer_name] signer1.email = envelope_args[:signer_email] - signer1.role_name = 'signer' + signer1.role_name = 'signer' signer1.note = '' signer1.routing_order = '1' signer1.status = envelope_args[:status] @@ -68,7 +69,7 @@ def make_envelope sign_here1 = DocuSign_eSign::SignHere.new sign_here1.document_id = '1' - sign_here1.name ='SignHereTab' + sign_here1.name = 'SignHereTab' sign_here1.page_number = '1' sign_here1.recipient_id = '1' sign_here1.tab_label = 'SignHereTab' @@ -77,9 +78,9 @@ def make_envelope # Add the tabs model (including the sign_here tabs) to the signer # The Tabs object wants arrays of the different field/tab types - signer1_tabs = DocuSign_eSign::Tabs.new ({ - signHereTabs: [sign_here1] - }) + signer1_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here1] + }) signer1.tabs = signer1_tabs # Add the recipients to the envelope object @@ -93,4 +94,5 @@ def make_envelope envelope_definition.status = envelope_args[:status] envelope_definition end -end \ No newline at end of file + #ds-snippet-end:eSign29Step3 +end diff --git a/app/services/e_sign/eg030_brands_apply_to_template_service.rb b/app/services/e_sign/eg030_brands_apply_to_template_service.rb index ea7e101..c532d7b 100644 --- a/app/services/e_sign/eg030_brands_apply_to_template_service.rb +++ b/app/services/e_sign/eg030_brands_apply_to_template_service.rb @@ -1,49 +1,48 @@ # frozen_string_literal: true class ESign::Eg030BrandsApplyToTemplateService - include ApiCreator - attr_reader :envelope_args, :args, :session, :request + attr_reader :args - def initialize(session, request) - @envelope_args = { - signer_email: request.params[:signerEmail].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params[:signerName].gsub(/([^\w \-\@\.\,])+/, ''), - cc_email: request.params['ccEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_name: request.params['ccName'].gsub(/([^\w \-\@\.\,])+/, ''), - status: 'sent' + include ApiCreator - } - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'] - } - @session = session - @request = request + def initialize(args) + @args = args end - def call - # ***DS.snippet.0.start - # Step 1. Obtain your OAuth token - # Step 2. Construct your API headers + def worker + # Obtain your OAuth token + # Construct your API headers envelope_api = create_envelope_api(args) - # Step 3. Construct your envelope JSON body - envelope_definition = make_envelope - # Step 4. Call the eSignature REST API - results = envelope_api.create_envelope args[:account_id], envelope_definition - session[:envelope_id] = results.envelope_id + # Construct your envelope JSON body + #ds-snippet-start:eSign30Step3 + envelope_definition = make_envelope(args[:envelope_args]) + #ds-snippet-end:eSign30Step3 + # Call the eSignature REST API + #ds-snippet-start:eSign30Step4 + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign30Step4 + results - # ***DS.snippet.0.end end private - def make_envelope + #ds-snippet-start:eSign30Step3 + def make_envelope(envelope_args) # Create the envelope definition with the template_id envelope_definition = DocuSign_eSign::EnvelopeDefinition.new # envelope_definition.envelope_id_stamping = true - envelope_definition.template_id = request.params[:templates] - envelope_definition.brand_id = request.params[:brands] + envelope_definition.template_id = envelope_args[:template_id] + envelope_definition.brand_id = envelope_args[:brand_id] envelope_definition.status = envelope_args[:status] # Create the template role elements to connect the signer and cc recipients # to the template @@ -62,4 +61,5 @@ def make_envelope envelope_definition.template_roles = [signer, cc] envelope_definition end -end \ No newline at end of file + #ds-snippet-end:eSign30Step3 +end diff --git a/app/services/e_sign/eg031_bulk_sending_envelopes_service.rb b/app/services/e_sign/eg031_bulk_sending_envelopes_service.rb index 77809eb..bc4cbc0 100644 --- a/app/services/e_sign/eg031_bulk_sending_envelopes_service.rb +++ b/app/services/e_sign/eg031_bulk_sending_envelopes_service.rb @@ -1,83 +1,125 @@ # frozen_string_literal: true class ESign::Eg031BulkSendingEnvelopesService - include ApiCreator attr_reader :args, :signers - def initialize(request, session) - @signers = { - signer_email: request.params['signerEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params['signerName'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_email: request.params['ccEmail'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_name: request.params['ccName'].gsub(/([^\w \-\@\.\,])+/, ''), - status: 'created', + include ApiCreator - signer_email1: request.params['signerEmail1'].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name1: request.params['signerName1'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_email1: request.params['ccEmail1'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_name1: request.params['ccName1'].gsub(/([^\w \-\@\.\,])+/, '') - } - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'] - } + def initialize(args, signers) + @args = args + @signers = signers end - def call - # Step 1. Obtain your OAuth token + def worker + # Construct your API headers + #ds-snippet-start:eSign31Step2 configuration = DocuSign_eSign::Configuration.new configuration.host = args[:base_path] api_client = DocuSign_eSign::ApiClient.new configuration + construct_api_headers(api_client, args) + #ds-snippet-end:eSign31Step2 - # Step 2. Construct your API headers - construct_api_headers(api_client) - - # Step 3. Create and submit the bulk sending list + # Create and submit the bulk sending list + #ds-snippet-start:eSign31Step3 bulk_envelopes_api = DocuSign_eSign::BulkEnvelopesApi.new api_client - bulk_sending_list = create_bulk_sending_list - bulk_list = bulk_envelopes_api.create_bulk_send_list(args[:account_id], bulk_sending_list) - bulk_list_id = bulk_list.list_id + bulk_sending_list = create_bulk_sending_list(signers) + bulk_list, _status, headers = bulk_envelopes_api.create_bulk_send_list_with_http_info(args[:account_id], bulk_sending_list) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] - # Step 4. Create the draft envelope + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + bulk_list_id = bulk_list.list_id + #ds-snippet-end:eSign31Step3 + + # Create the draft envelope + #ds-snippet-start:eSign31Step4 envelope_api = create_envelope_api(args) envelope_definition = make_envelope - envelope = envelope_api.create_envelope(args[:account_id], envelope_definition, options = DocuSign_eSign::CreateEnvelopeOptions.default) + envelope, _status, headers = envelope_api.create_envelope_with_http_info(args[:account_id], envelope_definition, DocuSign_eSign::CreateEnvelopeOptions.default) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + envelope_id = envelope.envelope_id + #ds-snippet-end:eSign31Step4 - # Step 5. Attach your bulk list ID to the envelope - envelope_api.create_custom_fields(args[:account_id], envelope_id, custom_fields(bulk_list_id)) + # Attach your bulk list ID to the envelope + #ds-snippet-start:eSign31Step5 + _results, _status, headers = envelope_api.create_custom_fields_with_http_info(args[:account_id], envelope_id, custom_fields(bulk_list_id)) - # Step 6. Add placeholder recipients - recipients = DocuSign_eSign::Recipients.new(signers: recipients_data) - envelope_api.create_recipient(args[:account_id], envelope_id, recipients, options = DocuSign_eSign::CreateRecipientOptions.default) + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] - # Step 7. Initiate bulk send + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign31Step5 + + # Initiate bulk send + #ds-snippet-start:eSign31Step6 bulk_send_request = DocuSign_eSign::BulkSendRequest.new(envelopeOrTemplateId: envelope_id) - batch = bulk_envelopes_api.create_bulk_send_request(args[:account_id], bulk_list_id, bulk_send_request) + batch, _status, headers = bulk_envelopes_api.create_bulk_send_request_with_http_info(args[:account_id], bulk_list_id, bulk_send_request) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + batch_id = batch.batch_id + #ds-snippet-end:eSign31Step6 + + # Confirm successful batch send + #ds-snippet-start:eSign31Step7 + results, _status, headers = bulk_envelopes_api.get_bulk_send_batch_status_with_http_info(args[:account_id], batch_id) - # Step 8. Confirm successful batch send - bulk_envelopes_api.get_bulk_send_batch_status(args[:account_id], batch_id) + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign31Step7 + + results end private - def construct_api_headers(api_client) + def construct_api_headers(api_client, args) api_client.default_headers['Authorization'] = "Bearer #{args[:access_token]}" - api_client.default_headers['Content-Type'] = "application/json;charset=UTF-8" - api_client.default_headers['Accept'] = "application/json, text/plain, */*" - api_client.default_headers['Accept-Encoding'] = "gzip, deflate, br" - api_client.default_headers['Accept-Language'] = "en-US,en;q=0.9" + api_client.default_headers['Content-Type'] = 'application/json;charset=UTF-8' + api_client.default_headers['Accept'] = 'application/json, text/plain, */*' + api_client.default_headers['Accept-Encoding'] = 'gzip, deflate, br' + api_client.default_headers['Accept-Language'] = 'en-US,en;q=0.9' end - def create_bulk_sending_list + def create_bulk_sending_list(signers) bulk_copies = [] recipient1 = DocuSign_eSign::BulkSendingCopyRecipient.new( roleName: 'signer', tabs: [], name: signers[:signer_name], - email: signers[:signer_email], + email: signers[:signer_email] ) # Create a cc recipient to receive a copy of the documents @@ -85,21 +127,21 @@ def create_bulk_sending_list roleName: 'cc', tabs: [], name: signers[:cc_name], - email: signers[:cc_email], + email: signers[:cc_email] ) recipient3 = DocuSign_eSign::BulkSendingCopyRecipient.new( roleName: 'signer', tabs: [], name: signers[:signer_name1], - email: signers[:signer_email1], + email: signers[:signer_email1] ) recipient4 = DocuSign_eSign::BulkSendingCopyRecipient.new( roleName: 'cc', tabs: [], name: signers[:cc_name1], - email: signers[:cc_email1], + email: signers[:cc_email1] ) # Add the recipients to the envelope object @@ -112,77 +154,87 @@ def create_bulk_sending_list recipients: [recipient3, recipient4], custom_fields: [] ) - bulk_copies.append(bulk_copy1, bulk_copy2) - bulk_sending_list = DocuSign_eSign::BulkSendingList.new( - name: "sample.csv", + bulk_copies.append(bulk_copy1, bulk_copy2) + DocuSign_eSign::BulkSendingList.new( + name: 'sample.csv', bulkCopies: bulk_copies ) - bulk_sending_list end def custom_fields(bulk_list_id) text_custom_fields = DocuSign_eSign::TextCustomField.new( - name:'mailingListId', + name: 'mailingListId', required: 'false', show: 'false', value: bulk_list_id ) - custom_fields = DocuSign_eSign::CustomFields.new( + DocuSign_eSign::CustomFields.new( listCustomFields: [], textCustomFields: [text_custom_fields] ) end - def recipients_data + def make_envelope + # Create the envelope definition + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + envelope_definition.email_subject = 'Please sign this document set' + pdf_filename = 'World_Wide_Corp_lorem.pdf' + # Add the documents + doc = DocuSign_eSign::Document.new + doc.document_base64 = Base64.encode64(File.binread(File.join('data', pdf_filename))) + doc.name = 'Lorem Ipsum' + doc.file_extension = 'pdf' + doc.document_id = '2' + signer = DocuSign_eSign::Signer.new( - name: "Multi Bulk Recipient::signer", - email: "multiBulkRecipients-signer@docusign.com", - roleName: "signer", - note: "", + name: 'Multi Bulk Recipient::signer', + email: 'multiBulkRecipients-signer@docusign.com', + roleName: 'signer', + note: '', routingOrder: 1, - status: "created", - templateAccessCodeRequired: "null", - deliveryMethod: "email", - recipientId: "10", - recipientType: "signer" + status: 'created', + templateAccessCodeRequired: 'null', + deliveryMethod: 'email', + recipientId: '1', + recipientType: 'signer' ) - cc = DocuSign_eSign::Signer.new( - name: "Multi Bulk Recipient::cc", - email: "multiBulkRecipients-cc@docusign.com", - roleName: "cc", - note: "", - routingOrder: 1, - status: "created", - templateAccessCodeRequired: "null", - deliveryMethod: "email", - recipientId: "11", - recipientType: "signer" + cc = DocuSign_eSign::CarbonCopy.new( + name: 'Multi Bulk Recipient::cc', + email: 'multiBulkRecipients-cc@docusign.com', + roleName: 'cc', + note: '', + routingOrder: 2, + status: 'created', + deliveryMethod: 'email', + recipientId: '2', + recipientType: 'cc' ) - [signer, cc] - end - def make_envelope - # Create the envelope definition - envelope_definition = DocuSign_eSign::EnvelopeDefinition.new - envelope_definition.email_subject = 'Please sign this document set' - # Add the documents - doc_b64 = "DQoNCg0KCQkJCXRleHQgZG9jDQoNCg0KDQoNCg0KUk0gIwlSTSAjCVJNICMNCg0KDQoNClxzMVwNCg0KLy9hbmNoMSANCgkvL2FuY2gyDQoJCS8vYW5jaDM=" - - # Create the document models - doc = DocuSign_eSign::Document.new( - # Create the DocuSign Document object - documentBase64: doc_b64, - name: 'NDA', # Can be different from the actual file name - fileExtension: 'txt', # Many different document types are accepted - documentId: '1' # A label used to reference the doc + # The Docusign platform searches throughout your envelope's documents for matching + # anchor strings. So the sign_here_2 tab will be used in both document 2 and 3 + # since they use the same anchor string for their "signer 1" tabs. + sign_here = DocuSign_eSign::SignHere.new + sign_here.anchor_string = '/sn1/' + sign_here.anchor_units = 'pixels' + sign_here.anchor_x_offset = '20' + sign_here.anchor_y_offset = '10' + # Tabs are set per recipient/signer + tabs = DocuSign_eSign::Tabs.new + tabs.sign_here_tabs = [sign_here] + signer.tabs = tabs + + # Add the recipients to the envelope object + recipients = DocuSign_eSign::Recipients.new( + signers: [signer], + carbonCopies: [cc] ) - + + envelope_definition.recipients = recipients # The order in the docs array determines the order in the envelope envelope_definition.documents = [doc] - envelope_definition.envelope_id_stamping = "true" - envelope_definition.status = "created" + envelope_definition.envelope_id_stamping = 'true' + envelope_definition.status = 'created' envelope_definition end - -end \ No newline at end of file +end diff --git a/app/services/e_sign/eg032_pauses_signature_workflow_service.rb b/app/services/e_sign/eg032_pauses_signature_workflow_service.rb index 9e805c3..e674019 100644 --- a/app/services/e_sign/eg032_pauses_signature_workflow_service.rb +++ b/app/services/e_sign/eg032_pauses_signature_workflow_service.rb @@ -1,35 +1,25 @@ # frozen_string_literal: true class ESign::Eg032PausesSignatureWorkflowService - include ApiCreator attr_reader :args, :signers - def initialize(session, request) - @signers = { - signerEmail1: request['signerEmail1'], - signerName1: request['signerName1'], - - signerEmail2: request['signerEmail2'], - signerName2: request['signerName2'] - } - - @args = { - accountId: session['ds_account_id'], - basePath: session['ds_base_path'], - accessToken: session['ds_access_token'], - status: 'sent' - } + include ApiCreator + + def initialize(args, signers) + @args = args + @signers = signers end - def call - # Step 2. Construct your API headers + def worker + #ds-snippet-start:eSign32Step2 configuration = DocuSign_eSign::Configuration.new configuration.host = args[:basePath] api_client = DocuSign_eSign::ApiClient.new configuration api_client.set_default_header('Authorization', "Bearer #{args[:accessToken]}") + #ds-snippet-end:eSign32Step2 - # Step 3. Construct the request body + #ds-snippet-start:eSign32Step3 envelope_definition = DocuSign_eSign::EnvelopeDefinition.new(emailSubject: 'EnvelopeWorkflowTest') # Create the document model. @@ -101,12 +91,25 @@ def call # 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_definition.status = args[:status] + #ds-snippet-end:eSign32Step3 - # Step 4. Call the eSignature API + #ds-snippet-start:eSign32Step4 envelopes_api = DocuSign_eSign::EnvelopesApi.new(api_client) - results = envelopes_api.create_envelope( + results, _status, headers = envelopes_api.create_envelope_with_http_info( args[:accountId], envelope_definition ) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign32Step4 + + results end end diff --git a/app/services/e_sign/eg033_unpauses_signature_workflow_service.rb b/app/services/e_sign/eg033_unpauses_signature_workflow_service.rb index 325191f..370ea68 100644 --- a/app/services/e_sign/eg033_unpauses_signature_workflow_service.rb +++ b/app/services/e_sign/eg033_unpauses_signature_workflow_service.rb @@ -1,43 +1,52 @@ # frozen_string_literal: true class ESign::Eg033UnpausesSignatureWorkflowService + attr_reader :args + include ApiCreator - attr_reader :args, :signers - - def initialize(session) - @args = { - accountId: session['ds_account_id'], - basePath: session['ds_base_path'], - accessToken: session['ds_access_token'], - envelopeId: session['envelope_id'], - status: 'in_progress' - } + + def initialize(args) + @args = args end - def call - # Step 2. Construct your API headers + def worker + #ds-snippet-start:eSign33Step2 configuration = DocuSign_eSign::Configuration.new configuration.host = args[:basePath] api_client = DocuSign_eSign::ApiClient.new configuration api_client.set_default_header('Authorization', "Bearer #{args[:accessToken]}") + #ds-snippet-end:eSign33Step2 - # Step 3. Construct the JSON body for your envelope + #ds-snippet-start:eSign33Step3 workflow = DocuSign_eSign::Workflow.new(status: args[:status]) envelope_definition = DocuSign_eSign::EnvelopeDefinition.new(workflow: workflow) + #ds-snippet-end:eSign33Step3 - # Step 4. Call the eSignature API + #ds-snippet-start:eSign33Step4 envelopes_api = DocuSign_eSign::EnvelopesApi.new(api_client) update_options = DocuSign_eSign::UpdateOptions.new update_options.resend_envelope = true - result = envelopes_api.update( + results, _status, headers = envelopes_api.update_with_http_info( args[:accountId], args[:envelopeId], envelope_definition, update_options ) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + results end + #ds-snippet-end:eSign33Step4 end diff --git a/app/services/e_sign/eg034_use_conditional_recipients_service.rb b/app/services/e_sign/eg034_use_conditional_recipients_service.rb index 8cf17bf..c4887cf 100644 --- a/app/services/e_sign/eg034_use_conditional_recipients_service.rb +++ b/app/services/e_sign/eg034_use_conditional_recipients_service.rb @@ -1,36 +1,26 @@ # frozen_string_literal: true class ESign::Eg034UseConditionalRecipientsService - include ApiCreator attr_reader :args, :signers - def initialize(session, request) - @signers = { - signerEmail1: request['signerEmail1'], - signerName1: request['signerName1'], - - signerEmailNotChecked: request['signerEmailNotChecked'], - signerNameNotChecked: request['signerNameNotChecked'], - - signerEmailChecked: request['signerEmailChecked'], - signerNameChecked: request['signerNameChecked'] - } + include ApiCreator - @args = { - accountId: session['ds_account_id'], - basePath: session['ds_base_path'], - accessToken: session['ds_access_token'] - } + def initialize(args, signers) + @args = args + @signers = signers end - def call + def worker # Step 2. Construct your API headers + #ds-snippet-start:eSign34Step2 configuration = DocuSign_eSign::Configuration.new configuration.host = args[:basePath] api_client = DocuSign_eSign::ApiClient.new configuration api_client.set_default_header('Authorization', "Bearer #{args[:accessToken]}") + #ds-snippet-end:eSign34Step2 # Step 3. Construct the request body + #ds-snippet-start:eSign34Step3 envelope_definition = DocuSign_eSign::EnvelopeDefinition.new(emailSubject: 'ApproveIfChecked') # Create the document model. @@ -134,7 +124,8 @@ def call tabId: 'ApprovalTab', operator: 'equals', value: false, - tabLabel: 'ApproveWhenChecked' + tabLabel: 'ApproveWhenChecked', + tabType: 'checkbox' ) filter2 = DocuSign_eSign::ConditionalRecipientRuleFilter.new( scope: 'tabs', @@ -142,7 +133,8 @@ def call tabId: 'ApprovalTab', operator: 'equals', value: true, - tabLabel: 'ApproveWhenChecked' + tabLabel: 'ApproveWhenChecked', + tabType: 'checkbox' ) # Create conditionalRecipientRuleCondition models @@ -185,13 +177,27 @@ def call # 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_definition.status = 'sent' + #ds-snippet-end:eSign34Step3 # Step 4. Call the eSignature API + #ds-snippet-start:eSign34Step4 envelopes_api = DocuSign_eSign::EnvelopesApi.new(api_client) - results = envelopes_api.create_envelope( + results, _status, headers = envelopes_api.create_envelope_with_http_info( args[:accountId], envelope_definition ) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign34Step4 + + results end end diff --git a/app/services/e_sign/eg035_scheduled_sending_service.rb b/app/services/e_sign/eg035_scheduled_sending_service.rb new file mode 100644 index 0000000..fc8f959 --- /dev/null +++ b/app/services/e_sign/eg035_scheduled_sending_service.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +class ESign::Eg035ScheduledSendingService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def worker + # Create the envelope request object + envelope_definition = make_envelope args[:envelope_args] + # Call Envelopes::create API method + # Exceptions will be caught by the calling function + envelope_api = create_envelope_api(args) + + # Step 3 start + #ds-snippet-start:eSign35Step3 + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign35Step3 + # Step 3 end + + envelope_id = results.envelope_id + { 'envelope_id' => envelope_id } + end + + private + + def make_envelope(envelope_args) + # document (PDF) has tag /sn1/ + # The envelope has one recipient + + # Step 2 start + #ds-snippet-start:eSign35Step2 + # Create the envelope definition + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + + envelope_definition.email_subject = 'Please sign this document' + + # Add the document + # Read file from a local directory + # The reads could raise an exception if the file is not available! + doc_pdf = Rails.application.config.doc_pdf + doc_b64 = Base64.encode64(File.binread(File.join('data', doc_pdf))) + + # Create the document model + document = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc_b64, + name: 'Lorem Ipsum', # Can be different from actual file name + fileExtension: 'pdf', # Many different document types are accepted + documentId: '1' # A label used to reference the doc + ) + + # The order in the docs array determines the order in the envelope + envelope_definition.documents = [document] + + # Create the signer recipient model + signer1 = DocuSign_eSign::Signer.new + signer1.email = envelope_args[:signer_email] + signer1.name = envelope_args[:signer_name] + signer1.recipient_id = '1' + signer1.routing_order = '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 + + # Create a signHere field (also known as a tab) on the document + # We're using anchor (autoPlace) positioning + # + # The Docusign platform searches throughout your envelope's documents for matching + # anchor strings. + sign_here = DocuSign_eSign::SignHere.new( + anchorString: '/sn1/', + anchorYOffset: '10', + anchorUnits: 'pixels', + anchorXOffset: '20' + ) + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object takes arrays of the different field/tab types + signer1_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here] + }) + + signer1.tabs = signer1_tabs + + # Add the recipients to the envelope object + recipients = DocuSign_eSign::Recipients.new( + signers: [signer1] + ) + envelope_definition.recipients = recipients + + # Create recipientRules model + rule = DocuSign_eSign::EnvelopeDelayRule.new( + resumeDate: envelope_args[:resume_date].to_s + ) + + scheduled_sending = DocuSign_eSign::ScheduledSending.new( + rules: [rule] + ) + + workflow = DocuSign_eSign::Workflow.new(scheduledSending: scheduled_sending) + # Add the workflow to the envelope object + envelope_definition.workflow = workflow + + # Request that the envelope be sent by setting status to "sent". + # To request that the envelope be created as a draft, set status to "created" + envelope_definition.status = envelope_args[:status] + #ds-snippet-end:eSign35Step2 + # Step 2 end + envelope_definition + end +end diff --git a/app/services/e_sign/eg035_sms_delivery_service.rb b/app/services/e_sign/eg035_sms_delivery_service.rb deleted file mode 100644 index 2fc6186..0000000 --- a/app/services/e_sign/eg035_sms_delivery_service.rb +++ /dev/null @@ -1,198 +0,0 @@ -# frozen_string_literal: true - -class ESign::Eg035SmsDeliveryService - include ApiCreator - attr_reader :args, :envelope_args - - def initialize(session, request, status) - @envelope_args = { - signer_email: request.params['signer_email'].gsub(/([^\w \-\@\.\,])+/, ''), - signer_name: request.params['signer_name'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_email: request.params['cc_email'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_name: request.params['cc_name'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_phone_number: request.params['cc_phone_number'].gsub(/([^\w \-\@\.\,])+/, ''), - cc_country_code: request.params['cc_country_code'].gsub(/([^\w \-\@\.\,])+/, ''), - phone_number: request.params['phone_number'].gsub(/([^\w \-\@\.\,])+/, ''), - country_code: request.params['country_code'].gsub(/([^\w \-\@\.\,])+/, ''), - status: status - } - @args = { - account_id: session['ds_account_id'], - base_path: session['ds_base_path'], - access_token: session['ds_access_token'], - envelope_args: @envelope_args - } - end - - def call - worker - end - - private - - def worker - # Create the envelope request object - envelope_definition = make_envelope - - # Step 3. Create and send the envelope - # Call Envelopes::create API method - # Exceptions will be caught by the calling function - envelope_api = create_envelope_api(args) - - results = envelope_api.create_envelope args[:account_id], envelope_definition - envelope_id = results.envelope_id - { 'envelope_id' => envelope_id } - end - - def make_envelope - # 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 via SMS - # After it is signed, a copy is sent to the cc person via SMS - - # Step 2. Create the envelope definition - envelope_definition = DocuSign_eSign::EnvelopeDefinition.new - - envelope_definition.email_subject = 'Please sign this document set' - - # Add the documents - doc1_b64 = Base64.encode64(create_document1) - # Read files 2 and 3 from a local directory - # The reads could raise an exception if the file is not available! - doc_docx = Rails.application.config.doc_docx - doc2_b64 = Base64.encode64(File.binread(File.join('data', doc_docx))) - doc_pdf = Rails.application.config.doc_pdf - doc3_b64 = Base64.encode64(File.binread(File.join('data', doc_pdf))) - - # Create the document models - document1 = DocuSign_eSign::Document.new( - # Create the DocuSign document object - documentBase64: doc1_b64, - name: 'Order acknowledgement', # Can be different from actual file name - fileExtension: 'html', # Many different document types are accepted - documentId: '1' # A label used to reference the doc - ) - document2 = DocuSign_eSign::Document.new( - # Create the DocuSign document object - documentBase64: doc2_b64, - name: 'Battle Plan', # Can be different from actual file name - fileExtension: 'docx', # Many different document types are accepted - documentId: '2' # A label used to reference the do - ) - document3 = DocuSign_eSign::Document.new( - # Create the DocuSign document object - documentBase64: doc3_b64, - name: 'Lorem Ipsum', # Can be different from actual file name - fileExtension: 'pdf', # Many different document types are accepted - documentId: '3' # A label used to reference the doc - ) - - # The order in the docs array determines the order in the envelope - envelope_definition.documents = [document1, document2, document3] - - phone_number = DocuSign_eSign::RecipientPhoneNumber.new - phone_number.country_code=envelope_args[:country_code] - phone_number.number=envelope_args[:phone_number] - - sms_notification = DocuSign_eSign::RecipientAdditionalNotification.new - sms_notification.phone_number = phone_number - sms_notification.secondary_delivery_method = "SMS" - - # Create the signer recipient model - signer1 = DocuSign_eSign::Signer.new - signer1.additional_notifications=[sms_notification] - signer1.email = envelope_args[:signer_email] - signer1.name = envelope_args[:signer_name] - signer1.recipient_id = '1' - signer1.routing_order = '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 - - # Create a RecipientPhoneNumber and add it to the additional SMS notification - cc_phone_number = DocuSign_eSign::RecipientPhoneNumber.new - cc_phone_number.country_code=envelope_args[:cc_country_code] - cc_phone_number.number=envelope_args[:cc_phone_number] - - - cc_sms_notification = DocuSign_eSign::RecipientAdditionalNotification.new - cc_sms_notification.phone_number=cc_phone_number - cc_sms_notification.secondary_delivery_method = "SMS" - - # Create a cc recipient to receive a copy of the documents - cc1 = DocuSign_eSign::CarbonCopy.new( - email: envelope_args[:cc_email], - name: envelope_args[:cc_name], - routingOrder: '2', - recipientId: '2', - additionalNotifications: [cc_sms_notification] - ) - # 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 sign_here_2 tab will be used in both document 2 and 3 - # since they use the same anchor string for their "signer 1" tabs. - sign_here1 = DocuSign_eSign::SignHere.new( - anchorString: '**signature_1**', - anchorYOffset: '10', - anchorUnits: 'pixels', - anchorXOffset: '20' - ) - - sign_here2 = DocuSign_eSign::SignHere.new( - anchorString: '/sn1/', - anchorYOffset: '10', - anchorUnits: 'pixels', - anchorXOffset: '20' - ) - # Add the tabs model (including the sign_here tabs) to the signer - # The Tabs object takes arrays of the different field/tab types - signer1_tabs = DocuSign_eSign::Tabs.new ({ - signHereTabs: [sign_here1, sign_here2] - }) - - signer1.tabs = signer1_tabs - - # Add the recipients to the envelope object - recipients = DocuSign_eSign::Recipients.new( - signers: [signer1], - carbonCopies: [cc1] - ) - # Request that the envelope be sent by setting status to "sent". - # To request that the envelope be created as a draft, set status to "created" - envelope_definition.recipients = recipients - envelope_definition.status = envelope_args[:status] - envelope_definition - end - - def create_document1 - " - - - - - - -

World Wide Corp

-

Order Processing Division

-

Ordered by #{args[:signer_name]}

-

Email: #{args[:signer_email]}

-

Copy to: #{args[:cc_name]}, #{args[:cc_email]}

-

- 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**/

- - " - end - end \ No newline at end of file diff --git a/app/services/e_sign/eg036_delayed_routing_service.rb b/app/services/e_sign/eg036_delayed_routing_service.rb new file mode 100644 index 0000000..6733b2a --- /dev/null +++ b/app/services/e_sign/eg036_delayed_routing_service.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +class ESign::Eg036DelayedRoutingService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def worker + # Create the envelope request object + envelope_definition = make_envelope args[:envelope_args] + # Call Envelopes::create API method + # Exceptions will be caught by the calling function + envelope_api = create_envelope_api(args) + + #ds-snippet-start:eSign36Step3 + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign36Step3 + envelope_id = results.envelope_id + { 'envelope_id' => envelope_id } + end + + private + + def make_envelope(envelope_args) + # Create the envelope definition + #ds-snippet-start:eSign36Step2 + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + + envelope_definition.email_subject = 'Please sign this document' + + # Add the document + # Read file from a local directory + # The reads could raise an exception if the file is not available! + doc_pdf = Rails.application.config.doc_pdf + doc_b64 = Base64.encode64(File.binread(File.join('data', doc_pdf))) + + # Create the document model + document = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc_b64, + name: 'Lorem Ipsum', # Can be different from actual file name + fileExtension: 'pdf', # Many different document types are accepted + documentId: '1' # A label used to reference the doc + ) + + # The order in the docs array determines the order in the envelope + envelope_definition.documents = [document] + + # Create the signer recipient model + signer1 = DocuSign_eSign::Signer.new + signer1.email = envelope_args[:signer1_email] + signer1.name = envelope_args[:signer1_name] + signer1.recipient_id = '1' + signer1.routing_order = '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 + + signer2 = DocuSign_eSign::Signer.new + signer2.email = envelope_args[:signer2_email] + signer2.name = envelope_args[:signer2_name] + signer2.recipient_id = '2' + signer2.routing_order = '2' + + # Create signHere fields (also known as tabs) on the document + # We're using anchor (autoPlace) positioning for the sign_here1 tab + # and we're using absolute positioning for the sign_here2 tab. + # + # The Docusign platform searches throughout your envelope's documents for matching + # anchor strings. + sign_here1 = DocuSign_eSign::SignHere.new( + anchorString: '/sn1/', + anchorYOffset: '10', + anchorUnits: 'pixels', + anchorXOffset: '20' + ) + + sign_here2 = DocuSign_eSign::SignHere.new( + xPosition: '320', + yPosition: '175', + pageNumber: '1', + documentId: '1' + ) + + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object takes arrays of the different field/tab types + signer1_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here1] + }) + + signer2_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here2] + }) + + signer1.tabs = signer1_tabs + signer2.tabs = signer2_tabs + + # Add the recipients to the envelope object + recipients = DocuSign_eSign::Recipients.new( + signers: [signer1, signer2] + ) + + envelope_definition.recipients = recipients + + # Create recipientRules model + delay_time = "0.#{envelope_args[:delay]}:00:00" + rule = DocuSign_eSign::EnvelopeDelayRule.new(delay: delay_time) + + delayed_routing = DocuSign_eSign::DelayedRouting.new(rules: [rule]) + + # Create a workflow model + workflow_step = DocuSign_eSign::WorkflowStep.new( + action: 'pause_before', + triggerOnItem: 'routing_order', + itemId: 2, + delayedRouting: delayed_routing + ) + workflow = DocuSign_eSign::Workflow.new(workflowSteps: [workflow_step]) + # Add the workflow to the envelope object + envelope_definition.workflow = workflow + + # Request that the envelope be sent by setting status to "sent". + # To request that the envelope be created as a draft, set status to "created" + envelope_definition.status = envelope_args[:status] + envelope_definition + #ds-snippet-end:eSign36Step2 + end +end diff --git a/app/services/e_sign/eg037_sms_delivery_service.rb b/app/services/e_sign/eg037_sms_delivery_service.rb new file mode 100644 index 0000000..3854414 --- /dev/null +++ b/app/services/e_sign/eg037_sms_delivery_service.rb @@ -0,0 +1,182 @@ +# frozen_string_literal: true + +class ESign::Eg037SmsDeliveryService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def worker + # Create the envelope request object + envelope_definition = make_envelope(args[:envelope_args]) + + # Create and send the envelope + # Call Envelopes::create API method + # Exceptions will be caught by the calling function + envelope_api = create_envelope_api(args) + #ds-snippet-start:eSign37Step3 + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign37Step3 + envelope_id = results.envelope_id + { 'envelope_id' => envelope_id } + end + + private + + #ds-snippet-start:eSign37Step2 + def make_envelope(envelope_args) + # 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 via SMS + # After it is signed, a copy is sent to the cc person via SMS + + # Create the envelope definition + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + + envelope_definition.email_subject = 'Please sign this document set' + + # Add the documents + doc1_b64 = Base64.encode64(create_document1(envelope_args)) + # Read files 2 and 3 from a local directory + # The reads could raise an exception if the file is not available! + doc_docx = Rails.application.config.doc_docx + doc2_b64 = Base64.encode64(File.binread(File.join('data', doc_docx))) + doc_pdf = Rails.application.config.doc_pdf + doc3_b64 = Base64.encode64(File.binread(File.join('data', doc_pdf))) + + # Create the document models + document1 = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc1_b64, + name: 'Order acknowledgement', # Can be different from actual file name + fileExtension: 'html', # Many different document types are accepted + documentId: '1' # A label used to reference the doc + ) + document2 = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc2_b64, + name: 'Battle Plan', # Can be different from actual file name + fileExtension: 'docx', # Many different document types are accepted + documentId: '2' # A label used to reference the do + ) + document3 = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc3_b64, + name: 'Lorem Ipsum', # Can be different from actual file name + fileExtension: 'pdf', # Many different document types are accepted + documentId: '3' # A label used to reference the doc + ) + + # The order in the docs array determines the order in the envelope + envelope_definition.documents = [document1, document2, document3] + + signer_phone_number = DocuSign_eSign::RecipientPhoneNumber.new + signer_phone_number.country_code = envelope_args[:country_code] + signer_phone_number.number = envelope_args[:phone_number] + + # Create the signer recipient model + signer1 = DocuSign_eSign::Signer.new + signer1.phone_number = signer_phone_number + signer1.name = envelope_args[:signer_name] + signer1.recipient_id = '1' + signer1.routing_order = '1' + signer1.delivery_method = envelope_args[:delivery_method] + + ## 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 + + cc_phone_number = DocuSign_eSign::RecipientPhoneNumber.new + cc_phone_number.country_code = envelope_args[:cc_country_code] + cc_phone_number.number = envelope_args[:cc_phone_number] + + # Create a cc recipient to receive a copy of the documents + cc1 = DocuSign_eSign::CarbonCopy.new + cc1.name = envelope_args[:cc_name] + cc1.routing_order = '2' + cc1.recipient_id = '2' + cc1.phone_number = cc_phone_number + cc1.delivery_method = envelope_args[:delivery_method] + + # 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 sign_here_2 tab will be used in both document 2 and 3 + # since they use the same anchor string for their "signer 1" tabs. + sign_here1 = DocuSign_eSign::SignHere.new( + anchorString: '**signature_1**', + anchorYOffset: '10', + anchorUnits: 'pixels', + anchorXOffset: '20' + ) + + sign_here2 = DocuSign_eSign::SignHere.new( + anchorString: '/sn1/', + anchorYOffset: '10', + anchorUnits: 'pixels', + anchorXOffset: '20' + ) + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object takes arrays of the different field/tab types + signer1_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here1, sign_here2] + }) + + signer1.tabs = signer1_tabs + + # Add the recipients to the envelope object + recipients = DocuSign_eSign::Recipients.new( + signers: [signer1], + carbonCopies: [cc1] + ) + # Request that the envelope be sent by setting status to "sent". + # To request that the envelope be created as a draft, set status to "created" + envelope_definition.recipients = recipients + envelope_definition.status = envelope_args[:status] + envelope_definition + end + + def create_document1(args) + " + + + + + + +

World Wide Corp

+

Order Processing Division

+

Ordered by #{args[:signer_name]}

+

Phone number: #{args[:phone_number]}

+

Copy to: #{args[:cc_name]}, #{args[:cc_phone_number]}

+

+ 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**/

+ + " + end + #ds-snippet-end:eSign37Step2 +end diff --git a/app/services/e_sign/eg038_responsive_signing_service.rb b/app/services/e_sign/eg038_responsive_signing_service.rb new file mode 100644 index 0000000..0b3307e --- /dev/null +++ b/app/services/e_sign/eg038_responsive_signing_service.rb @@ -0,0 +1,205 @@ +# frozen_string_literal: true + +class ESign::Eg038ResponsiveSigningService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + #ds-snippet-start:eSign38Step3 + def worker + ds_return_url = "#{args[:ds_ping_url]}/ds_common-return" + + # Create the envelope definition + envelope = make_envelope(args) + + # Call Docusign to create the envelope + envelope_api = create_envelope_api(args) + + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + envelope_id = results.envelope_id + # Save for future use within the example launcher + # session[:envelope_id] = envelope_id + + # Create the recipient view for the embedded signing + view_request = make_recipient_view_request(args, ds_return_url) + + # Call the CreateRecipientView API + results, _status, headers = envelope_api.create_recipient_view_with_http_info args[:account_id], envelope_id, view_request + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + # Step 4. Redirect the user to the embedded signing + # 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) + # Redirect to results.url + results.url + end + + private + + def make_recipient_view_request(args, ds_return_url) + view_request = DocuSign_eSign::RecipientViewRequest.new + # 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. It's usually better to use + # the session mechanism of your web framework. Query parameters + # can be changed/spoofed very easily. + view_request.return_url = "#{ds_return_url}?state=123" + + # How has your app authenticated the user? In addition to your app's + # authentication, you can include authenticate steps from Docusign; + # e.g., SMS authentication + view_request.authentication_method = 'none' + + # Recipient information must match the embedded recipient info + # that was used to create the envelope + view_request.email = args[:signer_email] + view_request.user_name = args[:signer_name] + view_request.client_user_id = args[:signer_client_id] + + # Docusign recommends that you redirect to Docusign for the embedded signing. There are + # multiple ways to save state. To maintain your application's session, use the pingUrl + # parameter. It causes the Docusign signing web page (not the Docusign server) + # to send pings via AJAX to your app + view_request.ping_frequency = '600' # seconds + # NOTE: The pings will only be sent if the pingUrl is an HTTPS address + view_request.ping_url = args[:ds_ping_url] # Optional setting + + view_request + end + #ds-snippet-end:eSign38Step3 + + #ds-snippet-start:eSign38Step2 + def make_envelope(args) + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + envelope_definition.email_subject = 'Example Signing Document' + + html_definition = DocuSign_eSign::DocumentHtmlDefinition.new + html_definition.source = get_html_content(args) + + doc = DocuSign_eSign::Document.new + doc.name = 'doc1.html' + doc.document_id = '1' + doc.html_definition = html_definition + + # The order in the docs array determines the order in the envelope + envelope_definition.documents = [doc] + # Create a signer recipient to sign the document, identified by name and email + # We're setting the parameters via the object creation + price_1 = 5 + formula_tab_1 = DocuSign_eSign::FormulaTab.new({ + font: 'helvetica', + fontSize: 'size11', + fontColor: 'black', + anchorString: '/l1e/', + anchorYOffset: '-8', + anchorUnits: 'pixels', + anchorXOffset: '105', + tabLabel: 'l1e', + formula: "[l1q] * #{price_1}", + roundDecimalPlaces: '0', + required: 'true', + locked: 'true', + disableAutoSize: 'false' + }) + + price_2 = 150 + formula_tab_2 = DocuSign_eSign::FormulaTab.new({ + font: 'helvetica', + fontSize: 'size11', + fontColor: 'black', + anchorString: '/l2e/', + anchorYOffset: '-8', + anchorUnits: 'pixels', + anchorXOffset: '105', + tabLabel: 'l2e', + formula: "[l2q] * #{price_2}", + roundDecimalPlaces: '0', + required: 'true', + locked: 'true', + disableAutoSize: 'false' + }) + + formula_tab_3 = DocuSign_eSign::FormulaTab.new({ + font: 'helvetica', + fontSize: 'size11', + fontColor: 'black', + anchorString: '/l3t/', + anchorYOffset: '-8', + anchorUnits: 'pixels', + anchorXOffset: '105', + tabLabel: 'l3t', + formula: '[l1e] + [l2e]', + roundDecimalPlaces: '0', + required: 'true', + locked: 'true', + disableAutoSize: 'false' + }) + + tabs = DocuSign_eSign::Tabs.new({ + formulaTabs: [formula_tab_1, formula_tab_2, formula_tab_3] + }) + + signer = DocuSign_eSign::Signer.new({ + email: args[:signer_email], + name: args[:signer_name], + clientUserId: args[:signer_client_id], + recipientId: 1, + role_name: 'Signer', + tabs: tabs + }) + + cc = DocuSign_eSign::CarbonCopy.new({ + email: args[:cc_email], name: args[:cc_name], recipientId: 2 + }) + + # Add the recipients to the envelope object + recipients = DocuSign_eSign::Recipients.new + recipients.signers = [signer] + recipients.carbon_copies = [cc] + + envelope_definition.recipients = recipients + # Request that the envelope be sent by setting status to "sent". + # To request that the envelope be created as a draft, set status to "created" + envelope_definition.status = 'sent' + envelope_definition + end + + def get_html_content(args) + doc_html = File.open(args[:doc_file]).read + # Substitute values into the HTML + # Substitute for: {signerName}, {signerEmail}, {ccName}, {ccEmail} + doc_html.gsub('{signerName}', args[:signer_name]) \ + .gsub('{signerEmail}', args[:signer_email]) \ + .gsub('{ccName}', args[:cc_name]) \ + .gsub('{ccEmail}', args[:cc_email]) \ + .gsub('/sn1/', '') \ + .gsub('/l1q/', '') \ + .gsub('/l2q/', '') + end + #ds-snippet-end:eSign38Step2 +end diff --git a/app/services/e_sign/eg039_signing_in_person_service.rb b/app/services/e_sign/eg039_signing_in_person_service.rb new file mode 100644 index 0000000..fc3a5d5 --- /dev/null +++ b/app/services/e_sign/eg039_signing_in_person_service.rb @@ -0,0 +1,144 @@ +# frozen_string_literal: true + +class ESign::Eg039SigningInPersonService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def worker + ds_ping_url = args[:ds_ping_url] + ds_return_url = "#{ds_ping_url}/ds_common-return" + pdf_filename = args[:pdf_filename] + host_email = args[:host_email] + host_name = args[:host_name] + signer_name = args[:signer_name] + + envelope = make_envelope(pdf_filename, host_email, host_name, signer_name) + + #ds-snippet-start:eSign39Step3 + envelope_api = create_envelope_api(args) + + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign39Step3 + + envelope_id = results.envelope_id + + #ds-snippet-start:eSign39Step5 + view_request = make_recipient_view_request(ds_return_url, ds_ping_url, host_email, host_name) + + # Call the CreateRecipientView API + results, _status, headers = envelope_api.create_recipient_view_with_http_info args[:account_id], envelope_id, view_request + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign39Step5 + + # Redirect the user to the embedded signing + # 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) + # Redirect to results.url + results.url + end + + private + + #ds-snippet-start:eSign39Step4 + def make_recipient_view_request(ds_return_url, ds_ping_url, host_email, host_name) + view_request = DocuSign_eSign::RecipientViewRequest.new + # 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. It's usually better to use + # the session mechanism of your web framework. Query parameters + # can be changed/spoofed very easily. + view_request.return_url = "#{ds_return_url}?state=123" + + # How has your app authenticated the user? In addition to your app's + # authentication, you can include authenticate steps from Docusign; + # e.g., SMS authentication + view_request.authentication_method = 'none' + + # Recipient information must match the embedded recipient info + # that was used to create the envelope + view_request.email = host_email + view_request.user_name = host_name + + # Docusign recommends that you redirect to Docusign for the embedded signing. There are + # multiple ways to save state. To maintain your application's session, use the pingUrl + # parameter. It causes the Docusign signing web page (not the Docusign server) + # to send pings via AJAX to your app + view_request.ping_frequency = '600' # seconds + # NOTE: The pings will only be sent if the pingUrl is an HTTPS address + view_request.ping_url = ds_ping_url # Optional setting + + view_request + end + #ds-snippet-end:eSign39Step4 + + #ds-snippet-start:eSign39Step2 + def make_envelope(pdf_filename, host_email, host_name, signer_name) + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + envelope_definition.email_subject = 'Please sign this document sent from Ruby SDK' + + doc1 = DocuSign_eSign::Document.new + doc1.document_base64 = Base64.encode64(File.binread(pdf_filename)) + doc1.name = 'Lorem Ipsum' + doc1.file_extension = 'pdf' + doc1.document_id = '1' + + # The order in the docs array determines the order in the envelope + envelope_definition.documents = [doc1] + # Create an in person signer recipient to sign the document + # We're setting the parameters via the object creation + + in_person_signer = DocuSign_eSign::InPersonSigner.new + in_person_signer.host_email = host_email + in_person_signer.host_name = host_name + in_person_signer.signer_name = signer_name + in_person_signer.recipient_id = '1' + in_person_signer.routing_order = '1' + + # The Docusign platform searches throughout your envelope's documents for matching + # anchor strings. + sign_here = DocuSign_eSign::SignHere.new + sign_here.anchor_string = '/sn1/' + sign_here.anchor_units = 'pixels' + sign_here.anchor_x_offset = '20' + sign_here.anchor_y_offset = '10' + # Tabs are set per recipient/signer + tabs = DocuSign_eSign::Tabs.new + tabs.sign_here_tabs = [sign_here] + in_person_signer.tabs = tabs + # Add the recipients to the envelope object + recipients = DocuSign_eSign::Recipients.new + recipients.in_person_signers = [in_person_signer] + + envelope_definition.recipients = recipients + # Request that the envelope be sent by setting status to "sent". + # To request that the envelope be created as a draft, set status to "created" + envelope_definition.status = 'sent' + envelope_definition + end + #ds-snippet-end:eSign39Step2 +end diff --git a/app/services/e_sign/eg040_set_document_visibility_service.rb b/app/services/e_sign/eg040_set_document_visibility_service.rb new file mode 100644 index 0000000..d9c08bb --- /dev/null +++ b/app/services/e_sign/eg040_set_document_visibility_service.rb @@ -0,0 +1,186 @@ +# frozen_string_literal: true + +class ESign::Eg040SetDocumentVisibilityService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:eSign40Step3 + envelope_definition = make_envelope args[:envelope_args] + #ds-snippet-end:eSign40Step3 + + # Exceptions will be caught by the calling function + #ds-snippet-start:eSign40Step4 + envelope_api = create_envelope_api(args) + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + envelope_id = results.envelope_id + #ds-snippet-end:eSign40Step4 + + { 'envelope_id' => envelope_id } + end + + private + + #ds-snippet-start:eSign40Step3 + def make_envelope(envelope_args) + # 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 1 + # recipient 2 - signer 2 + # recipient 3 - 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 + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + + envelope_definition.email_subject = 'Please sign this document set' + envelope_definition.enforce_signer_visibility = 'true' + + # Add the documents + doc1_b64 = Base64.encode64(create_document1(envelope_args)) + # Read files 2 and 3 from a local directory + # The reads could raise an exception if the file is not available! + doc2_b64 = Base64.encode64(File.binread(envelope_args[:doc_docx])) + doc3_b64 = Base64.encode64(File.binread(envelope_args[:doc_pdf])) + + # Create the document models + document1 = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc1_b64, + name: 'Order acknowledgement', # Can be different from actual file name + fileExtension: 'html', # Many different document types are accepted + documentId: '1' # A label used to reference the doc + ) + document2 = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc2_b64, + name: 'Battle Plan', # Can be different from actual file name + fileExtension: 'docx', # Many different document types are accepted + documentId: '2' # A label used to reference the do + ) + document3 = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc3_b64, + name: 'Lorem Ipsum', # Can be different from actual file name + fileExtension: 'pdf', # Many different document types are accepted + documentId: '3' # A label used to reference the doc + ) + + # The order in the docs array determines the order in the envelope + envelope_definition.documents = [document1, document2, document3] + + # Create the signer recipient model + signer1 = DocuSign_eSign::Signer.new + signer1.email = envelope_args[:signer1_email] + signer1.name = envelope_args[:signer1_name] + signer1.recipient_id = '1' + signer1.routing_order = '1' + signer1.excluded_documents = [2, 3] + + signer2 = DocuSign_eSign::Signer.new + signer2.email = envelope_args[:signer2_email] + signer2.name = envelope_args[:signer2_name] + signer2.recipient_id = '2' + signer2.routing_order = '2' + signer2.excluded_documents = [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 + + # Create a cc recipient to receive a copy of the documents + cc = DocuSign_eSign::CarbonCopy.new( + email: envelope_args[:cc_email], + name: envelope_args[:cc_name], + routingOrder: '3', + recipientId: '3' + ) + # 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 sign_here_2 tab will be used in both document 2 and 3 + # since they use the same anchor string for their "signer 1" tabs. + sign_here1 = DocuSign_eSign::SignHere.new( + anchorString: '**signature_1**', + anchorYOffset: '10', + anchorUnits: 'pixels', + anchorXOffset: '20' + ) + + sign_here2 = DocuSign_eSign::SignHere.new( + anchorString: '/sn1/', + anchorYOffset: '10', + anchorUnits: 'pixels', + anchorXOffset: '20' + ) + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object takes arrays of the different field/tab types + signer1_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here1] + }) + + signer1.tabs = signer1_tabs + + signer2_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here2] + }) + + signer2.tabs = signer2_tabs + + # Add the recipients to the envelope object + recipients = DocuSign_eSign::Recipients.new( + signers: [signer1, signer2], + carbonCopies: [cc] + ) + # Request that the envelope be sent by setting status to "sent". + # To request that the envelope be created as a draft, set status to "created" + envelope_definition.recipients = recipients + envelope_definition.status = envelope_args[:status] + envelope_definition + end + #ds-snippet-end:eSign40Step3 + + def create_document1(args) + " + + + + + + +

World Wide Corp

+

Order Processing Division

+

Ordered by #{args[:signer1_name]}

+

Email: #{args[:signer1_email]}

+

Copy to: #{args[:cc_name]}, #{args[:cc_email]}

+

+ 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**/

+ + " + end +end diff --git a/app/services/e_sign/eg041_cfr_embedded_signing_service.rb b/app/services/e_sign/eg041_cfr_embedded_signing_service.rb new file mode 100644 index 0000000..23031bc --- /dev/null +++ b/app/services/e_sign/eg041_cfr_embedded_signing_service.rb @@ -0,0 +1,146 @@ +# frozen_string_literal: true + +class ESign::Eg041CfrEmbeddedSigningService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def worker + envelope_args = args[:envelope_args] + + #ds-snippet-start:eSign41Step2 + accounts_api = create_account_api(args) + + # Obtain your workflow_id + workflow_results, _status, headers = accounts_api.get_account_identity_verification_with_http_info(args[:account_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + if workflow_results.identity_verification + workflow = workflow_results.identity_verification.find { |item| item.default_name == 'SMS for access & signatures' } + workflow_id = workflow.workflow_id if workflow + end + #ds-snippet-end:eSign41Step2 + + return 'invalid_workflow_id' if workflow_id.blank? + + #ds-snippet-start:eSign41Step4 + envelope_api = create_envelope_api(args) + envelope_definition = make_envelope(args[:envelope_args], workflow_id) + + envelope, _status, headers = envelope_api.create_envelope_with_http_info(args[:account_id], envelope_definition) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + envelope_id = envelope.envelope_id + #ds-snippet-end:eSign41Step4 + + #ds-snippet-start:eSign41Step5 + view_request = DocuSign_eSign::RecipientViewRequest.new({ + returnUrl: "#{envelope_args[:ds_return_url]}?state=123", + authenticationMethod: 'none', + email: envelope_args[:signer_email], + userName: envelope_args[:signer_name], + clientUserId: envelope_args[:signer_client_id], + pingFrequency: 600, + pingUrl: envelope_args[:ds_ping_url] + }) + #ds-snippet-end:eSign41Step5 + + #ds-snippet-start:eSign41Step6 + results, _status, headers = envelope_api.create_recipient_view_with_http_info(args[:account_id], envelope_id, view_request) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + results.url + #ds-snippet-end:eSign41Step6 + end + + private + + #ds-snippet-start:eSign41Step3 + def make_envelope(args, workflow_id) + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + envelope_definition.email_subject = 'Please sign this document sent from Ruby SDK' + + doc1 = DocuSign_eSign::Document.new + doc1.document_base64 = Base64.encode64(File.binread(args[:pdf_filename])) + doc1.name = 'Lorem Ipsum' + doc1.file_extension = 'pdf' + doc1.document_id = '1' + + # The order in the docs array determines the order in the envelope + envelope_definition.documents = [doc1] + + phone_number = DocuSign_eSign::RecipientIdentityPhoneNumber.new + phone_number.country_code = args[:country_code] + phone_number.number = args[:phone_number] + + input_option = DocuSign_eSign::RecipientIdentityInputOption.new + input_option.name = 'phone_number_list' + input_option.value_type = 'PhoneNumberList' + input_option.phone_number_list = [phone_number] + + identity_verification = DocuSign_eSign::RecipientIdentityVerification.new + + identity_verification.workflow_id = workflow_id + identity_verification.input_options = [input_option] + + # Create a signer recipient to sign the document, identified by name and email + # We're setting the parameters via the object creation + signer1 = DocuSign_eSign::Signer.new({ + email: args[:signer_email], + name: args[:signer_name], + clientUserId: args[:signer_client_id], + recipientId: 1, + identityVerification: identity_verification + }) + # The Docusign platform searches throughout your envelope's documents for matching + # anchor strings. So the sign_here_2 tab will be used in both document 2 and 3 + # since they use the same anchor string for their "signer 1" tabs. + sign_here = DocuSign_eSign::SignHere.new + sign_here.anchor_string = '/sn1/' + sign_here.anchor_units = 'pixels' + sign_here.anchor_x_offset = '20' + sign_here.anchor_y_offset = '-30' + # Tabs are set per recipient/signer + tabs = DocuSign_eSign::Tabs.new + tabs.sign_here_tabs = [sign_here] + signer1.tabs = tabs + # Add the recipients to the envelope object + recipients = DocuSign_eSign::Recipients.new + recipients.signers = [signer1] + + envelope_definition.recipients = recipients + # Request that the envelope be sent by setting status to "sent". + # To request that the envelope be created as a draft, set status to "created" + envelope_definition.status = 'sent' + envelope_definition + end + #ds-snippet-end:eSign41Step3 +end diff --git a/app/services/e_sign/eg042_document_generation_service.rb b/app/services/e_sign/eg042_document_generation_service.rb new file mode 100644 index 0000000..5e9a2ef --- /dev/null +++ b/app/services/e_sign/eg042_document_generation_service.rb @@ -0,0 +1,287 @@ +# frozen_string_literal: true + +class ESign::Eg042DocumentGenerationService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def worker + envelope_api = create_envelope_api(args) + template_api = create_template_api(args) + + account_id = args[:account_id] + envelope_args = args[:envelope_args] + + #ds-snippet-start:eSign42Step2 + template, _status, headers = template_api.create_template_with_http_info(account_id, template_data) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + template_id = template.template_id + #ds-snippet-end:eSign42Step2 + + #ds-snippet-start:eSign42Step3 + document_id = '1' + _results, _status, headers = template_api.update_document_with_http_info(account_id, document_id, template_id, template_document(envelope_args)) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign42Step3 + + #ds-snippet-start:eSign42Step4 + recipient_id = '1' + _results, _status, headers = template_api.create_tabs_with_http_info(account_id, recipient_id, template_id, recipient_tabs) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign42Step4 + + #ds-snippet-start:eSign42Step5 + envelope_definition = make_envelope(template_id, envelope_args) + envelope, _status, headers = envelope_api.create_envelope_with_http_info(account_id, envelope_definition) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + envelope_id = envelope.envelope_id + #ds-snippet-end:eSign42Step5 + + #ds-snippet-start:eSign42Step6 + doc_gen_form_fields_response, _status, headers = envelope_api.get_envelope_doc_gen_form_fields_with_http_info(account_id, envelope_id) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + document_id_guid = doc_gen_form_fields_response.doc_gen_form_fields[0].document_id + #ds-snippet-end:eSign42Step6 + + #ds-snippet-start:eSign42Step7 + form_fields_request = form_fields(envelope_args, document_id_guid) + _results, _status, headers = envelope_api.update_envelope_doc_gen_form_fields_with_http_info( + account_id, + envelope_id, + form_fields_request + ) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign42Step7 + + #ds-snippet-start:eSign42Step8 + send_envelope_req = DocuSign_eSign::Envelope.new(status: 'sent') + envelope, _status, headers = envelope_api.update_with_http_info(account_id, envelope_id, send_envelope_req) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign42Step8 + + { 'envelope_id' => envelope.envelope_id } + end + + private + + #ds-snippet-start:eSign42Step2 + def template_data + # Create recipients + signer = DocuSign_eSign::Signer.new( + roleName: 'signer', + recipientId: '1', + routingOrder: '1' + ) + recipients = DocuSign_eSign::Recipients.new( + signers: [signer] + ) + + # Create the envelope template model + DocuSign_eSign::EnvelopeTemplate.new( + name: 'Example document generation template', + description: 'Example template created via the API', + emailSubject: 'Please sign this document', + shared: 'false', + recipients: recipients, + status: 'created' + ) + end + #ds-snippet-end:eSign42Step2 + + #ds-snippet-start:eSign42Step3 + def template_document(args) + # Create the document model + document = DocuSign_eSign::Document.new( + documentBase64: Base64.encode64(File.binread(args[:doc_file])), + name: 'OfferLetterDemo.docx', + fileExtension: 'docx', + documentId: 1, + order: 1, + pages: 1 + ) + + DocuSign_eSign::EnvelopeDefinition.new( + documents: [document] + ) + end + #ds-snippet-end:eSign42Step3 + + #ds-snippet-start:eSign42Step4 + def recipient_tabs + # Create tabs + sign_here = DocuSign_eSign::SignHere.new( + anchorString: 'Employee Signature', + anchorUnits: 'pixels', + anchorXOffset: '5', + anchorYOffset: '-22' + ) + date_signed = DocuSign_eSign::DateSigned.new( + anchorString: 'Date Signed', + anchorUnits: 'pixels', + anchorYOffset: '-22' + ) + DocuSign_eSign::Tabs.new( + signHereTabs: [sign_here], + dateSignedTabs: [date_signed] + ) + end + #ds-snippet-end:eSign42Step4 + + #ds-snippet-start:eSign42Step5 + def make_envelope(template_id, args) + # Create the signer model + signer = DocuSign_eSign::TemplateRole.new( + email: args[:candidate_email], + name: args[:candidate_name], + roleName: 'signer' + ) + + # Create the envelope model + DocuSign_eSign::EnvelopeDefinition.new( + templateRoles: [signer], + status: 'created', + templateId: template_id + ) + end + #ds-snippet-end:eSign42Step5 + + #ds-snippet-start:eSign42Step7 + def form_fields(args, document_id_guid) + bonus_value = '20%' + + candidate_name_field = DocuSign_eSign::DocGenFormField.new( + name: 'Candidate_Name', + value: args[:candidate_name] + ) + manager_name_field = DocuSign_eSign::DocGenFormField.new( + name: 'Manager_Name', + value: args[:manager_name] + ) + job_title_field = DocuSign_eSign::DocGenFormField.new( + name: 'Job_Title', + value: args[:job_title] + ) + start_date_field = DocuSign_eSign::DocGenFormField.new( + name: 'Start_Date', + value: args[:start_date] + ) + + salary_row = DocuSign_eSign::DocGenFormFieldRowValue.new( + docGenFormFieldList: [ + DocuSign_eSign::DocGenFormField.new( + name: 'Compensation_Component', + value: 'Salary' + ), + DocuSign_eSign::DocGenFormField.new( + name: 'Details', + value: "$#{args[:salary]}" + ) + ] + ) + bonus_row = DocuSign_eSign::DocGenFormFieldRowValue.new( + docGenFormFieldList: [ + DocuSign_eSign::DocGenFormField.new( + name: 'Compensation_Component', + value: 'Bonus' + ), + DocuSign_eSign::DocGenFormField.new( + name: 'Details', + value: bonus_value + ) + ] + ) + rsus_row = DocuSign_eSign::DocGenFormFieldRowValue.new( + docGenFormFieldList: [ + DocuSign_eSign::DocGenFormField.new( + name: 'Compensation_Component', + value: 'RSUs' + ), + DocuSign_eSign::DocGenFormField.new( + name: 'Details', + value: args[:rsus] + ) + ] + ) + compensation_package = DocuSign_eSign::DocGenFormField.new( + name: 'Compensation_Package', + type: 'TableRow', + rowValues: [salary_row, bonus_row, rsus_row] + ) + doc_gen_form_fields_list = [ + candidate_name_field, manager_name_field, job_title_field, start_date_field, compensation_package + ] + + doc_gen_form_fields = DocuSign_eSign::DocGenFormFields.new( + documentId: document_id_guid, + docGenFormFieldList: doc_gen_form_fields_list + ) + + DocuSign_eSign::DocGenFormFieldRequest.new( + docGenFormFields: [doc_gen_form_fields] + ) + end + #ds-snippet-end:eSign42Step7 +end diff --git a/app/services/e_sign/eg043_shared_access_service.rb b/app/services/e_sign/eg043_shared_access_service.rb new file mode 100644 index 0000000..65b059c --- /dev/null +++ b/app/services/e_sign/eg043_shared_access_service.rb @@ -0,0 +1,158 @@ +# frozen_string_literal: true + +class ESign::Eg043SharedAccessService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def create_agent + #ds-snippet-start:eSign43Step2 + configuration = DocuSign_eSign::Configuration.new + configuration.host = args[:base_path] + api_client = DocuSign_eSign::ApiClient.new configuration + api_client.default_headers['Authorization'] = "Bearer #{args[:access_token]}" + #ds-snippet-end:eSign43Step2 + + #ds-snippet-start:eSign43Step3 + users_api = DocuSign_eSign::UsersApi.new api_client + #ds-snippet-end:eSign43Step3 + + # Check if active user already exists + begin + options = DocuSign_eSign::ListOptions.new + options.email = args[:email] + users, _status, headers = users_api.list_with_http_info args[:account_id], options + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + if users.result_set_size.to_i.positive? + user = users.users.find { |u| u.user_status == 'Active' } + return user unless user.nil? + end + rescue DocuSign_eSign::ApiError => e + error = JSON.parse e.response_body + raise unless error['errorCode'] == 'USER_LACKS_MEMBERSHIP' + end + + # Create new user + #ds-snippet-start:eSign43Step3 + new_users, _status, headers = users_api.create_with_http_info args[:account_id], new_users_definition(args) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + new_users.new_users[0] + #ds-snippet-end:eSign43Step3 + end + + def create_authorization + configuration = DocuSign_eSign::Configuration.new + configuration.host = args[:base_path] + api_client = DocuSign_eSign::ApiClient.new configuration + api_client.default_headers['Authorization'] = "Bearer #{args[:access_token]}" + + #ds-snippet-start:eSign43Step4 + accounts_api = DocuSign_eSign::AccountsApi.new api_client + + # Check if authorization with manage permission already exists + options = DocuSign_eSign::GetAgentUserAuthorizationsOptions.new + options.permissions = 'manage' + + authorizations, _status, headers = accounts_api.get_agent_user_authorizations_with_http_info(args[:account_id], args[:agent_user_id], options) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + return if authorizations.result_set_size.to_i.positive? + + # Create authorization + _results, _status, headers = accounts_api.create_user_authorization_with_http_info( + args[:account_id], + args[:user_id], + user_authorization_request(args) + ) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + return unless remaining && reset + + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + + #ds-snippet-end:eSign43Step4 + end + + #ds-snippet-start:eSign43Step3 + def new_users_definition(args) + agent = DocuSign_eSign::UserInformation.new( + userName: args[:user_name], + email: args[:email], + activationAccessCode: args[:activation] + ) + DocuSign_eSign::NewUsersDefinition.new(newUsers: [agent]) + end + #ds-snippet-end:eSign43Step3 + + #ds-snippet-start:eSign43Step4 + def user_authorization_request(args) + DocuSign_eSign::UserAuthorizationCreateRequest.new( + agentUser: DocuSign_eSign::AuthorizationUser.new( + accountId: args[:account_id], + userId: args[:agent_user_id] + ), + permission: 'manage' + ) + end + #ds-snippet-end:eSign43Step4 + + def get_envelopes + #ds-snippet-start:eSign43Step5 + configuration = DocuSign_eSign::Configuration.new + configuration.host = args[:base_path] + api_client = DocuSign_eSign::ApiClient.new configuration + api_client.default_headers['Authorization'] = "Bearer #{args[:access_token]}" + api_client.set_default_header('X-DocuSign-Act-On-Behalf', args[:user_id]) + envelopes_api = DocuSign_eSign::EnvelopesApi.new api_client + + options = DocuSign_eSign::ListStatusChangesOptions.new + options.from_date = (Date.today - 10).strftime('%Y/%m/%d') + results, _status, headers = envelopes_api.list_status_changes_with_http_info args[:account_id], options + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign43Step5 + + results + end +end diff --git a/app/services/e_sign/eg044_focused_view_service.rb b/app/services/e_sign/eg044_focused_view_service.rb new file mode 100644 index 0000000..271d1f2 --- /dev/null +++ b/app/services/e_sign/eg044_focused_view_service.rb @@ -0,0 +1,150 @@ +# frozen_string_literal: true + +class ESign::Eg044FocusedViewService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def worker + ds_ping_url = args[:ds_ping_url] + ds_return_url = "#{ds_ping_url}/ds_common-return" + signer_client_id = args[:signer_client_id] + pdf_filename = args[:pdf_filename] + signer_email = args[:signer_email] + signer_name = args[:signer_name] + + # Create the envelope definition + #ds-snippet-start:eSign44Step3 + envelope = make_envelope(args[:signer_client_id], pdf_filename, signer_email, signer_name) + + # Call Docusign to create the envelope + envelope_api = create_envelope_api(args) + + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + envelope_id = results.envelope_id + #ds-snippet-end:eSign44Step3 + # Save for future use within the example launcher + # session[:envelope_id] = envelope_id + + # Create the recipient view for the embedded signing + #ds-snippet-start:eSign44Step5 + view_request = make_recipient_view_request(signer_client_id, ds_return_url, ds_ping_url, signer_email, signer_name) + + # Call the CreateRecipientView API + results, _status, headers = envelope_api.create_recipient_view_with_http_info args[:account_id], envelope_id, view_request + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + # Redirect the user to the embedded signing + # 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) + # Redirect to results.url + results.url + #ds-snippet-end:eSign44Step5 + end + + private + + #ds-snippet-start:eSign44Step4 + def make_recipient_view_request(signer_client_id, ds_return_url, ds_ping_url, signer_email, signer_name) + view_request = DocuSign_eSign::RecipientViewRequest.new + # 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. It's usually better to use + # the session mechanism of your web framework. Query parameters + # can be changed/spoofed very easily. + view_request.return_url = "#{ds_return_url}?state=123" + + # How has your app authenticated the user? In addition to your app's + # authentication, you can include authenticate steps from Docusign; + # e.g., SMS authentication + view_request.authentication_method = 'none' + + # Recipient information must match the embedded recipient info + # that was used to create the envelope + view_request.email = signer_email + view_request.user_name = signer_name + view_request.client_user_id = signer_client_id + + # Docusign recommends that you redirect to Docusign for the embedded signing. There are + # multiple ways to save state. To maintain your application's session, use the pingUrl + # parameter. It causes the Docusign signing web page (not the Docusign server) + # to send pings via AJAX to your app + view_request.ping_frequency = '600' # seconds + # NOTE: The pings will only be sent if the pingUrl is an HTTPS address + view_request.ping_url = ds_ping_url # Optional setting + + view_request.frame_ancestors = ['http://localhost:3000', 'https://apps-d.docusign.com'] + view_request.message_origins = ['https://apps-d.docusign.com'] + + view_request + end + #ds-snippet-end:eSign44Step4 + + #ds-snippet-start:eSign44Step2 + def make_envelope(signer_client_id, pdf_filename, signer_email, signer_name) + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + envelope_definition.email_subject = 'Please sign this document sent from Ruby SDK' + + doc1 = DocuSign_eSign::Document.new + doc1.document_base64 = Base64.encode64(File.binread(pdf_filename)) + doc1.name = 'Lorem Ipsum' + doc1.file_extension = 'pdf' + doc1.document_id = '1' + + # The order in the docs array determines the order in the envelope + envelope_definition.documents = [doc1] + # Create a signer recipient to sign the document, identified by name and email + # We're setting the parameters via the object creation + signer1 = DocuSign_eSign::Signer.new({ + email: signer_email, name: signer_name, + clientUserId: signer_client_id, recipientId: 1 + }) + # The Docusign platform searches throughout your envelope's documents for matching + # anchor strings. So the sign_here_2 tab will be used in both document 2 and 3 + # since they use the same anchor string for their "signer 1" tabs. + sign_here = DocuSign_eSign::SignHere.new + sign_here.anchor_string = '/sn1/' + sign_here.anchor_units = 'pixels' + sign_here.anchor_x_offset = '20' + sign_here.anchor_y_offset = '10' + # Tabs are set per recipient/signer + tabs = DocuSign_eSign::Tabs.new + tabs.sign_here_tabs = [sign_here] + signer1.tabs = tabs + # Add the recipients to the envelope object + recipients = DocuSign_eSign::Recipients.new + recipients.signers = [signer1] + + envelope_definition.recipients = recipients + # Request that the envelope be sent by setting status to "sent". + # To request that the envelope be created as a draft, set status to "created" + envelope_definition.status = 'sent' + envelope_definition + end + #ds-snippet-end:eSign44Step2 +end diff --git a/app/services/e_sign/eg045_delete_restore_envelope_service.rb b/app/services/e_sign/eg045_delete_restore_envelope_service.rb new file mode 100644 index 0000000..34fb249 --- /dev/null +++ b/app/services/e_sign/eg045_delete_restore_envelope_service.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +class ESign::Eg045DeleteRestoreEnvelopeService + include ApiCreator + + def delete_envelope(args) + #ds-snippet-start:eSign45Step2 + folders_api = create_folders_api(args) + #ds-snippet-end:eSign45Step2 + + #ds-snippet-start:eSign45Step3 + folders_request = DocuSign_eSign::FoldersRequest.new + folders_request.envelope_ids = [args[:envelope_id]] + #ds-snippet-end:eSign45Step3 + + #ds-snippet-start:eSign45Step4 + results, _status, headers = folders_api.move_envelopes_with_http_info(args[:account_id], args[:delete_folder_id], folders_request) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign45Step4 + + results + end + + def move_envelope_to_folder(args) + folders_api = create_folders_api(args) + + #ds-snippet-start:eSign45Step6 + folders_request = DocuSign_eSign::FoldersRequest.new + folders_request.envelope_ids = [args[:envelope_id]] + folders_request.from_folder_id = args[:from_folder_id] + + results, _status, headers = folders_api.move_envelopes_with_http_info(args[:account_id], args[:folder_id], folders_request) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign45Step6 + + results + end + + def get_folders(args) + folders_api = create_folders_api(args) + + #ds-snippet-start:eSign45Step5 + results, _status, headers = folders_api.list_with_http_info(args[:account_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:eSign45Step5 + + results + end +end diff --git a/app/services/e_sign/eg046_multiple_delivery_service.rb b/app/services/e_sign/eg046_multiple_delivery_service.rb new file mode 100644 index 0000000..9d23ece --- /dev/null +++ b/app/services/e_sign/eg046_multiple_delivery_service.rb @@ -0,0 +1,192 @@ +# frozen_string_literal: true + +class ESign::Eg046MultipleDeliveryService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def worker + # Create the envelope request object + envelope_definition = make_envelope(args[:envelope_args]) + + # Create and send the envelope + # Call Envelopes::create API method + # Exceptions will be caught by the calling function + envelope_api = create_envelope_api(args) + #ds-snippet-start:eSign46Step3 + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + envelope_id = results.envelope_id + #ds-snippet-end:eSign46Step3 + { 'envelope_id' => envelope_id } + end + + private + + #ds-snippet-start:eSign46Step2 + def make_envelope(envelope_args) + # 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 via SMS + # After it is signed, a copy is sent to the cc person via SMS + + # Create the envelope definition + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + + envelope_definition.email_subject = 'Please sign this document set' + + # Add the documents + doc1_b64 = Base64.encode64(create_document1(envelope_args)) + # Read files 2 and 3 from a local directory + # The reads could raise an exception if the file is not available! + doc_docx = Rails.application.config.doc_docx + doc2_b64 = Base64.encode64(File.binread(File.join('data', doc_docx))) + doc_pdf = Rails.application.config.doc_pdf + doc3_b64 = Base64.encode64(File.binread(File.join('data', doc_pdf))) + + # Create the document models + document1 = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc1_b64, + name: 'Order acknowledgement', # Can be different from actual file name + fileExtension: 'html', # Many different document types are accepted + documentId: '1' # A label used to reference the doc + ) + document2 = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc2_b64, + name: 'Battle Plan', # Can be different from actual file name + fileExtension: 'docx', # Many different document types are accepted + documentId: '2' # A label used to reference the do + ) + document3 = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc3_b64, + name: 'Lorem Ipsum', # Can be different from actual file name + fileExtension: 'pdf', # Many different document types are accepted + documentId: '3' # A label used to reference the doc + ) + + # The order in the docs array determines the order in the envelope + envelope_definition.documents = [document1, document2, document3] + + signer_phone_number = DocuSign_eSign::RecipientPhoneNumber.new + signer_phone_number.country_code = envelope_args[:country_code] + signer_phone_number.number = envelope_args[:phone_number] + + signer_additional_notification = DocuSign_eSign::RecipientAdditionalNotification.new + signer_additional_notification.phone_number = signer_phone_number + signer_additional_notification.secondary_delivery_method = envelope_args[:delivery_method] + + # Create the signer recipient model + signer1 = DocuSign_eSign::Signer.new + signer1.name = envelope_args[:signer_name] + signer1.email = envelope_args[:signer_email] + signer1.recipient_id = '1' + signer1.routing_order = '1' + signer1.delivery_method = 'Email' + signer1.additional_notifications = [signer_additional_notification] + + ## 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 + + cc_phone_number = DocuSign_eSign::RecipientPhoneNumber.new + cc_phone_number.country_code = envelope_args[:cc_country_code] + cc_phone_number.number = envelope_args[:cc_phone_number] + + cc_additional_notification = DocuSign_eSign::RecipientAdditionalNotification.new + cc_additional_notification.phone_number = cc_phone_number + cc_additional_notification.secondary_delivery_method = envelope_args[:delivery_method] + + # Create a cc recipient to receive a copy of the documents + cc1 = DocuSign_eSign::CarbonCopy.new + cc1.name = envelope_args[:cc_name] + cc1.email = envelope_args[:cc_email] + cc1.routing_order = '2' + cc1.recipient_id = '2' + cc1.delivery_method = 'Email' + cc1.additional_notifications = [cc_additional_notification] + + # 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 sign_here_2 tab will be used in both document 2 and 3 + # since they use the same anchor string for their "signer 1" tabs. + sign_here1 = DocuSign_eSign::SignHere.new( + anchorString: '**signature_1**', + anchorYOffset: '10', + anchorUnits: 'pixels', + anchorXOffset: '20' + ) + + sign_here2 = DocuSign_eSign::SignHere.new( + anchorString: '/sn1/', + anchorYOffset: '10', + anchorUnits: 'pixels', + anchorXOffset: '20' + ) + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object takes arrays of the different field/tab types + signer1_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [sign_here1, sign_here2] + }) + + signer1.tabs = signer1_tabs + + # Add the recipients to the envelope object + recipients = DocuSign_eSign::Recipients.new( + signers: [signer1], + carbonCopies: [cc1] + ) + # Request that the envelope be sent by setting status to "sent". + # To request that the envelope be created as a draft, set status to "created" + envelope_definition.recipients = recipients + envelope_definition.status = envelope_args[:status] + envelope_definition + end + + def create_document1(args) + " + + + + + + +

World Wide Corp

+

Order Processing Division

+

Ordered by #{args[:signer_name]}

+

Phone number: #{args[:phone_number]}

+

Copy to: #{args[:cc_name]}, #{args[:cc_phone_number]}

+

+ 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**/

+ + " + end + #ds-snippet-end:eSign46Step2 +end diff --git a/app/services/e_sign/get_data_service.rb b/app/services/e_sign/get_data_service.rb new file mode 100644 index 0000000..f22b1ae --- /dev/null +++ b/app/services/e_sign/get_data_service.rb @@ -0,0 +1,46 @@ +class ESign::GetDataService + attr_reader :args + + def initialize(access_token, base_path) + @args = { + access_token: access_token, + base_path: base_path + } + end + + def get_current_user_email + worker + + user_info = @api_client.get_user_info(args[:access_token]) + raise 'The user does not have access to account' unless user_info + + user_info.email + end + + def get_current_user_name + worker + + user_info = @api_client.get_user_info(args[:access_token]) + raise 'The user does not have access to account' unless user_info + + user_info.name + end + + def cfr?(account_id) + worker + accounts_api = DocuSign_eSign::AccountsApi.new @api_client + account_details = accounts_api.get_account_information(account_id) + account_details.status21_cfr_part11 + end + + private + + def worker + configuration = DocuSign_eSign::Configuration.new + configuration.host = args[:base_path] + + @api_client = DocuSign_eSign::ApiClient.new(configuration) + @api_client.set_base_path(args[:base_path]) + @api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + end +end diff --git a/app/services/e_sign/eg001_embedded_signing_service.rb b/app/services/eg001_embedded_signing_service.rb similarity index 59% rename from app/services/e_sign/eg001_embedded_signing_service.rb rename to app/services/eg001_embedded_signing_service.rb index 8c398d5..b837ae9 100644 --- a/app/services/e_sign/eg001_embedded_signing_service.rb +++ b/app/services/eg001_embedded_signing_service.rb @@ -1,71 +1,86 @@ # frozen_string_literal: true -class ESign::Eg001EmbeddedSigningService +class Eg001EmbeddedSigningService + attr_reader :args + include ApiCreator - attr_reader :signer_email, :signer_name, :args - - def initialize(session, request) - @signer_email = request.params[:signerEmail].gsub(/([^\w \-\@\.\,])+/, '') - @signer_name = request.params[:signerName].gsub(/([^\w \-\@\.\,])+/, '') - @args = { - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token] - } - end - def call - redirect_url = worker + def initialize(args) + @args = args end - private - - # ***DS.snippet.0.start def worker - ds_ping_url = Rails.application.config.app_url + ds_ping_url = args[:ds_ping_url] ds_return_url = "#{ds_ping_url}/ds_common-return" - signer_client_id = 1000 - pdf_filename = 'World_Wide_Corp_lorem.pdf' + signer_client_id = args[:signer_client_id] + pdf_filename = args[:pdf_filename] + signer_email = args[:signer_email] + signer_name = args[:signer_name] - # Step 1. Create the envelope definition - envelope = make_envelope(signer_client_id, pdf_filename) + # Create the envelope definition + #ds-snippet-start:eSign1Step3 + envelope = make_envelope(args[:signer_client_id], pdf_filename, signer_email, signer_name) - # Step 2. Call DocuSign to create the envelope + # Call Docusign to create the envelope envelope_api = create_envelope_api(args) - results = envelope_api.create_envelope args[:account_id], envelope + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + envelope_id = results.envelope_id + #ds-snippet-end # Save for future use within the example launcher # session[:envelope_id] = envelope_id - # Step 3. Create the recipient view for the embedded signing - view_request = make_recipient_view_request(signer_client_id, ds_return_url, ds_ping_url - ) + # Create the recipient view for the embedded signing + #ds-snippet-start:eSign1Step5 + view_request = make_recipient_view_request(signer_client_id, ds_return_url, ds_ping_url, signer_email, signer_name) # Call the CreateRecipientView API - results = envelope_api.create_recipient_view args[:account_id], envelope_id, view_request + results, _status, headers = envelope_api.create_recipient_view_with_http_info args[:account_id], envelope_id, view_request + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end - # Step 4. Redirect the user to the embedded signing + # Redirect the user to the embedded signing # 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) # Redirect to results.url results.url + #ds-snippet-end end - def make_recipient_view_request(signer_client_id, ds_return_url, ds_ping_url) + private + + #ds-snippet-start:eSign1Step4 + def make_recipient_view_request(signer_client_id, ds_return_url, ds_ping_url, signer_email, signer_name) view_request = DocuSign_eSign::RecipientViewRequest.new # 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. It's usually better to use + # the Docusign signing. It's usually better to use # the session mechanism of your web framework. Query parameters # can be changed/spoofed very easily. - view_request.return_url = ds_return_url + '?state=123' + view_request.return_url = "#{ds_return_url}?state=123" # How has your app authenticated the user? In addition to your app's - # authentication, you can include authenticate steps from DocuSign; + # authentication, you can include authenticate steps from Docusign; # e.g., SMS authentication view_request.authentication_method = 'none' @@ -75,9 +90,9 @@ def make_recipient_view_request(signer_client_id, ds_return_url, ds_ping_url) view_request.user_name = signer_name view_request.client_user_id = signer_client_id - # DocuSign recommends that you redirect to DocuSign for the embedded signing. There are + # Docusign recommends that you redirect to Docusign for the embedded signing. There are # multiple ways to save state. To maintain your application's session, use the pingUrl - # parameter. It causes the DocuSign signing web page (not the DocuSign server) + # parameter. It causes the Docusign signing web page (not the Docusign server) # to send pings via AJAX to your app view_request.ping_frequency = '600' # seconds # NOTE: The pings will only be sent if the pingUrl is an HTTPS address @@ -85,13 +100,15 @@ def make_recipient_view_request(signer_client_id, ds_return_url, ds_ping_url) view_request end + #ds-snippet-end - def make_envelope(signer_client_id, pdf_filename) + #ds-snippet-start:eSign1Step2 + def make_envelope(signer_client_id, pdf_filename, signer_email, signer_name) envelope_definition = DocuSign_eSign::EnvelopeDefinition.new envelope_definition.email_subject = 'Please sign this document sent from Ruby SDK' doc1 = DocuSign_eSign::Document.new - doc1.document_base64 = Base64.encode64(File.binread(File.join('data', pdf_filename))) + doc1.document_base64 = Base64.encode64(File.binread(pdf_filename)) doc1.name = 'Lorem Ipsum' doc1.file_extension = 'pdf' doc1.document_id = '1' @@ -100,11 +117,11 @@ def make_envelope(signer_client_id, pdf_filename) envelope_definition.documents = [doc1] # Create a signer recipient to sign the document, identified by name and email # We're setting the parameters via the object creation - signer1 = DocuSign_eSign::Signer.new ({ - email: signer_email, name: signer_name, - clientUserId: signer_client_id, recipientId: 1 - }) - # The DocuSign platform searches throughout your envelope's documents for matching + signer1 = DocuSign_eSign::Signer.new({ + email: signer_email, name: signer_name, + clientUserId: signer_client_id, recipientId: 1 + }) + # The Docusign platform searches throughout your envelope's documents for matching # anchor strings. So the sign_here_2 tab will be used in both document 2 and 3 # since they use the same anchor string for their "signer 1" tabs. sign_here = DocuSign_eSign::SignHere.new @@ -126,5 +143,5 @@ def make_envelope(signer_client_id, pdf_filename) envelope_definition.status = 'sent' envelope_definition end - # ***DS.snippet.0.end -end \ No newline at end of file + #ds-snippet-end +end diff --git a/app/services/jwt_auth/jwt_creator.rb b/app/services/jwt_auth/jwt_creator.rb index dae03df..a0853d9 100644 --- a/app/services/jwt_auth/jwt_creator.rb +++ b/app/services/jwt_auth/jwt_creator.rb @@ -1,119 +1,143 @@ -require 'yaml' - -module JwtAuth - class JwtCreator - include ApiCreator - - attr_reader :session, :api_client - - # DocuSign authorization URI to obtain individual consent - # https://developers.docusign.com/platform/auth/jwt/jwt-get-token - # https://developers.docusign.com/platform/auth/consent/obtaining-individual-consent/ - def self.consent_url - # GET /oauth/auth - # This endpoint is used to obtain consent and is the first step in several authentication flows. - # https://developers.docusign.com/platform/auth/reference/obtain-consent - scope = "signature" - if Rails.configuration.examples_API['Rooms'] == true - scope = "signature dtr.rooms.read dtr.rooms.write dtr.documents.read dtr.documents.write dtr.profile.read dtr.profile.write dtr.company.read dtr.company.write room_forms" - elsif Rails.configuration.examples_API['Click'] == true - scope = "signature click.manage click.send" - end - scope = "#{scope} impersonation" - base_uri = "#{Rails.configuration.authorization_server}/oauth/auth" - response_type = "code" - scopes = ERB::Util.url_encode(scope) # https://developers.docusign.com/platform/auth/reference/scopes/ - client_id = Rails.configuration.jwt_integration_key - redirect_uri = Rails.configuration.app_url - consent_url = "#{base_uri}?response_type=#{response_type}&scope=#{scopes}&client_id=#{client_id}&redirect_uri=#{redirect_uri}" - Rails.logger.info "==> Obtain Consent Grant required: #{consent_url}" - consent_url - end - - def initialize(session) - @session = session - @api_client = create_initial_api_client(host: Rails.configuration.aud, debugging: false) - @scope = "signature" - if Rails.configuration.examples_API['Rooms'] == true - scope = "signature dtr.rooms.read dtr.rooms.write dtr.documents.read dtr.documents.write dtr.profile.read dtr.profile.write dtr.company.read dtr.company.write room_forms" - elsif Rails.configuration.examples_API['Click'] == true - scope = "signature click.manage click.send" - end - @scope = "#{scope} impersonation" - end - - # @return [Boolean] `true` if the token was successfully updated, `false` if consent still needs to be grant'ed - def check_jwt_token - rsa_pk = docusign_rsa_private_key_file - begin - # docusign_esign: POST /oauth/token - # This endpoint enables you to exchange an authorization code or JWT token for an access token. - # https://developers.docusign.com/platform/auth/reference/obtain-access-token - token = api_client.request_jwt_user_token(Rails.configuration.jwt_integration_key, Rails.configuration.impersonated_user_guid, rsa_pk, expires_in=3600, @scope) - rescue OpenSSL::PKey::RSAError => exception - Rails.logger.error exception.inspect - if File.read(rsa_pk).starts_with? '{RSA_PRIVATE_KEY}' - fail "Please add your private RSA key to: #{rsa_pk}" - else - raise - end - rescue DocuSign_eSign::ApiError => exception - Rails.logger.warn exception.inspect - body = JSON.parse(exception.response_body) - - if body['error'] == "consent_required" - false - else - details = <<~TXT - See: https://support.docusign.com/articles/DocuSign-Developer-Support-FAQs#Troubleshoot-JWT-invalid_grant - or https://developers.docusign.com/esign-rest-api/guides/authentication/oauth2-code-grant#troubleshooting-errors - or try enabling `configuration.debugging = true` in the initialize method above for more logging output - TXT - fail "JWT response error: `#{body}`. #{details}" - end - else - update_account_info(token) - true - end - end - - private - - def update_account_info(token) - # docusign_esign: GET /oauth/userinfo - # This endpoint returns information on the caller, including their name, email, account, and organizational information. - # The response includes the base_uri needed to interact with the DocuSign APIs. - # https://developers.docusign.com/platform/auth/reference/user-info - user_info_response = api_client.get_user_info(token.access_token) - accounts = user_info_response.accounts - target_account_id = Rails.configuration.target_account_id - account = get_account(accounts, target_account_id) - store_data(token, user_info_response, account) - - api_client.config.host = account.base_uri - Rails.logger.info "==> JWT: Received token for impersonated user which will expire in: #{token.expires_in.to_i.seconds / 1.hour} hour at: #{Time.at(token.expires_in.to_i.seconds.from_now)}" - end - - def store_data(token, user_info, account) - session[:ds_access_token] = token.access_token - session[:ds_expires_at] = token.expires_in.to_i.seconds.from_now.to_i - session[:ds_user_name] = user_info.name - session[:ds_account_id] = account.account_id - session[:ds_base_path] = account.base_uri - session[:ds_account_name] = account.account_name - end - - def get_account(accounts, target_account_id) - if target_account_id.present? - return accounts.find { |acct| acct.account_id == target_account_id } - raise "The user does not have access to account #{target_account_id}" - else - accounts.find(&:is_default) - end - end - - def docusign_rsa_private_key_file - File.join(Rails.root, 'config', 'docusign_private_key.txt') - end - end -end +require 'yaml' + +module JwtAuth + class JwtCreator + include ApiCreator + + attr_reader :session, :api_client, :state + + # Docusign authorization URI to obtain individual consent + # https://developers.docusign.com/platform/auth/jwt/jwt-get-token + # https://developers.docusign.com/platform/auth/consent/obtaining-individual-consent/ + def self.consent_url(state, api) + # GET /oauth/auth + # This endpoint is used to obtain consent and is the first step in several authentication flows. + # https://developers.docusign.com/platform/auth/reference/obtain-consent + scope = 'signature impersonation' if %w[eSignature Monitor].include?(api) + scope = 'signature impersonation dtr.rooms.read dtr.rooms.write dtr.documents.read dtr.documents.write dtr.profile.read dtr.profile.write dtr.company.read dtr.company.write room_forms' if api == 'Rooms' + scope = 'signature impersonation click.manage click.send' if api == 'Click' + scope = 'signature impersonation organization_read group_read permission_read user_read user_write account_read domain_read identity_provider_read user_data_redact asset_group_account_read asset_group_account_clone_write asset_group_account_clone_read organization_sub_account_write organization_sub_account_read' if api == 'Admin' + scope = 'signature webforms_read webforms_instance_read webforms_instance_write' if api == 'WebForms' + scope = 'signature organization_read notary_read notary_write' if api == 'Notary' + scope = 'signature adm_store_unified_repo_read' if api == 'ConnectedFields' + + base_uri = "#{Rails.configuration.authorization_server}/oauth/auth" + response_type = 'code' + scopes = ERB::Util.url_encode(scope) # https://developers.docusign.com/platform/auth/reference/scopes/ + client_id = Rails.configuration.jwt_integration_key + redirect_uri = "#{Rails.configuration.app_url}/auth/docusign/callback" + consent_url = "#{base_uri}?response_type=#{response_type}&scope=#{scopes}&client_id=#{client_id}&state=#{state}&redirect_uri=#{redirect_uri}" + Rails.logger.info "==> Obtain Consent Grant required: #{consent_url}" + consent_url + end + + def initialize(session) + @session = session + scope = 'signature impersonation' + @client_module = DocuSign_eSign + if session[:api] == 'Rooms' + scope = "#{scope} signature dtr.rooms.read dtr.rooms.write dtr.documents.read dtr.documents.write dtr.profile.read dtr.profile.write dtr.company.read dtr.company.write room_forms" + @client_module = DocuSign_Rooms + end + if session[:api] == 'Click' + scope = 'signature click.manage click.send' + @client_module = DocuSign_Click + end + @client_module = DocuSign_Monitor if session[:api] == 'Monitor' + if session[:api] == 'Admin' + scope = 'signature organization_read group_read permission_read user_read user_write account_read domain_read identity_provider_read user_data_redact asset_group_account_read asset_group_account_clone_write asset_group_account_clone_read organization_sub_account_write organization_sub_account_read' + @client_module = DocuSign_Admin + end + if session[:api] == 'WebForms' + scope = 'signature webforms_read webforms_instance_read webforms_instance_write' + @client_module = DocuSign_WebForms + end + if session[:api] == 'Notary' + scope = 'signature organization_read notary_read notary_write' + @client_module = DocuSign_eSign + end + if session[:api] == 'ConnectedFields' + scope = 'signature adm_store_unified_repo_read' + @client_module = DocuSign_eSign + end + + @scope = scope + @api_client = create_initial_api_client(host: Rails.configuration.aud, client_module: @client_module, debugging: false) + end + + # @return [Boolean] `true` if the token was successfully updated, `false` if consent still needs to be grant'ed + def check_jwt_token + rsa_pk = docusign_rsa_private_key_file + begin + # docusign_esign: POST /oauth/token + # This endpoint enables you to exchange an authorization code or JWT token for an access token. + # https://developers.docusign.com/platform/auth/reference/obtain-access-token + token = api_client.request_jwt_user_token(Rails.configuration.jwt_integration_key, Rails.configuration.impersonated_user_guid, rsa_pk, 3600, @scope) + rescue OpenSSL::PKey::RSAError => e + Rails.logger.error e.inspect + raise "Please add your private RSA key to: #{rsa_pk}" if File.read(rsa_pk).starts_with? '{RSA_PRIVATE_KEY}' + + raise + rescue @client_module::ApiError => e + Rails.logger.warn e.inspect + + return false if e.response_body.nil? + + body = JSON.parse(e.response_body) + + if body['error'] == 'consent_required' + false + else + details = <<~TXT + See: https://support.docusign.com/articles/DocuSign-Developer-Support-FAQs#Troubleshoot-JWT-invalid_grant + or https://developers.docusign.com/esign-rest-api/guides/authentication/oauth2-code-grant#troubleshooting-errors + or try enabling `configuration.debugging = true` in the initialize method above for more logging output + TXT + raise "JWT response error: `#{body}`. #{details}" + end + else + update_account_info(token) + true + end + end + + private + + def update_account_info(token) + # docusign_esign: GET /oauth/userinfo + # This endpoint returns information on the caller, including their name, email, account, and organizational information. + # The response includes the base_uri needed to interact with the Docusign APIs. + # https://developers.docusign.com/platform/auth/reference/user-info + user_info_response = api_client.get_user_info(token.access_token) + accounts = user_info_response.accounts + target_account_id = Rails.configuration.target_account_id + account = get_account(accounts, target_account_id) + store_data(token, user_info_response, account) + + api_client.config.host = account.base_uri + Rails.logger.info "==> JWT: Received token for impersonated user which will expire in: #{token.expires_in.to_i.seconds / 1.hour} hour at: #{Time.at(token.expires_in.to_i.seconds.from_now)}" + end + + def store_data(token, user_info, account) + session[:ds_access_token] = token.access_token + session[:ds_expires_at] = token.expires_in.to_i.seconds.from_now.to_i + session[:ds_user_name] = user_info.name + session[:ds_account_id] = account.account_id + session[:ds_base_path] = account.base_uri + session[:ds_account_name] = account.account_name + end + + def get_account(accounts, target_account_id) + if target_account_id.present? + return accounts.find { |acct| acct.account_id == target_account_id } + raise "The user does not have access to account #{target_account_id}" + else + accounts.find(&:is_default) + end + end + + def docusign_rsa_private_key_file + File.join(Rails.root, 'config', 'docusign_private_key.txt') + end + end +end diff --git a/app/services/monitor_api/eg001_get_monitoring_dataset_service.rb b/app/services/monitor_api/eg001_get_monitoring_dataset_service.rb new file mode 100644 index 0000000..dd9faf7 --- /dev/null +++ b/app/services/monitor_api/eg001_get_monitoring_dataset_service.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +class MonitorApi::Eg001GetMonitoringDatasetService + attr_reader :args + + def initialize(args) + @args = args + end + + def worker + #ds-snippet-start:Monitor1Step2 + configuration = DocuSign_Monitor::Configuration.new + configuration.host = Rails.configuration.monitor_host + configuration.debugging = true + api_client = DocuSign_Monitor::ApiClient.new configuration + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Monitor1Step2 + + #ds-snippet-start:Monitor1Step3 + monitor_api = DocuSign_Monitor::DataSetApi.new(api_client) + begin + cursor_date = Date.today.prev_day + cursor_value = cursor_date.strftime('%Y-%m-%dT00:00:00Z') + limit = 2000 + function_results = [] + options = DocuSign_Monitor::GetStreamOptions.new + options.limit = limit + + loop do + options.cursor = cursor_value unless cursor_value.empty? + + cursored_results, _status, headers = monitor_api.get_stream_with_http_info(args[:data_set_name], args[:version], options) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + end_cursor = cursored_results.end_cursor + + break if cursor_value == end_cursor + + cursor_value = end_cursor + function_results.push(cursored_results.data) + end + @response = function_results + #ds-snippet-end:Monitor1Step3 + rescue Exception => e + # error, probalby no Monitor enabled + @response = e + else + Rails.logger.info 'Responses for loops are displayed here. Only the final loop is displayed on the response page' + Rails.logger.info @response.inspect + ensure + return @response + end + end +end diff --git a/app/services/notary/eg004_send_with_third_party_notary_service.rb b/app/services/notary/eg004_send_with_third_party_notary_service.rb new file mode 100644 index 0000000..5261465 --- /dev/null +++ b/app/services/notary/eg004_send_with_third_party_notary_service.rb @@ -0,0 +1,145 @@ +# frozen_string_literal: true + +class Notary::Eg004SendWithThirdPartyNotaryService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def worker + # Create the envelope request object + #ds-snippet-start:Notary4Step4 + envelope_definition = make_envelope args[:envelope_args] + # Call Envelopes::create API method + # Exceptions will be caught by the calling function + envelope_api = create_envelope_api(args) + + results, _status, headers = envelope_api.create_envelope_with_http_info args[:account_id], envelope_definition + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + envelope_id = results.envelope_id + { 'envelope_id' => envelope_id } + #ds-snippet-end:Notary4Step4 + end + + private + + #ds-snippet-start:Notary4Step3 + def make_envelope(envelope_args) + # The envelope has two recipients: + # recipient 1 - notary + # recipient 2 - signer + # 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 + envelope_definition = DocuSign_eSign::EnvelopeDefinition.new + + envelope_definition.email_subject = 'Please sign this document set' + + # Add the document + doc_b64 = Base64.encode64(File.binread(envelope_args[:doc_path])) + + # Create the document model + document = DocuSign_eSign::Document.new( + # Create the Docusign document object + documentBase64: doc_b64, + name: 'Order acknowledgement', # Can be different from actual file name + fileExtension: 'html', # Many different document types are accepted + documentId: '1' # A label used to reference the doc + ) + + # The order in the docs array determines the order in the envelope + envelope_definition.documents = [document] + + sign_here_1 = DocuSign_eSign::SignHere.new( + documentId: '1', + xPosition: '200', + yPosition: '235', + pageNumber: '1' + ) + sign_here_2 = DocuSign_eSign::SignHere.new( + stampType: 'stamp', + documentId: '1', + xPosition: '200', + yPosition: '150', + pageNumber: '1' + ) + + # Add the tabs model (including the sign_here tabs) to the signer + # The Tabs object takes arrays of the different field/tab types + signer_tab = DocuSign_eSign::Tabs.new( + signHereTabs: [sign_here_1, sign_here_2] + ) + + # Create the signer recipient model + signer = DocuSign_eSign::Signer.new( + clientUserId: '1000', + email: envelope_args[:signer_email], + name: envelope_args[:signer_name], + recipientId: '2', + routingOrder: '1', + notaryId: '1', + tabs: signer_tab + ) + + notary_seal = DocuSign_eSign::NotarySeal.new( + xPosition: '300', + yPosition: '235', + documentId: '1', + pageNumber: '1' + ) + + notary_sign_here = DocuSign_eSign::SignHere.new( + xPosition: '300', + yPosition: '150', + documentId: '1', + pageNumber: '1' + ) + + notary_tabs = DocuSign_eSign::Tabs.new( + signHereTabs: [notary_sign_here], + notarySealTabs: [notary_seal] + ) + + notary_recipient = DocuSign_eSign::NotaryRecipient.new( + name: 'Notary', + recipientId: '1', + routingOrder: '1', + tabs: notary_tabs, + notaryType: 'remote', + notarySourceType: 'thirdparty', + notaryThirdPartyPartner: 'onenotary', + recipientSignatureProviders: [ + { + sealDocumentsWithTabsOnly: 'false', + signatureProviderName: 'ds_authority_idv', + signatureProviderOptions: {} + } + ] + ) + + # Add the recipients to the envelope object + recipients = DocuSign_eSign::Recipients.new( + signers: [signer], + notaries: [notary_recipient] + ) + # Request that the envelope be sent by setting status to "sent". + # To request that the envelope be created as a draft, set status to "created" + envelope_definition.recipients = recipients + envelope_definition.status = 'sent' + envelope_definition + end + #ds-snippet-end:Notary4Step3 +end diff --git a/app/services/room_api/eg001_create_room_with_data_service.rb b/app/services/room_api/eg001_create_room_with_data_service.rb index 141f70a..511fd8a 100644 --- a/app/services/room_api/eg001_create_room_with_data_service.rb +++ b/app/services/room_api/eg001_create_room_with_data_service.rb @@ -3,52 +3,59 @@ class RoomApi::Eg001CreateRoomWithDataService attr_reader :args - def initialize(session, request) - @args = { - room_name: request.params[:roomName], - office_id: RoomApi::GetDataService.new(session).get_offices[0]['officeId'], - role_id: RoomApi::GetDataService.new(session).get_roles[2]['roleId'], - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token] - } + def initialize(args) + @args = args end - def call - worker - end - - private - def worker configuration = DocuSign_Rooms::Configuration.new configuration.host = Rails.configuration.rooms_host + #ds-snippet-start:Rooms1Step2 api_client = DocuSign_Rooms::ApiClient.new(configuration) - api_client.set_default_header("Authorization", "Bearer #{args[:access_token]}") + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Rooms1Step2 + #ds-snippet-start:Rooms1Step4 rooms_api = DocuSign_Rooms::RoomsApi.new(api_client) - response = rooms_api.create_room(args[:account_id], body) + results, _status, headers = rooms_api.create_room_with_http_info(args[:account_id], body(args)) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Rooms1Step4 + + results end - def body + private + + def body(args) + #ds-snippet-start:Rooms1Step3 DocuSign_Rooms::RoomForCreate.new( - { - name: args[:room_name], - roleId: args[:role_id], - transactionSideId: "buy", - fieldData: DocuSign_Rooms::FieldDataForCreate.new( - data:{ - address1: '123 EZ Street', - address2: 'unit 10', - city: 'Galaxian', - state: 'US-HI', - postalCode: '88888', - companyRoomStatus: '5', - comments: 'Lorem ipsum dolor sit amet, consectetur adipiscin' - }) - } + { + name: args[:room_name], + roleId: args[:role_id], + transactionSideId: 'buy', + fieldData: DocuSign_Rooms::FieldDataForCreate.new( + data: { + address1: '123 EZ Street', + address2: 'unit 10', + city: 'Galaxian', + state: 'US-HI', + postalCode: '88888', + companyRoomStatus: '5', + comments: 'Lorem ipsum dolor sit amet, consectetur adipiscin' + } + ) + } ) + #ds-snippet-end:Rooms1Step3 end -end \ No newline at end of file +end diff --git a/app/services/room_api/eg002_create_room_with_template_service.rb b/app/services/room_api/eg002_create_room_with_template_service.rb index c7dd105..212eeaf 100644 --- a/app/services/room_api/eg002_create_room_with_template_service.rb +++ b/app/services/room_api/eg002_create_room_with_template_service.rb @@ -3,54 +3,60 @@ class RoomApi::Eg002CreateRoomWithTemplateService attr_reader :args - def initialize(session, request) - @args = { - room_name: request.params[:roomName], - office_id: RoomApi::GetDataService.new(session).get_offices[0]['officeId'], - role_id: RoomApi::GetDataService.new(session).get_roles[2]['roleId'], - template_id: request.params['templateId'], - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token] - } + def initialize(args) + @args = args end - def call - worker - end - - private - def worker + #ds-snippet-start:Rooms2Step2 configuration = DocuSign_Rooms::Configuration.new configuration.host = Rails.configuration.rooms_host api_client = DocuSign_Rooms::ApiClient.new(configuration) - api_client.set_default_header("Authorization", "Bearer #{args[:access_token]}") + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Rooms2Step2 + #ds-snippet-start:Rooms2Step5 rooms_api = DocuSign_Rooms::RoomsApi.new(api_client) - response = rooms_api.create_room(args[:account_id], body) + results, _status, headers = rooms_api.create_room_with_http_info(args[:account_id], body(args)) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Rooms2Step5 + + results end - def body + private + + def body(args) + #ds-snippet-start:Rooms2Step4 DocuSign_Rooms::RoomForCreate.new( - { - name: args[:room_name], - roleId: args[:role_id], - transactionSideId: "listbuy", - roomTemplateId: args[:template_id], - officeId: args[:office_id], - fieldData: DocuSign_Rooms::FieldDataForCreate.new( - data:{ - address1: '123 EZ Street', - address2: 'unit 10', - city: 'Galaxian', - state: 'US-HI', - postalCode: '88888', - companyRoomStatus: '5', - comments: 'Lorem ipsum dolor sit amet, consectetur adipiscin' - }) - } + { + name: args[:room_name], + roleId: args[:role_id], + transactionSideId: 'listbuy', + roomTemplateId: args[:template_id], + officeId: args[:office_id], + fieldData: DocuSign_Rooms::FieldDataForCreate.new( + data: { + address1: '123 EZ Street', + address2: 'unit 10', + city: 'Galaxian', + state: 'US-HI', + postalCode: '88888', + companyRoomStatus: '5', + comments: 'Lorem ipsum dolor sit amet, consectetur adipiscin' + } + ) + } ) + #ds-snippet-end:Rooms2Step4 end -end \ No newline at end of file +end diff --git a/app/services/room_api/eg003_export_data_from_room_service.rb b/app/services/room_api/eg003_export_data_from_room_service.rb index 7424de8..66fa494 100644 --- a/app/services/room_api/eg003_export_data_from_room_service.rb +++ b/app/services/room_api/eg003_export_data_from_room_service.rb @@ -1,31 +1,35 @@ # frozen_string_literal: true class RoomApi::Eg003ExportDataFromRoomService - attr :args - - def initialize(session, request) - @args = { - room_id: request.params['roomId'], - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token] - } - end + attr_reader :args - def call - worker + def initialize(args) + @args = args end - private - def worker + #ds-snippet-start:Rooms3Step2 configuration = DocuSign_Rooms::Configuration.new configuration.host = Rails.configuration.rooms_host api_client = DocuSign_Rooms::ApiClient.new(configuration) - api_client.set_default_header("Authorization", "Bearer #{args[:access_token]}") + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Rooms3Step2 + #ds-snippet-start:Rooms3Step3 rooms_api = DocuSign_Rooms::RoomsApi.new(api_client) - response = rooms_api.get_room_field_data(args[:room_id],args[:account_id]) + results, _status, headers = rooms_api.get_room_field_data_with_http_info(args[:room_id], args[:account_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Rooms3Step3 + + results end -end \ No newline at end of file +end diff --git a/app/services/room_api/eg004_add_forms_to_room_service.rb b/app/services/room_api/eg004_add_forms_to_room_service.rb index 18771c3..ded319f 100644 --- a/app/services/room_api/eg004_add_forms_to_room_service.rb +++ b/app/services/room_api/eg004_add_forms_to_room_service.rb @@ -3,37 +3,42 @@ class RoomApi::Eg004AddFormsToRoomService attr_reader :args - def initialize(session, request) - @args = { - form_id: request.params['formId'], - room_id: request.params['roomId'], - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token] - } + def initialize(args) + @args = args end - def call - worker - end - - private - def worker + #ds-snippet-start:Rooms4Step2 configuration = DocuSign_Rooms::Configuration.new configuration.host = Rails.configuration.rooms_host api_client = DocuSign_Rooms::ApiClient.new(configuration) - api_client.set_default_header("Authorization", "Bearer #{args[:access_token]}") + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Rooms4Step2 + #ds-snippet-start:Rooms4Step4 rooms_api = DocuSign_Rooms::RoomsApi.new(api_client) - rooms_api.add_form_to_room(args[:room_id], args[:account_id], body) + results, _status, headers = rooms_api.add_form_to_room_with_http_info(args[:room_id], args[:account_id], body(args[:form_id])) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Rooms4Step4 + + results end - def body + def body(form_id) + #ds-snippet-start:Rooms4Step3 DocuSign_Rooms::FormForAdd.new({ - formId: args[:form_id] - }) + formId: form_id + }) + #ds-snippet-end:Rooms4Step3 end -end \ No newline at end of file +end diff --git a/app/services/room_api/eg005_get_rooms_with_filters_service.rb b/app/services/room_api/eg005_get_rooms_with_filters_service.rb index 0564517..2941748 100644 --- a/app/services/room_api/eg005_get_rooms_with_filters_service.rb +++ b/app/services/room_api/eg005_get_rooms_with_filters_service.rb @@ -1,36 +1,41 @@ # frozen_string_literal: true class RoomApi::Eg005GetRoomsWithFiltersService - attr :args - - def initialize(session, request) - @args = { - date_from: request.params[:date_from], - date_to: request.params[:date_to], - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token] - } - end + attr_reader :args - def call - worker + def initialize(args) + @args = args end - private - def worker configuration = DocuSign_Rooms::Configuration.new configuration.host = Rails.configuration.rooms_host + #ds-snippet-start:Rooms5Step2 api_client = DocuSign_Rooms::ApiClient.new(configuration) - api_client.set_default_header("Authorization", "Bearer #{args[:access_token]}") + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Rooms5Step2 + #ds-snippet-start:Rooms5Step3 filters = DocuSign_Rooms::GetRoomsOptions.new filters.field_data_changed_start_date = args[:date_from] filters.field_data_changed_end_date = args[:date_to] + #ds-snippet-end:Rooms5Step3 + #ds-snippet-start:Rooms5Step4 rooms_api = DocuSign_Rooms::RoomsApi.new(api_client) - response = rooms_api.get_rooms(args[:account_id], filters) + results, _status, headers = rooms_api.get_rooms_with_http_info(args[:account_id], filters) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Rooms5Step4 + + results end end diff --git a/app/services/room_api/eg006_create_an_external_form_fill_session_service.rb b/app/services/room_api/eg006_create_an_external_form_fill_session_service.rb index a6e0707..45183f4 100644 --- a/app/services/room_api/eg006_create_an_external_form_fill_session_service.rb +++ b/app/services/room_api/eg006_create_an_external_form_fill_session_service.rb @@ -3,42 +3,44 @@ class RoomApi::Eg006CreateAnExternalFormFillSessionService attr_reader :args - def initialize(session, request) - @args = { - form_id: request.params['formId'], - room_id: request.params['roomId'], - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token] - } + def initialize(args) + @args = args end - def call - worker - end - - private - def worker + #ds-snippet-start:Rooms6Step2 configuration = DocuSign_Rooms::Configuration.new configuration.host = Rails.configuration.rooms_host - api_client = DocuSign_Rooms::ApiClient.new(configuration) - api_client.set_default_header("Authorization", "Bearer #{args[:access_token]}") + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Rooms6Step2 + #ds-snippet-start:Rooms6Step4 rooms_api = DocuSign_Rooms::ExternalFormFillSessionsApi.new(api_client) + results, _status, headers = rooms_api.create_external_form_fill_session_with_http_info(args[:account_id], body(args)) - begin - rooms_api.create_external_form_fill_session(args[:account_id], body) - rescue Exception => e - return + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" end + #ds-snippet-end:Rooms6Step4 + + results end - def body + private + + #ds-snippet-start:Rooms6Step3 + def body(args) DocuSign_Rooms::ExternalFormFillSessionForCreate.new({ - formId: args[:form_id], - roomId: args[:room_id] - }) + formId: args[:form_id], + roomId: args[:room_id], + xFrameAllowedUrl: "http://#{args[:allowed_host]}" + }) end -end \ No newline at end of file + #ds-snippet-end:Rooms6Step3 +end diff --git a/app/services/room_api/eg007_create_form_group_service.rb b/app/services/room_api/eg007_create_form_group_service.rb index a6bd3d1..243e31d 100644 --- a/app/services/room_api/eg007_create_form_group_service.rb +++ b/app/services/room_api/eg007_create_form_group_service.rb @@ -3,42 +3,45 @@ class RoomApi::Eg007CreateFormGroupService attr_reader :args - def initialize(session, request) - @args = { - group_name: request.params[:group_name], - account_id: session[:ds_account_id], - access_token: session[:ds_access_token] - } + def initialize(args) + @args = args end - def call - worker - end - - private - def worker - # Step 2 start + #ds-snippet-start:Rooms7Step2 configuration = DocuSign_Rooms::Configuration.new configuration.host = Rails.configuration.rooms_host api_client = DocuSign_Rooms::ApiClient.new(configuration) - api_client.set_default_header("Authorization", "Bearer #{args[:access_token]}") - # Step 2 end + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Rooms7Step2 - # Step 4 start + #ds-snippet-start:Rooms7Step4 rooms_api = DocuSign_Rooms::FormGroupsApi.new(api_client) - rooms_api.create_form_group(args[:account_id], body) - # Step 4 end + results, _status, headers = rooms_api.create_form_group_with_http_info(args[:account_id], body(args)) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + #ds-snippet-end:Rooms7Step4 + + results end - def body - # Step 3 start + private + + def body(args) + #ds-snippet-start:Rooms7Step3 DocuSign_Rooms::RoomForCreate.new( - { - name: args[:group_name] - } + { + name: args[:group_name] + } ) - # Step 3 end + #ds-snippet-end:Rooms7Step3 end end diff --git a/app/services/room_api/eg008_grant_office_access_to_form_group_service.rb b/app/services/room_api/eg008_grant_office_access_to_form_group_service.rb index b036f1a..3ea7d77 100644 --- a/app/services/room_api/eg008_grant_office_access_to_form_group_service.rb +++ b/app/services/room_api/eg008_grant_office_access_to_form_group_service.rb @@ -3,38 +3,36 @@ class RoomApi::Eg008GrantOfficeAccessToFormGroupService attr_reader :args - def initialize(session, request) - @args = { - office_id: request.params[:office_id], - form_group_id: request.params[:form_group_id], - account_id: session[:ds_account_id], - access_token: session[:ds_access_token] - } + def initialize(args) + @args = args end - def call - worker - end - - private - def worker - # Step 2 start + #ds-snippet-start:Rooms8Step2 configuration = DocuSign_Rooms::Configuration.new configuration.host = Rails.configuration.rooms_host api_client = DocuSign_Rooms::ApiClient.new(configuration) - api_client.set_default_header("Authorization", "Bearer #{args[:access_token]}") - # Step 2 end + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Rooms8Step2 - # Step 5 start + #ds-snippet-start:Rooms8Step5 form_groups_api = DocuSign_Rooms::FormGroupsApi.new(api_client) begin - response = form_groups_api.grant_office_access_to_form_group(args[:form_group_id], args[:office_id], args[:account_id]) - rescue Exception => e - return {exception: 'Failed to grant office access to a form group'} + response, _status, headers = form_groups_api.grant_office_access_to_form_group_with_http_info(args[:form_group_id], args[:office_id], args[:account_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + rescue Exception + return { exception: 'Failed to grant office access to a form group' } end - # Step 5 end + #ds-snippet-end:Rooms8Step5 response end end diff --git a/app/services/room_api/eg009_assign_form_to_form_group_service.rb b/app/services/room_api/eg009_assign_form_to_form_group_service.rb index a24d015..7764724 100644 --- a/app/services/room_api/eg009_assign_form_to_form_group_service.rb +++ b/app/services/room_api/eg009_assign_form_to_form_group_service.rb @@ -3,48 +3,48 @@ class RoomApi::Eg009AssignFormToFormGroupService attr_reader :args - def initialize(session, request) - @args = { - form_id: request.params[:form_id], - form_group_id: request.params[:form_group_id], - account_id: session[:ds_account_id], - access_token: session[:ds_access_token] - } + def initialize(args) + @args = args end - def call - worker - end - - private - def worker - # Step 2 start + #ds-snippet-start:Rooms9Step2 configuration = DocuSign_Rooms::Configuration.new configuration.host = Rails.configuration.rooms_host api_client = DocuSign_Rooms::ApiClient.new(configuration) - api_client.set_default_header("Authorization", "Bearer #{args[:access_token]}") - # Step 2 end + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:Rooms9Step2 - # Step 6 start + #ds-snippet-start:Rooms9Step6 form_groups_api = DocuSign_Rooms::FormGroupsApi.new(api_client) begin - response = form_groups_api.assign_form_group_form(args[:form_group_id], args[:account_id], body) - rescue Exception => e + response, _status, headers = form_groups_api.assign_form_group_form_with_http_info(args[:form_group_id], args[:account_id], body(args)) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + rescue Exception return { exception: 'Failed to assign a form to a form group' } end - # Step 6 end + #ds-snippet-end:Rooms9Step6 response end - def body - # Step 5 start + private + + def body(args) + #ds-snippet-start:Rooms9Step5 DocuSign_Rooms::FormGroupFormToAssign.new( { formId: args[:form_id] } ) - # Step 5 end + #ds-snippet-end:Rooms9Step5 end end diff --git a/app/services/room_api/get_data_service.rb b/app/services/room_api/get_data_service.rb index 3be2df4..e4b244d 100644 --- a/app/services/room_api/get_data_service.rb +++ b/app/services/room_api/get_data_service.rb @@ -1,88 +1,172 @@ -# frozen_string_literal: true - -class RoomApi::GetDataService - attr_reader :args - - def initialize(session, options = {}) - @args = { - account_id: session[:ds_account_id], - base_path: session[:ds_base_path], - access_token: session[:ds_access_token], - room_id: options - } - end - - def get_offices - worker - - offices_api = DocuSign_Rooms::OfficesApi.new(@api_client) - offices = offices_api.get_offices(args[:account_id]) - offices.as_json['officeSummaries'] - end - - def get_roles - worker - - roles_api = DocuSign_Rooms::RolesApi.new(@api_client) - roles = roles_api.get_roles(args[:account_id]) - roles.as_json['roles'] - end - - def get_rooms - worker - - rooms_api = DocuSign_Rooms::RoomsApi.new(@api_client) - rooms = rooms_api.get_rooms(args[:account_id]) - rooms.as_json['rooms'] - end - - def get_templates - worker - - templates_api = DocuSign_Rooms::RoomTemplatesApi.new(@api_client) - templates = templates_api.get_room_templates(args[:account_id]) - templates.as_json['roomTemplates'] - end - - def get_form_libraries - worker - - form_libraries_api = DocuSign_Rooms::FormLibrariesApi.new(@api_client) - begin - form_libraries = form_libraries_api.get_form_libraries(args[:account_id]) - rescue Exception => e - return - end - - form_libraries_id = form_libraries.as_json['formsLibrarySummaries'].first['formsLibraryId'] - - forms_api = DocuSign_Rooms::FormLibrariesApi.new(@api_client) - forms = forms_api.get_form_library_forms(form_libraries_id, args[:account_id]) - forms.as_json['forms'] - end - - def get_forms_from_room - worker - - room_forms_api = DocuSign_Rooms::RoomsApi.new(@api_client) - forms = room_forms_api.get_documents(args[:room_id], args[:account_id]) - forms.as_json['documents'] - end - - def get_form_groups - worker - - form_groups_api = DocuSign_Rooms::FormGroupsApi.new(@api_client) - form_groups = form_groups_api.get_form_groups(args[:account_id]) - form_groups.as_json['formGroups'] - end - - private - - def worker - configuration = DocuSign_Rooms::Configuration.new - configuration.host = "https://demo.rooms.docusign.com/restapi" - @api_client = DocuSign_Rooms::ApiClient.new(configuration) - @api_client.set_default_header("Authorization", "Bearer #{args[:access_token]}") - end -end +# frozen_string_literal: true + +class RoomApi::GetDataService + attr_reader :args + + def initialize(session, options = {}) + @args = { + account_id: session[:ds_account_id], + base_path: session[:ds_base_path], + access_token: session[:ds_access_token], + room_id: options + } + end + + def get_offices + worker + + #ds-snippet-start:Rooms8Step3 + offices_api = DocuSign_Rooms::OfficesApi.new(@api_client) + offices, _status, headers = offices_api.get_offices_with_http_info(args[:account_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + offices.as_json['officeSummaries'] + #ds-snippet-end:Rooms8Step3 + end + + def get_default_admin_role_id + worker + + roles_api = DocuSign_Rooms::RolesApi.new(@api_client) + roles, _status, headers = roles_api.get_roles_with_http_info(args[:account_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + default_admin_role = roles.as_json['roles'].find { |role| role['name'] == 'Default Admin' } + default_admin_role['roleId'] + end + + def get_rooms + worker + + rooms_api = DocuSign_Rooms::RoomsApi.new(@api_client) + rooms, _status, headers = rooms_api.get_rooms_with_http_info(args[:account_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + rooms.as_json['rooms'] + end + + def get_templates + worker + + templates_api = DocuSign_Rooms::RoomTemplatesApi.new(@api_client) + templates, _status, headers = templates_api.get_room_templates_with_http_info(args[:account_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + templates.as_json['roomTemplates'] + end + + def get_form_libraries + worker + + form_libraries_api = DocuSign_Rooms::FormLibrariesApi.new(@api_client) + begin + form_libraries, _status, headers = form_libraries_api.get_form_libraries_with_http_info(args[:account_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + rescue Exception + return + end + + form_libraries_id = form_libraries.as_json['formsLibrarySummaries'].find { |lib| lib['formCount'].positive? }['formsLibraryId'] + + forms_api = DocuSign_Rooms::FormLibrariesApi.new(@api_client) + forms, _status, headers = forms_api.get_form_library_forms_with_http_info(form_libraries_id, args[:account_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + forms.as_json['forms'] + end + + def get_forms_from_room + worker + + room_forms_api = DocuSign_Rooms::RoomsApi.new(@api_client) + forms, _status, headers = room_forms_api.get_documents_with_http_info(args[:room_id], args[:account_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + forms.as_json['documents'] + end + + def get_form_groups + worker + + #ds-snippet-start:Rooms8Step4 + form_groups_api = DocuSign_Rooms::FormGroupsApi.new(@api_client) + form_groups, _status, headers = form_groups_api.get_form_groups_with_http_info(args[:account_id]) + + remaining = headers['X-RateLimit-Remaining'] + reset = headers['X-RateLimit-Reset'] + + if remaining && reset + reset_date = Time.at(reset.to_i).utc + puts "API calls remaining: #{remaining}" + puts "Next Reset: #{reset_date}" + end + + form_groups.as_json['formGroups'] + #ds-snippet-end:Rooms8Step4 + end + + private + + def worker + configuration = DocuSign_Rooms::Configuration.new + configuration.host = 'https://demo.rooms.docusign.com/restapi' + @api_client = DocuSign_Rooms::ApiClient.new(configuration) + @api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + end +end diff --git a/app/services/utils.rb b/app/services/utils.rb new file mode 100644 index 0000000..c4fb4cf --- /dev/null +++ b/app/services/utils.rb @@ -0,0 +1,69 @@ +module Utils + class ManifestUtils + def get_manifest(manifest_url) + uri = URI(manifest_url) + res = Net::HTTP.get_response(uri) + JSON.parse(res.body) + end + + def get_example(manifest, number, api_name = 'eSignature') + manifest['APIs'].each do |api| + next unless api_name == api['Name'] + + api['Groups'].each do |group| + group['Examples'].each do |example| + return example if example['ExampleNumber'] == number + end + end + end + end + end + + class DocuSignUtils + def get_user_id(args) + configuration = DocuSign_eSign::Configuration.new + configuration.host = args[:base_path] + api_client = DocuSign_eSign::ApiClient.new configuration + api_client.set_base_path(args[:base_path]) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + user_info = api_client.get_user_info(args[:access_token]) + user_info.sub + end + + def get_folder_id_by_name(folders, folder_name) + folders.each do |folder| + return folder.folder_id if folder.name.downcase == folder_name.downcase + + if folder.folders&.any? + folder_id = get_folder_id_by_name(folder.folders, folder_name) + return folder_id if folder_id + end + end + + nil + end + end + + class FileUtils + def replace_template_id(file_path, template_id) + content = File.read(file_path) + + content.gsub!('template-id', template_id) + + File.write(file_path, content) + end + end + + class URLUtils + def get_parameter_value_from_url(url, param_name) + parsed_url = URI.parse(url) + query_params = URI.decode_www_form(parsed_url.query || '') + + # Access the parameter value (returns a list) + param_value_list = query_params.assoc(param_name) + + # If the parameter exists, return the first value; otherwise, return nil + param_value_list ? param_value_list[1] : nil + end + end +end diff --git a/app/services/webforms/eg001_create_instance_service.rb b/app/services/webforms/eg001_create_instance_service.rb new file mode 100644 index 0000000..83a8573 --- /dev/null +++ b/app/services/webforms/eg001_create_instance_service.rb @@ -0,0 +1,158 @@ +# frozen_string_literal: true + +class Webforms::Eg001CreateInstanceService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def create_web_form_template + templates_api = create_template_api args + + options = DocuSign_eSign::ListTemplatesOptions.new + options.search_text = args[:template_name] + web_forms_templates = templates_api.list_templates(args[:account_id], options) + + if web_forms_templates.result_set_size.to_i.positive? + template_id = web_forms_templates.envelope_templates[0].template_id + else + template_req_object = make_web_forms_template + template = templates_api.create_template(args[:account_id], template_req_object) + template_id = template.template_id + end + + template_id + end + + def list_web_forms + #ds-snippet-start:WebForms1Step2 + configuration = DocuSign_WebForms::Configuration.new + api_client = DocuSign_WebForms::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:WebForms1Step2 + + #ds-snippet-start:WebForms1Step3 + webforms_api = DocuSign_WebForms::FormManagementApi.new(api_client) + + options = DocuSign_WebForms::ListFormsOptions.new + options.search = args[:form_name] + + webforms_api.list_forms(args[:account_id], options) + #ds-snippet-end:WebForms1Step3 + end + + def create_web_form_instance(form_id) + configuration = DocuSign_WebForms::Configuration.new + + api_client = DocuSign_WebForms::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + + #ds-snippet-start:WebForms1Step4 + web_form_values = { + 'PhoneNumber' => '555-555-5555', + 'Yes' => ['Yes'], + 'Company' => 'Tally', + 'JobTitle' => 'Programmer Writer' + } + web_form_req_object = DocuSign_WebForms::CreateInstanceRequestBody.new({ + 'clientUserId' => args[:client_user_id], + 'formValues' => web_form_values, + 'expirationOffset' => '24' + }) + #ds-snippet-end:WebForms1Step4 + + #ds-snippet-start:WebForms1Step5 + webforms_api = DocuSign_WebForms::FormInstanceManagementApi.new(api_client) + webforms_api.create_instance(args[:account_id], form_id, web_form_req_object) + #ds-snippet-end:WebForms1Step5 + end + + private + + def make_web_forms_template + template_name = args[:template_name] + doc_file = 'World_Wide_Corp_Web_Form.pdf' + base64_file_content = Base64.encode64(File.binread(File.join('data', doc_file))) + + # Create the document model + document = DocuSign_eSign::Document.new({ + # Create the Docusign document object + 'documentBase64' => base64_file_content, + 'name' => 'World_Wide_Web_Form', # Can be different from actual file name + 'fileExtension' => 'pdf', # Many different document types are accepted + 'documentId' => '1' # A label used to reference the doc + }) + + # Create the signer recipient model + # Since these are role definitions, no name/email: + signer = DocuSign_eSign::Signer.new({ + 'roleName' => 'signer', 'recipientId' => '1', 'routingOrder' => '1' + }) + # Create fields using absolute positioning + # Create a sign_here tab (field on the document) + sign_here = DocuSign_eSign::SignHere.new( + 'documentId' => '1', 'tabLabel' => 'Signature', + 'anchorString' => '/SignHere/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + check = DocuSign_eSign::Checkbox.new( + 'documentId' => '1', 'tabLabel' => 'Yes', + 'anchorString' => '/SMS/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + text1 = DocuSign_eSign::Text.new( + 'documentId' => '1', 'tabLabel' => 'FullName', + 'anchorString' => '/FullName/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + text2 = DocuSign_eSign::Text.new( + 'documentId' => '1', 'tabLabel' => 'PhoneNumber', + 'anchorString' => '/PhoneNumber/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + text3 = DocuSign_eSign::Text.new( + 'documentId' => '1', 'tabLabel' => 'Company', + 'anchorString' => '/Company/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + text4 = DocuSign_eSign::Text.new( + 'documentId' => '1', 'tabLabel' => 'JobTitle', + 'anchorString' => '/JobTitle/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + date_signed = DocuSign_eSign::DateSigned.new( + 'documentId' => '1', 'tabLabel' => 'DateSigned', + 'anchorString' => '/Date/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + + # Add the tabs model to the signer + # The Tabs object takes arrays of the different field/tab types + signer.tabs = DocuSign_eSign::Tabs.new( + 'signHereTabs' => [sign_here], + 'checkboxTabs' => [check], + 'textTabs' => [text1, text2, text3, text4], + 'dateSignedTabs' => [date_signed] + ) + # Create top two objects + envelope_template_definition = DocuSign_eSign::EnvelopeTemplate.new( + 'description' => 'Example template created via the eSignature API', + 'shared' => 'false' + ) + + # Top object: + DocuSign_eSign::EnvelopeTemplate.new( + 'documents' => [document], + 'name' => template_name, + 'emailSubject' => 'Please sign this document', + 'envelopeTemplateDefinition' => envelope_template_definition, + 'recipients' => DocuSign_eSign::Recipients.new( + 'signers' => [signer] + ), + 'status' => 'created' + ) + end +end diff --git a/app/services/webforms/eg002_create_remote_instance_service.rb b/app/services/webforms/eg002_create_remote_instance_service.rb new file mode 100644 index 0000000..94acc4c --- /dev/null +++ b/app/services/webforms/eg002_create_remote_instance_service.rb @@ -0,0 +1,167 @@ +# frozen_string_literal: true + +class Webforms::Eg002CreateRemoteInstanceService + attr_reader :args + + include ApiCreator + + def initialize(args) + @args = args + end + + def create_web_form_template + templates_api = create_template_api args + + options = DocuSign_eSign::ListTemplatesOptions.new + options.search_text = args[:template_name] + web_forms_templates = templates_api.list_templates(args[:account_id], options) + + if web_forms_templates.result_set_size.to_i.positive? + template_id = web_forms_templates.envelope_templates[0].template_id + else + template_req_object = make_web_forms_template + template = templates_api.create_template(args[:account_id], template_req_object) + template_id = template.template_id + end + + template_id + end + + def list_web_forms + #ds-snippet-start:WebForms2Step2 + configuration = DocuSign_WebForms::Configuration.new + api_client = DocuSign_WebForms::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + #ds-snippet-end:WebForms2Step2 + + #ds-snippet-start:WebForms2Step3 + webforms_api = DocuSign_WebForms::FormManagementApi.new(api_client) + + options = DocuSign_WebForms::ListFormsOptions.new + options.search = args[:form_name] + + webforms_api.list_forms(args[:account_id], options) + #ds-snippet-end:WebForms2Step3 + end + + def create_web_form_instance(form_id) + configuration = DocuSign_WebForms::Configuration.new + + api_client = DocuSign_WebForms::ApiClient.new(configuration) + api_client.set_default_header('Authorization', "Bearer #{args[:access_token]}") + + #ds-snippet-start:WebForms2Step4 + web_form_values = { + 'PhoneNumber' => '555-555-5555', + 'Yes' => ['Yes'], + 'Company' => 'Tally', + 'JobTitle' => 'Programmer Writer' + } + recipient = DocuSign_WebForms::CreateInstanceRequestBodyRecipients.new( + { + 'roleName' => 'signer', + 'name' => args[:signer_name], + 'email' => args[:signer_email] + } + ) + web_form_req_object = DocuSign_WebForms::CreateInstanceRequestBody.new( + { + 'formValues' => web_form_values, + 'recipients' => [recipient], + 'sendOption' => 'now' + } + ) + #ds-snippet-end:WebForms2Step4 + + #ds-snippet-start:WebForms2Step5 + webforms_api = DocuSign_WebForms::FormInstanceManagementApi.new(api_client) + webforms_api.create_instance(args[:account_id], form_id, web_form_req_object) + #ds-snippet-end:WebForms2Step5 + end + + private + + def make_web_forms_template + template_name = args[:template_name] + doc_file = 'World_Wide_Corp_Web_Form.pdf' + base64_file_content = Base64.encode64(File.binread(File.join('data', doc_file))) + + # Create the document model + document = DocuSign_eSign::Document.new({ + # Create the Docusign document object + 'documentBase64' => base64_file_content, + 'name' => 'World_Wide_Web_Form', # Can be different from actual file name + 'fileExtension' => 'pdf', # Many different document types are accepted + 'documentId' => '1' # A label used to reference the doc + }) + + # Create the signer recipient model + # Since these are role definitions, no name/email: + signer = DocuSign_eSign::Signer.new({ + 'roleName' => 'signer', 'recipientId' => '1', 'routingOrder' => '1' + }) + # Create fields using absolute positioning + # Create a sign_here tab (field on the document) + sign_here = DocuSign_eSign::SignHere.new( + 'documentId' => '1', 'tabLabel' => 'Signature', + 'anchorString' => '/SignHere/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + check = DocuSign_eSign::Checkbox.new( + 'documentId' => '1', 'tabLabel' => 'Yes', + 'anchorString' => '/SMS/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + text1 = DocuSign_eSign::Text.new( + 'documentId' => '1', 'tabLabel' => 'FullName', + 'anchorString' => '/FullName/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + text2 = DocuSign_eSign::Text.new( + 'documentId' => '1', 'tabLabel' => 'PhoneNumber', + 'anchorString' => '/PhoneNumber/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + text3 = DocuSign_eSign::Text.new( + 'documentId' => '1', 'tabLabel' => 'Company', + 'anchorString' => '/Company/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + text4 = DocuSign_eSign::Text.new( + 'documentId' => '1', 'tabLabel' => 'JobTitle', + 'anchorString' => '/JobTitle/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + date_signed = DocuSign_eSign::DateSigned.new( + 'documentId' => '1', 'tabLabel' => 'DateSigned', + 'anchorString' => '/Date/', 'anchorUnits' => 'pixels', + 'anchorXOffset' => '0', 'anchorYOffset' => '0' + ) + + # Add the tabs model to the signer + # The Tabs object takes arrays of the different field/tab types + signer.tabs = DocuSign_eSign::Tabs.new( + 'signHereTabs' => [sign_here], + 'checkboxTabs' => [check], + 'textTabs' => [text1, text2, text3, text4], + 'dateSignedTabs' => [date_signed] + ) + # Create top two objects + envelope_template_definition = DocuSign_eSign::EnvelopeTemplate.new( + 'description' => 'Example template created via the eSignature API', + 'shared' => 'false' + ) + + # Top object: + DocuSign_eSign::EnvelopeTemplate.new( + 'documents' => [document], + 'name' => template_name, + 'emailSubject' => 'Please sign this document', + 'envelopeTemplateDefinition' => envelope_template_definition, + 'recipients' => DocuSign_eSign::Recipients.new( + 'signers' => [signer] + ), + 'status' => 'created' + ) + end +end diff --git a/app/views/.DS_Store b/app/views/.DS_Store index d4a0fa4..d1e123c 100644 Binary files a/app/views/.DS_Store and b/app/views/.DS_Store differ diff --git a/app/views/admin_api/aeg001_create_user/get.html.erb b/app/views/admin_api/aeg001_create_user/get.html.erb new file mode 100644 index 0000000..27be449 --- /dev/null +++ b/app/views/admin_api/aeg001_create_user/get.html.erb @@ -0,0 +1,61 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% user_name_index = 0 %> +<% first_name_index = 1 %> +<% last_name_index = 2 %> +<% email_index = 3 %> +<% permission_index = 4 %> +<% group_index = 5 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + "> +
+
+ + "> +
+
+ + "> +
+
+ + "> + <%= render('partials/email_will_not_be_shared') %> +
+ <% if @permission_profiles.present? %> +
+ + <%= select_tag "permission_profile_id", options_for_select(@permission_profiles.map { |obj| [obj.permission_profile_name, obj.permission_profile_id] }), { :class => 'form-control' } %> +
+ <% else %> +
+

Problem: Please first create a Docusign permission profile. +
Thank you.

+
+ <% end %> + <% if @groups.present? %> +
+ + <%= select_tag "group_id", options_for_select(@groups.map { |obj| [obj.group_name, obj.group_id] }), { :class => 'form-control' } %> +
+ <% else %> +
+

Problem: Please first create an eSignature group. +
Thank you.

+
+ <% end %> + <%= render('partials/submit_button') %> +
diff --git a/app/views/admin_api/aeg002_create_active_clm_esign_user/get.html.erb b/app/views/admin_api/aeg002_create_active_clm_esign_user/get.html.erb new file mode 100644 index 0000000..efce466 --- /dev/null +++ b/app/views/admin_api/aeg002_create_active_clm_esign_user/get.html.erb @@ -0,0 +1,75 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% user_name_index = 0 %> +<% first_name_index = 1 %> +<% last_name_index = 2 %> +<% user_email_index = 3 %> +<% esign_profile_index = 4 %> +<% clm_profile_index = 5 %> +<% group_index = 6 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + "> +
+
+ + "> +
+
+ + "> +
+
+ + "> + <%= render('partials/email_will_not_be_shared') %> +
+ <% if @esign_permission_profiles.present? %> +
+ + <%= select_tag "esign_permission_profile_id", options_for_select(@esign_permission_profiles.map { |obj| [obj['permission_profile_name'], obj['permission_profile_id']] }), { :class => 'form-control' } %> +
+ <% else %> +
+

Problem: Please first create an eSignature permission profile. +
Thank you.

+
+ <% end %> + <% if @clm_permission_profiles.present? %> +
+ + <%= select_tag "clm_permission_profile_id", options_for_select(@clm_permission_profiles.map { |obj| [obj['permission_profile_name'], obj['permission_profile_id']] }), { :class => 'form-control' } %> +
+ <% else %> +
+

Problem: Please first create a CLM permission profile. +
Thank you.

+
+ <% end %> + <% if @ds_groups.present? %> +
+ + <%= select_tag "ds_group_id", options_for_select(@ds_groups.map { |obj| [obj['group_name'], obj['ds_group_id']] }), { :class => 'form-control' } %> +
+ <% else %> +
+

Problem: Please first create a Docusign Admin group. +
Thank you.

+
+ <% end %> + + + <%= render('partials/submit_button') %> +
diff --git a/app/views/admin_api/aeg003_bulk_export_user_data/get.html.erb b/app/views/admin_api/aeg003_bulk_export_user_data/get.html.erb new file mode 100644 index 0000000..ce45f3c --- /dev/null +++ b/app/views/admin_api/aeg003_bulk_export_user_data/get.html.erb @@ -0,0 +1,5 @@ +<%= render('partials/example_info') %> + +
+ <%= render('partials/submit_button') %> +
\ No newline at end of file diff --git a/app/views/admin_api/aeg004_import_user/get.html.erb b/app/views/admin_api/aeg004_import_user/get.html.erb new file mode 100644 index 0000000..ce45f3c --- /dev/null +++ b/app/views/admin_api/aeg004_import_user/get.html.erb @@ -0,0 +1,5 @@ +<%= render('partials/example_info') %> + +
+ <%= render('partials/submit_button') %> +
\ No newline at end of file diff --git a/app/views/admin_api/aeg004_import_user/get_status.html.erb b/app/views/admin_api/aeg004_import_user/get_status.html.erb new file mode 100644 index 0000000..94819d2 --- /dev/null +++ b/app/views/admin_api/aeg004_import_user/get_status.html.erb @@ -0,0 +1,17 @@ +<% if @status == "completed" %> + +

<%= @h1 %>

+ <% if !@message.nil? %> +

<%= @message.html_safe %>

+ <% end %> + + + +

+ +

Continue

+ +<% else %> +

Request not complete

+

The request has not completed, please refresh this page

+<% end %> diff --git a/app/views/admin_api/aeg005_audit_users/get.html.erb b/app/views/admin_api/aeg005_audit_users/get.html.erb new file mode 100644 index 0000000..f9adbfb --- /dev/null +++ b/app/views/admin_api/aeg005_audit_users/get.html.erb @@ -0,0 +1,5 @@ +<%= render('partials/example_info') %> + +
+ <%= render('partials/submit_button') %> +
diff --git a/app/views/admin_api/aeg006_get_user_profile_by_email/get.html.erb b/app/views/admin_api/aeg006_get_user_profile_by_email/get.html.erb new file mode 100644 index 0000000..71083d4 --- /dev/null +++ b/app/views/admin_api/aeg006_get_user_profile_by_email/get.html.erb @@ -0,0 +1,19 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% email_index = 0 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + "> + <%= render('partials/email_will_not_be_shared') %> +
+ <%= render('partials/submit_button') %> +
diff --git a/app/views/admin_api/aeg007_get_user_profile_by_user_id/get.html.erb b/app/views/admin_api/aeg007_get_user_profile_by_user_id/get.html.erb new file mode 100644 index 0000000..632ceef --- /dev/null +++ b/app/views/admin_api/aeg007_get_user_profile_by_user_id/get.html.erb @@ -0,0 +1,18 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% user_id_index = 0 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " + required pattern="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"> +
+ <%= render('partials/submit_button') %> +
diff --git a/app/views/admin_api/aeg008_update_user_product_permission_profile/get.html.erb b/app/views/admin_api/aeg008_update_user_product_permission_profile/get.html.erb new file mode 100644 index 0000000..2b5e65c --- /dev/null +++ b/app/views/admin_api/aeg008_update_user_product_permission_profile/get.html.erb @@ -0,0 +1,68 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% product_index = 0 %> +<% permission_index = 1 %> +<% redirect_to2_index = 0 %> + +<% if @email_ok %> + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize format_string(@example["Forms"][form_index]["FormName"], @email) %> + <% end %> + +
+
+ + <%= select_tag "product_id", options_for_select(@product_list.map { |obj| [obj['product_name'], obj['product_id']] }), { :class => 'form-control' } %> +
+ + <% if @clm_permission_profiles.present? %> +
+ + <%= select_tag "clm_permission_profile_id", options_for_select(@clm_permission_profiles.map { |obj| [obj['permission_profile_name'], obj['permission_profile_id']] }), { :class => 'form-control' } %> +
+ <% else %> +
+

Problem: Please first create a CLM permission profile. +
Thank you.

+
+ <% end %> + + <% if @esign_permission_profiles.present? %> + + <% else %> +
+

Problem: Please first create an eSignature permission profile.

+
Thank you.

+
+ <% end %> + + + + <%= render('partials/submit_button') %> +
+ + + +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to2_index]["RedirectText"], 'href="aeg002"') %> + +
+ <%= render('partials/continue_button') %> +
+<% end %> \ No newline at end of file diff --git a/app/views/admin_api/aeg009_delete_user_product_permission_profile/get.html.erb b/app/views/admin_api/aeg009_delete_user_product_permission_profile/get.html.erb new file mode 100644 index 0000000..fc298b8 --- /dev/null +++ b/app/views/admin_api/aeg009_delete_user_product_permission_profile/get.html.erb @@ -0,0 +1,32 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% product_index = 0 %> +<% redirect_to2_index = 0 %> + +<% if @email_ok %> + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize format_string(@example["Forms"][form_index]["FormName"], @email) %> + <% end %> + +
+ <% if @permission_profile_list.present? %> +
+ + <%= select_tag "product_id", options_for_select(@permission_profile_list.map { |obj| [obj['permission_name'], obj['product_id']] }), { :class => 'form-control' } %> +
+ <% else %> +
+

Problem: Please first create an eSignature permission profile. +
Thank you.

+
+ <% end %> + <%= render('partials/submit_button') %> +
+<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to2_index]["RedirectText"], 'href="aeg002"') %> + +
+ <%= render('partials/continue_button') %> +
+<% end %> \ No newline at end of file diff --git a/app/views/admin_api/aeg010_delete_user_data_from_organization/get.html.erb b/app/views/admin_api/aeg010_delete_user_data_from_organization/get.html.erb new file mode 100644 index 0000000..685e1c9 --- /dev/null +++ b/app/views/admin_api/aeg010_delete_user_data_from_organization/get.html.erb @@ -0,0 +1,19 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% email_index = 0 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + "> + <%= @manifest["SupportingTexts"]["HelpingTexts"]["EmailAddressOfUserToDelete"] %> +
+ <%= render('partials/submit_button') %> +
diff --git a/app/views/admin_api/aeg011_delete_user_data_from_account/get.html.erb b/app/views/admin_api/aeg011_delete_user_data_from_account/get.html.erb new file mode 100644 index 0000000..88035e0 --- /dev/null +++ b/app/views/admin_api/aeg011_delete_user_data_from_account/get.html.erb @@ -0,0 +1,19 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% user_id_index = 0 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " + required pattern="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"> + <%= @manifest["SupportingTexts"]["HelpingTexts"]["UserIDOfUserToDelete"] %> +
+ <%= render('partials/submit_button') %> +
diff --git a/app/views/admin_api/aeg012_clone_account/get.html.erb b/app/views/admin_api/aeg012_clone_account/get.html.erb new file mode 100644 index 0000000..73eca05 --- /dev/null +++ b/app/views/admin_api/aeg012_clone_account/get.html.erb @@ -0,0 +1,40 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% source_account_id_index = 0 %> +<% target_account_name_index = 1 %> +<% target_account_first_name_index = 2 %> +<% target_account_last_name_index = 3 %> +<% target_account_email_index = 4 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + <%= select_tag "source_account_id", options_for_select(@accounts.map { |obj| [obj.account_name, obj.account_id] }), { :class => 'form-control' } %> +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ <%= render('partials/submit_button') %> +
diff --git a/app/views/admin_api/aeg013_create_account/get.html.erb b/app/views/admin_api/aeg013_create_account/get.html.erb new file mode 100644 index 0000000..32c5dc0 --- /dev/null +++ b/app/views/admin_api/aeg013_create_account/get.html.erb @@ -0,0 +1,29 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% email_index = 0 %> +<% first_name_index = 1 %> +<% last_name_index = 2 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + +
+
+ + +
+
+ + +
+ <%= render('partials/submit_button') %> +
diff --git a/app/views/admin_api/index.html.erb b/app/views/admin_api/index.html.erb new file mode 100644 index 0000000..9ffac1e --- /dev/null +++ b/app/views/admin_api/index.html.erb @@ -0,0 +1,54 @@ +
+
+ + + + + + + +
+

Ruby Launcher

+

Run and explore Docusign Admin API code examples with Authorization Code Grant or JWT Grant authentication

+
+ +
+
+
+ +
+ <% if @show_doc %> +

Documentation on using JWT Grant or Authorization Code Grant from a Ruby on Rails application.

+ <% end %> + + <% @manifest["Groups"].each { |group| %> +

<%= group["Name"] %>

+ + <% group["Examples"].each { |example| %> + <% if not example["SkipForLanguages"] or not example["SkipForLanguages"].include? "ruby" %> +

"> + "> + <%= example["ExampleName"] %> + +

+ +

<%= sanitize example["ExampleDescription"] %>

+ + <% if example["LinksToAPIMethod"].length > 1 %> + <%= sanitize @manifest["SupportingTexts"]["APIMethodUsedPlural"] %> + <% else %> + <%= sanitize @manifest["SupportingTexts"]["APIMethodUsed"] %> + <% end %> + + <% example["LinksToAPIMethod"].each do |link| %> + "><%= link["PathName"] %> + <% end %> + <% end %> +

+ <% } %> + <% } %> + +
+ + + diff --git a/app/views/clickwrap/ceg001_create_clickwrap/get.html.erb b/app/views/clickwrap/ceg001_create_clickwrap/get.html.erb new file mode 100644 index 0000000..e8c9605 --- /dev/null +++ b/app/views/clickwrap/ceg001_create_clickwrap/get.html.erb @@ -0,0 +1,18 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% click_name_index = 0 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + "> +
+ <%= render('partials/submit_button') %> +
\ No newline at end of file diff --git a/app/views/clickwrap/ceg002_activate_clickwrap/get.html.erb b/app/views/clickwrap/ceg002_activate_clickwrap/get.html.erb new file mode 100644 index 0000000..849c03f --- /dev/null +++ b/app/views/clickwrap/ceg002_activate_clickwrap/get.html.erb @@ -0,0 +1,25 @@ +<%= render('partials/example_info') %> + +<% redirect_to1_index = 0 %> +<% clickwrap_id_index = 0 %> +<% form_index = 0 %> + +<% if @clickwraps.count.positive? %> + +
+ <% if @example["Forms"][0]["FormName"] %> + <%= sanitize @example["Forms"][0]["FormName"] %> + <% end %> + +
+ + <%= select_tag "clickwrapId", options_for_select(@clickwraps.map { |obj| [obj['clickwrapName'], obj['clickwrapId']] }), {:class => 'form-control'} %> +
+ + <%= render('partials/submit_button') %> +
+ +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to1_index]["RedirectText"], 'href="ceg001"') %> + +<% end %> \ No newline at end of file diff --git a/app/views/clickwrap/ceg003_create_new_clickwrap_version/get.html.erb b/app/views/clickwrap/ceg003_create_new_clickwrap_version/get.html.erb new file mode 100644 index 0000000..758bd64 --- /dev/null +++ b/app/views/clickwrap/ceg003_create_new_clickwrap_version/get.html.erb @@ -0,0 +1,16 @@ +<%= render('partials/example_info') %> + +<% redirect_to1_index = 0 %> + +<% if session[:clickwrap_id] %> +

The clickwrap you created via example 1 will be queried.

+ +
+ + <%= render('partials/submit_button') %> +
+ +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to1_index]["RedirectText"], 'href="ceg001"') %> + +<% end %> \ No newline at end of file diff --git a/app/views/clickwrap/ceg004_list_clickwraps/get.html.erb b/app/views/clickwrap/ceg004_list_clickwraps/get.html.erb new file mode 100644 index 0000000..7766e2d --- /dev/null +++ b/app/views/clickwrap/ceg004_list_clickwraps/get.html.erb @@ -0,0 +1,6 @@ +<%= render('partials/example_info') %> + +
+ + <%= render('partials/submit_button') %> +
\ No newline at end of file diff --git a/app/views/clickwrap/ceg005_clickwrap_responses/get.html.erb b/app/views/clickwrap/ceg005_clickwrap_responses/get.html.erb new file mode 100644 index 0000000..77600a4 --- /dev/null +++ b/app/views/clickwrap/ceg005_clickwrap_responses/get.html.erb @@ -0,0 +1,24 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% click_id_index = 0 %> +<% redirect_to1_index = 0 %> + +<% if session[:clickwrap_id] %> +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + "> +
+ <%= render('partials/submit_button') %> +
+ +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to1_index]["RedirectText"], 'href="ceg001"') %> + +<% end %> \ No newline at end of file diff --git a/app/views/clickwrap/ceg006_embed_clickwrap/get.html.erb b/app/views/clickwrap/ceg006_embed_clickwrap/get.html.erb new file mode 100644 index 0000000..ab1bbf8 --- /dev/null +++ b/app/views/clickwrap/ceg006_embed_clickwrap/get.html.erb @@ -0,0 +1,63 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% clickwrap_id_index = 0 %> +<% full_name_index = 1 %> +<% email_index = 2 %> +<% company_index = 3 %> +<% title_index = 4 %> +<% date_index = 5 %> +<% redirect_to1_index = 0 %> +<% redirect_to2_index = 1 %> + +<% if @clickwraps.count.positive? %> +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + <%= select_tag "clickwrapId", options_for_select(@clickwraps.map { |obj| [obj['clickwrapName'], obj['clickwrapId']] }), {:class => 'form-control'} %> +
+

+ <%= sanitize @manifest["SupportingTexts"]["HelpingTexts"]["DynamicContentValue"] %> +

+

+ <%= sanitize @manifest["SupportingTexts"]["HelpingTexts"]["DynamicContentNote"] %> +

+
+ + " required /> +
+
+ + " required + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " required> +
+
+ + " required> +
+
+ + " required> +
+ <%= render('partials/submit_button') %> +
+ +<% elsif @inactive_clickwraps.count.positive? %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to2_index]["RedirectText"], 'href="ceg002"') %> +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to1_index]["RedirectText"], 'href="ceg001"') %> +<% end %> diff --git a/app/views/clickwrap/ceg006_embed_clickwrap/results.html.erb b/app/views/clickwrap/ceg006_embed_clickwrap/results.html.erb new file mode 100644 index 0000000..86c2e3f --- /dev/null +++ b/app/views/clickwrap/ceg006_embed_clickwrap/results.html.erb @@ -0,0 +1,28 @@ +

<%= @title %>

+<% if !@message.nil? %> +

<%= @message.html_safe %>

+<% end %> + +<% if !@agreementUrl.nil? %> +

+ <%= sanitize @manifest["SupportingTexts"]["HelpingTexts"]["EmbedClickwrapURL"] %> <%= @agreementUrl.html_safe %> +

+<% end %> + +

+ <%= sanitize @manifest["SupportingTexts"]["HelpingTexts"]["NOTAGREED"] %> +

+ +

Continue

+ +
+ +<%= javascript_tag do %> + docuSignClick.Clickwrap.render({ + agreementUrl: "<%= @agreementUrl.html_safe %>", + onAgreed: function () { + // Triggered if the user has just agreed + document.getElementById("agreementStatus").innerHTML = "<%= sanitize @manifest["SupportingTexts"]["HelpingTexts"]["AGREED"] %>"; + } + }, "#ds-terms-of-service"); +<% end %> diff --git a/app/views/clickwrap/eg001_create_clickwrap/get.html.erb b/app/views/clickwrap/eg001_create_clickwrap/get.html.erb deleted file mode 100644 index 12d4592..0000000 --- a/app/views/clickwrap/eg001_create_clickwrap/get.html.erb +++ /dev/null @@ -1,20 +0,0 @@ -

1. Creating a clickwrap

-

This example demonstrates how to use the DocuSign Click API to create a - clickwrap that you can embed in your website or app.

- -

API method used: - ClickWraps::createClickwrap -

- -

- View source file <%= @source_file %> on GitHub. -

- -
-
- - -
- -
\ No newline at end of file diff --git a/app/views/clickwrap/eg002_activate_clickwrap/get.html.erb b/app/views/clickwrap/eg002_activate_clickwrap/get.html.erb deleted file mode 100644 index c5d61dd..0000000 --- a/app/views/clickwrap/eg002_activate_clickwrap/get.html.erb +++ /dev/null @@ -1,37 +0,0 @@ -

2. Activating a clickwrap

-

- This example demonstrates how to use the DocuSign Click API to activate a new - clickwrap that you have already created. -
- By default, new clickwraps are inactive. You must activate your clickwrap before - you can use it. -

- -

API method used: - ClickWraps::updateClickwrapVersion -

- -

- View source file <%= @source_file %> on GitHub. -

- -<% if session[:clickwrap_id] %> - -

- The clickwrap you created via example 1 will be queried. -

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

- Problem: please first create a clickwrap using - example 1. -
- Thank you. -

- -<% end %> \ No newline at end of file diff --git a/app/views/clickwrap/eg003_create_new_clickwrap_version/get.html.erb b/app/views/clickwrap/eg003_create_new_clickwrap_version/get.html.erb deleted file mode 100644 index ad9847b..0000000 --- a/app/views/clickwrap/eg003_create_new_clickwrap_version/get.html.erb +++ /dev/null @@ -1,36 +0,0 @@ -

3. Creating a new clickwrap version

-

- This example demonstrates how to use the DocuSign Click API to create a new version - of a clickwrap. -
- You can specify whether you require users who have - previously accepted the clickwrap to accept the new version when they - return to your website. -

- -

API method used: - ClickWraps::createClickwrapVersion -

- -

- View source file <%= @source_file %> on GitHub. -

- -<% if session[:clickwrap_id] %> -

The clickwrap you created via example 1 will be queried.

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

- Problem: please first create a clickwrap using - example 1. -
- Thank you. -

- -<% end %> \ No newline at end of file diff --git a/app/views/clickwrap/eg004_list_clickwraps/get.html.erb b/app/views/clickwrap/eg004_list_clickwraps/get.html.erb deleted file mode 100644 index 9aebd75..0000000 --- a/app/views/clickwrap/eg004_list_clickwraps/get.html.erb +++ /dev/null @@ -1,18 +0,0 @@ -

4. Getting a list of clickwraps

-

- This example demonstrates how to use the DocuSign Click API to get a list of - clickwraps associated with a specific DocuSign user. -

- -

API method used: - ClickWraps::getClickwraps -

- -

- View source file <%= @source_file %> on GitHub. -

- -
- - -
\ No newline at end of file diff --git a/app/views/clickwrap/eg005_clickwrap_responses/get.html.erb b/app/views/clickwrap/eg005_clickwrap_responses/get.html.erb deleted file mode 100644 index 2e6b860..0000000 --- a/app/views/clickwrap/eg005_clickwrap_responses/get.html.erb +++ /dev/null @@ -1,41 +0,0 @@ -

5. Getting clickwrap responses

-

- This example demonstrates how to use the DocuSign Click API to get a list of - clickwrap responses associated with a specific DocuSign user. -

- -

API method used: - ClickWraps::getClickwrapAgreements -

- -

- View source file <%= @source_file %> on GitHub. -

- -<% if session[:clickwrap_id] %> -

- The clickwrap you created via example 1 will be queried. -
- Please enter a value that uniquely identifies a user, such as an email - address or employee ID -

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

- Problem: please first create a clickwrap using - example 1. -
- Thank you. -

- -<% end %> \ No newline at end of file diff --git a/app/views/clickwrap/index.html.erb b/app/views/clickwrap/index.html.erb index f843cc9..5057376 100644 --- a/app/views/clickwrap/index.html.erb +++ b/app/views/clickwrap/index.html.erb @@ -1,16 +1,15 @@ -<% if !session[:ds_user_name] %> - +
-
+
@@ -18,74 +17,39 @@ -<% end %>
-

Welcome

-

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

<% if @show_doc %>

Documentation on using JWT or OAuth Authorization Code Grant from a Ruby Rails application.

<% end %> -

Basic Examples

- -

1. Creating a clickwrap

-

- This example demonstrates how to use DocuSign Click to create a - clickwrap that you can embed in your website or app. -

-

- API methods used: - ClickWraps::createClickwrap -

- -

2. Activate a clickwrap

-

- This example demonstrates how to use DocuSign Click to activate a new - clickwrap that you have already created. -
- By default, new clickwraps are inactive. You must activate your clickwrap before - you can use it. -

-

- API methods used: - ClickWraps::updateClickwrapVersion -

- -

3. Creating a new clickwrap version

-

- This example demonstrates how to use the Click API to create a new version - of a clickwrap. -
- You can specify whether you require users who have - previously accepted the clickwrap to accept the new version when they - return to your website. -

-

- API methods used: - ClickWraps::createClickwrapVersion -

- -

4. Getting a list of clickwraps

-

- This example demonstrates how to use the Click API to get a list of - clickwraps associated with a specific DocuSign user. -

-

- API methods used: - ClickWraps::getClickwraps -

- -

5. Getting clickwrap responses

-

- This example demonstrates how to use the Click API to get a list of - clickwraps associated with a specific DocuSign user. -

-

- API methods used: - ClickWraps::getClickwrapAgreements -

+ <% @manifest["Groups"].each { |group| %> +

<%= group["Name"] %>

+ + <% group["Examples"].each { |example| %> + <% if not example["SkipForLanguages"] or not example["SkipForLanguages"].include? "ruby" %> +

"> + "> + <%= example["ExampleName"] %> + +

+ +

<%= sanitize example["ExampleDescription"] %>

+ + <% if example["LinksToAPIMethod"].length > 1 %> + <%= sanitize @manifest["SupportingTexts"]["APIMethodUsedPlural"] %> + <% else %> + <%= sanitize @manifest["SupportingTexts"]["APIMethodUsed"] %> + <% end %> + + <% example["LinksToAPIMethod"].each do |link| %> + "><%= link["PathName"] %> + <% end %> + <% end %> +

+ <% } %> + <% } %>
diff --git a/app/views/connect/cneg001_validate_webhook_message/get.html.erb b/app/views/connect/cneg001_validate_webhook_message/get.html.erb new file mode 100644 index 0000000..1ae1ede --- /dev/null +++ b/app/views/connect/cneg001_validate_webhook_message/get.html.erb @@ -0,0 +1,28 @@ +<%= render('partials/example_info') %> + +

+ Prerequisite: See How to validate an HMAC signature. +

+ +<% form_index = 0 %> +<% secret_index = 0 %> +<% payload_index = 1 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required> +
+
+ + +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/connected_fields/feg001_set_connected_fields/get.html.erb b/app/views/connected_fields/feg001_set_connected_fields/get.html.erb new file mode 100644 index 0000000..cd55915 --- /dev/null +++ b/app/views/connected_fields/feg001_set_connected_fields/get.html.erb @@ -0,0 +1,36 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> +<% app_index = 2 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+
+ + +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/ds_common/ds_must_authenticate.erb b/app/views/ds_common/ds_must_authenticate.erb index 959a806..03759d0 100644 --- a/app/views/ds_common/ds_must_authenticate.erb +++ b/app/views/ds_common/ds_must_authenticate.erb @@ -1,17 +1,19 @@
-

Please Authenticate with DocuSign

+ <%= sanitize @manifest["SupportingTexts"]["LoginPage"]["LoginHeader"] %>

<%= hidden_field_tag :authenticity_token, form_authenticity_token %>


- +


-

You need to authenticate with DocuSign to continue your request.

+

<%= @manifest["SupportingTexts"]["LoginPage"]["LoginHelperText"] %>

diff --git a/app/views/ds_common/ds_return.html.erb b/app/views/ds_common/ds_return.html.erb index 428965e..7a96b0e 100644 --- a/app/views/ds_common/ds_return.html.erb +++ b/app/views/ds_common/ds_return.html.erb @@ -1,20 +1,20 @@ -

Returned data from DocuSign!

+

Returned data from Docusign!

Data:

<% if @event %> -

event: <%= @event %>. This event parameter is supplied by DocuSign. +

event: <%= @event %>. This event parameter is supplied by Docusign. Since it could have been spoofed, don't make business decisions based on - its value. Instead, query DocuSign as appropriate.

+ its value. Instead, query Docusign as appropriate.

<% end %> <% if @envelope_id %> -

envelopeId: <%= @envelope_id %>. The envelopeId parameter is supplied by DocuSign. +

envelopeId: <%= @envelope_id %>. The envelopeId parameter is supplied by Docusign. Since it could have been spoofed, don't make business decisions based on - its value. Instead, query DocuSign as appropriate.

+ its value. Instead, query Docusign as appropriate.

<% end %> <% if @state %> -

state: <%= @state %>. This example state was sent to DocuSign and has now been received back. +

state: <%= @state %>. This example state was sent to Docusign and has now been received back. It is usually better to store state in your web framework's session.

<% end %> diff --git a/app/views/ds_common/error.erb b/app/views/ds_common/error.erb index f3260d9..6794a71 100644 --- a/app/views/ds_common/error.erb +++ b/app/views/ds_common/error.erb @@ -5,10 +5,11 @@ <% if @error_code %>

<%= @error_code %>: <%= @error_message %>

+<% end %> <% if @error_information %>

<%== @error_information %>

<% end %> -<% else %> +<% if @err %>

<%= @err %>

<% end %> diff --git a/app/views/ds_common/example_done.erb b/app/views/ds_common/example_done.erb index 3d3bda0..bb49251 100644 --- a/app/views/ds_common/example_done.erb +++ b/app/views/ds_common/example_done.erb @@ -1,4 +1,4 @@ -

<%= @h1 %>

+

<%= @title %>

<% if !@message.nil? %>

<%= @message.html_safe %>

<% end %> @@ -11,4 +11,12 @@ <% end %> -

Continue

\ No newline at end of file +<% if @check_status %> +

Check the request status

+<% end %> + +<% if @redirect_url %> +

Continue

+<% else %> +

Continue

+<% end %> diff --git a/app/views/ds_common/index.html.erb b/app/views/ds_common/index.html.erb index 25059b3..6e094c1 100644 --- a/app/views/ds_common/index.html.erb +++ b/app/views/ds_common/index.html.erb @@ -1,15 +1,14 @@ -<% if !session[:ds_user_name] %>
-

Ruby Launcher

-

Welcome to the DocuSign Ruby examples using multiple OAuth flows (JWT and Authorization Code Grant).

+

Run and explore Click API code examples with Authorization Code Grant or JWT Grant authentication

- +
+
@@ -17,285 +16,88 @@ -<% end %> - - <% # Future: add a table of contents or navigation pane - # Eg, see https://stackoverflow.com/questions/21868610/make-column-fixed-position-in-bootstrap - %>
-

Welcome

-

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

<% if @show_doc %> -

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

+

Documentation on using JWT or OAuth Authorization Code Grant from a Ruby on Rails application.

<% end %> -

Basic Examples

- -

1. Use embedded signing

-

This example sends an envelope, and then uses embedded signing for the first signer. - With embedded signing, the DocuSign signing is initiated from your website. -

-

API methods used: - 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) - is used to position the signing fields in the documents. -

-

API method used: - Envelopes::create. -

- -

3. List envelopes in the user's account

-

List the envelopes created in the last 30 days. -

-

API method used: - Envelopes::listStatusChanges. -

- -

4. 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. -

-

API method used: - 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. -

- -

6. List an envelope's documents

-

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

-

API method used: - 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. -

- -

8. Create a template

-

Create a template with two roles, signer and cc.

-

API methods used: - 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.

-

API method used: - Envelopes::create. -

- -

10. Send an envelope using binary document transfer

-

The envelope includes a pdf, Word, and HTML document.

-

Multipart data transfer is used to send the documents in binary format to DocuSign. - Binary transfer is 33% more efficient than base64 encoding and is recommended for documents over 15M Bytes. - Binary transfer is not yet supported by the SDK. -

-

API method used: - Envelopes::create. -

- -

11. Use embedded sending

-

An envelope will be created in draft mode. The DocuSign - web tool (NDSE) will then be shown, enabling further updates - to the envelope before it is sent. -

-

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

12. Embedded DocuSign web tool

-

Redirect the user to the DocuSign web tool.

-

API method used: - EnvelopeViews::createConsole. -

- -

13. 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 - additional document to the envelope by using the - Composite Templates - feature.

-

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

- -

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

-

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

-

API method used: - Envelopes::create. -

- - -

15. Get the tab data from an envelope

-

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

-

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

- -

16. Set tab values for a 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. -

- -

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. -

- -

18. List envelope custom metadata field values

-

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

-

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

- -

Recipient Authentication

-

19. Send an envelope with Access Code Recipient Authentication

-

Submit an envelope with an access code for multi-factor authentication.

-

API method used: - Envelopes::create. -

- -

20. Send an envelope with SMS Recipient Authentication

-

Submit an envelope with a text message for multi-factor authentication.

-

API method used: - Envelopes::create. -

- -

21. Send an envelope with Phone Recipient Authentication

-

Submit an envelope with a voice call to provide multi-factor authentication.

-

API method used: - Envelopes::create. -

- -

22. Send an envelope with Recipient Knowledged Based Authentication

-

Submit an envelope with multiple choice, public records based questions to provide multi-factor authentication.

-

API method used: - Envelopes::create. -

- -

23. IDV Authentication

-

This is an example of an envelope utilizing Id verification authentication for a recipient.

-

API methods used: - Accounts::getAccountIdentityVerificationsList. - Envelopes::create. -

- -

24. Creating a Permission Profile

-

- This example demonstrates how to create a user group's permission profile. -

-

API method used: - Groups::create. -

- -

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

-

- This example updates an individual setting on a permission profile. -

-

API method used: - AccountPermissionProfiles::update. -

- -

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

-

API method used: - Envelopes::create. -

- -

31. Bulk sending envelopes to multiple recipients

-

This example will show you how to create and send an envelope to multiple recipients using bulk lists.

-

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

- -

Advanced recipient routing

-

32. Pausing a signature workflow

-

Pauses signature workflow.

-

API method used: - Envelopes::create. -

- -

33. Unpausing a signature workflow

-

Unpauses a signature workflow.

-

API method used: - Envelopes::update. -

- -

34. Using conditional recipients

-

Uses conditional recipients.

-

API method used: - Envelopes::create. -

- -

Premium Features

-

35. Request Signature by SMS

-

- This example demonstrates how to send a signature request for a signer (and CC) to read and sign via an SMS message. -

-

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

-
- - +
+ + + +
+ + <% @manifest["APIs"].each { |api| %> + <% api["Groups"].each { |group| %> +

<%= group["Name"] %>

+ + <% group["Examples"].each { |example| %> + <% if example_available? example %> + <% if api["Name"].downcase != "esignature" || + example["CFREnabled"] == "AllAccounts" || + @status_cfr == "enabled" && example["CFREnabled"] == "CFROnly" || + @status_cfr != "enabled" && example["CFREnabled"] == "NonCFR" + %> + <% prefix = if api["Name"] == "Admin" + "a" + elsif api["Name"] == "Click" + "c" + elsif api["Name"] == "Rooms" + "r" + elsif api["Name"] == "Monitor" + "m" + elsif api["Name"] == "Connect" + "cn" + elsif api["Name"] == "WebForms" + "w" + elsif api["Name"] == "Notary" + "n" + elsif api["Name"] == "ConnectedFields" + "f" + else + "e" + end %> + +

"> + "> + <%= example["ExampleName"] %> + +

+ +

<%= sanitize example["ExampleDescription"] %>

+ + <% if example["LinksToAPIMethod"] %> + <% if example["LinksToAPIMethod"].length > 1 %> + <%= sanitize @manifest["SupportingTexts"]["APIMethodUsedPlural"] %> + <% else %> + <%= sanitize @manifest["SupportingTexts"]["APIMethodUsed"] %> + <% end %> + + <% example["LinksToAPIMethod"].each do |link| %> + "><%= link["PathName"] %> + <% end %> +

+ <% end %> + <% end %> + <% end %> + <% } %> + <% } %> + <% } %> +
+ + + + + + + + + + diff --git a/app/views/e_sign/eeg002_signing_via_email/get.html.erb b/app/views/e_sign/eeg002_signing_via_email/get.html.erb new file mode 100644 index 0000000..740b325 --- /dev/null +++ b/app/views/e_sign/eeg002_signing_via_email/get.html.erb @@ -0,0 +1,40 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> +<% cc_email_index = 2 %> +<% cc_name_index = 3 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+
+ + " required /> + <%= render('partials/email_should_differ_from_signer') %> +
+
+ + " name="ccName" + required> +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg003_list_envelopes/get.html.erb b/app/views/e_sign/eeg003_list_envelopes/get.html.erb new file mode 100644 index 0000000..4bf9d9e --- /dev/null +++ b/app/views/e_sign/eeg003_list_envelopes/get.html.erb @@ -0,0 +1,5 @@ +<%= render('partials/example_info') %> + + + <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg004_envelope_info/get.html.erb b/app/views/e_sign/eeg004_envelope_info/get.html.erb new file mode 100644 index 0000000..a551238 --- /dev/null +++ b/app/views/e_sign/eeg004_envelope_info/get.html.erb @@ -0,0 +1,16 @@ +<%= render('partials/example_info') %> + +<% redirect_to2_index = 0 %> + + <% if @envelope_ok %> + + <%= render('partials/submit_button') %> + + +<% else %> +<%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to2_index]["RedirectText"], 'href="eeg002"') %> + + + <%= render('partials/continue_button') %> + +<% end %> diff --git a/app/views/e_sign/eeg005_envelope_recipients/get.html.erb b/app/views/e_sign/eeg005_envelope_recipients/get.html.erb new file mode 100644 index 0000000..6d88541 --- /dev/null +++ b/app/views/e_sign/eeg005_envelope_recipients/get.html.erb @@ -0,0 +1,21 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% redirect_to2_index = 0 %> + + <% if @envelope_ok %> + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + + <%= render('partials/submit_button') %> + + +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to2_index]["RedirectText"], 'href="eeg002"') %> + + + <%= render('partials/continue_button') %> + +<% end %> diff --git a/app/views/e_sign/eeg006_envelope_docs/get.html.erb b/app/views/e_sign/eeg006_envelope_docs/get.html.erb new file mode 100644 index 0000000..4611406 --- /dev/null +++ b/app/views/e_sign/eeg006_envelope_docs/get.html.erb @@ -0,0 +1,21 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% redirect_to2_index = 0 %> + + <% if @envelope_ok %> + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + + <%= render('partials/submit_button') %> + + +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to2_index]["RedirectText"], 'href="eeg002"') %> + + + <%= render('partials/continue_button') %> + +<% end %> diff --git a/app/views/e_sign/eeg007_envelope_get_doc/get.html.erb b/app/views/e_sign/eeg007_envelope_get_doc/get.html.erb new file mode 100644 index 0000000..254f241 --- /dev/null +++ b/app/views/e_sign/eeg007_envelope_get_doc/get.html.erb @@ -0,0 +1,37 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% doc_index = 0 %> +<% redirect_to2_index = 0 %> +<% redirect_to6_index = 1 %> + +<% if !@envelope_ok %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to2_index]["RedirectText"], 'href="eeg002"') %> + + + <%= render('partials/continue_button') %> + +<% elsif !@documents_ok %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to6_index]["RedirectText"], 'href="eeg006"') %> + + + <%= render('partials/continue_button') %> + +<% else %> + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + +
+ <%= render('partials/submit_button') %> + +<% end %> diff --git a/app/views/e_sign/eeg008_create_template/get.html.erb b/app/views/e_sign/eeg008_create_template/get.html.erb new file mode 100644 index 0000000..ebf2e7e --- /dev/null +++ b/app/views/e_sign/eeg008_create_template/get.html.erb @@ -0,0 +1,5 @@ +<%= render('partials/example_info') %> + + + <%= render('partials/submit_button') %> + diff --git a/app/views/e_sign/eeg009_use_template/get.html.erb b/app/views/e_sign/eeg009_use_template/get.html.erb new file mode 100644 index 0000000..ab7e376 --- /dev/null +++ b/app/views/e_sign/eeg009_use_template/get.html.erb @@ -0,0 +1,52 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> +<% cc_email_index = 2 %> +<% cc_name_index = 3 %> +<% redirect_to8_index = 0 %> + + <% if @template_ok %> + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+
+ + " required /> + <%= render('partials/email_should_differ_from_signer') %> +
+
+ + " name="ccName" + required> +
+ <%= render('partials/submit_button') %> + + +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to8_index]["RedirectText"], 'href="eeg008"') %> + + + <%= render('partials/continue_button') %> + +<% end %> diff --git a/app/views/e_sign/eeg010_send_binary_docs/get.html.erb b/app/views/e_sign/eeg010_send_binary_docs/get.html.erb new file mode 100644 index 0000000..61befd6 --- /dev/null +++ b/app/views/e_sign/eeg010_send_binary_docs/get.html.erb @@ -0,0 +1,42 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> +<% cc_email_index = 2 %> +<% cc_name_index = 3 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+
+ + " required> + <%= render('partials/email_should_differ_from_signer') %> +
+
+ + " name="ccName" + required> +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg011_embedded_sending/get.html.erb b/app/views/e_sign/eeg011_embedded_sending/get.html.erb new file mode 100644 index 0000000..912ff17 --- /dev/null +++ b/app/views/e_sign/eeg011_embedded_sending/get.html.erb @@ -0,0 +1,50 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% view_index = 0 %> +<% signer_email_index = 1 %> +<% signer_name_index = 2 %> +<% cc_email_index = 3 %> +<% cc_name_index = 4 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + +
+
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+
+ + " required /> + <%= render('partials/email_should_differ_from_signer') %> +
+
+ + " name="ccName" + required> +
+ <%= render('partials/submit_button') %> + diff --git a/app/views/e_sign/eeg012_embedded_console/get.html.erb b/app/views/e_sign/eeg012_embedded_console/get.html.erb new file mode 100644 index 0000000..e382b79 --- /dev/null +++ b/app/views/e_sign/eeg012_embedded_console/get.html.erb @@ -0,0 +1,25 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% view_index = 0 %> +<% redirect_to2_index = 0 %> + + <% if !@envelope_ok %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to2_index]["RedirectText"], 'href="eeg002"') %> +<% end %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + +
+ <%= render('partials/submit_button') %> + diff --git a/app/views/e_sign/eeg013_add_doc_to_template/get.html.erb b/app/views/e_sign/eeg013_add_doc_to_template/get.html.erb new file mode 100644 index 0000000..5cf3d46 --- /dev/null +++ b/app/views/e_sign/eeg013_add_doc_to_template/get.html.erb @@ -0,0 +1,78 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> +<% cc_email_index = 2 %> +<% cc_name_index = 3 %> +<% item_index = 4 %> +<% quantity_index = 5 %> +<% redirect_to8_index = 0 %> + + <% if @template_ok %> + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+
+ + " required /> + <%= render('partials/email_should_differ_from_signer') %> +
+
+ + " name="ccName" + required> +
+


+
+ + +
+
+ + +
+ <%= render('partials/submit_button') %> + + +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to8_index]["RedirectText"], 'href="eeg008"') %> + + + <%= render('partials/continue_button') %> + +<% end %> \ No newline at end of file diff --git a/app/views/e_sign/eeg014_collect_payment/get.html.erb b/app/views/e_sign/eeg014_collect_payment/get.html.erb new file mode 100644 index 0000000..111e84a --- /dev/null +++ b/app/views/e_sign/eeg014_collect_payment/get.html.erb @@ -0,0 +1,49 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> +<% cc_email_index = 2 %> +<% cc_name_index = 3 %> +<% set_getaway_index = 0 %> + +<% if @gateway_ok %> + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+
+ + " required /> + <%= render('partials/email_should_differ_from_signer') %> +
+
+ + " name="ccName" + required> +
+ + <%= render('partials/submit_button') %> + +<% else %> + <%= sanitize @example["RedirectsToOtherCodeExamples"][set_getaway_index]["RedirectText"] %> + +<% end %> \ No newline at end of file diff --git a/app/views/e_sign/eeg015_get_envelope_tab_data/get.html.erb b/app/views/e_sign/eeg015_get_envelope_tab_data/get.html.erb new file mode 100644 index 0000000..b209679 --- /dev/null +++ b/app/views/e_sign/eeg015_get_envelope_tab_data/get.html.erb @@ -0,0 +1,21 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% redirect_to9_index = 0 %> + + <% if @envelope_ok %> + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + + <%= render('partials/submit_button') %> + + +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to9_index]["RedirectText"], 'href="eeg009"') %> + + + <%= render('partials/continue_button') %> + +<% end %> diff --git a/app/views/e_sign/eeg016_set_envelope_tab_data/get.html.erb b/app/views/e_sign/eeg016_set_envelope_tab_data/get.html.erb new file mode 100644 index 0000000..eca3750 --- /dev/null +++ b/app/views/e_sign/eeg016_set_envelope_tab_data/get.html.erb @@ -0,0 +1,27 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_name_index = 0 %> +<% signer_email_index = 1 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg017_set_template_tab_values/get.html.erb b/app/views/e_sign/eeg017_set_template_tab_values/get.html.erb new file mode 100644 index 0000000..84f4ccc --- /dev/null +++ b/app/views/e_sign/eeg017_set_template_tab_values/get.html.erb @@ -0,0 +1,52 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> +<% cc_name_index = 2 %> +<% cc_email_index = 3 %> +<% redirect_to8_index = 0 %> + + <% if @template_ok %> + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+
+ + " name="ccName" + required> +
+
+ + " required /> + <%= render('partials/email_should_differ_from_signer') %> +
+ <%= render('partials/submit_button') %> + + + <% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to8_index]["RedirectText"], 'href="eeg008"') %> + + + <%= render('partials/continue_button') %> + + <% end %> \ No newline at end of file diff --git a/app/views/e_sign/eeg018_get_envelope_custom_field_data/get.html.erb b/app/views/e_sign/eeg018_get_envelope_custom_field_data/get.html.erb new file mode 100644 index 0000000..d225eb2 --- /dev/null +++ b/app/views/e_sign/eeg018_get_envelope_custom_field_data/get.html.erb @@ -0,0 +1,21 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% redirect_to16_index = 0 %> + + <% if @envelope_ok %> + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + + <%= render('partials/submit_button') %> + + +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to16_index]["RedirectText"], 'href="eeg016"') %> + + + <%= render('partials/continue_button') %> + +<% end %> diff --git a/app/views/e_sign/eeg019_access_code_authentication/get.html.erb b/app/views/e_sign/eeg019_access_code_authentication/get.html.erb new file mode 100644 index 0000000..433cd4b --- /dev/null +++ b/app/views/e_sign/eeg019_access_code_authentication/get.html.erb @@ -0,0 +1,35 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% code_index = 0 %> +<% signer_email_index = 1 %> +<% signer_name_index = 2 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required> + Provide this string to a recipient that is different such as in person or by mail or via different email. +
+
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg020_phone_authentication/get.html.erb b/app/views/e_sign/eeg020_phone_authentication/get.html.erb new file mode 100644 index 0000000..2e930df --- /dev/null +++ b/app/views/e_sign/eeg020_phone_authentication/get.html.erb @@ -0,0 +1,44 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% code_index = 0 %> +<% phone_index = 1 %> +<% signer_email_index = 2 %> +<% signer_name_index = 3 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required> + The country code for the phone number below. +
+
+ + " required> + <%= render('partials/phone_will_not_be_shared') %> +
+
+ + " required + required> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signer_name" + required> +
+ + <%= render('partials/submit_button') %> + diff --git a/app/views/e_sign/eeg022_kba_authentication/get.html.erb b/app/views/e_sign/eeg022_kba_authentication/get.html.erb new file mode 100644 index 0000000..ac89ee5 --- /dev/null +++ b/app/views/e_sign/eeg022_kba_authentication/get.html.erb @@ -0,0 +1,26 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + required> +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg023_idv_authentication/get.html.erb b/app/views/e_sign/eeg023_idv_authentication/get.html.erb new file mode 100644 index 0000000..90272df --- /dev/null +++ b/app/views/e_sign/eeg023_idv_authentication/get.html.erb @@ -0,0 +1,26 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + required> +
+ <%= render('partials/submit_button') %> + diff --git a/app/views/e_sign/eeg024_permission_create/get.html.erb b/app/views/e_sign/eeg024_permission_create/get.html.erb new file mode 100644 index 0000000..e77dafd --- /dev/null +++ b/app/views/e_sign/eeg024_permission_create/get.html.erb @@ -0,0 +1,17 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% profile_index = 0 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required /> +
+ <%= render('partials/submit_button') %> + diff --git a/app/views/e_sign/eg025_permissions_set_user_group/get.html.erb b/app/views/e_sign/eeg025_permissions_set_user_group/get.html.erb similarity index 52% rename from app/views/e_sign/eg025_permissions_set_user_group/get.html.erb rename to app/views/e_sign/eeg025_permissions_set_user_group/get.html.erb index 7608575..f7d3fce 100644 --- a/app/views/e_sign/eg025_permissions_set_user_group/get.html.erb +++ b/app/views/e_sign/eeg025_permissions_set_user_group/get.html.erb @@ -1,19 +1,16 @@ -

25. Setting a permission profile

+<%= render('partials/example_info') %> -

- Updates the group name and modifies, or sets, the permission profile for the group -

+<% form_index = 0 %> +<% profile_index = 0 %> +<% group_index = 1 %> -

API method used: - Groups::update. -

- -

- View source file <%= @source_file %> on GitHub. -

+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> +
- +
- +
- + <%= render('partials/submit_button') %> diff --git a/app/views/e_sign/eeg026_permissions_change_single_setting/get.html.erb b/app/views/e_sign/eeg026_permissions_change_single_setting/get.html.erb new file mode 100644 index 0000000..c4f83e7 --- /dev/null +++ b/app/views/e_sign/eeg026_permissions_change_single_setting/get.html.erb @@ -0,0 +1,20 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% profile_index = 0 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + +
+ <%= render('partials/submit_button') %> + diff --git a/app/views/e_sign/eeg027_permissions_delete/get.html.erb b/app/views/e_sign/eeg027_permissions_delete/get.html.erb new file mode 100644 index 0000000..c4f83e7 --- /dev/null +++ b/app/views/e_sign/eeg027_permissions_delete/get.html.erb @@ -0,0 +1,20 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% profile_index = 0 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + +
+ <%= render('partials/submit_button') %> + diff --git a/app/views/e_sign/eeg028_brands_creating/get.html.erb b/app/views/e_sign/eeg028_brands_creating/get.html.erb new file mode 100644 index 0000000..eac0438 --- /dev/null +++ b/app/views/e_sign/eeg028_brands_creating/get.html.erb @@ -0,0 +1,24 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% brand_name_index = 0 %> +<% language_index = 1 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required /> +
+ +
+ + <%= select_tag "defaultBrandLanguage", options_for_select(language_list), { id: "defaultBrandLanguage", name: "defaultBrandLanguage", class: "form-control" } %> +
+ + <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg029_brands_apply_to_envelope/get.html.erb b/app/views/e_sign/eeg029_brands_apply_to_envelope/get.html.erb new file mode 100644 index 0000000..a5b564a --- /dev/null +++ b/app/views/e_sign/eeg029_brands_apply_to_envelope/get.html.erb @@ -0,0 +1,36 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> +<% brand_index = 2 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+
+ + +
+ <%= render('partials/submit_button') %> + diff --git a/app/views/e_sign/eeg030_brands_apply_to_template/get.html.erb b/app/views/e_sign/eeg030_brands_apply_to_template/get.html.erb new file mode 100644 index 0000000..c0b04d4 --- /dev/null +++ b/app/views/e_sign/eeg030_brands_apply_to_template/get.html.erb @@ -0,0 +1,60 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> +<% cc_email_index = 2 %> +<% cc_name_index = 3 %> +<% brand_index = 4 %> +<% redirect_to8_index = 1 %> + +<% if @template_ok %> + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+
+ + " required> + <%= render('partials/email_should_differ_from_signer') %> +
+
+ + " name="ccName" + required> +
+
+ + +
+ <%= render('partials/submit_button') %> + + +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to8_index]["RedirectText"], 'href="eeg008"') %> + + + <%= render('partials/continue_button') %> + +<% end %> \ No newline at end of file diff --git a/app/views/e_sign/eeg031_bulk_sending_envelopes/get.html.erb b/app/views/e_sign/eeg031_bulk_sending_envelopes/get.html.erb new file mode 100644 index 0000000..52a3acf --- /dev/null +++ b/app/views/e_sign/eeg031_bulk_sending_envelopes/get.html.erb @@ -0,0 +1,76 @@ +<%= render('partials/example_info') %> + +<% bulk1_form_index = 0 %> +<% bulk2_form_index = 1 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> +<% cc_email_index = 2 %> +<% cc_name_index = 3 %> + + +
+
+ <% if @example["Forms"][bulk1_form_index]["FormName"] %> +
<%= sanitize @example["Forms"][bulk1_form_index]["FormName"] %>
+ <% end %> + +
+ + " + required class='form-control'/> +
+
+ + " + required class='form-control'/> +
+
+ + " + required class='form-control'/> +
+
+ + " + required class='form-control'/> +
+
+ +
+ <% if @example["Forms"][bulk2_form_index]["FormName"] %> +
<%= sanitize @example["Forms"][bulk2_form_index]["FormName"] %>
+ <% end %> + +
+ + " + required class='form-control'/> +
+
+ + " + required class='form-control'/> +
+
+ + " + required class='form-control'/> +
+
+ + " + required class='form-control'/> +
+
+
+ + <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg032_pauses_signature_workflow/get.html.erb b/app/views/e_sign/eeg032_pauses_signature_workflow/get.html.erb new file mode 100644 index 0000000..a6f99bc --- /dev/null +++ b/app/views/e_sign/eeg032_pauses_signature_workflow/get.html.erb @@ -0,0 +1,50 @@ +<%= render('partials/example_info') %> + +<% signer1_form_index = 0 %> +<% signer2_form_index = 1 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> + + + <% if @example["Forms"][signer1_form_index]["FormName"] %> +

<%= sanitize @example["Forms"][signer1_form_index]["FormName"] %>

+ <% end %> + +
+
+ + " required + value="<%= @config.signer_email %>"> +
+
+ + " + name="signerName1" value="<%= @config.signer_name %>" required> +
+
+ + <% if @example["Forms"][signer2_form_index]["FormName"] %> +

<%= sanitize @example["Forms"][signer2_form_index]["FormName"] %>

+ <% end %> + +
+
+ + " + value="pat@example.com"/> +
+
+ + " + name="signerName2" value="Pat Johnson"> +
+
+ + <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eg032_pauses_signature_workflow/return.html.erb b/app/views/e_sign/eeg032_pauses_signature_workflow/return.html.erb similarity index 80% rename from app/views/e_sign/eg032_pauses_signature_workflow/return.html.erb rename to app/views/e_sign/eeg032_pauses_signature_workflow/return.html.erb index c2c5e7a..6a2fabd 100644 --- a/app/views/e_sign/eg032_pauses_signature_workflow/return.html.erb +++ b/app/views/e_sign/eeg032_pauses_signature_workflow/return.html.erb @@ -5,7 +5,7 @@ Envelope ID: <%= @envelop_id %>

- To resume a workflow after first recipient signs the envelope use example 33 + To resume a workflow after first recipient signs the envelope use Unpause a signature workflow

Continue

\ No newline at end of file diff --git a/app/views/e_sign/eeg033_unpauses_signature_workflow/get.html.erb b/app/views/e_sign/eeg033_unpauses_signature_workflow/get.html.erb new file mode 100644 index 0000000..bd71b37 --- /dev/null +++ b/app/views/e_sign/eeg033_unpauses_signature_workflow/get.html.erb @@ -0,0 +1,13 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% redirect_to32_index = 0 %> + +<% if session[:envelope_id] %> + <%= form_tag eeg033_path, method: :put do -%> + <%= submit_tag 'Submit', class: 'btn btn-docu'%> + <%- end -%> +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to32_index]["RedirectText"], 'href="eeg032"') %> +<% end %> \ No newline at end of file diff --git a/app/views/e_sign/eg033_unpauses_signature_workflow/return.html.erb b/app/views/e_sign/eeg033_unpauses_signature_workflow/return.html.erb similarity index 100% rename from app/views/e_sign/eg033_unpauses_signature_workflow/return.html.erb rename to app/views/e_sign/eeg033_unpauses_signature_workflow/return.html.erb diff --git a/app/views/e_sign/eeg034_use_conditional_recipients/get.html.erb b/app/views/e_sign/eeg034_use_conditional_recipients/get.html.erb new file mode 100644 index 0000000..f47f0db --- /dev/null +++ b/app/views/e_sign/eeg034_use_conditional_recipients/get.html.erb @@ -0,0 +1,72 @@ +<%= render('partials/example_info') %> + +<% signer1_form_index = 0 %> +<% signer2_unchecked_form_index = 1 %> +<% signer2_checked_form_index = 2 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> + + + <% if @example["Forms"][signer1_form_index]["FormName"] %> +

<%= sanitize @example["Forms"][signer1_form_index]["FormName"] %>

+ <% end %> + +
+
+ + " + required value="<%= @config.signer_email %>"> +
+
+ + " + name="signerName1" value="<%= @config.signer_name %>" required> +
+
+ + + <% if @example["Forms"][signer2_unchecked_form_index]["FormName"] %> +

<%= sanitize @example["Forms"][signer2_unchecked_form_index]["FormName"] %>

+ <% end %> + +
+
+ + " + value="pat@example.com"/> +
+
+ + " + name="signerNameNotChecked" value="Pat Johnson"> +
+
+ + <% if @example["Forms"][signer2_checked_form_index]["FormName"] %> +

<%= sanitize @example["Forms"][signer2_checked_form_index]["FormName"] %>

+ <% end %> + +
+
+ + " + value="mat@example.com"/> +
+
+ + " + name="signerNameChecked" value="Mat Johnson"> +
+
+ + <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eg034_use_conditional_recipients/return.html.erb b/app/views/e_sign/eeg034_use_conditional_recipients/return.html.erb similarity index 100% rename from app/views/e_sign/eg034_use_conditional_recipients/return.html.erb rename to app/views/e_sign/eeg034_use_conditional_recipients/return.html.erb diff --git a/app/views/e_sign/eeg035_scheduled_sending/get.html.erb b/app/views/e_sign/eeg035_scheduled_sending/get.html.erb new file mode 100644 index 0000000..59198a9 --- /dev/null +++ b/app/views/e_sign/eeg035_scheduled_sending/get.html.erb @@ -0,0 +1,33 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> +<% date_index = 2 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " + name="signer_name" value="<%= @config.signer_name %>" required /> +
+
+ + + Please choose a date in the future. +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg036_delayed_routing/get.html.erb b/app/views/e_sign/eeg036_delayed_routing/get.html.erb new file mode 100644 index 0000000..741096c --- /dev/null +++ b/app/views/e_sign/eeg036_delayed_routing/get.html.erb @@ -0,0 +1,46 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer1_email_index = 0 %> +<% signer1_name_index = 1 %> +<% signer2_email_index = 2 %> +<% signer2_name_index = 3 %> +<% delay_index = 4 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " + required value="<%= @config.signer_email %>" /> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " + name="signer1Name" value="<%= @config.signer_name %>" required /> +
+
+ + " required /> +
+
+ + " + name="signer2Name" required /> +
+
+ + +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg037_sms_delivery/get.html.erb b/app/views/e_sign/eeg037_sms_delivery/get.html.erb new file mode 100644 index 0000000..afa8ca9 --- /dev/null +++ b/app/views/e_sign/eeg037_sms_delivery/get.html.erb @@ -0,0 +1,68 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_code_index = 0 %> +<% signer_phone_index = 1 %> +<% signer_name_index = 2 %> +<% cc_code_index = 3 %> +<% cc_phone_index = 4 %> +<% cc_name_index = 5 %> +<% delivery_method_index = 6 %> +<% sms_index = 7 %> +<% whatsapp_index = 8 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+
+ + + + +
+ +
+ + " required /> + The country code for the phone number below. +
+
+ + " required /> + <%= render('partials/phone_will_not_be_shared') %> +
+
+ + " name="signer_name" + value="<%= @config.signer_name %>" required /> +
+
+ + " required /> + The country code for the phone number below. +
+
+ + " required /> + <%= render('partials/phone_will_not_be_shared') %> +
+
+ + " name="cc_name" + required /> +
+ <%= render('partials/submit_button') %> + diff --git a/app/views/e_sign/eeg038_responsive_signing/get.html.erb b/app/views/e_sign/eeg038_responsive_signing/get.html.erb new file mode 100644 index 0000000..4f51f85 --- /dev/null +++ b/app/views/e_sign/eeg038_responsive_signing/get.html.erb @@ -0,0 +1,42 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> +<% cc_email_index = 2 %> +<% cc_name_index = 3 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " + name="signerName" value="<%= @config.signer_name %>" required> +
+
+ + " required /> + <%= render('partials/email_should_differ_from_signer') %> +
+
+ + " name="ccName" + required> +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg039_signing_in_person/get.html.erb b/app/views/e_sign/eeg039_signing_in_person/get.html.erb new file mode 100644 index 0000000..7eede7a --- /dev/null +++ b/app/views/e_sign/eeg039_signing_in_person/get.html.erb @@ -0,0 +1,19 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_name_index = 0 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " + name="signer_name" required> +
+ + <%= render('partials/submit_button') %> + diff --git a/app/views/e_sign/eeg040_set_document_visibility/get.html.erb b/app/views/e_sign/eeg040_set_document_visibility/get.html.erb new file mode 100644 index 0000000..0912f7e --- /dev/null +++ b/app/views/e_sign/eeg040_set_document_visibility/get.html.erb @@ -0,0 +1,57 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer1_email_index = 0 %> +<% signer1_name_index = 1 %> +<% signer2_email_index = 2 %> +<% signer2_name_index = 3 %> +<% cc_email_index = 4 %> +<% cc_name_index = 5 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signer1Name" + value="<%= @config.signer_name %>" required> +
+
+ + " required> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signer2Name" + required> +
+
+ + " required /> + <%= render('partials/email_should_differ_from_signer') %> +
+
+ + " name="ccName" + required> +
+ <%= render('partials/submit_button') %> + diff --git a/app/views/e_sign/eeg041_cfr_embedded_signing/get.html.erb b/app/views/e_sign/eeg041_cfr_embedded_signing/get.html.erb new file mode 100644 index 0000000..16b9af2 --- /dev/null +++ b/app/views/e_sign/eeg041_cfr_embedded_signing/get.html.erb @@ -0,0 +1,43 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> +<% country_code_index = 2 %> +<% phone_number_index = 3 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+
+ + " required /> + The country code for the phone number below. +
+
+ + " required /> + <%= render('partials/phone_will_not_be_shared') %> +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg042_document_generation/get.html.erb b/app/views/e_sign/eeg042_document_generation/get.html.erb new file mode 100644 index 0000000..9ba7f3e --- /dev/null +++ b/app/views/e_sign/eeg042_document_generation/get.html.erb @@ -0,0 +1,57 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% candidate_email_index = 0 %> +<% candidate_name_index = 1 %> +<% manager_name_index = 2 %> +<% job_title_index = 3 %> +<% salary_index = 4 %> +<% start_date_index = 5 %> +<% rsus_index = 6 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + + <%= render('partials/email_will_not_be_shared') %> +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg043_shared_access/get.html.erb b/app/views/e_sign/eeg043_shared_access/get.html.erb new file mode 100644 index 0000000..494fe8f --- /dev/null +++ b/app/views/e_sign/eeg043_shared_access/get.html.erb @@ -0,0 +1,30 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% email_index = 0 %> +<% username_index = 1 %> +<% activation_index = 2 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + +
+
+ + +
+
+ + + <%= @manifest["SupportingTexts"]["HelpingTexts"]["SaveAgentActivationCode"] %> +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg044_focused_view/embed.html.erb b/app/views/e_sign/eeg044_focused_view/embed.html.erb new file mode 100644 index 0000000..ebabf39 --- /dev/null +++ b/app/views/e_sign/eeg044_focused_view/embed.html.erb @@ -0,0 +1,78 @@ +
+

The document has been embedded with focused view.

+
+ + + + + + + Signing + + + +
+ + + + +

Continue

+ + + + + \ No newline at end of file diff --git a/app/views/e_sign/eeg044_focused_view/get.html.erb b/app/views/e_sign/eeg044_focused_view/get.html.erb new file mode 100644 index 0000000..94bca70 --- /dev/null +++ b/app/views/e_sign/eeg044_focused_view/get.html.erb @@ -0,0 +1,27 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " + name="signerName" value="<%= @config.signer_name %>" required> +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/e_sign/eeg045_delete_restore_envelope/delete.html.erb b/app/views/e_sign/eeg045_delete_restore_envelope/delete.html.erb new file mode 100644 index 0000000..d946c0e --- /dev/null +++ b/app/views/e_sign/eeg045_delete_restore_envelope/delete.html.erb @@ -0,0 +1,21 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% envelope_id_index = 0 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + "> + <%= @manifest["SupportingTexts"]["HelpingTexts"]["DefaultEnvelopeId"] %> +
+ + <%= render('partials/submit_button') %> + + diff --git a/app/views/e_sign/eeg045_delete_restore_envelope/restore.html.erb b/app/views/e_sign/eeg045_delete_restore_envelope/restore.html.erb new file mode 100644 index 0000000..0e6d375 --- /dev/null +++ b/app/views/e_sign/eeg045_delete_restore_envelope/restore.html.erb @@ -0,0 +1,21 @@ +

<%= @example["ExampleName"] %>

+

<%= sanitize @example["ExampleDescription"] %>

+<%= sanitize format_string(@manifest["SupportingTexts"]["HelpingTexts"]["EnvelopeWillBeRestored"], @envelope_id) %> + +<% form_index = 0 %> +<% folder_name_index = 1 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + "> +
+ + <%= render('partials/submit_button') %> + + diff --git a/app/views/e_sign/eeg046_multiple_delivery/get.html.erb b/app/views/e_sign/eeg046_multiple_delivery/get.html.erb new file mode 100644 index 0000000..5e73c41 --- /dev/null +++ b/app/views/e_sign/eeg046_multiple_delivery/get.html.erb @@ -0,0 +1,90 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% delivery_method_index = 0 %> +<% sms_delivery_method_index = 1 %> +<% whatsapp_delivery_method_index = 2 %> +<% signer_name_index = 3 %> +<% signer_email_index = 4 %> +<% signer_code_index = 5 %> +<% signer_phone_index = 6 %> +<% cc_name_index = 7 %> +<% cc_email_index = 8 %> +<% cc_code_index = 9 %> +<% cc_phone_index = 10 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+
+ + + + + + +
+ +
+ + " name="signer_name" + value="<%= @config.signer_name %>" required /> +
+
+ + " name="signer_email" + value="<%= @config.signer_email %>" required /> + <%= render('partials/email_will_not_be_shared') %> +
+ +
+ + " required /> + <%= render('partials/country_code_text') %> +
+
+ + " required /> + <%= render('partials/phone_will_not_be_shared') %> +
+ +
+ + " name="cc_name" + required /> +
+
+ + " name="cc_email" + required /> + <%= render('partials/email_will_not_be_shared') %> +
+ +
+ + " required /> + <%= render('partials/country_code_text') %> +
+
+ + " required /> + <%= render('partials/phone_will_not_be_shared') %> +
+ + <%= render('partials/submit_button') %> + diff --git a/app/views/e_sign/eg001_embedded_signing/get.html.erb b/app/views/e_sign/eg001_embedded_signing/get.html.erb deleted file mode 100644 index 753ca7d..0000000 --- a/app/views/e_sign/eg001_embedded_signing/get.html.erb +++ /dev/null @@ -1,31 +0,0 @@ -

1. Use 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: the DocuSign signing is initiated from your website.

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

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

-

- View source file <%= @source_file %> on GitHub. -

- - -
- - - We'll never share your email with anyone else. -
-
- - -
- - \ No newline at end of file diff --git a/app/views/e_sign/eg002_signing_via_email/get.html.erb b/app/views/e_sign/eg002_signing_via_email/get.html.erb deleted file mode 100644 index 0fa0add..0000000 --- a/app/views/e_sign/eg002_signing_via_email/get.html.erb +++ /dev/null @@ -1,46 +0,0 @@ -

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

-

The envelope includes a pdf, Word, and HTML 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) to multiple recipients, - with multiple documents, and with signature fields for the documents.

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - Envelopes::create. -

- -

- View source file <%= @source_file %> 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. -
-
- - -
- - \ No newline at end of file diff --git a/app/views/e_sign/eg003_list_envelopes/get.html.erb b/app/views/e_sign/eg003_list_envelopes/get.html.erb deleted file mode 100644 index a3fb9c9..0000000 --- a/app/views/e_sign/eg003_list_envelopes/get.html.erb +++ /dev/null @@ -1,18 +0,0 @@ -

3. 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.

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - Envelopes::listStatusChanges. -

-

- View source file <%= @source_file %> on GitHub. -

- - - - \ No newline at end of file diff --git a/app/views/e_sign/eg004_envelope_info/get.html.erb b/app/views/e_sign/eg004_envelope_info/get.html.erb deleted file mode 100644 index 62324e6..0000000 --- a/app/views/e_sign/eg004_envelope_info/get.html.erb +++ /dev/null @@ -1,38 +0,0 @@ -

4. 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. -

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - Envelopes::get. -

- -

- View source file <%= @source_file %> on GitHub. -

- - <% if @envelope_ok %> -

The envelope you created via example 2 will be queried.

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

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

- - - - -<% end %> diff --git a/app/views/e_sign/eg005_envelope_recipients/get.html.erb b/app/views/e_sign/eg005_envelope_recipients/get.html.erb deleted file mode 100644 index b909ffa..0000000 --- a/app/views/e_sign/eg005_envelope_recipients/get.html.erb +++ /dev/null @@ -1,29 +0,0 @@ -

5. List an envelope's recipients and their status

-

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

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - EnvelopeRecipients::list. -

-

- View source file <%= @source_file %> on GitHub. -

- - <% if @envelope_ok %> -

The envelope you created via example 2 will be queried.

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

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

- - - - -<% end %> diff --git a/app/views/e_sign/eg006_envelope_docs/get.html.erb b/app/views/e_sign/eg006_envelope_docs/get.html.erb deleted file mode 100644 index 26b91b6..0000000 --- a/app/views/e_sign/eg006_envelope_docs/get.html.erb +++ /dev/null @@ -1,35 +0,0 @@ -

6. List an envelope's documents

-

List the 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.

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - EnvelopeDocuments::list. -

- -

- View source file <%= @source_file %> on GitHub. -

- - <% if @envelope_ok %> -

The envelope you created via example 2 will be queried.

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

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

- - - - -<% end %> diff --git a/app/views/e_sign/eg007_envelope_get_doc/get.html.erb b/app/views/e_sign/eg007_envelope_get_doc/get.html.erb deleted file mode 100644 index 3e44943..0000000 --- a/app/views/e_sign/eg007_envelope_get_doc/get.html.erb +++ /dev/null @@ -1,58 +0,0 @@ - -

7. Download a document from an envelope

-

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

-

Document download options:

-
    -
  • Combined: a PDF with the combined content of all documents and the - certificate
  • -
  • Zip Archive: a ZIP archive that contains all of the PDF documents, - the certificate, and any .WAV files used for voice authentication.
  • -
  • The envelope's individual documents
  • -
  • The envelope's Certificate of Completion
  • -
- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - EnvelopeDocuments::get. -

- -

- View source file <%= @source_file %> on GitHub. -

- -<% if !@envelope_ok %> -

Problem: please first create an envelope using example 2.
- You will then need to use example 6 to create the list of documents.
- Thank you.

- - - - -<% elsif !@documents_ok %> -

Problem: please first create a list of the envelope's documents using - example 6.
- Thank you.

- - - - -<% else %> -

Please choose a document.
- The document list is from your results for example 6.

- - -
- - -
- - -<% end %> diff --git a/app/views/e_sign/eg008_create_template/get.html.erb b/app/views/e_sign/eg008_create_template/get.html.erb deleted file mode 100644 index da1fdf3..0000000 --- a/app/views/e_sign/eg008_create_template/get.html.erb +++ /dev/null @@ -1,27 +0,0 @@ -

8. Create a template

-

Create a template with two roles, signer and cc. - The template includes three documents. -

- -

Templates are usually created using the DocuSign web tool. This example creates - a template that will later be used by other examples in this launcher.

- -

This example first lists the user's templates and only creates a new template if one does not already exist in your account. -

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

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

- -

- View source file <%= @source_file %> on GitHub. -

- - - - diff --git a/app/views/e_sign/eg009_use_template/get.html.erb b/app/views/e_sign/eg009_use_template/get.html.erb deleted file mode 100644 index a989103..0000000 --- a/app/views/e_sign/eg009_use_template/get.html.erb +++ /dev/null @@ -1,59 +0,0 @@ -

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.

- -

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. -

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - Envelopes::create. -

-

- View source file <%= @source_file %> on GitHub. -

- - <% if @template_ok %> -

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. -
-
- - -
- - - -<% else %> -

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

- - - - -<% end %> diff --git a/app/views/e_sign/eg010_send_binary_docs/get.html.erb b/app/views/e_sign/eg010_send_binary_docs/get.html.erb deleted file mode 100644 index bc0d219..0000000 --- a/app/views/e_sign/eg010_send_binary_docs/get.html.erb +++ /dev/null @@ -1,47 +0,0 @@ -

10. Send an envelope using binary document transfer

-

The envelope includes a pdf, Word, and HTML document.

-

Multipart data transfer is used to send the documents in binary format to DocuSign. - Binary transfer is 33% more efficient than base64 encoding and is recommended for documents over 15M Bytes. -

- -

Since binary transfer is not yet supported by the SDK, this example uses the API directly and - demonstrates how to do so.

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - Envelopes::create. -

- -

- View source file <%= @source_file %> 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. -
-
- - -
- - \ No newline at end of file diff --git a/app/views/e_sign/eg011_embedded_sending/get.html.erb b/app/views/e_sign/eg011_embedded_sending/get.html.erb deleted file mode 100644 index d217cbd..0000000 --- a/app/views/e_sign/eg011_embedded_sending/get.html.erb +++ /dev/null @@ -1,60 +0,0 @@ -

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 - (AutoPlace) - is used to position the signing fields in the documents.

-

DocuSign recommends that you enhance your application as needed to avoid the need - for the embedded sender view. -

-

For example, if the sender view is used to optionally - add additional documents to the envelope, enhance your app to provide the - needed feature (see example 13 for adding additional documents programmatically). -

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API methods used: - Envelopes::create and - EnvelopeViews::createSender. -

- -

- View source file <%= @source_file %> 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/app/views/e_sign/eg012_embedded_console/get.html.erb b/app/views/e_sign/eg012_embedded_console/get.html.erb deleted file mode 100644 index 370fecc..0000000 --- a/app/views/e_sign/eg012_embedded_console/get.html.erb +++ /dev/null @@ -1,38 +0,0 @@ -

12. Embedded DocuSign web tool

-

Redirect the user to the DocuSign web tool.

-

Use this API call to open the DocuSign web tool, the NDSE (New DocuSign Signing Experience), - with the user already logged in.

-

The starting view can be either an envelope's documents or the web tool's front page.

-

The user does not necessarily return from the NDSE, so using this API call is often a final - step for your application. - Or you can open the NDSE in a new tab for the user. -

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - EnvelopeViews::createConsole. -

- -

- View source file <%= @source_file %> on GitHub. -

- - <% if !@envelope_ok %> -

Optional: to use the Envelope's document view please first create an envelope using - example 2.

-<% end %> - - -
- - -
- - diff --git a/app/views/e_sign/eg013_add_doc_to_template/get.html.erb b/app/views/e_sign/eg013_add_doc_to_template/get.html.erb deleted file mode 100644 index 9d7f4f4..0000000 --- a/app/views/e_sign/eg013_add_doc_to_template/get.html.erb +++ /dev/null @@ -1,90 +0,0 @@ -

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

- -<% if @template_ok %> -

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 - feature.

-

In this example, the additional document is an HTML document that - includes order details with information from the form below.

-

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 is initiated from your website.

-<% end %> - - <% if @show_doc %> -

Documentation about this example.

-<% end %> - -

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

-

- View source file <%= @source_file %> on GitHub. -

- - <% if @template_ok %> -

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. -
-
- - -
-


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

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

- - - - -<% end %> \ No newline at end of file diff --git a/app/views/e_sign/eg014_collect_payment/get.html.erb b/app/views/e_sign/eg014_collect_payment/get.html.erb deleted file mode 100644 index 9216986..0000000 --- a/app/views/e_sign/eg014_collect_payment/get.html.erb +++ /dev/null @@ -1,51 +0,0 @@ -

14. Send an envelope with an order form, including a payment field.

- -

Anchor strings - (AutoPlace) - are used to position the fields in the documents. -

- -<% if @showDoc %> -

Documentation about this example.

-<% end %> - -

API method used: - Envelopes::create. -

- -

-View source file <%= @source_file %> on GitHub. -

- -<% if @gateway_ok %> - -
- - - We'll never share your email with anyone else. -
-
- - -
-
- - - The email for the cc recipient must be different from the signer's email. -
-
- - -
- - - -<% else %> -

Problem: please set the gateway_account_id value in the config/application.rb configuration file.

- -<% end %> \ No newline at end of file diff --git a/app/views/e_sign/eg015_get_envelope_tab_data/get.html.erb b/app/views/e_sign/eg015_get_envelope_tab_data/get.html.erb deleted file mode 100644 index af61e84..0000000 --- a/app/views/e_sign/eg015_get_envelope_tab_data/get.html.erb +++ /dev/null @@ -1,36 +0,0 @@ -

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. -

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - EnvelopeFormData::get. -

- -

- View source file <%= @source_file %> on GitHub. -

- - <% if @envelope_ok %> -

The envelope you created from an earlier example will be queried.

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

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

- - - - -<% end %> diff --git a/app/views/e_sign/eg016_set_envelope_tab_data/get.html.erb b/app/views/e_sign/eg016_set_envelope_tab_data/get.html.erb deleted file mode 100644 index e144d94..0000000 --- a/app/views/e_sign/eg016_set_envelope_tab_data/get.html.erb +++ /dev/null @@ -1,39 +0,0 @@ - -

16. Set tab values for a envelope

-

- This example creates an envelope 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.

- - -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

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

- - -

- View source file <%= @source_file %> on GitHub. -

- - - -
- - - We'll never share your email with anyone else. -
-
- - -
- - \ No newline at end of file diff --git a/app/views/e_sign/eg017_set_template_tab_values/get.html.erb b/app/views/e_sign/eg017_set_template_tab_values/get.html.erb deleted file mode 100644 index 83c6c47..0000000 --- a/app/views/e_sign/eg017_set_template_tab_values/get.html.erb +++ /dev/null @@ -1,59 +0,0 @@ - -

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.

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - Envelopes::create. -

- -

- View source file <%= @source_file %> on GitHub. -

- - - <% if @template_ok %> -

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. -
-
- - -
- - - - <% else %> -

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

- - - - - <% end %> \ No newline at end of file diff --git a/app/views/e_sign/eg018_get_envelope_custom_field_data/get.html.erb b/app/views/e_sign/eg018_get_envelope_custom_field_data/get.html.erb deleted file mode 100644 index 92d0f45..0000000 --- a/app/views/e_sign/eg018_get_envelope_custom_field_data/get.html.erb +++ /dev/null @@ -1,39 +0,0 @@ -

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. -

-<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - EnvelopeCustomFields::list. -

- -

- View source file <%= @source_file %> on GitHub. -

- - <% if @envelope_ok %> -

The last envelope you created with the launcher will be queried.

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

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

- - - - -<% end %> diff --git a/app/views/e_sign/eg019_access_code_authentication/get.html.erb b/app/views/e_sign/eg019_access_code_authentication/get.html.erb deleted file mode 100644 index 70ca581..0000000 --- a/app/views/e_sign/eg019_access_code_authentication/get.html.erb +++ /dev/null @@ -1,45 +0,0 @@ -

19. Requiring an Access Code 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 An Access Code for Recipient Authentication. -

-<% if @show_doc %> -

Documentation about this example.

-<% end %> - - -

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

- -

- View source file <%= @source_file %> on GitHub. -

- - -
- -
- - - Provide this string to a recipient that is different such as in person or by mail or via different email. -
- - - We'll never share your email with anyone else. -
-
- - -
- - \ No newline at end of file diff --git a/app/views/e_sign/eg020_sms_authentication/get.html.erb b/app/views/e_sign/eg020_sms_authentication/get.html.erb deleted file mode 100644 index 33fa6f3..0000000 --- a/app/views/e_sign/eg020_sms_authentication/get.html.erb +++ /dev/null @@ -1,41 +0,0 @@ -

20. Requiring an SMS Code 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 an SMS Code for Recipient Authentication. -

-<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - Envelopes::create. -

- -

- View source file <%= @source_file %> on GitHub. -

- - -
- - - We'll never share your phone number with anyone else. - - - We'll never share your email with anyone else. -
-
- - -
- - \ No newline at end of file diff --git a/app/views/e_sign/eg021_phone_authentication/get.html.erb b/app/views/e_sign/eg021_phone_authentication/get.html.erb deleted file mode 100644 index e4544b0..0000000 --- a/app/views/e_sign/eg021_phone_authentication/get.html.erb +++ /dev/null @@ -1,41 +0,0 @@ -

21. Requiring Phone Authentication 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 a phone call for Recipient Authentication. -

-<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - Envelopes::create. -

- -

- View source file <%= @source_file %> on GitHub. -

- - -
- - - We'll never share your phone number with anyone else. - - - We'll never share your email with anyone else. -
-
- - -
- - \ No newline at end of file diff --git a/app/views/e_sign/eg022_kba_authentication/get.html.erb b/app/views/e_sign/eg022_kba_authentication/get.html.erb deleted file mode 100644 index fb39ead..0000000 --- a/app/views/e_sign/eg022_kba_authentication/get.html.erb +++ /dev/null @@ -1,36 +0,0 @@ -

22. Requiring Knowledge Based Authentication (KBA) 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 KBA for Recipient Authentication. -

-<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - Envelopes::create. -

- -

- View source file <%= @source_file %> on GitHub. -

- - -
- - - We'll never share your email with anyone else. -
-
- - -
- - \ No newline at end of file diff --git a/app/views/e_sign/eg023_idv_authentication/get.html.erb b/app/views/e_sign/eg023_idv_authentication/get.html.erb deleted file mode 100644 index 202af2c..0000000 --- a/app/views/e_sign/eg023_idv_authentication/get.html.erb +++ /dev/null @@ -1,34 +0,0 @@ -

23. Send an envelope with recipient Id Verification authentication.

-

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

-

This is an example of an envelope utilizing IDV authentication for multi-factor verification of a recipient. IDV is a service offered by DocuSign that allows your reicpient to upload a photo of a government issued id for verification.

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - Envelopes::create. -

- -

- View source file <%= @source_file %> on GitHub. -

- - -
- - - We'll never share your email with anyone else. -
-
- - -
- - \ No newline at end of file diff --git a/app/views/e_sign/eg024_permission_create/get.html.erb b/app/views/e_sign/eg024_permission_create/get.html.erb deleted file mode 100644 index 56eed5b..0000000 --- a/app/views/e_sign/eg024_permission_create/get.html.erb +++ /dev/null @@ -1,20 +0,0 @@ -

24. Creating a permission profile

- -

- This code example demonstrates how to create a profile permission -

- -

API method used: - AccountPermissionProfiles::create. - -

- View source file <%= @source_file %> on GitHub. -

- - -
- - -
- - diff --git a/app/views/e_sign/eg026_permissions_change_single_setting/get.html.erb b/app/views/e_sign/eg026_permissions_change_single_setting/get.html.erb deleted file mode 100644 index a171adf..0000000 --- a/app/views/e_sign/eg026_permissions_change_single_setting/get.html.erb +++ /dev/null @@ -1,23 +0,0 @@ -

26. Updating individual permission settings

- -

- This code example demonstrates how to change setting in the existion permission profile. -

- -

API method used: - AccountPermissionProfiles::update. -

-

- View source file <%= @source_file %> on GitHub. -

- -
- - -
- - diff --git a/app/views/e_sign/eg027_permissions_delete/get.html.erb b/app/views/e_sign/eg027_permissions_delete/get.html.erb deleted file mode 100644 index 0f53a9b..0000000 --- a/app/views/e_sign/eg027_permissions_delete/get.html.erb +++ /dev/null @@ -1,24 +0,0 @@ -

27. Deleting a permission profile

- -

- This method deletes a permission profile from an account. -

- -

API method used: - AccountPermissionProfiles::delete. -

- -

- View source file <%= @source_file %> on GitHub. -

- -
- - -
- - diff --git a/app/views/e_sign/eg028_brands_creating/get.html.erb b/app/views/e_sign/eg028_brands_creating/get.html.erb deleted file mode 100644 index 7eb5c49..0000000 --- a/app/views/e_sign/eg028_brands_creating/get.html.erb +++ /dev/null @@ -1,26 +0,0 @@ -

28. Creating a brand

-

The brand includes a Brand Name Configure Brands.

-

This is an example demonstrates how to create a brand.

- -<% if @show_doc %> -

Documentation about this example.

-<% end %> - -

API method used: - AccountsBrands::create. -

- - - -
- - -
- -
- - <%= select_tag "defaultBrandLanguage", options_for_select(language_list), { id: "defaultBrandLanguage", name: "defaultBrandLanguage", class: "form-control" } %> -
- - - \ No newline at end of file diff --git a/app/views/e_sign/eg029_brands_apply_to_envelope/get.html.erb b/app/views/e_sign/eg029_brands_apply_to_envelope/get.html.erb deleted file mode 100644 index f32ee07..0000000 --- a/app/views/e_sign/eg029_brands_apply_to_envelope/get.html.erb +++ /dev/null @@ -1,39 +0,0 @@ -

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. -

- -

API method used: - Envelopes::create. -

- -

- View source file <%= @source_file %> on GitHub. -

- - -
- - - We'll never share your email with anyone else. -
-
- - -
-
- - -
- - diff --git a/app/views/e_sign/eg030_brands_apply_to_template/get.html.erb b/app/views/e_sign/eg030_brands_apply_to_template/get.html.erb deleted file mode 100644 index 1c038ee..0000000 --- a/app/views/e_sign/eg030_brands_apply_to_template/get.html.erb +++ /dev/null @@ -1,56 +0,0 @@ -

30. Applying a brand to an envelope using a template

- -

- This code example demonstrates how to apply a brand to a template -

- -

API method used: - Envelopes::create. -

- -

- View source file <%= @source_file %> 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/app/views/e_sign/eg031_bulk_sending_envelopes/get.html.erb b/app/views/e_sign/eg031_bulk_sending_envelopes/get.html.erb deleted file mode 100644 index 184208f..0000000 --- a/app/views/e_sign/eg031_bulk_sending_envelopes/get.html.erb +++ /dev/null @@ -1,76 +0,0 @@ -

31. Bulk sending envelopes to multiple recipients

- -

- Method BulkSend:createBulkSendList creates a bulk list that you can to use an envelope to up - to 1,000 recipients at once. -

- -

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

- -

- View source file <%= @source_file %> on GitHub. -

- - -

Bulk copy #1

-
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- -

Bulk copy #2

-
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- - \ No newline at end of file diff --git a/app/views/e_sign/eg032_pauses_signature_workflow/get.html.erb b/app/views/e_sign/eg032_pauses_signature_workflow/get.html.erb deleted file mode 100644 index ccd6b03..0000000 --- a/app/views/e_sign/eg032_pauses_signature_workflow/get.html.erb +++ /dev/null @@ -1,47 +0,0 @@ -

32. Pausing a signature workflow

- -

- This example demonstrates how to create an envelope where the workflow is paused before the envelope is sent to a second recipient. - The envelope includes a txt document. - For resuming workflow see example 33 -

- -

API method used: - Envelopes::create, -

- -

- View source file <%= @source_file %> on GitHub. -

- - -

Signed #1

-
-
- - -
-
- - -
-
-

Signed #2

-
-
- - -
-
- - -
-
- - - \ No newline at end of file diff --git a/app/views/e_sign/eg033_unpauses_signature_workflow/get.html.erb b/app/views/e_sign/eg033_unpauses_signature_workflow/get.html.erb deleted file mode 100644 index 5c0bdd6..0000000 --- a/app/views/e_sign/eg033_unpauses_signature_workflow/get.html.erb +++ /dev/null @@ -1,24 +0,0 @@ -

33. Unpausing a signature workflow

- -

- This example demonstrates how to resume an envelope workflow that has been paused. -

- -

API method used: - Envelopes::update, -

- -

- View source file <%= @source_file %> on GitHub. -

-<% if session[:envelope_id] %> - <%= form_tag eg033_path, method: :put do -%> - <%= submit_tag 'Submit', class: 'btn btn-docu'%> - <%- end -%> -<% else %> -

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

-<% end %> \ No newline at end of file diff --git a/app/views/e_sign/eg034_use_conditional_recipients/get.html.erb b/app/views/e_sign/eg034_use_conditional_recipients/get.html.erb deleted file mode 100644 index f4d0b1b..0000000 --- a/app/views/e_sign/eg034_use_conditional_recipients/get.html.erb +++ /dev/null @@ -1,64 +0,0 @@ -

33. Using conditional recipients

- -

- This example demonstrates how to create an envelope where the workflow is routed to different recipients based on the value of a transaction. -

- -

- The envelope includes a txt document. -

- -

API method used: - Envelopes::create, -

- -

- View source file <%= @source_file %> on GitHub. -

- - -

Signed #1

-
-
- - -
-
- - -
-
- -

Conditional signer #2 (when not checked)

-
-
- - -
-
- - -
-
- -

Conditional signer #2 (when checked)

-
-
- - -
-
- - -
-
- - - \ No newline at end of file diff --git a/app/views/e_sign/eg035_sms_delivery/get.html.erb b/app/views/e_sign/eg035_sms_delivery/get.html.erb deleted file mode 100644 index ee6a49c..0000000 --- a/app/views/e_sign/eg035_sms_delivery/get.html.erb +++ /dev/null @@ -1,64 +0,0 @@ -

35. Send an envelope via SMS delivery

- -

-This is a general example of creating and sending an envelope (a signing request) to a recipient and notifying that recipient via SMS delivery. -

- -

API method used: - Envelopes::create, -

- -

- View source file <%= @source_file %> on GitHub. -

- - -
- - The country code for the phone number below. -
-
- - This phone number will receive a notification. We'll never share your phone number with anyone else. -
-
- - - We'll never share your email with anyone else. -
-
- - -
-
- - The country code for the phone number below. -
-
- - This phone number will receive a notification. We'll never share your phone number with anyone else. -
-
- - - The email for the cc recipient must be different from the signer's email. -
-
- - -
- - \ No newline at end of file diff --git a/app/views/eeg001_embedded_signing/get.html.erb b/app/views/eeg001_embedded_signing/get.html.erb new file mode 100644 index 0000000..94bca70 --- /dev/null +++ b/app/views/eeg001_embedded_signing/get.html.erb @@ -0,0 +1,27 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> + + + <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " + name="signerName" value="<%= @config.signer_name %>" required> +
+ <%= render('partials/submit_button') %> + \ No newline at end of file diff --git a/app/views/layouts/_head.erb b/app/views/layouts/_head.erb index 9157635..bc7b30a 100644 --- a/app/views/layouts/_head.erb +++ b/app/views/layouts/_head.erb @@ -5,13 +5,13 @@ - + <% if @title %> <%= @title %> <% else %> - DocuSign Ruby Code Examples + Docusign Ruby Code Examples <% end %> @@ -24,7 +24,7 @@

Ruby Launcher

-

Welcome to the DocuSign Ruby examples using multiple OAuth flows (JWT and Authorization Code Grant).

+

Run and explore eSignature REST API code examples with Authorization Code Grant or JWT Grant authentication

- +
+ + + + + + +
+

Ruby Launcher

+

Run and explore Monitor API code examples with JWT Grant authentication

+
+ +
+
+ +
+ +
+ <% if @show_doc %> +

Documentation on using JWT Authorization from a Ruby Rails application.

+ <% end %> + + <% @manifest["Groups"].each { |group| %> +

<%= group["Name"] %>

+ + <% group["Examples"].each { |example| %> + <% if not example["SkipForLanguages"] or not example["SkipForLanguages"].include? "ruby" %> +

"> + "> + <%= example["ExampleName"] %> + +

+ +

<%= sanitize example["ExampleDescription"] %>

+ + <% if example["LinksToAPIMethod"].length > 1 %> + <%= sanitize @manifest["SupportingTexts"]["APIMethodUsedPlural"] %> + <% else %> + <%= sanitize @manifest["SupportingTexts"]["APIMethodUsed"] %> + <% end %> + + <% example["LinksToAPIMethod"].each do |link| %> + "><%= link["PathName"] %> + <% end %> + <% end %> +

+ <% } %> + <% } %> + +
+ + + diff --git a/app/views/monitor_api/meg001_get_monitoring_dataset/get.html.erb b/app/views/monitor_api/meg001_get_monitoring_dataset/get.html.erb new file mode 100644 index 0000000..ce45f3c --- /dev/null +++ b/app/views/monitor_api/meg001_get_monitoring_dataset/get.html.erb @@ -0,0 +1,5 @@ +<%= render('partials/example_info') %> + +
+ <%= render('partials/submit_button') %> +
\ No newline at end of file diff --git a/app/views/notary/neg004_send_with_third_party_notary/get.html.erb b/app/views/notary/neg004_send_with_third_party_notary/get.html.erb new file mode 100644 index 0000000..043c707 --- /dev/null +++ b/app/views/notary/neg004_send_with_third_party_notary/get.html.erb @@ -0,0 +1,27 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% signer_email_index = 0 %> +<% signer_name_index = 1 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= render('partials/email_will_not_be_shared') %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+ <%= render('partials/submit_button') %> +
\ No newline at end of file diff --git a/app/views/partials/_continue_button.erb b/app/views/partials/_continue_button.erb new file mode 100644 index 0000000..c811c29 --- /dev/null +++ b/app/views/partials/_continue_button.erb @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/views/partials/_country_code_text.erb b/app/views/partials/_country_code_text.erb new file mode 100644 index 0000000..315fbe5 --- /dev/null +++ b/app/views/partials/_country_code_text.erb @@ -0,0 +1 @@ +<%= @manifest["SupportingTexts"]["HelpingTexts"]["CountryCodeText"] %> \ No newline at end of file diff --git a/app/views/partials/_email_should_differ_from_signer.erb b/app/views/partials/_email_should_differ_from_signer.erb new file mode 100644 index 0000000..2ed2e20 --- /dev/null +++ b/app/views/partials/_email_should_differ_from_signer.erb @@ -0,0 +1 @@ +<%= @manifest["SupportingTexts"]["HelpingTexts"]["CCEmailShouldDifferFromSigner"] %> \ No newline at end of file diff --git a/app/views/partials/_email_will_not_be_shared.erb b/app/views/partials/_email_will_not_be_shared.erb new file mode 100644 index 0000000..33b098d --- /dev/null +++ b/app/views/partials/_email_will_not_be_shared.erb @@ -0,0 +1 @@ +<%= @manifest["SupportingTexts"]["HelpingTexts"]["EmailWontBeShared"] %> \ No newline at end of file diff --git a/app/views/partials/_example_info.erb b/app/views/partials/_example_info.erb new file mode 100644 index 0000000..71adef9 --- /dev/null +++ b/app/views/partials/_example_info.erb @@ -0,0 +1,30 @@ +

<%= @example["ExampleName"] %>

+

<%= sanitize @example["ExampleDescription"] %>

+ +<% if @show_doc %> +

Documentation about this example.

+<% end %> + +<% if @example["Notes"] %> + <%= sanitize @example["Notes"] %> +<% end %> + +<% if @example["LinksToAPIMethod"] %> + <% if @example["LinksToAPIMethod"].length > 1 %> + <%= sanitize @manifest["SupportingTexts"]["APIMethodUsedPlural"] %> + <% else %> + <%= sanitize @manifest["SupportingTexts"]["APIMethodUsed"] %> + <% end %> + + <% @example["LinksToAPIMethod"].each do |link| %> + "><%= link["PathName"] %> + <% end %> +<% end %> + +<% if @example["LinkToHowTo"] %> +

Prerequisite: See "><%= @example["LinkToHowTo"][0]["PathName"] %>

+<% end %> + +

+ <%= sanitize format_string(@manifest["SupportingTexts"]["ViewSourceFile"], "#{@source_file}" ) %> +

\ No newline at end of file diff --git a/app/views/partials/_phone_will_not_be_shared.erb b/app/views/partials/_phone_will_not_be_shared.erb new file mode 100644 index 0000000..63439ef --- /dev/null +++ b/app/views/partials/_phone_will_not_be_shared.erb @@ -0,0 +1 @@ +<%= "#{@manifest["SupportingTexts"]["HelpingTexts"]["PhoneNumberWillBeNotified"]} #{@manifest["SupportingTexts"]["HelpingTexts"]["PhoneNumberWontBeShared"]}" %> \ No newline at end of file diff --git a/app/views/partials/_submit_button.erb b/app/views/partials/_submit_button.erb new file mode 100644 index 0000000..6f54a77 --- /dev/null +++ b/app/views/partials/_submit_button.erb @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/views/room_api/eg001_create_room_with_data/_information.html.erb b/app/views/room_api/eg001_create_room_with_data/_information.html.erb deleted file mode 100644 index d9d9a51..0000000 --- a/app/views/room_api/eg001_create_room_with_data/_information.html.erb +++ /dev/null @@ -1,10 +0,0 @@ -

1. Create a new room

-

This is a general example of creating a room.

- -

API methods used: - Roles::GetRoles and - Rooms::CreateRoom. -

-

- View source file <%= @source_file %> on GitHub. -

\ No newline at end of file diff --git a/app/views/room_api/eg001_create_room_with_data/get.html.erb b/app/views/room_api/eg001_create_room_with_data/get.html.erb deleted file mode 100644 index e477c6a..0000000 --- a/app/views/room_api/eg001_create_room_with_data/get.html.erb +++ /dev/null @@ -1,9 +0,0 @@ -<%=render 'room_api/eg001_create_room_with_data/information' %> - -
-
- - -
- -
\ No newline at end of file diff --git a/app/views/room_api/eg002_create_room_with_template/_information.html.erb b/app/views/room_api/eg002_create_room_with_template/_information.html.erb deleted file mode 100644 index 1f54f28..0000000 --- a/app/views/room_api/eg002_create_room_with_template/_information.html.erb +++ /dev/null @@ -1,14 +0,0 @@ -

2. Create room with a template

-

- This code example demonstrates creating a DocuSign room using a predefined template. If you've created a template for your organization in either Rooms for Mortgage or Rooms for Real Estate, you can create rooms based on this template using the Rooms API. -

- -

API methods used: - Roles::GetRoles and - Rooms::CreateRoom and - RoomTemplates::GetRoomTemplates. -

- -

- View source file <%= @source_file %> on GitHub. -

\ No newline at end of file diff --git a/app/views/room_api/eg002_create_room_with_template/get.html.erb b/app/views/room_api/eg002_create_room_with_template/get.html.erb deleted file mode 100644 index 5580622..0000000 --- a/app/views/room_api/eg002_create_room_with_template/get.html.erb +++ /dev/null @@ -1,26 +0,0 @@ -<%=render 'room_api/eg002_create_room_with_template/information' %> - - -<% unless @templates.blank? %> - -
-
- - - - - <%= select_tag "templateId", options_for_select(@templates.map { |obj| [obj['name'], obj['templateId']] }), {:class => 'form-control'} %> -
- -
- -<% else %> - - -

- Problem: you don't have any templates. Please first create a form template using - DociSign::Admin - Thank you. -

- -<% end %> diff --git a/app/views/room_api/eg003_export_data_from_room/_information.html.erb b/app/views/room_api/eg003_export_data_from_room/_information.html.erb deleted file mode 100644 index 9afa218..0000000 --- a/app/views/room_api/eg003_export_data_from_room/_information.html.erb +++ /dev/null @@ -1,14 +0,0 @@ -

3. Export data from a room

-

- This code example demonstrates how to export rooms data from a - DocuSign room.. -

- -

- API methods used: - Rooms::GetRooms. -

- -

- View source file <%= @source_file %> on GitHub. -

\ No newline at end of file diff --git a/app/views/room_api/eg003_export_data_from_room/get.html.erb b/app/views/room_api/eg003_export_data_from_room/get.html.erb deleted file mode 100644 index d15d810..0000000 --- a/app/views/room_api/eg003_export_data_from_room/get.html.erb +++ /dev/null @@ -1,24 +0,0 @@ -<%=render 'room_api/eg003_export_data_from_room/information' %> - - -<% if @rooms.count.positive? %> - -
-
- - <%= select_tag "roomId", options_for_select(@rooms.map { |obj| [obj['name'], obj['roomId']] }), {:class => 'form-control'} %> -
- - -
- -<% else %> -

- Problem: you don't have any rooms. Please first create a room using - Create a new room - or - Create room with a template. -
- Thank you. -

-<% end %> diff --git a/app/views/room_api/eg004_add_forms_to_room/_information.html.erb b/app/views/room_api/eg004_add_forms_to_room/_information.html.erb deleted file mode 100644 index 07071b0..0000000 --- a/app/views/room_api/eg004_add_forms_to_room/_information.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -

4. Adding forms to a room

-

- This code example demonstrates how to attach forms to a room using the Rooms API. -

-

- API methods used: - Rooms::GetRooms and - FormLibraries::GetFormLibraryForms. -

-

- View source file <%= @source_file %> on GitHub. -

\ No newline at end of file diff --git a/app/views/room_api/eg005_get_rooms_with_filters/_information.html.erb b/app/views/room_api/eg005_get_rooms_with_filters/_information.html.erb deleted file mode 100644 index 70d65d2..0000000 --- a/app/views/room_api/eg005_get_rooms_with_filters/_information.html.erb +++ /dev/null @@ -1,14 +0,0 @@ -

5. Get Rooms with filters

-

- This code example demonstrates how to return rooms filtered by your parameters using the - Rooms API. - This specific code example filters for all rooms that have had their field data updated within the last 10 days, as shown on the Details tab in the UI. -

-

- API methods used: - Rooms::GetRooms and - Rooms::CreateRoom. -

-

- View source file <%= @source_file %> on GitHub. -

diff --git a/app/views/room_api/eg006_create_an_external_form_fill_session/_information.html.erb b/app/views/room_api/eg006_create_an_external_form_fill_session/_information.html.erb deleted file mode 100644 index 91096a7..0000000 --- a/app/views/room_api/eg006_create_an_external_form_fill_session/_information.html.erb +++ /dev/null @@ -1,17 +0,0 @@ -

6. Creating an external form fill session

-

- The DocuSign Rooms API offers developers the option to have users fill out forms without logging in to the Rooms UI through the use of an - external form fill session. - This code example demonstrates how to create an external form fill session using the - Rooms API. - The result of this code example is the URL for the form fill session, which you can embed in your integration or send to the user. -

-

- API methods used: - Rooms::GetRooms and - FormLibraries: GetFormLibraries and - FormLibraries::GetFormLibraryForms. -

-

- View source file <%= @source_file %> on GitHub. -

\ No newline at end of file diff --git a/app/views/room_api/eg006_create_an_external_form_fill_session/get_forms.html.erb b/app/views/room_api/eg006_create_an_external_form_fill_session/get_forms.html.erb deleted file mode 100644 index fe00bb8..0000000 --- a/app/views/room_api/eg006_create_an_external_form_fill_session/get_forms.html.erb +++ /dev/null @@ -1,26 +0,0 @@ -<%= render 'room_api/eg006_create_an_external_form_fill_session/information' %> - -<% unless @form_libraries.blank? %> - -
-
- - > - <%= select_tag "formId", options_for_select(@form_libraries.map { |obj| [obj['name'], obj['docuSignFormId']] }), {:class => 'form-control'} %> -
- -
- -<% else %> - -
-

- Problem: DocuSign Forms is not enabled for this company. Please contact - DocuSign Rooms Support - to enable Forms. -
- Thank you. -

-
- -<% end %> \ No newline at end of file diff --git a/app/views/room_api/eg006_create_an_external_form_fill_session/get_rooms.html.erb b/app/views/room_api/eg006_create_an_external_form_fill_session/get_rooms.html.erb deleted file mode 100644 index 42ad1f9..0000000 --- a/app/views/room_api/eg006_create_an_external_form_fill_session/get_rooms.html.erb +++ /dev/null @@ -1,26 +0,0 @@ -<%= render 'room_api/eg006_create_an_external_form_fill_session/information' %> - -<% if @rooms.count.positive? %> - -
-
- - <%= select_tag "roomId", options_for_select(@rooms.map { |obj| [obj['name'], obj['roomId']] }), {:class => 'form-control'} %> -
- -
- -<% else %> - -
-

- Problem: you don't have any rooms. Please first create a room using - Create a new room - or - Create room with a template. -
- Thank you. -

-
- -<% end %> \ No newline at end of file diff --git a/app/views/room_api/eg007_create_form_group/_information.html.erb b/app/views/room_api/eg007_create_form_group/_information.html.erb deleted file mode 100644 index 16c26cd..0000000 --- a/app/views/room_api/eg007_create_form_group/_information.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -

7. How to create a form group

-

- This example demonstrates how to create a form group - for your DocuSign Rooms for Real Estate account. -

-

API methods used: - FormGroups::CreateFormGroup -

-

- View source file <%= @source_file %> on GitHub. -

\ No newline at end of file diff --git a/app/views/room_api/eg007_create_form_group/get.html.erb b/app/views/room_api/eg007_create_form_group/get.html.erb deleted file mode 100644 index 17ab797..0000000 --- a/app/views/room_api/eg007_create_form_group/get.html.erb +++ /dev/null @@ -1,9 +0,0 @@ -<%=render 'room_api/eg007_create_form_group/information' %> - -
-
- - > -
- -
\ No newline at end of file diff --git a/app/views/room_api/eg008_grant_office_access_to_form_group/_information.html.erb b/app/views/room_api/eg008_grant_office_access_to_form_group/_information.html.erb deleted file mode 100644 index 07c5031..0000000 --- a/app/views/room_api/eg008_grant_office_access_to_form_group/_information.html.erb +++ /dev/null @@ -1,16 +0,0 @@ -

8. Grant office access to a form group

-

- This example demonstrates how to assign an office to a - form group - for your DocuSign Rooms for Real Estate account. Granting office access to a form group will enable you to filter - which form groups are available based on that office. -

-

- API methods used: - FormGroups:GetFormGroups - and - Offices:GetOffices -

-

- View source file <%= @source_file %> on GitHub. -

\ No newline at end of file diff --git a/app/views/room_api/eg008_grant_office_access_to_form_group/get.erb b/app/views/room_api/eg008_grant_office_access_to_form_group/get.erb deleted file mode 100644 index 6178c4f..0000000 --- a/app/views/room_api/eg008_grant_office_access_to_form_group/get.erb +++ /dev/null @@ -1,24 +0,0 @@ -<%= render 'room_api/eg008_grant_office_access_to_form_group/information' %> - -<% if @form_groups.present? %> -
-
- - <%= select_tag "form_group_id", options_for_select(@form_groups.map { |obj| [obj['name'], obj['formGroupId']] }), { :class => 'form-control' } %> -
-
- - <%= select_tag "office_id", options_for_select(@offices.map { |obj| [obj['name'], obj['officeId']] }), { :class => 'form-control' } %> -
- -
-<% else %> -
-

- Problem: you don't have any form groups. Please first create a them using - Create form group -
- Thank you. -

-
-<% end %> \ No newline at end of file diff --git a/app/views/room_api/eg009_assign_form_to_form_group/_information.html.erb b/app/views/room_api/eg009_assign_form_to_form_group/_information.html.erb deleted file mode 100644 index 933e52d..0000000 --- a/app/views/room_api/eg009_assign_form_to_form_group/_information.html.erb +++ /dev/null @@ -1,18 +0,0 @@ -

9. Assign a form to a form group

-

- This example demonstrates how to assign a form to a - form group - for your DocuSign Rooms for Real Estate account. As a prerequisite, ensure that you have - created a form group - and set the office ID on this form group - before proceeding. -

-

- API methods used: - FormGroups:GetFormGroups - and - FormLibraries::GetFormLibraryForms. -

-

- View source file <%= @source_file %> on GitHub. -

\ No newline at end of file diff --git a/app/views/room_api/eg009_assign_form_to_form_group/get.erb b/app/views/room_api/eg009_assign_form_to_form_group/get.erb deleted file mode 100644 index ab192db..0000000 --- a/app/views/room_api/eg009_assign_form_to_form_group/get.erb +++ /dev/null @@ -1,24 +0,0 @@ -<%= render 'room_api/eg009_assign_form_to_form_group/information' %> - -<% if @form_groups.present? %> -
-
- - <%= select_tag "form_group_id", options_for_select(@form_groups.map { |obj| [obj['name'], obj['formGroupId']] }), { :class => 'form-control' } %> -
-
- - <%= select_tag "form_id", options_for_select(@forms.map { |obj| [obj['name'], obj['libraryFormId']] }), { :class => 'form-control' } %> -
- -
-<% else %> -
-

- Problem: you don't have any form groups. Please first create a them using - Create form group -
- Thank you. -

-
-<% end %> \ No newline at end of file diff --git a/app/views/room_api/index.html.erb b/app/views/room_api/index.html.erb index a259b94..fe0840f 100644 --- a/app/views/room_api/index.html.erb +++ b/app/views/room_api/index.html.erb @@ -1,148 +1,60 @@ -<% if !session[:ds_user_name] %> - -
-
- - - - - - - -
-

Ruby Launcher

-

Welcome to the DocuSign Ruby examples using multiple OAuth flows (JWT and Authorization - Code Grant).

-
- -
-
+ +
+
+ + + + + + + +
+

Ruby Launcher

+

Welcome to the Docusign Ruby examples using multiple OAuth flows (JWT and Authorization + Code Grant).

+
+ +
-<% end %> +

Welcome

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

+ examples for the Docusign Rooms API.

<% if @show_doc %>

Documentation on using JWT or OAuth Authorization Code Grant from a Ruby Rails application.

<% end %> -

Basic Examples

- -

1. Create room with data

-

- This example creates a room. -

-

- API methods used: - Roles::GetRoles - and - Rooms::CreateRoom. -

- -

2. Create room with a template

-

- This code example demonstrates creating a DocuSign Room using a predefined template. If you've created a template - for your organization in either Rooms for Mortgage or Rooms for Real Estate, you can create rooms based on this - template using the Rooms API. -

-

- API methods used: - Roles::GetRoles - and - Rooms::CreateRoom - and - RoomTemplates::GetRoomTemplates. -

- -

3. Export data from a room

-

- This code example demonstrates how to export rooms data from a - DocuSign Room.. -

-

- API methods used: - Rooms::GetRooms. -

- -

4. Adding forms to a room

-

- This code example demonstrates how to attach forms to a room using the Rooms API. -

-

- API methods used: - Rooms::GetRooms - and - FormLibraries::GetFormLibraryForms. -

+ <% @manifest["Groups"].each { |group| %> +

<%= group["Name"] %>

-

5. Get Rooms with filters

-

- This code example demonstrates how to return rooms filtered by your parameters using the - Rooms API. - This specific code example filters for all rooms that have had their field data updated within the last 10 days, as - shown on the Details tab in the UI. -

-

- API methods used: - Rooms::GetRooms - and - Rooms::CreateRoom. -

+ <% group["Examples"].each { |example| %> + <% if not example["SkipForLanguages"] or not example["SkipForLanguages"].include? "ruby" %> +

"> + "> + <%= example["ExampleName"] %> + +

-

6. Creating an external form fill session

-

- The DocuSign Rooms API offers developers the option to have users fill out forms without logging in to the Rooms UI - through the use of an - external form fill session. - This code example demonstrates how to create an external form fill session using the - Rooms API: - the result of this code example is the URL for the form fill session, which you can embed in your integration or - send to the user. -

-

- API methods used: - Rooms::GetRooms - and - FormLibraries: - GetFormLibraries and - FormLibraries::GetFormLibraryForms. -

+

<%= sanitize example["ExampleDescription"] %>

-

7. Creating a form group

-

- Creates a new form group with the name given in the name property of the request body. -

-

- API methods used: - FormGroups::CreateFormGroup. -

+ <% if example["LinksToAPIMethod"].length > 1 %> + <%= sanitize @manifest["SupportingTexts"]["APIMethodUsedPlural"] %> + <% else %> + <%= sanitize @manifest["SupportingTexts"]["APIMethodUsed"] %> + <% end %> -

8. Grant office access to a form group

-

- This example demonstrates how to assign an office to a form group for your DocuSign Rooms for Real Estate account. - Granting office access to a form group will enable you to filter which form groups are available based on that office. -

-

- API methods used: - FormGroups:GetFormGroups - and - Offices:GetOffices -

+ <% example["LinksToAPIMethod"].each do |link| %> + "><%= link["PathName"] %> + <% end %> + <% end %> +

+ <% } %> + <% } %> -

9. Assign a form to a form group

-

- How to assign a form to a form group for your DocuSign Rooms for Real Estate account. -

-

- API methods used: - FormGroups:GetFormGroups - and - FormLibraries::GetFormLibraryForms. -

diff --git a/app/views/room_api/reg001_create_room_with_data/get.html.erb b/app/views/room_api/reg001_create_room_with_data/get.html.erb new file mode 100644 index 0000000..5b973e8 --- /dev/null +++ b/app/views/room_api/reg001_create_room_with_data/get.html.erb @@ -0,0 +1,17 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% room_name_index = 0 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + "> +
+ <%= render('partials/submit_button') %> +
\ No newline at end of file diff --git a/app/views/room_api/reg002_create_room_with_template/get.html.erb b/app/views/room_api/reg002_create_room_with_template/get.html.erb new file mode 100644 index 0000000..01dbdba --- /dev/null +++ b/app/views/room_api/reg002_create_room_with_template/get.html.erb @@ -0,0 +1,31 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% room_name_index = 0 %> +<% template_index = 1 %> +<% create_room_redirect_index = 0 %> + +<% unless @templates.blank? %> +<% %> +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + "> + + + <%= select_tag "templateId", options_for_select(@templates.map { |obj| [obj['name'], obj['templateId']] }), {:class => 'form-control'} %> +
+ <%= render('partials/submit_button') %> +
+ +<% else %> + + + <%= sanitize @example["RedirectsToOtherCodeExamples"][create_room_redirect_index]["RedirectText"] %> + +<% end %> diff --git a/app/views/room_api/reg003_export_data_from_room/get.html.erb b/app/views/room_api/reg003_export_data_from_room/get.html.erb new file mode 100644 index 0000000..e1ccf41 --- /dev/null +++ b/app/views/room_api/reg003_export_data_from_room/get.html.erb @@ -0,0 +1,24 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% room_id_index = 0 %> +<% redirect_to1_index = 0 %> + +<% if @rooms.count.positive? %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + <%= select_tag "roomId", options_for_select(@rooms.map { |obj| [obj['name'], obj['roomId']] }), {:class => 'form-control'} %> +
+ + <%= render('partials/submit_button') %> +
+ +<% else %> + <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to1_index]["RedirectText"], 'href="reg001"') %> +<% end %> diff --git a/app/views/room_api/eg004_add_forms_to_room/get.html.erb b/app/views/room_api/reg004_add_forms_to_room/get.html.erb similarity index 51% rename from app/views/room_api/eg004_add_forms_to_room/get.html.erb rename to app/views/room_api/reg004_add_forms_to_room/get.html.erb index f1f9982..fd40618 100644 --- a/app/views/room_api/eg004_add_forms_to_room/get.html.erb +++ b/app/views/room_api/reg004_add_forms_to_room/get.html.erb @@ -1,31 +1,33 @@ -<%=render 'room_api/eg004_add_forms_to_room/information' %> +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% room_id_index = 0 %> +<% form_id_index = 1 %> +<% redirect_to1_index = 0 %> <% unless @form_libraries.blank? %> <% if @form_libraries.count.positive? && @rooms.count.positive? %>
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> +
- + <%= select_tag "roomId", options_for_select(@rooms.map { |obj| [obj['name'], obj['roomId']] }), {:class => 'form-control'} %> - + <%= select_tag "formId", options_for_select(@form_libraries.map { |obj| [obj['name'], obj['libraryFormId']] }), {:class => 'form-control'} %>
- + <%= render('partials/submit_button') %>
<% elsif @rooms.count.zero? %>
-

- Problem: you don't have any rooms. Please first create a room using - Create a new room - or - Create room with a template. -
- Thank you. -

+ <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to1_index]["RedirectText"], 'href="reg001"') %>
<% end %> @@ -35,8 +37,8 @@

- Problem: DocuSign Forms is not enabled for this company. Please contact - DocuSign Rooms Support + Problem: Docusign Forms is not enabled for this company. Please contact + Docusign Rooms Support to enable Forms.
Thank you. diff --git a/app/views/room_api/eg005_get_rooms_with_filters/get.html.erb b/app/views/room_api/reg005_get_rooms_with_filters/get.html.erb similarity index 50% rename from app/views/room_api/eg005_get_rooms_with_filters/get.html.erb rename to app/views/room_api/reg005_get_rooms_with_filters/get.html.erb index ac2da11..db5ed54 100644 --- a/app/views/room_api/eg005_get_rooms_with_filters/get.html.erb +++ b/app/views/room_api/reg005_get_rooms_with_filters/get.html.erb @@ -1,16 +1,24 @@ -<%=render 'room_api/eg005_get_rooms_with_filters/information' %> +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% start_index = 0 %> +<% end_index = 1 %> <% if @rooms.count.positive? %>

+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> +
- + max=<%= Time.now %>>
- + max=<%= Time.now %>>
- + <%= render('partials/submit_button') %>
<% else %> @@ -18,9 +26,9 @@

Problem: you don't have any rooms. Please first create a room using - Create a new room + Create a new room or - Create room with a template. + Create room with a template.
Thank you.

diff --git a/app/views/room_api/reg006_create_an_external_form_fill_session/get_forms.html.erb b/app/views/room_api/reg006_create_an_external_form_fill_session/get_forms.html.erb new file mode 100644 index 0000000..6b83800 --- /dev/null +++ b/app/views/room_api/reg006_create_an_external_form_fill_session/get_forms.html.erb @@ -0,0 +1,28 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% form_id_index = 0 %> +<% redirect_to4_index = 0 %> + +<% unless @form_libraries.blank? %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + > + <%= select_tag "formId", options_for_select(@form_libraries.map { |obj| [obj['name'], obj['docuSignFormId']] }), {:class => 'form-control'} %> +
+ <%= render('partials/submit_button') %> +
+ +<% else %> + +
+ <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to4_index]["RedirectText"], 'href="reg004"') %> +
+ +<% end %> \ No newline at end of file diff --git a/app/views/room_api/reg006_create_an_external_form_fill_session/get_rooms.html.erb b/app/views/room_api/reg006_create_an_external_form_fill_session/get_rooms.html.erb new file mode 100644 index 0000000..0189098 --- /dev/null +++ b/app/views/room_api/reg006_create_an_external_form_fill_session/get_rooms.html.erb @@ -0,0 +1,27 @@ +<%= render('partials/example_info') %> + +<% form_index = 1 %> +<% room_index = 0 %> +<% redirect_to1_index = 1 %> + +<% if @rooms.count.positive? %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + <%= select_tag "roomId", options_for_select(@rooms.map { |obj| [obj['name'], obj['roomId']] }), {:class => 'form-control'} %> +
+ <%= render('partials/submit_button') %> +
+ +<% else %> + +
+ <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to1_index]["RedirectText"], 'href="reg001"') %> +
+ +<% end %> \ No newline at end of file diff --git a/app/views/room_api/reg006_create_an_external_form_fill_session/results.html.erb b/app/views/room_api/reg006_create_an_external_form_fill_session/results.html.erb new file mode 100644 index 0000000..3ed56ad --- /dev/null +++ b/app/views/room_api/reg006_create_an_external_form_fill_session/results.html.erb @@ -0,0 +1,17 @@ +

<%= @title %>

+<% if !@message.nil? %> +

<%= @message.html_safe %>

+<% end %> + +<%= @json %> + + + + +

Continue

diff --git a/app/views/room_api/reg007_create_form_group/get.html.erb b/app/views/room_api/reg007_create_form_group/get.html.erb new file mode 100644 index 0000000..7276b98 --- /dev/null +++ b/app/views/room_api/reg007_create_form_group/get.html.erb @@ -0,0 +1,18 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% form_group_index = 0 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " + value=<%="RubyFormGroup##{rand(100)}"%>> +
+ <%= render('partials/submit_button') %> +
\ No newline at end of file diff --git a/app/views/room_api/reg008_grant_office_access_to_form_group/get.erb b/app/views/room_api/reg008_grant_office_access_to_form_group/get.erb new file mode 100644 index 0000000..472da17 --- /dev/null +++ b/app/views/room_api/reg008_grant_office_access_to_form_group/get.erb @@ -0,0 +1,28 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% office_index = 0 %> +<% form_group_index = 1 %> +<% redirect_to7_index = 0 %> + +<% if @form_groups.present? %> +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + <%= select_tag "form_group_id", options_for_select(@form_groups.map { |obj| [obj['name'], obj['formGroupId']] }), { :class => 'form-control' } %> +
+
+ + <%= select_tag "office_id", options_for_select(@offices.map { |obj| [obj['name'], obj['officeId']] }), { :class => 'form-control' } %> +
+ <%= render('partials/submit_button') %> +
+<% else %> +
+ <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to7_index]["RedirectText"], 'href="reg007"') %> +
+<% end %> \ No newline at end of file diff --git a/app/views/room_api/reg009_assign_form_to_form_group/get.erb b/app/views/room_api/reg009_assign_form_to_form_group/get.erb new file mode 100644 index 0000000..b76c58b --- /dev/null +++ b/app/views/room_api/reg009_assign_form_to_form_group/get.erb @@ -0,0 +1,28 @@ +<%= render('partials/example_info') %> + +<% form_index = 0 %> +<% form_id_index = 0 %> +<% form_group_id_index = 1 %> +<% redirect_to7_index = 0 %> + +<% if @form_groups.present? %> +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + <%= select_tag "form_group_id", options_for_select(@form_groups.map { |obj| [obj['name'], obj['formGroupId']] }), { :class => 'form-control' } %> +
+
+ + <%= select_tag "form_id", options_for_select(@forms.map { |obj| [obj['name'], obj['libraryFormId']] }), { :class => 'form-control' } %> +
+ <%= render('partials/submit_button') %> +
+<% else %> +
+ <%= sanitize format_string(@example["RedirectsToOtherCodeExamples"][redirect_to7_index]["RedirectText"], 'href="reg007"') %> +
+<% end %> \ No newline at end of file diff --git a/app/views/room_api/return.html.erb b/app/views/room_api/return.html.erb index 5b0db4e..d884e11 100644 --- a/app/views/room_api/return.html.erb +++ b/app/views/room_api/return.html.erb @@ -2,7 +2,7 @@

Result

-<% if @scenarios_name == 'eg006_create_an_external_form_fill_session' %> +<% if @scenarios_name == 'reg006_create_an_external_form_fill_session' %> >link <% end %> diff --git a/app/views/webforms/weg001_create_instance/get.html.erb b/app/views/webforms/weg001_create_instance/get.html.erb new file mode 100644 index 0000000..a56f414 --- /dev/null +++ b/app/views/webforms/weg001_create_instance/get.html.erb @@ -0,0 +1,5 @@ +<%= render('partials/example_info') %> + +
+ <%= render('partials/submit_button') %> +
\ No newline at end of file diff --git a/app/views/webforms/weg001_create_instance/web_form_create.html.erb b/app/views/webforms/weg001_create_instance/web_form_create.html.erb new file mode 100644 index 0000000..6c5af78 --- /dev/null +++ b/app/views/webforms/weg001_create_instance/web_form_create.html.erb @@ -0,0 +1,6 @@ +

<%= @title %>

+

<%= sanitize @description %>

+ +
+ <%= render('partials/submit_button') %> +
\ No newline at end of file diff --git a/app/views/webforms/weg001_create_instance/web_form_embed.html.erb b/app/views/webforms/weg001_create_instance/web_form_embed.html.erb new file mode 100644 index 0000000..c42be1c --- /dev/null +++ b/app/views/webforms/weg001_create_instance/web_form_embed.html.erb @@ -0,0 +1,96 @@ + + + + + + + +
+
+

Embedded Webform Example

+
+

Web Form will render here

+
+
+
+ + + +

Continue

+ + + + + + + \ No newline at end of file diff --git a/app/views/webforms/weg002_create_remote_instance/get.html.erb b/app/views/webforms/weg002_create_remote_instance/get.html.erb new file mode 100644 index 0000000..fce42c5 --- /dev/null +++ b/app/views/webforms/weg002_create_remote_instance/get.html.erb @@ -0,0 +1,5 @@ +<%= render('partials/example_info') %> + +
+ <%= render('partials/submit_button') %> +
\ No newline at end of file diff --git a/app/views/webforms/weg002_create_remote_instance/web_form_create.html.erb b/app/views/webforms/weg002_create_remote_instance/web_form_create.html.erb new file mode 100644 index 0000000..7aa71ca --- /dev/null +++ b/app/views/webforms/weg002_create_remote_instance/web_form_create.html.erb @@ -0,0 +1,6 @@ +

<%= @title %>

+

<%= sanitize @description %>

+ +
+ <%= render('partials/submit_button') %> +
\ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..3aebf0b --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,135 @@ +# Starter pipeline +# Start with a minimal pipeline that you can customize to build and deploy your code. +# Add steps that build, run tests, deploy, and more: +# https://aka.ms/yaml +resources: + repositories: + - repository: launcher-automation + type: github + name: docusign/launcher-automation + ref: main + endpoint: launcherAutomationServiceConnection + +pr: +- master +- releases/* + +pool: + name: launcher-automation-pool + +steps: +- script: echo Hello, world! + displayName: 'Run a one-line script' +- checkout: self +- checkout: launcher-automation + +- script: dir $(Build.SourcesDirectory) + +- task: DownloadSecureFile@1 + name: ruby_dockerfile + displayName: 'download Dockerfile' + inputs: + secureFile: 'ruby.Dockerfile' + +- script: | + echo "place ruby.Dockerfile" + echo $(ruby_dockerfile.secureFilePath) + cp $(ruby_Dockerfile.secureFilePath) code-examples-ruby-private/Dockerfile + displayName: 'place Dockerfile' + +- script: | + allure –-version + java --version + javac --version + mvn --version + docker --version + +- task: DownloadSecureFile@1 + name: tests_config_properties + displayName: 'download config.properties' + inputs: + secureFile: 'tests_config.properties' + + +- script: | + echo "place config.properties" + echo $(tests_config_properties.secureFilePath) + cp $(tests_config_properties.secureFilePath) launcher-automation/src/main/resources/config.properties + displayName: 'place config.properties' + +- task: DownloadSecureFile@1 + name: ruby_appsettings + displayName: 'download appsettings.yml' + inputs: + secureFile: 'ruby.appsettings.yml' + + +- script: | + echo "place appsettings.yml" + cp $(ruby_appsettings.secureFilePath) code-examples-ruby-private/config/appsettings.yml + displayName: 'place appsettings.yml' + + +- task: DownloadSecureFile@1 + name: ruby_private_key + displayName: 'download private.key' + inputs: + secureFile: 'private.key' + + +- script: | + echo "place private.key" + cp $(ruby_private_key.secureFilePath) code-examples-ruby-private/config/docusign_private_key.txt + displayName: 'place docusign_private_key.txt' + +- script: dir $(Build.SourcesDirectory) + +- script: dir $(Build.SourcesDirectory)/code-examples-ruby-private + +- script: | + echo "Checking for running Docker containers..." + containers=$(docker ps -q) + if [ ! -z "$containers" ]; then + echo "Stopping running Docker containers..." + docker stop $(docker ps -q) + else + echo "No Docker containers are running." + fi + displayName: "check for running containers" + +- script: | + docker system prune -a --force + displayName: "cleanup docker files" + +- task: Docker@2 + displayName: Build ruby image + inputs: + command: build + repository: 'launcher-automation-ruby' + dockerfile: '$(Build.SourcesDirectory)/code-examples-ruby-private/Dockerfile' + buildContext: '$(Build.SourcesDirectory)/code-examples-ruby-private ' + tags: | + latest + +- script: | + docker run -p 3000:3000 -d launcher-automation-ruby:latest + displayName: 'start ruby app' + +- script: | + cd launcher-automation + mvn clean test -DsuiteXmlFile="ruby_suite.xml" + displayName: 'Ruby app tests' + +- script: | + docker stop $(docker ps -a -q) + docker rm $(docker ps -a -q) + displayName: 'stop ruby app' + +- script: | + allure generate --clean --output $(Build.SourcesDirectory)/ruby-allure-output '$(Build.SourcesDirectory)/launcher-automation/target/allure-results' + displayName: generate allure html reports + +- task: PublishAllureReport@1 + displayName: 'Publish Allure Report' + inputs: + reportDir: '$(Build.SourcesDirectory)/ruby-allure-output' diff --git a/bin/rails b/bin/rails index 3504c3f..7bcc36e 100755 --- a/bin/rails +++ b/bin/rails @@ -1,11 +1,4 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -begin - load File.expand_path('spring', __dir__) -rescue LoadError => e - raise unless e.message.include?('spring') -end -APP_PATH = File.expand_path('../config/application', __dir__) -require_relative '../config/boot' -require 'rails/commands' +#!/usr/bin/env ruby.exe +APP_PATH = File.expand_path("../config/application", __dir__) +require_relative "../config/boot" +require "rails/commands" diff --git a/bin/rake b/bin/rake index 1fe6cf0..01f7fc0 100755 --- a/bin/rake +++ b/bin/rake @@ -1,11 +1,4 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -begin - load File.expand_path('spring', __dir__) -rescue LoadError => e - raise unless e.message.include?('spring') -end -require_relative '../config/boot' -require 'rake' +#!/usr/bin/env ruby.exe +require_relative "../config/boot" +require "rake" Rake.application.run diff --git a/bin/setup b/bin/setup index c2e43ce..5e463ee 100755 --- a/bin/setup +++ b/bin/setup @@ -1,38 +1,33 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require 'fileutils' -include FileUtils +#!/usr/bin/env ruby.exe +require "fileutils" # path to your application root. -APP_ROOT = File.expand_path('..', __dir__) +APP_ROOT = File.expand_path("..", __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") end -chdir APP_ROOT do - # This script is a starting point to setup your application. +FileUtils.chdir APP_ROOT do + # This script is a way to set up or update your development environment automatically. + # This script is idempotent, so that you can run it at any time and get an expectable outcome. # Add necessary setup steps to this file. - puts '== Installing dependencies ==' - system! 'gem install bundler --conservative' - system('bundle check') || system!('bundle install') - - # Install JavaScript dependencies if using Yarn - # system('bin/yarn') + puts "== Installing dependencies ==" + system! "gem install bundler --conservative" + system("bundle check") || system!("bundle install") # puts "\n== Copying sample files ==" - # unless File.exist?('config/database.yml') - # cp 'config/database.yml.sample', 'config/database.yml' + # unless File.exist?("config/database.yml") + # FileUtils.cp "config/database.yml.sample", "config/database.yml" # end puts "\n== Preparing database ==" - system! 'bin/rails db:setup' + system! "bin/rails db:prepare" puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rails log:clear tmp:clear' + system! "bin/rails log:clear tmp:clear" puts "\n== Restarting application server ==" - system! 'bin/rails restart' + system! "bin/rails restart" end diff --git a/config/application.rb b/config/application.rb index 7592639..4930ccd 100644 --- a/config/application.rb +++ b/config/application.rb @@ -7,16 +7,16 @@ module CodeExamplesRuby class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 5.2 - # Configuration for DocuSign example. + # Configuration for Docusign example. # For a production application, you will store the credentials # in config/environments/development.rb, production.rb, test.rb, etc config.app_url = 'http://localhost:3000' # The public url of the application. - # Init DocuSign configuration, loaded from config/appsettings.yml file - DOCUSIGN_CONFIG = YAML.load_file("#{Rails.root.to_s}/config/appsettings.yml")[Rails.env] - DOCUSIGN_CONFIG.map do |k,v| - config.send("#{k}=", v) - end - + # Init Docusign configuration, loaded from config/appsettings.yml file + DOCUSIGN_CONFIG = YAML.load_file("#{Rails.root}/config/appsettings.yml")[Rails.env] + DOCUSIGN_CONFIG.map do |k, v| + config.send("#{k}=", v) + end + # Settings in config/environments/* take precedence over those specified here. # Application configuration can go into files in config/initializers # -- all .rb files in that directory are automatically loaded after loading diff --git a/config/appsettings.example.yml b/config/appsettings.example.yml index 22d5d39..f2a6d9b 100644 --- a/config/appsettings.example.yml +++ b/config/appsettings.example.yml @@ -4,11 +4,6 @@ default: &default app_url: http://localhost:3000 # quickstart is an optional setting and can be enabled by setting it to: true quickstart: {QUICKSTART_VALUE} - # Set only one examples_API type to 'true' and set the remaining to 'false'. - examples_API: - eSignature: true - Rooms: false - Click: false # NOTE: The terms "client_id" and "Integration key" are synonyms. They refer to the same thing # The integration_key value is the same between the development account and production account integration_key: {INTEGRATION_KEY_AUTH_CODE} @@ -26,19 +21,26 @@ default: &default authorization_server: https://account-d.docusign.com aud: account-d.docusign.com rooms_host: "https://demo.rooms.docusign.com/restapi" + monitor_host: "https://lens-d.docusign.net" + admin_host: "https://api-d.docusign.net/management" + webforms_host: "https://apps-d.docusign.com/api/webforms" allow_silent_authentication: 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 false, the users default account will be used. - target_account_id: false # Payment gateway information is optional. It is only needed for example 14. + # Set if you want a specific Docusign AccountId, If false, the users default account will be used. + target_account_id: false # Payment gateway information is optional. It is only needed for Send a request for Accept Payments. # See the PAYMENTS_INSTALLATION.md file for instructions gateway_account_id: {DS_PAYMENT_GATEWAY_ID} # The remainder of this file is already configured. demo_doc_path: demo_documents doc_docx: World_Wide_Corp_Battle_Plan_Trafalgar.docx doc_pdf: World_Wide_Corp_lorem.pdf doc_terms_pdf: Term_Of_Service.pdf + offer_letter_dynamic_table: Offer_Letter_Dynamic_Table.docx + web_form_template_file: World_Wide_Corp_Form.pdf + web_form_config_file: web-form-config.json gateway_name: "stripe" gateway_display_name: "Stripe" github_example_url: https://github.com/docusign/code-examples-ruby/tree/master/app/services/ + example_manifest_url: "https://raw.githubusercontent.com/docusign/code-examples-csharp/master/manifest/CodeExamplesManifest.json" documentation: false api_only: false diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index 4dc2cd1..31c74ef 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -15,7 +15,7 @@ # GET "/auth/docusign/callback?error=access_denied&error_message=The%20user%20did%20not%20consent%20to%20connecting%20the%20application.&state= # OmniAuth.config.failure_raise_out_environments = [] # defaults to: ['development'] -OmniAuth.config.allowed_request_methods = [:post, :get] +OmniAuth.config.allowed_request_methods = %i[post get] config = Rails.application.config config.middleware.use OmniAuth::Builder do @@ -23,6 +23,11 @@ # OAuth2 login response callback message configuration is in OmniAuth::Strategies::Docusign in lib/docusign.rb provider :docusign, config.integration_key, config.integration_secret, setup: lambda { |env| strategy = env['omniauth.strategy'] + + # params = strategy.request.params + # examples_API = params['examples_API'] + # strategy.request.params.delete('examples_API') + strategy.options[:client_options].site = config.app_url strategy.options[:prompt] = 'login' strategy.options[:oauth_base_uri] = config.authorization_server @@ -31,13 +36,28 @@ strategy.options[:client_options].authorize_url = "#{strategy.options[:oauth_base_uri]}/oauth/auth" strategy.options[:client_options].user_info_url = "#{strategy.options[:oauth_base_uri]}/oauth/userinfo" strategy.options[:client_options].token_url = "#{strategy.options[:oauth_base_uri]}/oauth/token" - unless strategy.options[:allow_silent_authentication] - strategy.options[:authorize_params].prompt = strategy.options.prompt + strategy.options[:authorize_params].prompt = strategy.options.prompt unless strategy.options[:allow_silent_authentication] + session = strategy.session + + unless session[:pkce_failed] + strategy.options[:pkce] = true end - if Rails.configuration.examples_API['Rooms'] == true - strategy.options[:authorize_params].scope = "signature dtr.rooms.read dtr.rooms.write dtr.documents.read dtr.documents.write dtr.profile.read dtr.profile.write dtr.company.read dtr.company.write room_forms" - elsif Rails.configuration.examples_API['Click'] == true - strategy.options[:authorize_params].scope = "signature click.manage click.send" + + case session[:api] + when 'eSignature' + strategy.options[:authorize_params].scope = 'signature' + when 'Rooms' + strategy.options[:authorize_params].scope = 'signature dtr.rooms.read dtr.rooms.write dtr.documents.read dtr.documents.write dtr.profile.read dtr.profile.write dtr.company.read dtr.company.write room_forms' + when 'Click' + strategy.options[:authorize_params].scope = 'signature click.manage click.send' + when 'Admin' + strategy.options[:authorize_params].scope = 'signature organization_read group_read permission_read user_read user_write account_read domain_read identity_provider_read user_data_redact asset_group_account_read asset_group_account_clone_write asset_group_account_clone_read organization_sub_account_write organization_sub_account_read' + when 'WebForms' + strategy.options[:authorize_params].scope = 'signature webforms_read webforms_instance_read webforms_instance_write' + when 'Notary' + strategy.options[:authorize_params].scope = 'signature organization_read notary_read notary_write' + when 'ConnectedFields' + strategy.options[:authorize_params].scope = 'signature adm_store_unified_repo_read' end } end diff --git a/config/routes.rb b/config/routes.rb index d87ea50..a2300ea 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,189 +1,301 @@ -# frozen_string_literal: true - -Rails.application.routes.draw do - if Rails.configuration.examples_API['Rooms'] == true - scope module: 'room_api' do - get 'eg001' => 'eg001_create_room_with_data#get' - post 'eg001' => 'eg001_create_room_with_data#create' - - get 'eg002' => 'eg002_create_room_with_template#get' - post 'eg002' => 'eg002_create_room_with_template#create' - - get 'eg003' => 'eg003_export_data_from_room#get' - post 'eg003' => 'eg003_export_data_from_room#create' - - get 'eg004' => 'eg004_add_forms_to_room#get' - post 'eg004' => 'eg004_add_forms_to_room#create' - - get 'eg005' => 'eg005_get_rooms_with_filters#get' - post 'eg005' => 'eg005_get_rooms_with_filters#create' - - get 'eg006' => 'eg006_create_an_external_form_fill_session#get_rooms' - get 'eg006_forms' => 'eg006_create_an_external_form_fill_session#get_forms' - post 'eg006' => 'eg006_create_an_external_form_fill_session#create' - - get 'eg007' => 'eg007_create_form_group#get' - post 'eg007' => 'eg007_create_form_group#create' - - get 'eg008' => 'eg008_grant_office_access_to_form_group#get' - post 'eg008' => 'eg008_grant_office_access_to_form_group#create' - - get 'eg009' => 'eg009_assign_form_to_form_group#get' - post 'eg009' => 'eg009_assign_form_to_form_group#create' - end - elsif Rails.configuration.examples_API['Click'] == true - scope module: 'clickwrap' do - get 'eg001' => 'eg001_create_clickwrap#get' - post 'eg001' => 'eg001_create_clickwrap#create' - - get 'eg002' => 'eg002_activate_clickwrap#get' - post 'eg002' => 'eg002_activate_clickwrap#create' - - get 'eg003' => 'eg003_create_new_clickwrap_version#get' - post 'eg003' => 'eg003_create_new_clickwrap_version#create' - - get 'eg004' => 'eg004_list_clickwraps#get' - post 'eg004' => 'eg004_list_clickwraps#create' - - get 'eg005' => 'eg005_clickwrap_responses#get' - post 'eg005' => 'eg005_clickwrap_responses#create' - end - else - scope module: 'e_sign' do - # Example controllers... - get 'eg001' => 'eg001_embedded_signing#get' - post 'eg001' => 'eg001_embedded_signing#create' - - get 'eg002' => 'eg002_signing_via_email#get' - post 'eg002' => 'eg002_signing_via_email#create' - - get 'eg003' => 'eg003_list_envelopes#get' - post 'eg003' => 'eg003_list_envelopes#create' - - get 'eg004' => 'eg004_envelope_info#get' - post 'eg004' => 'eg004_envelope_info#create' - - get 'eg005' => 'eg005_envelope_recipients#get' - post 'eg005' => 'eg005_envelope_recipients#create' - - get 'eg006' => 'eg006_envelope_docs#get' - post 'eg006' => 'eg006_envelope_docs#create' - - get 'eg007' => 'eg007_envelope_get_doc#get' - post 'eg007' => 'eg007_envelope_get_doc#create' - - get 'eg008' => 'eg008_create_template#get' - post 'eg008' => 'eg008_create_template#create' - - get 'eg009' => 'eg009_use_template#get' - post 'eg009' => 'eg009_use_template#create' - - get 'eg010' => 'eg010_send_binary_docs#get' - post 'eg010' => 'eg010_send_binary_docs#create' - - get 'eg011' => 'eg011_embedded_sending#get' - post 'eg011' => 'eg011_embedded_sending#create' - - get 'eg012' => 'eg012_embedded_console#get' - post 'eg012' => 'eg012_embedded_console#create' - - get 'eg013' => 'eg013_add_doc_to_template#get' - post 'eg013' => 'eg013_add_doc_to_template#create' - - get 'eg014' => 'eg014_collect_payment#get' - post 'eg014' => 'eg014_collect_payment#create' - - get 'eg015' => 'eg015_get_envelope_tab_data#get' - post 'eg015' => 'eg015_get_envelope_tab_data#create' - - get 'eg016' => 'eg016_set_envelope_tab_data#get' - post 'eg016' => 'eg016_set_envelope_tab_data#create' - - get 'eg017' => 'eg017_set_template_tab_values#get' - post 'eg017' => 'eg017_set_template_tab_values#create' - - get 'eg018' => 'eg018_get_envelope_custom_field_data#get' - post 'eg018' => 'eg018_get_envelope_custom_field_data#create' - - get 'eg019' => 'eg019_access_code_authentication#get' - post 'eg019' => 'eg019_access_code_authentication#create' - - get 'eg020' => 'eg020_sms_authentication#get' - post 'eg020' => 'eg020_sms_authentication#create' - - get 'eg021' => 'eg021_phone_authentication#get' - post 'eg021' => 'eg021_phone_authentication#create' - - get 'eg022' => 'eg022_kba_authentication#get' - post 'eg022' => 'eg022_kba_authentication#create' - - get 'eg023' => 'eg023_idv_authentication#get' - post 'eg023' => 'eg023_idv_authentication#create' - - get 'eg024' => 'eg024_permission_create#get' - post 'eg024' => 'eg024_permission_create#create' - - get 'eg025' => 'eg025_permissions_set_user_group#get' - post 'eg025' => 'eg025_permissions_set_user_group#create' - - get 'eg026' => 'eg026_permissions_change_single_setting#get' - post 'eg026' => 'eg026_permissions_change_single_setting#create' - - get 'eg027' => 'eg027_permissions_delete#get' - post 'eg027' => 'eg027_permissions_delete#create' - - get 'eg028' => 'eg028_brands_creating#get' - post 'eg028' => 'eg028_brands_creating#create' - - get 'eg029' => 'eg029_brands_apply_to_envelope#get' - post 'eg029' => 'eg029_brands_apply_to_envelope#create' - - get 'eg030' => 'eg030_brands_apply_to_template#get' - post 'eg030' => 'eg030_brands_apply_to_template#create' - - get 'eg031' => 'eg031_bulk_sending_envelopes#get' - post 'eg031' => 'eg031_bulk_sending_envelopes#create' - - get 'eg032' => 'eg032_pauses_signature_workflow#get' - post 'eg032' => 'eg032_pauses_signature_workflow#create' - - get 'eg033' => 'eg033_unpauses_signature_workflow#get' - put 'eg033' => 'eg033_unpauses_signature_workflow#update' - - get 'eg034' => 'eg034_use_conditional_recipients#get' - post 'eg034' => 'eg034_use_conditional_recipients#create' - - get 'eg035' => 'eg035_sms_delivery#get' - post 'eg035' => 'eg035_sms_delivery#create' - end - end - - root 'ds_common#index' - - # Login starts with POST'ing to: /auth/docusign - # /auth/docusign is an internal route created by OmniAuth and the docusign strategy from: /lib/docusign.rb - # Should be POST, see: https://nvd.nist.gov/vuln/detail/CVE-2015-9284 - # get '/ds/login' => redirect('/auth/docusign') - - # Handle OmniAuth OAuth2 login callback result that includes the AuthHash - get '/auth/:provider/callback', to: 'session#create' - - # Handle OmniAuth OAuth2 login exceptions in non development environments: - get '/auth/failure', to: 'session#omniauth_failure' - - # Logout - get '/ds/logout', to: 'session#destroy' - - get '/ds_common-return' => 'ds_common#ds_return' - get '/ds/mustAuthenticate' => 'ds_common#ds_must_authenticate' - post '/ds/mustAuthenticate' => 'ds_common#ds_must_authenticate' - - get '/ds/session' => 'session#show' - # default root - - get 'ds_common/index' - get 'example_done' => 'ds_common#example_done' - get 'error' => 'ds_common#error' - - # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html -end +# frozen_string_literal: true + +Rails.application.routes.draw do + scope module: 'room_api' do + get 'reg001' => 'reg001_create_room_with_data#get' + post 'reg001' => 'reg001_create_room_with_data#create' + + get 'reg002' => 'reg002_create_room_with_template#get' + post 'reg002' => 'reg002_create_room_with_template#create' + + get 'reg003' => 'reg003_export_data_from_room#get' + post 'reg003' => 'reg003_export_data_from_room#create' + + get 'reg004' => 'reg004_add_forms_to_room#get' + post 'reg004' => 'reg004_add_forms_to_room#create' + + get 'reg005' => 'reg005_get_rooms_with_filters#get' + post 'reg005' => 'reg005_get_rooms_with_filters#create' + + get 'reg006' => 'reg006_create_an_external_form_fill_session#get_rooms' + get 'reg006_forms' => 'reg006_create_an_external_form_fill_session#get_forms' + post 'reg006' => 'reg006_create_an_external_form_fill_session#create' + + get 'reg007' => 'reg007_create_form_group#get' + post 'reg007' => 'reg007_create_form_group#create' + + get 'reg008' => 'reg008_grant_office_access_to_form_group#get' + post 'reg008' => 'reg008_grant_office_access_to_form_group#create' + + get 'reg009' => 'reg009_assign_form_to_form_group#get' + post 'reg009' => 'reg009_assign_form_to_form_group#create' + end + + scope module: 'clickwrap' do + get 'ceg001' => 'ceg001_create_clickwrap#get' + post 'ceg001' => 'ceg001_create_clickwrap#create' + + get 'ceg002' => 'ceg002_activate_clickwrap#get' + post 'ceg002' => 'ceg002_activate_clickwrap#create' + + get 'ceg003' => 'ceg003_create_new_clickwrap_version#get' + post 'ceg003' => 'ceg003_create_new_clickwrap_version#create' + + get 'ceg004' => 'ceg004_list_clickwraps#get' + post 'ceg004' => 'ceg004_list_clickwraps#create' + + get 'ceg005' => 'ceg005_clickwrap_responses#get' + post 'ceg005' => 'ceg005_clickwrap_responses#create' + + get 'ceg006' => 'ceg006_embed_clickwrap#get' + post 'ceg006' => 'ceg006_embed_clickwrap#create' + end + + scope module: 'monitor_api' do + get 'meg001' => 'meg001_get_monitoring_dataset#get' + post 'meg001' => 'meg001_get_monitoring_dataset#create' + end + + scope module: 'admin_api' do + get 'aeg001' => 'aeg001_create_user#get' + post 'aeg001' => 'aeg001_create_user#create' + + get 'aeg002' => 'aeg002_create_active_clm_esign_user#get' + post 'aeg002' => 'aeg002_create_active_clm_esign_user#create' + + get 'aeg003' => 'aeg003_bulk_export_user_data#get' + post 'aeg003' => 'aeg003_bulk_export_user_data#create' + + get 'aeg004' => 'aeg004_import_user#get' + post 'aeg004' => 'aeg004_import_user#create' + get 'aeg004status' => 'aeg004_import_user#check_status' + + get 'aeg005' => 'aeg005_audit_users#get' + post 'aeg005' => 'aeg005_audit_users#create' + + get 'aeg006' => 'aeg006_get_user_profile_by_email#get' + post 'aeg006' => 'aeg006_get_user_profile_by_email#create' + + get 'aeg007' => 'aeg007_get_user_profile_by_user_id#get' + post 'aeg007' => 'aeg007_get_user_profile_by_user_id#create' + + get 'aeg008' => 'aeg008_update_user_product_permission_profile#get' + post 'aeg008' => 'aeg008_update_user_product_permission_profile#create' + + get 'aeg009' => 'aeg009_delete_user_product_permission_profile#get' + post 'aeg009' => 'aeg009_delete_user_product_permission_profile#create' + + get 'aeg010' => 'aeg010_delete_user_data_from_organization#get' + post 'aeg010' => 'aeg010_delete_user_data_from_organization#create' + + get 'aeg011' => 'aeg011_delete_user_data_from_account#get' + post 'aeg011' => 'aeg011_delete_user_data_from_account#create' + + get 'aeg012' => 'aeg012_clone_account#get' + post 'aeg012' => 'aeg012_clone_account#create' + + get 'aeg013' => 'aeg013_create_account#get' + post 'aeg013' => 'aeg013_create_account#create' + end + + get '/eeg001' => 'eeg001_embedded_signing#get' + post '/eeg001' => 'eeg001_embedded_signing#create' + + scope module: 'e_sign' do + # Example controllers... + get 'eeg002' => 'eeg002_signing_via_email#get' + post 'eeg002' => 'eeg002_signing_via_email#create' + + get 'eeg003' => 'eeg003_list_envelopes#get' + post 'eeg003' => 'eeg003_list_envelopes#create' + + get 'eeg004' => 'eeg004_envelope_info#get' + post 'eeg004' => 'eeg004_envelope_info#create' + + get 'eeg005' => 'eeg005_envelope_recipients#get' + post 'eeg005' => 'eeg005_envelope_recipients#create' + + get 'eeg006' => 'eeg006_envelope_docs#get' + post 'eeg006' => 'eeg006_envelope_docs#create' + + get 'eeg007' => 'eeg007_envelope_get_doc#get' + post 'eeg007' => 'eeg007_envelope_get_doc#create' + + get 'eeg008' => 'eeg008_create_template#get' + post 'eeg008' => 'eeg008_create_template#create' + + get 'eeg009' => 'eeg009_use_template#get' + post 'eeg009' => 'eeg009_use_template#create' + + get 'eeg010' => 'eeg010_send_binary_docs#get' + post 'eeg010' => 'eeg010_send_binary_docs#create' + + get 'eeg011' => 'eeg011_embedded_sending#get' + post 'eeg011' => 'eeg011_embedded_sending#create' + + get 'eeg012' => 'eeg012_embedded_console#get' + post 'eeg012' => 'eeg012_embedded_console#create' + + get 'eeg013' => 'eeg013_add_doc_to_template#get' + post 'eeg013' => 'eeg013_add_doc_to_template#create' + + get 'eeg014' => 'eeg014_collect_payment#get' + post 'eeg014' => 'eeg014_collect_payment#create' + + get 'eeg015' => 'eeg015_get_envelope_tab_data#get' + post 'eeg015' => 'eeg015_get_envelope_tab_data#create' + + get 'eeg016' => 'eeg016_set_envelope_tab_data#get' + post 'eeg016' => 'eeg016_set_envelope_tab_data#create' + + get 'eeg017' => 'eeg017_set_template_tab_values#get' + post 'eeg017' => 'eeg017_set_template_tab_values#create' + + get 'eeg018' => 'eeg018_get_envelope_custom_field_data#get' + post 'eeg018' => 'eeg018_get_envelope_custom_field_data#create' + + get 'eeg019' => 'eeg019_access_code_authentication#get' + post 'eeg019' => 'eeg019_access_code_authentication#create' + + get 'eeg020' => 'eeg020_phone_authentication#get' + post 'eeg020' => 'eeg020_phone_authentication#create' + + get 'eeg022' => 'eeg022_kba_authentication#get' + post 'eeg022' => 'eeg022_kba_authentication#create' + + get 'eeg023' => 'eeg023_idv_authentication#get' + post 'eeg023' => 'eeg023_idv_authentication#create' + + get 'eeg024' => 'eeg024_permission_create#get' + post 'eeg024' => 'eeg024_permission_create#create' + + get 'eeg025' => 'eeg025_permissions_set_user_group#get' + post 'eeg025' => 'eeg025_permissions_set_user_group#create' + + get 'eeg026' => 'eeg026_permissions_change_single_setting#get' + post 'eeg026' => 'eeg026_permissions_change_single_setting#create' + + get 'eeg027' => 'eeg027_permissions_delete#get' + post 'eeg027' => 'eeg027_permissions_delete#create' + + get 'eeg028' => 'eeg028_brands_creating#get' + post 'eeg028' => 'eeg028_brands_creating#create' + + get 'eeg029' => 'eeg029_brands_apply_to_envelope#get' + post 'eeg029' => 'eeg029_brands_apply_to_envelope#create' + + get 'eeg030' => 'eeg030_brands_apply_to_template#get' + post 'eeg030' => 'eeg030_brands_apply_to_template#create' + + get 'eeg031' => 'eeg031_bulk_sending_envelopes#get' + post 'eeg031' => 'eeg031_bulk_sending_envelopes#create' + + get 'eeg032' => 'eeg032_pauses_signature_workflow#get' + post 'eeg032' => 'eeg032_pauses_signature_workflow#create' + + get 'eeg033' => 'eeg033_unpauses_signature_workflow#get' + put 'eeg033' => 'eeg033_unpauses_signature_workflow#update' + + get 'eeg034' => 'eeg034_use_conditional_recipients#get' + post 'eeg034' => 'eeg034_use_conditional_recipients#create' + + get 'eeg035' => 'eeg035_scheduled_sending#get' + post 'eeg035' => 'eeg035_scheduled_sending#create' + + get 'eeg036' => 'eeg036_delayed_routing#get' + post 'eeg036' => 'eeg036_delayed_routing#create' + + get 'eeg037' => 'eeg037_sms_delivery#get' + post 'eeg037' => 'eeg037_sms_delivery#create' + + get 'eeg038' => 'eeg038_responsive_signing#get' + post 'eeg038' => 'eeg038_responsive_signing#create' + + get 'eeg039' => 'eeg039_signing_in_person#get' + post 'eeg039' => 'eeg039_signing_in_person#create' + + get 'eeg040' => 'eeg040_set_document_visibility#get' + post 'eeg040' => 'eeg040_set_document_visibility#create' + + get 'eeg041' => 'eeg041_cfr_embedded_signing#get' + post 'eeg041' => 'eeg041_cfr_embedded_signing#create' + + get 'eeg042' => 'eeg042_document_generation#get' + post 'eeg042' => 'eeg042_document_generation#create' + + post 'eeg043' => 'eeg043_shared_access#create_agent' + get 'eeg043' => 'eeg043_shared_access#get' + get 'eeg043auth' => 'eeg043_shared_access#create_authorization' + get 'eeg043reauthenticate' => 'eeg043_shared_access#reauthenticate' + get 'eeg043envelopes' => 'eeg043_shared_access#list_envelopes' + + get 'eeg044' => 'eeg044_focused_view#get' + post 'eeg044' => 'eeg044_focused_view#create' + + get 'eeg045' => 'eeg045_delete_restore_envelope#get_delete_envelope' + post 'eeg045' => 'eeg045_delete_restore_envelope#delete_envelope' + get 'eeg045restore' => 'eeg045_delete_restore_envelope#get_restore_envelope' + post 'eeg045restore' => 'eeg045_delete_restore_envelope#restore_envelope' + + get 'eeg046' => 'eeg046_multiple_delivery#get' + post 'eeg046' => 'eeg046_multiple_delivery#create' + end + + scope module: 'connect' do + get 'cneg001' => 'cneg001_validate_webhook_message#get' + post 'cneg001' => 'cneg001_validate_webhook_message#create' + end + + scope module: 'webforms' do + get 'weg001' => 'weg001_create_instance#get' + get 'weg001webForm' => 'weg001_create_instance#get_web_form_create_view' + post 'weg001' => 'weg001_create_instance#create_web_form_template' + post 'weg001webForm' => 'weg001_create_instance#create_web_form_instance' + + get 'weg002' => 'weg002_create_remote_instance#get' + get 'weg002webForm' => 'weg002_create_remote_instance#get_web_form_create_view' + post 'weg002' => 'weg002_create_remote_instance#create_web_form_template' + post 'weg002webForm' => 'weg002_create_remote_instance#create_web_form_instance' + end + + scope module: 'notary' do + get 'neg004' => 'neg004_send_with_third_party_notary#get' + post 'neg004' => 'neg004_send_with_third_party_notary#create' + end + + scope module: 'connected_fields' do + get 'feg001' => 'feg001_set_connected_fields#get' + post 'feg001' => 'feg001_set_connected_fields#create' + end + + root 'ds_common#index' + + # Login starts with POST'ing to: /auth/docusign + # /auth/docusign is an internal route created by OmniAuth and the docusign strategy from: /lib/docusign.rb + # Should be POST, see: https://nvd.nist.gov/vuln/detail/CVE-2015-9284 + # get '/ds/login' => redirect('/auth/docusign') + + # Handle OmniAuth OAuth2 login callback result that includes the AuthHash + get '/auth/:provider/callback', to: 'session#create' + + # Handle OmniAuth OAuth2 login exceptions in non development environments: + get '/auth/failure', to: 'session#omniauth_failure' + + # Logout + get '/ds/logout', to: 'session#destroy' + + get '/ds_common-return' => 'ds_common#ds_return' + get '/ds/mustAuthenticate' => 'ds_common#ds_must_authenticate' + post '/ds/mustAuthenticate' => 'ds_common#ds_must_authenticate' + get '/ds/mustAuthenticateJwt' => 'ds_common#ds_must_authenticate_jwt' + post '/ds/mustAuthenticateJwt' => 'ds_common#ds_must_authenticate' + + get '/ds/session' => 'session#show' + # default root + + get 'ds_common/index' + get 'example_done' => 'ds_common#example_done' + get 'error' => 'ds_common#error' + + # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html +end diff --git a/connect_test.rb b/connect_test.rb new file mode 100644 index 0000000..267a44c --- /dev/null +++ b/connect_test.rb @@ -0,0 +1,19 @@ +def ComputeHash(secret, payload) + require 'openssl' + require 'base64' + + digest = OpenSSL::Digest.new('sha256') + hashBytes = OpenSSL::HMAC.digest(digest, secret, payload) + base64Hash = Base64.encode64(hashBytes) + return base64Hash; +end + +def HashIsValid(secret, payload, signature) + ver = ComputeHash(secret, payload) + OpenSSL.secure_compare(ver.chomp, signature) +end + +secret = 'xxoxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxGE=' # Replace this value with your own secret +signature = 'c60xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxew=' # Replace this value with your own signature +payload = File.read('payload.txt') # Read the payload from a file named payload.txt +puts HashIsValid(secret, payload, signature) # should return true for valid \ No newline at end of file diff --git a/data/Offer_Letter_Demo.docx b/data/Offer_Letter_Demo.docx new file mode 100644 index 0000000..a8d6d59 Binary files /dev/null and b/data/Offer_Letter_Demo.docx differ diff --git a/data/Offer_Letter_Dynamic_Table.docx b/data/Offer_Letter_Dynamic_Table.docx new file mode 100644 index 0000000..8c109be Binary files /dev/null and b/data/Offer_Letter_Dynamic_Table.docx differ diff --git a/data/World_Wide_Corp_Web_Form.pdf b/data/World_Wide_Corp_Web_Form.pdf new file mode 100644 index 0000000..d6b9527 Binary files /dev/null and b/data/World_Wide_Corp_Web_Form.pdf differ diff --git a/data/order_form.html b/data/order_form.html index cddf370..15b87dd 100644 --- a/data/order_form.html +++ b/data/order_form.html @@ -29,7 +29,7 @@

Ordered by {signerName}

Harmonica - /l1q/ + /l1q/ $5 @@ -39,7 +39,7 @@

Ordered by {signerName}

Xylophone - /l2q/ + /l2q/ $150 diff --git a/data/userData.csv b/data/userData.csv new file mode 100644 index 0000000..125030a --- /dev/null +++ b/data/userData.csv @@ -0,0 +1,3 @@ +AccountID,FirstName,LastName,UserEmail,PermissionSet +{account_id},First1,Last1,example1@sampleemail.example,DS Admin +{account_id},First2,Last2,example2@sampleemail.example,DS Admin \ No newline at end of file diff --git a/data/web-form-config.json b/data/web-form-config.json new file mode 100644 index 0000000..921bc76 --- /dev/null +++ b/data/web-form-config.json @@ -0,0 +1 @@ +{ "id": "608a6c8a-16b2-4419-92f4-d06bc3af6e53", "accountId": "85443307-xxxx-xxxx-xxxx-87671e153075", "isPublished": true, "isEnabled": true, "hasDraftChanges": false, "formState": "active", "formProperties": { "name": "Web Form Example Template", "isPrivateAccess": false, "allowSending": true }, "formMetadata": { "source": "templates", "createdDateTime": "2025-08-19T21:15:03.323Z", "publishedSlug": "d12c1e15b310d55b4e530edfd64c1b63", "owner": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "lastModifiedDateTime": "2025-08-19T21:18:40.409Z", "lastModifiedBy": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "publishedComponentNames": { "signer_name": "TextBox", "signer_email": "Email", "FullName": "TextBox", "PhoneNumber": "TextBox", "Yes": "CheckboxGroup", "Company": "TextBox", "JobTitle": "TextBox", "$recipients": { "41f3109b-ff3a-4c48-a40c-eac994ad0988": { "components": { "signer_name": { "type": "TextBox" }, "signer_email": { "type": "Email" }, "FullName": { "type": "TextBox" }, "PhoneNumber": { "type": "TextBox" }, "Yes": { "type": "CheckboxGroup" }, "Company": { "type": "TextBox" }, "JobTitle": { "type": "TextBox" } } } } }, "admModelNamespace": "docusign.forms._85443307_664c_4c85_882f_87671e153075._608a6c8a_16b2_4419_92f4_d06bc3af6e53", "formContentModifiedBy": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "formContentModifiedDateTime": "2025-08-19T21:18:09.337Z", "admModelVersion": "1.0.0", "type": "hasEsignTemplate", "formPropertiesModifiedBy": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "formPropertiesModifiedDateTime": "2025-08-19T21:15:39.578Z", "sender": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "lastSenderConsentDateTime": "2025-08-19T21:18:34.468Z", "lastPublishedBy": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "lastPublishedDateTime": "2025-08-19T21:18:40.409Z", "lastEnabledBy": { "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "userName": "Raileen Del Rosario" }, "lastEnabledDateTime": "2025-08-19T21:18:40.409Z" }, "formContent": { "components": { "Root_Of_Journey": { "children": [ "View_41f3109b-ff3a-4c48-a40c-eac994ad0988" ], "componentKey": "Root_Of_Journey", "componentName": "Root_Of_Journey", "componentRules": {}, "componentType": "Root", "schemaVersion": 2, "text": "" }, "View_41f3109b-ff3a-4c48-a40c-eac994ad0988": { "children": [ "Welcome_qXWJJEvM", "Step_69uweXrH", "Summary_2KLFQRxQ", "ESignAction_7XmnXEcI", "Thankyou_feHjbCWS" ], "componentKey": "View_41f3109b-ff3a-4c48-a40c-eac994ad0988", "componentType": "View", "isEnabled": true, "isSender": false, "roleName": "signer" }, "Welcome_qXWJJEvM": { "startButtonText": "Start", "subText": "Part-Time Work Application", "text": "Welcome", "componentKey": "Welcome_qXWJJEvM", "componentType": "Welcome" }, "Step_69uweXrH": { "children": [ "TextBox_OWBHFHgL", "Email_cJoe6lNy", "TextBox_FPT2Gs29", "TextBox_BOTyRGju", "CheckboxGroup_MyyHfWle", "TextBox_mwg3Cl1O", "TextBox_AHodbvx_" ], "componentKey": "Step_69uweXrH", "componentName": "Step_69uweXrH", "componentType": "Step", "text": "Part-Time Work Application" }, "Summary_2KLFQRxQ": { "subText": "Please review the information you have entered:", "text": "Summary", "componentKey": "Summary_2KLFQRxQ", "componentType": "Summary" }, "ESignAction_7XmnXEcI": { "componentKey": "ESignAction_7XmnXEcI", "componentType": "ESignAction", "documentInfoMap": { "Document_fRTvNpa8": { "documentId": "1", "name": "World_Wide_Web_Form", "order": "1" } }, "enableDocumentFieldEditing": true, "primaryRecipientId": "1", "recipientInfoMap": { "1": { "emailComponentKey": "Email_cJoe6lNy", "emailFromTemplate": "", "nameComponentKey": "TextBox_OWBHFHgL", "nameFromTemplate": "", "recipientId": "1", "recipientType": "signer", "roleName": "signer", "routingOrder": "1", "recipientViewId": "41f3109b-ff3a-4c48-a40c-eac994ad0988" } }, "requireRemoteSigning": false, "tabInfoMap": { "c995df10-9141-4686-b9bf-f1dfc4121d33": { "componentKey": "TextBox_FPT2Gs29", "locked": "false", "recipientId": "1", "tabId": "c995df10-9141-4686-b9bf-f1dfc4121d33", "tabLabel": "FullName", "tabType": "text" }, "71ccfefa-5469-493e-b0c7-886c65b84742": { "componentKey": "TextBox_BOTyRGju", "locked": "false", "recipientId": "1", "tabId": "71ccfefa-5469-493e-b0c7-886c65b84742", "tabLabel": "PhoneNumber", "tabType": "text" }, "184f8a65-f2be-48b9-a30e-15592c59b335": { "componentKey": "TextBox_mwg3Cl1O", "locked": "false", "recipientId": "1", "tabId": "184f8a65-f2be-48b9-a30e-15592c59b335", "tabLabel": "Company", "tabType": "text" }, "e7d30733-ce70-4fee-93a0-ea10ecc014b1": { "componentKey": "TextBox_AHodbvx_", "locked": "false", "recipientId": "1", "tabId": "e7d30733-ce70-4fee-93a0-ea10ecc014b1", "tabLabel": "JobTitle", "tabType": "text" }, "0f1e242d-550d-4729-a141-de0dce1d1b6c": { "componentKey": "CheckboxGroup_MyyHfWle", "locked": "false", "name": "Yes", "recipientId": "1", "selected": "false", "tabId": "0f1e242d-550d-4729-a141-de0dce1d1b6c", "tabLabel": "Yes", "tabType": "checkbox" } }, "templateInfoMap": { "2230c545-1680-47aa-8581-7d2284093f46": { "lastModified": "2025-08-19T21:15:03.9670000Z", "name": "Web Form Copy - Web Form Example Template", "owner": { "userName": "Raileen Del Rosario", "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "email": "example@email.com" }, "templateId": "template-id" } } }, "Thankyou_feHjbCWS": { "confirmationButtonText": "Done", "confirmationButtonUrl": "", "showConfirmationButton": false, "subText": "We've received your form.", "text": "Thank you", "componentKey": "Thankyou_feHjbCWS", "componentType": "Thankyou" }, "TextBox_OWBHFHgL": { "componentKey": "TextBox_OWBHFHgL", "componentName": "signer_name", "componentType": "TextBox", "description": "", "label": "Signer Name", "maxLength": 4000, "multiLine": false, "placeholder": "", "required": true }, "Email_cJoe6lNy": { "componentKey": "Email_cJoe6lNy", "componentName": "signer_email", "componentType": "Email", "description": "", "label": "Signer Email", "maxLength": 4000, "multiLine": false, "placeholder": "", "required": true }, "TextBox_FPT2Gs29": { "componentKey": "TextBox_FPT2Gs29", "componentName": "FullName", "componentType": "TextBox", "description": "", "label": "Full Name", "maxLength": 4000, "multiLine": false, "placeholder": "", "required": true }, "TextBox_BOTyRGju": { "componentKey": "TextBox_BOTyRGju", "componentName": "PhoneNumber", "componentType": "TextBox", "description": "", "label": "Phone Number", "maxLength": 4000, "multiLine": false, "placeholder": "", "required": true }, "CheckboxGroup_MyyHfWle": { "componentKey": "CheckboxGroup_MyyHfWle", "componentName": "Yes", "componentType": "CheckboxGroup", "description": "", "label": "I prefer to be contacted by text.", "options": [ { "label": "Yes", "optionKey": "i_BVfm2G", "selected": false, "value": "Yes" } ] }, "TextBox_mwg3Cl1O": { "componentKey": "TextBox_mwg3Cl1O", "componentName": "Company", "componentType": "TextBox", "description": "", "label": "Company", "maxLength": 4000, "multiLine": false, "placeholder": "", "required": true }, "TextBox_AHodbvx_": { "componentKey": "TextBox_AHodbvx_", "componentName": "JobTitle", "componentType": "TextBox", "description": "", "label": "Job Title", "maxLength": 4000, "multiLine": false, "placeholder": "", "required": true } }, "isStandalone": false, "templates": [ { "originalTemplateId": "template-id", "clonedTemplateId": "template-id", "importedDateTime": "2025-08-19T21:15:05.277Z", "recipientIds": [ "1" ] } ] }, "formLocale": "en", "versionId": 1, "eSignTemplates": [ { "templateId": "template-id", "uri": "/templates/template-id", "name": "Web Form Copy - Web Form Example Template", "shared": "false", "passwordProtected": "false", "description": "Example template created via the API", "created": "2025-08-19T21:15:03.7170000Z", "lastModified": "2025-08-19T21:18:40.2070000Z", "lastModifiedBy": { "userName": "Raileen Del Rosario", "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "email": "example@email.com", "uri": "/users/53d98270-xxxx-xxxx-xxxx-6d080391b538" }, "lastUsed": "2025-08-19T21:15:03.9830000Z", "owner": { "userName": "Raileen Del Rosario", "userId": "53d98270-xxxx-xxxx-xxxx-6d080391b538", "email": "example@email.com" }, "pageCount": "1", "folderId": "cf5522c4-cac6-4f81-9a57-d5fe179cae1c", "folderName": "Deleted Items", "folderIds": [ "cf5522c4-cac6-4f81-9a57-d5fe179cae1c" ], "autoMatch": "true", "autoMatchSpecifiedByUser": "false", "documents": [ { "documentId": "1", "uri": "/envelopes/2230c545-1680-47aa-8581-7d2284093f46/documents/1", "name": "World_Wide_Web_Form", "order": "1", "pages": "1", "documentBase64": "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MDc6MDA8L3htcDpDcmVhdGVEYXRlPg0KICAgICAgPHhtcDpNb2RpZnlEYXRlPjIwMjUtMDgtMTlUMTQ6MTg6NDAtMDc6MDA8L3htcDpNb2RpZnlEYXRlPg0KICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAyNS0wOC0xOVQxNDoxODo0MC0wNzowMDwveG1wOk1ldGFkYXRhRGF0ZT4NCiAgICA8L3JkZjpEZXNjcmlwdGlvbj4NCiAgICA8cmRmOkRlc2NyaXB0aW9uIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgcmRmOmFib3V0PSIiPg0KICAgICAgPGRjOmNyZWF0b3I+DQogICAgICAgIDxyZGY6U2VxIC8+DQogICAgICA8L2RjOmNyZWF0b3I+DQogICAgICA8ZGM6dGl0bGU+DQogICAgICAgIDxyZGY6QWx0Pg0KICAgICAgICAgIDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCI+V29ybGRfV2lkZV9Db3JwX1dlYl9Gb3JtPC9yZGY6bGk+DQogICAgICAgIDwvcmRmOkFsdD4NCiAgICAgIDwvZGM6dGl0bGU+DQogICAgICA8ZGM6Zm9ybWF0PmFwcGxpY2F0aW9uL3BkZjwvZGM6Zm9ybWF0Pg0KICAgIDwvcmRmOkRlc2NyaXB0aW9uPg0KICA8L3JkZjpSREY+DQo8L3g6eG1wbWV0YT4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPD94cGFja2V0IGVuZD0idyI/PgplbmRzdHJlYW0KZW5kb2JqCjM2IDAgb2JqCjw8L0ZpbHRlci9GbGF0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", "display": "inline", "includeInDownload": "true", "signerMustAcknowledge": "no_interaction", "templateLocked": "false", "templateRequired": "false" } ], "emailSubject": "Please sign this document", "emailBlurb": "", "signingLocation": "Online", "authoritativeCopy": "false", "enforceSignerVisibility": "false", "enableWetSign": "true", "allowMarkup": "false", "allowReassign": "true", "customFields": { "textCustomFields": [ { "fieldId": "11230853228", "name": "ModelNamespace", "show": "false", "required": "false", "value": "docusign.forms._85443307_664c_4c85_882f_87671e153075._608a6c8a_16b2_4419_92f4_d06bc3af6e53" }, { "fieldId": "11230853229", "name": "ModelVersion", "show": "false", "required": "false", "value": "1" }, { "fieldId": "11230853230", "name": "ModelAccount", "show": "false", "required": "false", "value": "85443307-664c-4c85-882f-87671e153075" } ], "listCustomFields": [] }, "recipients": { "signers": [ { "defaultRecipient": "false", "tabs": { "signHereTabs": [ { "stampType": "signature", "name": "SignHere", "tabLabel": "Signature", "scaleValue": "1", "optional": "false", "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "126", "yPosition": "374", "anchorString": "/SignHere/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "be01d0ca-1057-455f-9af6-95f6074b5cf6", "tabType": "signhere" } ], "dateSignedTabs": [ { "name": "DateSigned", "value": "", "tabLabel": "DateSigned", "localePolicy": {}, "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "409", "yPosition": "396", "anchorString": "/Date/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "a0276278-95a3-4de2-a374-15e92510112e", "tabType": "datesigned" } ], "textTabs": [ { "requireAll": "false", "value": "", "originalValue": "", "required": "true", "locked": "false", "concealValueOnDocument": "false", "disableAutoSize": "false", "maxLength": "4000", "tabLabel": "FullName", "bold": "false", "italic": "false", "underline": "false", "localePolicy": {}, "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "145", "yPosition": "234", "width": "0", "height": "0", "anchorString": "/FullName/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "c995df10-9141-4686-b9bf-f1dfc4121d33", "mergeFieldXml": "{\"adm\":{\"path\":\"ADM.Form.FullName\"}}", "tabType": "text" }, { "requireAll": "false", "value": "", "originalValue": "", "required": "true", "locked": "false", "concealValueOnDocument": "false", "disableAutoSize": "false", "maxLength": "4000", "tabLabel": "PhoneNumber", "bold": "false", "italic": "false", "underline": "false", "localePolicy": {}, "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "167", "yPosition": "261", "width": "0", "height": "0", "anchorString": "/PhoneNumber/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "71ccfefa-5469-493e-b0c7-886c65b84742", "mergeFieldXml": "{\"adm\":{\"path\":\"ADM.Form.PhoneNumber\"}}", "tabType": "text" }, { "requireAll": "false", "value": "", "originalValue": "", "required": "true", "locked": "false", "concealValueOnDocument": "false", "disableAutoSize": "false", "maxLength": "4000", "tabLabel": "Company", "bold": "false", "italic": "false", "underline": "false", "localePolicy": {}, "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "182", "yPosition": "315", "width": "0", "height": "0", "anchorString": "/Company/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "184f8a65-f2be-48b9-a30e-15592c59b335", "mergeFieldXml": "{\"adm\":{\"path\":\"ADM.Form.Company\"}}", "tabType": "text" }, { "requireAll": "false", "value": "", "originalValue": "", "required": "true", "locked": "false", "concealValueOnDocument": "false", "disableAutoSize": "false", "maxLength": "4000", "tabLabel": "JobTitle", "bold": "false", "italic": "false", "underline": "false", "localePolicy": {}, "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "137", "yPosition": "342", "width": "0", "height": "0", "anchorString": "/Title/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "e7d30733-ce70-4fee-93a0-ea10ecc014b1", "mergeFieldXml": "{\"adm\":{\"path\":\"ADM.Form.JobTitle\"}}", "tabType": "text" } ], "checkboxTabs": [ { "name": "Yes", "tabLabel": "Yes", "selected": "false", "selectedOriginal": "false", "requireInitialOnSharedChange": "false", "bold": "false", "italic": "false", "underline": "false", "required": "true", "locked": "false", "documentId": "1", "recipientId": "1", "pageNumber": "1", "xPosition": "237", "yPosition": "288", "width": "0", "height": "0", "anchorString": "/SMS/", "anchorXOffset": "20", "anchorYOffset": "10", "anchorUnits": "pixels", "anchorCaseSensitive": "false", "anchorMatchWholeWord": "true", "anchorHorizontalAlignment": "left", "anchorTabProcessorVersion": "v1_3", "tabId": "0f1e242d-550d-4729-a141-de0dce1d1b6c", "mergeFieldXml": "{\"adm\":{\"path\":\"ADM.Form.Yes.Yes\"}}", "tabType": "checkbox" } ] }, "signInEachLocation": "false", "agentCanEditEmail": "false", "agentCanEditName": "false", "requireUploadSignature": "false", "name": "", "email": "", "recipientId": "1", "recipientIdGuid": "00000000-0000-0000-0000-000000000000", "accessCode": "", "requireIdLookup": "false", "routingOrder": "1", "note": "", "roleName": "signer", "completedCount": "0", "deliveryMethod": "email", "templateLocked": "false", "templateRequired": "false", "inheritEmailNotificationConfiguration": "false", "recipientType": "signer" } ], "agents": [], "editors": [], "intermediaries": [], "carbonCopies": [], "certifiedDeliveries": [], "inPersonSigners": [], "seals": [], "witnesses": [], "notaries": [], "recipientCount": "1" }, "envelopeIdStamping": "true", "autoNavigation": "true", "uSigState": "esign", "allowComments": "true", "disableResponsiveDocument": "true", "anySigner": null, "envelopeLocation": "current_site" } ] } \ No newline at end of file diff --git a/db/schema.rb b/db/schema.rb index 3c0c23d..eb3a310 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -12,7 +12,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2018_12_03_073200) do +ActiveRecord::Schema.define(version: 20_181_203_073_200) do create_table 'sessions', force: :cascade do |t| t.datetime 'created_at', null: false t.datetime 'updated_at', null: false diff --git a/jwt_console_project/jwt_config.example.yml b/jwt_console_project/jwt_config.example.yml new file mode 100644 index 0000000..8180e06 --- /dev/null +++ b/jwt_console_project/jwt_config.example.yml @@ -0,0 +1,4 @@ +jwt_integration_key: {INTEGRATION_KEY_JWT} +impersonated_user_guid: {IMPERSONATED_USER_ID} +authorization_server: account-d.docusign.com +pdf_filename: '../data/World_Wide_Corp_lorem.pdf' diff --git a/jwt_console_project/jwt_console.rb b/jwt_console_project/jwt_console.rb new file mode 100644 index 0000000..19326da --- /dev/null +++ b/jwt_console_project/jwt_console.rb @@ -0,0 +1,123 @@ +require 'bundler/inline' + +gemfile do + source 'https://rubygems.org' + gem 'docusign_esign', ' ~> 5.1.0' +end + +class ESign +end + +require 'docusign_esign' +require_relative '../app/services/api_creator' +require_relative '../app/services/e_sign/eg002_signing_via_email_service' +require 'yaml' + +$SCOPES = %w[ + signature impersonation +] + +def load_config_data + config_file_path = 'jwt_config.yml' + begin + config_file_contents = File.read(config_file_path) + rescue Errno::ENOENT + warn 'Missing config file' + raise + end + YAML.unsafe_load(config_file_contents) +end + +def get_consent + url_scopes = $SCOPES.join('+') + # Construct consent URL + redirect_uri = 'https://developers.docusign.com/platform/auth/consent' + consent_url = "https://#{CONFIG['authorization_server']}/oauth/auth?response_type=code&" \ + "scope=#{url_scopes}&client_id=#{CONFIG['jwt_integration_key']}&" \ + "redirect_uri=#{redirect_uri}" + + puts 'Open the following URL in your browser to grant consent to the application:' + puts consent_url + puts "Consent granted? \n 1)Yes \n 2)No" + continue = gets + if continue.chomp == '1' + true + else + puts 'Please grant consent' + exit + end +end + +def authenticate + configuration = DocuSign_eSign::Configuration.new + configuration.debugging = true + api_client = DocuSign_eSign::ApiClient.new(configuration) + api_client.set_oauth_base_path(CONFIG['authorization_server']) + + rsa_pk = 'docusign_private_key.txt' + begin + token = api_client.request_jwt_user_token(CONFIG['jwt_integration_key'], CONFIG['impersonated_user_guid'], rsa_pk, 3600, $SCOPES) + user_info_response = api_client.get_user_info(token.access_token) + account = user_info_response.accounts.find(&:is_default) + + { + access_token: token.access_token, + account_id: account.account_id, + base_path: account.base_uri + } + rescue OpenSSL::PKey::RSAError => e + Rails.logger.error e.inspect + + raise "Please add your private RSA key to: #{rsa_pk}" if File.read(rsa_pk).starts_with? '{RSA_PRIVATE_KEY}' + + raise + rescue DocuSign_eSign::ApiError => e + body = JSON.parse(e.response_body) + if body['error'] == 'consent_required' + authenticate if get_consent + else + puts 'API Error' + puts body['error'] + puts body['message'] + exit + end + end +end + +def get_args(apiAccountId, accessToken, basePath) + puts "Enter the signer's email address: " + signerEmail = gets.chomp + puts "Enter the signer's name: " + signerName = gets.chomp + puts "Enter the carbon copy's email address: " + ccSignerEmail = gets.chomp + puts "Enter the carbon copy's name: " + ccSignerName = gets.chomp + + envelope_args = { + signer_email: signerEmail, + signer_name: signerName, + cc_email: ccSignerEmail, + cc_name: ccSignerName, + status: 'sent', + doc_docx: '../data/World_Wide_Corp_Battle_Plan_Trafalgar.docx', + doc_pdf: '../data/World_Wide_Corp_lorem.pdf' + } + { + account_id: apiAccountId, + base_path: basePath, + access_token: accessToken, + envelope_args: envelope_args + } +end + +def main + load_config_data + account_info = authenticate + args = get_args(account_info[:account_id], account_info[:access_token], account_info[:base_path]) + results = ESign::Eg002SigningViaEmailService.new(args).worker + puts "Successfully sent envelope with envelope ID: #{results['envelope_id']}" +end + +CONFIG = load_config_data +main diff --git a/lib/docusign.rb b/lib/docusign.rb index 807e6fe..3617969 100644 --- a/lib/docusign.rb +++ b/lib/docusign.rb @@ -9,8 +9,8 @@ class Docusign < OmniAuth::Strategies::OAuth2 # The name of the strategy, used in config/initializer/omniauth.rb option :name, 'docusign' - # These are called after the OAuth2 login authentication has succeeded and are part of the DocuSign callback response message: - # transforms the DocuSign login response from the raw_info https://github.com/omniauth/omniauth/wiki/Strategy-Contribution-Guide#defining-the-callback-phase + # These are called after the OAuth2 login authentication has succeeded and are part of the Docusign callback response message: + # transforms the Docusign login response from the raw_info https://github.com/omniauth/omniauth/wiki/Strategy-Contribution-Guide#defining-the-callback-phase # into the standardized schema required by OmniAuth https://github.com/omniauth/omniauth/wiki/Auth-Hash-Schema # and gets exposed through the "request.env[omniauth.auth]" to the SessionController#create uid { raw_info['sub'] } @@ -47,15 +47,13 @@ def raw_info # @param items is an array of Hash'es that has the keys: account_id, is_default, account_name, base_uri def fetch_account(items) - if options.target_account_id - @account = items.find { |item| item['account_id'] == options.target_account_id } - else - @account = items.find { |item| item['is_default'] } - end - - if @account.blank? - raise %'Could not find account information for the user in the "accounts" of raw_info: #{@raw_info}' - end + @account = if options.target_account_id + items.find { |item| item['account_id'] == options.target_account_id } + else + items.find { |item| item['is_default'] } + end + + raise %(Could not find account information for the user in the "accounts" of raw_info: #{@raw_info}) if @account.blank? end end end diff --git a/public/banner-code.png b/public/banner-code.png deleted file mode 100644 index 292d14e..0000000 Binary files a/public/banner-code.png and /dev/null differ diff --git a/public/favicon.ico b/public/favicon.ico index e69de29..fd990b6 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/header.png b/public/header.png new file mode 100644 index 0000000..a7fcac1 Binary files /dev/null and b/public/header.png differ diff --git a/quick_acg/.gitattributes b/quick_acg/.gitattributes new file mode 100644 index 0000000..31eeee0 --- /dev/null +++ b/quick_acg/.gitattributes @@ -0,0 +1,7 @@ +# See https://git-scm.com/docs/gitattributes for more about git attribute files. + +# Mark the database schema as having been generated. +db/schema.rb linguist-generated + +# Mark any vendored files as having been vendored. +vendor/* linguist-vendored diff --git a/quick_acg/.gitignore b/quick_acg/.gitignore new file mode 100644 index 0000000..4857d33 --- /dev/null +++ b/quick_acg/.gitignore @@ -0,0 +1,29 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile '~/.gitignore_global' + +# Ignore bundler config. +/.bundle + +# Ignore the default SQLite database. +/db/*.sqlite3 +/db/*.sqlite3-* + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore pidfiles, but keep the directory. +/tmp/pids/* +!/tmp/pids/ +!/tmp/pids/.keep + + +/public/assets + +# Ignore master key for decrypting credentials and more. +/config/master.key diff --git a/quick_acg/.ruby-version b/quick_acg/.ruby-version new file mode 100644 index 0000000..c0013a8 --- /dev/null +++ b/quick_acg/.ruby-version @@ -0,0 +1 @@ +ruby-2.7.3 diff --git a/quick_acg/Gemfile b/quick_acg/Gemfile new file mode 100644 index 0000000..96c931e --- /dev/null +++ b/quick_acg/Gemfile @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' +git_source(:github) { |repo| "https://github.com/#{repo}.git" } + +ruby '~>3.1.2' + +# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' +gem 'rails', '~> 7.2.2.1' +# Use sqlite3 as the database for Active Record +gem 'sqlite3', '~> 2.1.1' +# Use Puma as the app server +gem 'puma', '~> 6.6.0' +# Use SCSS for stylesheets +gem 'sass-rails', '~> 6.0.0' +# Use Uglifier as compressor for JavaScript assets +gem 'uglifier', '~> 4.2.1' +# See https://github.com/rails/execjs#readme for more supported runtimes +# gem 'mini_racer', platforms: :ruby + +# Use CoffeeScript for .coffee assets and views +gem 'coffee-rails', '~> 5.0.0' +# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks +gem 'turbolinks', '~> 5.2.1' +# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder +gem 'jbuilder', '~> 2.13.0' +# Use Redis adapter to run Action Cable in production +# gem 'redis', '~> 4.0' +# Use ActiveModel has_secure_password +# gem 'bcrypt', '~> 3.1.7' + +# Use ActiveStorage variant +# gem 'mini_magick', '~> 4.8' + +# Use Capistrano for deployment +# gem 'capistrano-rails', group: :development + +# Reduces boot times through caching; required in config/boot.rb +if RUBY_PLATFORM =~ /mswin/ + gem 'bootsnap', '>= 1.1.0', '< 1.4.2', require: false +else + gem 'bootsnap', '~> 1.7.3', require: false +end + +group :development, :test do + # Call 'byebug' anywhere in the code to stop execution and get a debugger console + gem 'byebug', '~> 11.1.3', platforms: %i[mri mingw x64_mingw] +end + +group :development do + # Access an interactive console on exception pages or by calling 'console' anywhere in the code. + gem 'listen', '~> 3.9.0' + gem 'web-console', '~> 4.2.1' + # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring + gem 'pry-nav', '~> 1.0.0' + gem 'pry-rails', '~> 0.3.11' + gem 'spring', '~> 4.3.0' + gem 'spring-watcher-listen', '~> 2.1.0' +end + +group :test do + # Adds support for Capybara system testing and selenium driver + gem 'capybara', '~> 3.40.0' + gem 'selenium-webdriver', '~> 4.25.0' + # Easy installation and use of chromedriver to run system tests with Chrome + gem 'chromedriver-helper', '~> 2.1.1' + gem 'test-unit' +end + +gem 'docusign_esign', '~> 5.1.0' +gem 'omniauth-oauth2', '~> 1.8.0' +gem 'omniauth-rails_csrf_protection' + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem 'tzinfo-data', '~> 1.2025.2' +gem 'wdm', '>= 0.2.0', platforms: %i[mingw mswin x64_mingw] diff --git a/quick_acg/Gemfile.lock b/quick_acg/Gemfile.lock new file mode 100644 index 0000000..433bbbf --- /dev/null +++ b/quick_acg/Gemfile.lock @@ -0,0 +1,359 @@ +GEM + remote: https://rubygems.org/ + specs: + actioncable (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + zeitwerk (~> 2.6) + actionmailbox (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) + actionmailer (7.2.2.1) + actionpack (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (7.2.2.1) + actionview (= 7.2.2.1) + activesupport (= 7.2.2.1) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4, < 3.2) + rack-session (>= 1.0.1) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (7.2.2.1) + actionpack (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.2.2.1) + activesupport (= 7.2.2.1) + builder (~> 3.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.2.2.1) + activesupport (= 7.2.2.1) + globalid (>= 0.3.6) + activemodel (7.2.2.1) + activesupport (= 7.2.2.1) + activerecord (7.2.2.1) + activemodel (= 7.2.2.1) + activesupport (= 7.2.2.1) + timeout (>= 0.4.0) + activestorage (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activesupport (= 7.2.2.1) + marcel (~> 1.0) + activesupport (7.2.2.1) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) + archive-zip (0.12.0) + io-like (~> 0.3.0) + base64 (0.2.0) + benchmark (0.4.1) + bigdecimal (3.1.7) + bindex (0.8.1) + bootsnap (1.7.7) + msgpack (~> 1.0) + builder (3.2.4) + byebug (11.1.3) + capybara (3.40.0) + addressable + matrix + mini_mime (>= 0.1.3) + nokogiri (~> 1.11) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) + chromedriver-helper (2.1.1) + archive-zip (~> 0.10) + nokogiri (~> 1.8) + coderay (1.1.3) + coffee-rails (5.0.0) + coffee-script (>= 2.2.0) + railties (>= 5.2.0) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.12.2) + concurrent-ruby (1.3.4) + connection_pool (2.4.1) + crass (1.0.6) + date (3.4.1) + docusign_esign (5.1.0) + addressable (~> 2.7, >= 2.7.0) + json (~> 2.1, >= 2.1.0) + jwt (~> 2.2, >= 2.2.1) + typhoeus (~> 1.0, >= 1.0.1) + drb (2.2.1) + erubi (1.12.0) + ethon (0.16.0) + ffi (>= 1.15.0) + execjs (2.9.1) + faraday (2.9.0) + faraday-net_http (>= 2.0, < 3.2) + faraday-net_http (3.1.0) + net-http + ffi (1.16.3-x64-mingw-ucrt) + globalid (1.2.1) + activesupport (>= 6.1) + hashie (5.0.0) + i18n (1.14.4) + concurrent-ruby (~> 1.0) + io-console (0.7.2) + io-like (0.3.1) + irb (1.14.1) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + jbuilder (2.13.0) + actionview (>= 5.0.0) + activesupport (>= 5.0.0) + json (2.12.2) + jwt (2.8.1) + base64 + listen (3.9.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + logger (1.6.1) + loofah (2.22.0) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) + mail (2.8.1) + mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp + marcel (1.0.4) + matrix (0.4.2) + method_source (1.1.0) + mini_mime (1.1.5) + minitest (5.22.3) + msgpack (1.7.2) + multi_xml (0.6.0) + net-http (0.4.1) + uri + net-imap (0.5.8) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-smtp (0.5.1) + net-protocol + nio4r (2.7.4) + nokogiri (1.16.4-x64-mingw-ucrt) + racc (~> 1.4) + oauth2 (2.0.9) + faraday (>= 0.17.3, < 3.0) + jwt (>= 1.0, < 3.0) + multi_xml (~> 0.5) + rack (>= 1.2, < 4) + snaky_hash (~> 2.0) + version_gem (~> 1.1) + omniauth (2.1.2) + hashie (>= 3.4.6) + rack (>= 2.2.3) + rack-protection + omniauth-oauth2 (1.8.0) + oauth2 (>= 1.4, < 3) + omniauth (~> 2.0) + omniauth-rails_csrf_protection (1.0.1) + actionpack (>= 4.2) + omniauth (~> 2.0) + power_assert (2.0.3) + pry (0.14.2) + coderay (~> 1.1) + method_source (~> 1.0) + pry-nav (1.0.0) + pry (>= 0.9.10, < 0.15) + pry-rails (0.3.11) + pry (>= 0.13.0) + psych (5.1.2) + stringio + public_suffix (5.0.5) + puma (6.6.0) + nio4r (~> 2.0) + racc (1.7.3) + rack (3.0.10) + rack-protection (4.0.0) + base64 (>= 0.1.0) + rack (>= 3.0.0, < 4) + rack-session (2.0.0) + rack (>= 3.0.0) + rack-test (2.1.0) + rack (>= 1.3) + rackup (2.1.0) + rack (>= 3) + webrick (~> 1.8) + rails (7.2.2.1) + actioncable (= 7.2.2.1) + actionmailbox (= 7.2.2.1) + actionmailer (= 7.2.2.1) + actionpack (= 7.2.2.1) + actiontext (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activemodel (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + bundler (>= 1.15.0) + railties (= 7.2.2.1) + rails-dom-testing (2.2.0) + activesupport (>= 5.0.0) + minitest + nokogiri (>= 1.6) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) + irb (~> 1.13) + rackup (>= 1.0.0) + rake (>= 12.2) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + rake (13.2.1) + rb-fsevent (0.11.2) + rb-inotify (0.10.1) + ffi (~> 1.0) + rdoc (6.6.3.1) + psych (>= 4.0.0) + regexp_parser (2.9.0) + reline (0.5.2) + io-console (~> 0.5) + rexml (3.3.8) + rubyzip (2.3.2) + sass-rails (6.0.0) + sassc-rails (~> 2.1, >= 2.1.1) + sassc (2.4.0) + ffi (~> 1.9) + sassc-rails (2.1.2) + railties (>= 4.0.0) + sassc (>= 2.0) + sprockets (> 3.0) + sprockets-rails + tilt + securerandom (0.3.1) + selenium-webdriver (4.25.0) + base64 (~> 0.2) + logger (~> 1.4) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2, < 3.0) + websocket (~> 1.0) + snaky_hash (2.0.1) + hashie + version_gem (~> 1.1, >= 1.1.1) + spring (4.3.0) + spring-watcher-listen (2.1.0) + listen (>= 2.7, < 4.0) + spring (>= 4) + sprockets (4.2.1) + concurrent-ruby (~> 1.0) + rack (>= 2.2.4, < 4) + sprockets-rails (3.4.2) + actionpack (>= 5.2) + activesupport (>= 5.2) + sprockets (>= 3.0.0) + sqlite3 (2.1.1-x64-mingw-ucrt) + stringio (3.1.0) + test-unit (3.6.2) + power_assert + thor (1.3.1) + tilt (2.3.0) + timeout (0.4.3) + turbolinks (5.2.1) + turbolinks-source (~> 5.2) + turbolinks-source (5.2.0) + typhoeus (1.4.1) + ethon (>= 0.9.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + tzinfo-data (1.2025.2) + tzinfo (>= 1.0.0) + uglifier (4.2.1) + execjs (>= 0.3.0, < 3) + uri (0.13.0) + useragent (0.16.10) + version_gem (1.1.4) + wdm (0.2.0) + web-console (4.2.1) + actionview (>= 6.0.0) + activemodel (>= 6.0.0) + bindex (>= 0.4.0) + railties (>= 6.0.0) + webrick (1.8.1) + websocket (1.2.11) + websocket-driver (0.8.0) + base64 + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) + xpath (3.2.0) + nokogiri (~> 1.8) + zeitwerk (2.6.13) + +PLATFORMS + x64-mingw-ucrt + +DEPENDENCIES + bootsnap (~> 1.7.3) + byebug (~> 11.1.3) + capybara (~> 3.40.0) + chromedriver-helper (~> 2.1.1) + coffee-rails (~> 5.0.0) + docusign_esign (~> 5.1.0) + jbuilder (~> 2.13.0) + listen (~> 3.9.0) + omniauth-oauth2 (~> 1.8.0) + omniauth-rails_csrf_protection + pry-nav (~> 1.0.0) + pry-rails (~> 0.3.11) + puma (~> 6.6.0) + rails (~> 7.2.2.1) + sass-rails (~> 6.0.0) + selenium-webdriver (~> 4.25.0) + spring (~> 4.3.0) + spring-watcher-listen (~> 2.1.0) + sqlite3 (~> 2.1.1) + test-unit + turbolinks (~> 5.2.1) + tzinfo-data (~> 1.2025.2) + uglifier (~> 4.2.1) + wdm (>= 0.2.0) + web-console (~> 4.2.1) + +RUBY VERSION + ruby 3.1.2p20 + +BUNDLED WITH + 2.4.22 diff --git a/quick_acg/README.md b/quick_acg/README.md new file mode 100644 index 0000000..7db80e4 --- /dev/null +++ b/quick_acg/README.md @@ -0,0 +1,24 @@ +# README + +This README would normally document whatever steps are necessary to get the +application up and running. + +Things you may want to cover: + +* Ruby version + +* System dependencies + +* Configuration + +* Database creation + +* Database initialization + +* How to run the test suite + +* Services (job queues, cache servers, search engines, etc.) + +* Deployment instructions + +* ... diff --git a/quick_acg/Rakefile b/quick_acg/Rakefile new file mode 100644 index 0000000..e85f913 --- /dev/null +++ b/quick_acg/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative 'config/application' + +Rails.application.load_tasks diff --git a/quick_acg/app/assets/config/manifest.js b/quick_acg/app/assets/config/manifest.js new file mode 100644 index 0000000..5cc2c08 --- /dev/null +++ b/quick_acg/app/assets/config/manifest.js @@ -0,0 +1,3 @@ +//= link_tree ../images +//= link_directory ../javascripts .js +//= link_directory ../stylesheets .css \ No newline at end of file diff --git a/quick_acg/app/controllers/ds_common_controller.rb b/quick_acg/app/controllers/ds_common_controller.rb new file mode 100644 index 0000000..16e63b1 --- /dev/null +++ b/quick_acg/app/controllers/ds_common_controller.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class DsCommonController < ApplicationController + def index + session[:api] = 'eSignature' + @show_doc = Rails.application.config.documentation + handle_redirects + end + Rails.configuration.file_watcher + def handle_redirects + if session[:quickstarted].nil? + session[:quickstarted] = true + redirect_to '/auth/docusign' + else + return redirect_to '/auth/docusign' if session[:ds_access_token].nil? || session[:ds_base_path].nil? + + enableCFR = ESign::GetDataService.new(session[:ds_access_token], session[:ds_base_path]).cfr?(session[:ds_account_id]) + if enableCFR == 'enabled' + session[:status_cfr] = 'enabled' + redirect_to '/eeg041' + else + redirect_to '/eeg001' + end + end + end + + def ds_must_authenticate + redirect_to '/auth/docusign' + end +end diff --git a/quick_acg/app/views/ds_common/error.erb b/quick_acg/app/views/ds_common/error.erb new file mode 100644 index 0000000..b431df0 --- /dev/null +++ b/quick_acg/app/views/ds_common/error.erb @@ -0,0 +1,53 @@ + + + + + + + + + <% if @title %> + <%= @title %> + <% else %> + Docusign Ruby Code Examples + <% end %> + + + + + + + + + +
+
+ <% title = "Error" %> + +

Problem: an error occurred

+

Error information:

+ + <% if @error_code %> +

<%= @error_code %>: <%= @error_message %>

+ <% end %> + <% if @error_information %> +

<%== @error_information %>

+ <% end %> + <% if @err %> +

+

<%= @err %>
+

+ <% end %> + + +

Continue

+
+
+ + \ No newline at end of file diff --git a/quick_acg/app/views/e_sign/eeg041_cfr_embedded_signing/get.html.erb b/quick_acg/app/views/e_sign/eeg041_cfr_embedded_signing/get.html.erb new file mode 100644 index 0000000..0d0dcd9 --- /dev/null +++ b/quick_acg/app/views/e_sign/eeg041_cfr_embedded_signing/get.html.erb @@ -0,0 +1,113 @@ + + + + + + + + + <% if @title %> + <%= @title %> + <% else %> + Docusign Ruby Code Examples + <% end %> + + + + + + + + +
+
+

Use 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: the Docusign signing is initiated from your + website.

+ + <% if @show_doc %> +

Documentation about this example.

+ <% end %> + +

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

+

+ View source file <%= @source_file %> on GitHub. +

+ + <% form_index = 0 %> + <% signer_email_index = 0 %> + <% signer_name_index = 1 %> + <% country_code_index = 2 %> + <% phone_number_index = 3 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= @manifest["SupportingTexts"]["HelpingTexts"]["EmailWontBeShared"] %> +
+
+ + " name="signerName" + value="<%= @config.signer_name %>" required> +
+
+ + " required /> + The country code for the phone number below. +
+
+ + " required /> + <%= "#{@manifest["SupportingTexts"]["HelpingTexts"]["PhoneNumberWillBeNotified"]} #{@manifest["SupportingTexts"]["HelpingTexts"]["PhoneNumberWontBeShared"]}" %> +
+ +
+
+
+ + \ No newline at end of file diff --git a/quick_acg/app/views/eeg001_embedded_signing/get.html.erb b/quick_acg/app/views/eeg001_embedded_signing/get.html.erb new file mode 100644 index 0000000..52f1ded --- /dev/null +++ b/quick_acg/app/views/eeg001_embedded_signing/get.html.erb @@ -0,0 +1,97 @@ + + + + + + + + + <% if @title %> + <%= @title %> + <% else %> + Docusign Ruby Code Examples + <% end %> + + + + + + + + +
+
+

Use 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: the Docusign signing is initiated from your + website.

+ + <% if @show_doc %> +

Documentation about this example.

+ <% end %> + +

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

+

+ View source file <%= @source_file %> on GitHub. +

+ + <% form_index = 0 %> + <% signer_email_index = 0 %> + <% signer_name_index = 1 %> + +
+ <% if @example["Forms"][form_index]["FormName"] %> + <%= sanitize @example["Forms"][form_index]["FormName"] %> + <% end %> + +
+ + " required + value="<%= @config.signer_email %>"> + <%= @manifest["SupportingTexts"]["HelpingTexts"]["EmailWontBeShared"] %> +
+
+ + " + name="signerName" value="<%= @config.signer_name %>" required> +
+ +
+
+
+ + \ No newline at end of file diff --git a/quick_acg/bin/bundle b/quick_acg/bin/bundle new file mode 100644 index 0000000..d34d131 --- /dev/null +++ b/quick_acg/bin/bundle @@ -0,0 +1,116 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'bundle' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require 'rubygems' + +m = Module.new do + module_function + + def invoked_as_script? + File.expand_path($0) == File.expand_path(__FILE__) + end + + def env_var_version + ENV['BUNDLER_VERSION'] + end + + def cli_arg_version + return unless invoked_as_script? # don't want to hijack other binstubs + return unless 'update'.start_with?(ARGV.first || ' ') # must be running `bundle update` + + bundler_version = nil + update_index = nil + ARGV.each_with_index do |a, i| + bundler_version = a if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN + next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ + + bundler_version = Regexp.last_match(1) + update_index = i + end + bundler_version + end + + def gemfile + gemfile = ENV['BUNDLE_GEMFILE'] + return gemfile if gemfile && !gemfile.empty? + + File.expand_path('../Gemfile', __dir__) + end + + def lockfile + lockfile = + case File.basename(gemfile) + when 'gems.rb' then gemfile.sub(/\.rb$/, gemfile) + else "#{gemfile}.lock" + end + File.expand_path(lockfile) + end + + def lockfile_version + return unless File.file?(lockfile) + + lockfile_contents = File.read(lockfile) + return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + + Regexp.last_match(1) + end + + def bundler_version + @bundler_version ||= + env_var_version || cli_arg_version || + lockfile_version + end + + def bundler_requirement + return "#{Gem::Requirement.default}.a" unless bundler_version + + bundler_gem_version = Gem::Version.new(bundler_version) + + requirement = bundler_gem_version.approximate_recommendation + + return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new('2.7.0') + + requirement += '.a' if bundler_gem_version.prerelease? + + requirement + end + + def load_bundler! + ENV['BUNDLE_GEMFILE'] ||= gemfile + + activate_bundler + end + + def activate_bundler + gem_error = activation_error_handling do + gem 'bundler', bundler_requirement + end + return if gem_error.nil? + + require_error = activation_error_handling do + require 'bundler/version' + end + return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + + warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" + exit 42 + end + + def activation_error_handling + yield + nil + rescue StandardError, LoadError => e + e + end +end + +m.load_bundler! + +load Gem.bin_path('bundler', 'bundle') if m.invoked_as_script? diff --git a/quick_acg/bin/rails b/quick_acg/bin/rails new file mode 100644 index 0000000..bec72ac --- /dev/null +++ b/quick_acg/bin/rails @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby.exe +APP_PATH = File.expand_path('../config/application', __dir__) +require_relative '../config/boot' +require 'rails/commands' diff --git a/quick_acg/bin/rake b/quick_acg/bin/rake new file mode 100644 index 0000000..f6ed5a2 --- /dev/null +++ b/quick_acg/bin/rake @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby.exe +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/quick_acg/bin/setup b/quick_acg/bin/setup new file mode 100644 index 0000000..b6c604a --- /dev/null +++ b/quick_acg/bin/setup @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby.exe +require 'fileutils' + +# path to your application root. +APP_ROOT = File.expand_path('..', __dir__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +FileUtils.chdir APP_ROOT do + # This script is a way to set up or update your development environment automatically. + # This script is idempotent, so that you can run it at any time and get an expectable outcome. + # Add necessary setup steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + # puts "\n== Copying sample files ==" + # unless File.exist?("config/database.yml") + # FileUtils.cp "config/database.yml.sample", "config/database.yml" + # end + + puts "\n== Preparing database ==" + system! 'bin/rails db:prepare' + + puts "\n== Removing old logs and tempfiles ==" + system! 'bin/rails log:clear tmp:clear' + + puts "\n== Restarting application server ==" + system! 'bin/rails restart' +end diff --git a/quick_acg/config.ru b/quick_acg/config.ru new file mode 100644 index 0000000..842bccc --- /dev/null +++ b/quick_acg/config.ru @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +# This file is used by Rack-based servers to start the application. + +require_relative 'config/environment' + +run Rails.application diff --git a/quick_acg/config/application.rb b/quick_acg/config/application.rb new file mode 100644 index 0000000..973b98b --- /dev/null +++ b/quick_acg/config/application.rb @@ -0,0 +1,25 @@ +require_relative 'boot' +require 'rails/all' +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) +module CodeExamplesRuby + class Application < Rails::Application + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 5.2 + # Configuration for Docusign example. + # For a production application, you will store the credentials + # in config/environments/development.rb, production.rb, test.rb, etc + config.app_url = 'http://localhost:3000' # The public url of the application. + # Init Docusign configuration, loaded from config/appsettings.yml file + DOCUSIGN_CONFIG = YAML.load_file(File.join(Rails.root, '../config/appsettings.yml'), aliases: true)[Rails.env] + DOCUSIGN_CONFIG.map do |k, v| + config.send("#{k}=", v) + end + + # Settings in config/environments/* take precedence over those specified here. + # Application configuration can go into files in config/initializers + # -- all .rb files in that directory are automatically loaded after loading + # the framework and any gems in your application. + end +end diff --git a/quick_acg/config/boot.rb b/quick_acg/config/boot.rb new file mode 100644 index 0000000..30f5120 --- /dev/null +++ b/quick_acg/config/boot.rb @@ -0,0 +1,3 @@ +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) + +require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/quick_acg/config/credentials.yml.enc b/quick_acg/config/credentials.yml.enc new file mode 100644 index 0000000..5dfeb60 --- /dev/null +++ b/quick_acg/config/credentials.yml.enc @@ -0,0 +1 @@ +VwFdOXZ+C/1eCY+cD7sJAKNMTTKqGujd2ae3jErSN7BCN8xJ0g2OiEDiyDKBbq7AWIHxdxzXQ1CBCG2ly0ApaZSQxaL0Kb1sPe4WcV5YZZvLw0JDJVMk7x/NPsvTjzCJs4ybfSUT/8hMs4xDGzKtxOlCYVMXrAdPyzVebV/7K5GhPTJxJTS9TopfxoCeaaGZtpM5yfUqPAKTLnxUGM3q0ul1vc+vJZNmN6clUzDBVT0K1FR9bzcRFML8QPwP3yZIRpg6+R+c+ggYAd8H5F92iFAE0QnxZQwgr2JGGJEzibIIg6Y33+Pjp4RV0WdL4OgfY43VRZp+3n9So2+SH64veB2KV+nWNAYKKXZpZCnKjuDlur6DUd1Q6VNfF5vVxGkpwza2ABJKuQMgslA6OCeYE0fBZ/AZ01oiCyue--z8P9Lw0Um2HS3bJl--JO10GQB7RkBd6Fh59SM41w== \ No newline at end of file diff --git a/quick_acg/config/database.yml b/quick_acg/config/database.yml new file mode 100644 index 0000000..fcba57f --- /dev/null +++ b/quick_acg/config/database.yml @@ -0,0 +1,25 @@ +# SQLite. Versions 3.8.0 and up are supported. +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem "sqlite3" +# +default: &default + adapter: sqlite3 + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 + +development: + <<: *default + database: db/development.sqlite3 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: db/test.sqlite3 + +production: + <<: *default + database: db/production.sqlite3 diff --git a/quick_acg/config/environment.rb b/quick_acg/config/environment.rb new file mode 100644 index 0000000..426333b --- /dev/null +++ b/quick_acg/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative 'application' + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/quick_acg/config/environments/development.rb b/quick_acg/config/environments/development.rb new file mode 100644 index 0000000..3566bec --- /dev/null +++ b/quick_acg/config/environments/development.rb @@ -0,0 +1,63 @@ +require 'active_support/core_ext/integer/time' +require 'yaml' + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # In the development environment your application's code is reloaded any time + # it changes. This slows down response time but is perfect for development + # since you don't have to restart the web server when you make code changes. + config.cache_classes = false + + # Do not eager load code on boot. + config.eager_load = false + + # Show full error reports. + config.consider_all_requests_local = true + + # Enable server timing + config.server_timing = true + + # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. + if Rails.root.join('tmp/caching-dev.txt').exist? + config.action_controller.perform_caching = true + config.action_controller.enable_fragment_cache_logging = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{2.days.to_i}" + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = :page_load + + # Highlight code that triggered database queries in logs. + config.active_record.verbose_query_logs = true + + # Suppress logger output for asset requests. + config.assets.quiet = true + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true + + # Uncomment if you wish to allow Action Cable access from any origin. + # config.action_cable.disable_request_forgery_protection = true +end diff --git a/quick_acg/config/environments/production.rb b/quick_acg/config/environments/production.rb new file mode 100644 index 0000000..f174ab7 --- /dev/null +++ b/quick_acg/config/environments/production.rb @@ -0,0 +1,75 @@ +require 'active_support/core_ext/integer/time' + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.cache_classes = true + + # Eager load code on boot. This eager loads most of Rails and + # your application in memory, allowing both threaded web servers + # and those relying on copy on write to perform better. + # Rake tasks automatically ignore this option for performance. + config.eager_load = true + + # Full error reports are disabled and caching is turned on. + config.consider_all_requests_local = false + config.action_controller.perform_caching = true + + # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] + # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = true + + # Disable serving static files from the `/public` folder by default since + # Apache or NGINX already handles this. + config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? + + # Compress CSS using a preprocessor. + # config.assets.css_compressor = :sass + + # Do not fallback to assets pipeline if a precompiled asset is missed. + config.assets.compile = false + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.asset_host = "http://assets.example.com" + + # Specifies the header that your server uses for sending files. + # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache + # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + # config.force_ssl = true + + # Include generic and useful information about system operation, but avoid logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). + config.log_level = :info + + # Prepend all log lines with the following tags. + config.log_tags = [:request_id] + + # Use a different cache store in production. + # config.cache_store = :mem_cache_store + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Don't log any deprecations. + config.active_support.report_deprecations = false + + # Use default logging formatter so that PID and timestamp are not suppressed. + config.log_formatter = ::Logger::Formatter.new + + # Use a different logger for distributed setups. + # require "syslog/logger" + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name") + + if ENV['RAILS_LOG_TO_STDOUT'].present? + logger = ActiveSupport::Logger.new(STDOUT) + logger.formatter = config.log_formatter + config.logger = ActiveSupport::TaggedLogging.new(logger) + end + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false +end diff --git a/quick_acg/config/environments/test.rb b/quick_acg/config/environments/test.rb new file mode 100644 index 0000000..16b3759 --- /dev/null +++ b/quick_acg/config/environments/test.rb @@ -0,0 +1,50 @@ +require 'active_support/core_ext/integer/time' + +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Turn false under Spring and add config.action_view.cache_template_loading = true. + config.cache_classes = true + + # Eager loading loads your whole application. When running a single test locally, + # this probably isn't necessary. It's a good idea to do in a continuous integration + # system, or in some way before deploying your code. + config.eager_load = ENV['CI'].present? + + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.enabled = true + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{1.hour.to_i}" + } + + # Show full error reports and disable caching. + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + config.cache_store = :null_store + + # Raise exceptions instead of rendering exception templates. + config.action_dispatch.show_exceptions = false + + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true +end diff --git a/quick_acg/config/initializers/assets.rb b/quick_acg/config/initializers/assets.rb new file mode 100644 index 0000000..fe48fc3 --- /dev/null +++ b/quick_acg/config/initializers/assets.rb @@ -0,0 +1,12 @@ +# Be sure to restart your server when you modify this file. + +# Version of your assets, change this if you want to expire all your assets. +Rails.application.config.assets.version = '1.0' + +# Add additional assets to the asset load path. +# Rails.application.config.assets.paths << Emoji.images_path + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in the app/assets +# folder are already added. +# Rails.application.config.assets.precompile += %w( admin.js admin.css ) diff --git a/quick_acg/config/initializers/omniauth.rb b/quick_acg/config/initializers/omniauth.rb new file mode 100644 index 0000000..3e72d08 --- /dev/null +++ b/quick_acg/config/initializers/omniauth.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'docusign' + +# Defaults to STDOUT: https://github.com/omniauth/omniauth#logging +# Logs entries like: +# (docusign) Setup endpoint detected, running now. +# (docusign) Request phase initiated. +# (docusign) Callback phase initiated. +OmniAuth.config.logger = Rails.logger + +# https://github.com/omniauth/omniauth/wiki/FAQ#omniauthfailureendpoint-does-not-redirect-in-development-mode +# otherwise a callback exception like the following will not get caught: +# OmniAuth::Strategies::OAuth2::CallbackError (access_denied) +# GET "/auth/docusign/callback?error=access_denied&error_message=The%20user%20did%20not%20consent%20to%20connecting%20the%20application.&state= +OmniAuth.config.failure_raise_out_environments = [] # defaults to: ['development'] + +OmniAuth.config.allowed_request_methods = %i[post get] + +config = Rails.application.config +config.middleware.use OmniAuth::Builder do + # OAuth2 login request configuration + # OAuth2 login response callback message configuration is in OmniAuth::Strategies::Docusign in lib/docusign.rb + provider :docusign, config.integration_key, config.integration_secret, setup: lambda { |env| + strategy = env['omniauth.strategy'] + + # params = strategy.request.params + # examples_API = params['examples_API'] + # strategy.request.params.delete('examples_API') + + strategy.options[:client_options].site = config.app_url + strategy.options[:prompt] = 'login' + strategy.options[:oauth_base_uri] = config.authorization_server + strategy.options[:target_account_id] = config.target_account_id + strategy.options[:allow_silent_authentication] = config.allow_silent_authentication + strategy.options[:client_options].authorize_url = "#{strategy.options[:oauth_base_uri]}/oauth/auth" + strategy.options[:client_options].user_info_url = "#{strategy.options[:oauth_base_uri]}/oauth/userinfo" + strategy.options[:client_options].token_url = "#{strategy.options[:oauth_base_uri]}/oauth/token" + strategy.options[:authorize_params].prompt = strategy.options.prompt unless strategy.options[:allow_silent_authentication] + session = strategy.session + + unless session[:pkce_failed] + strategy.options[:pkce] = true + end + + case session[:api] + when 'Rooms' + strategy.options[:authorize_params].scope = 'signature dtr.rooms.read dtr.rooms.write dtr.documents.read dtr.documents.write dtr.profile.read dtr.profile.write dtr.company.read dtr.company.write room_forms' + when 'Click' + strategy.options[:authorize_params].scope = 'signature click.manage click.send' + when 'Admin' + strategy.options[:authorize_params].scope = 'signature organization_read group_read permission_read user_read user_write account_read domain_read identity_provider_read user_data_redact asset_group_account_read asset_group_account_clone_write asset_group_account_clone_read' + end + } +end diff --git a/quick_acg/config/puma.rb b/quick_acg/config/puma.rb new file mode 100644 index 0000000..e9dc159 --- /dev/null +++ b/quick_acg/config/puma.rb @@ -0,0 +1,43 @@ +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers: a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum; this matches the default thread size of Active Record. +# +max_threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 } +min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count } +threads min_threads_count, max_threads_count + +# Specifies the `worker_timeout` threshold that Puma will use to wait before +# terminating a worker in development environments. +# +worker_timeout 3600 if ENV.fetch('RAILS_ENV', 'development') == 'development' + +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +# +port ENV.fetch('PORT') { 3000 } + +# Specifies the `environment` that Puma will run in. +# +environment ENV.fetch('RAILS_ENV') { 'development' } + +# Specifies the `pidfile` that Puma will use. +pidfile ENV.fetch('PIDFILE') { 'tmp/pids/server.pid' } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked web server processes. If using threads and workers together +# the concurrency of the application would be max `threads` * `workers`. +# Workers do not work on JRuby or Windows (both of which do not support +# processes). +# +# workers ENV.fetch("WEB_CONCURRENCY") { 2 } + +# Use the `preload_app!` method when specifying a `workers` number. +# This directive tells Puma to first boot the application and load code +# before forking the application. This takes advantage of Copy On Write +# process behavior so workers use less memory. +# +# preload_app! + +# Allow puma to be restarted by `bin/rails restart` command. +plugin :tmp_restart diff --git a/quick_acg/config/routes.rb b/quick_acg/config/routes.rb new file mode 100644 index 0000000..99c540a --- /dev/null +++ b/quick_acg/config/routes.rb @@ -0,0 +1,43 @@ +require_relative '../../app/controllers/application_controller' +require_relative '../../app/controllers/eg_controller' +require_relative '../../app/controllers/session_controller' +require_relative '../../app/services/api_creator' +require_relative '../../app/controllers/eeg001_embedded_signing_controller' +require_relative '../../app/services/eg001_embedded_signing_service' +require_relative '../../app/services/utils' + +class ESign +end + +require_relative '../../app/controllers/e_sign/eeg041_cfr_embedded_signing_controller' +require_relative '../../app/services/e_sign/eg041_cfr_embedded_signing_service' +require_relative '../../app/services/e_sign/get_data_service' + +Rails.application.routes.draw do + root 'ds_common#index' + + get '/eeg001' => 'eeg001_embedded_signing#get' + post '/eeg001' => 'eeg001_embedded_signing#create' + + scope module: 'e_sign' do + get 'eeg041' => 'eeg041_cfr_embedded_signing#get' + post 'eeg041' => 'eeg041_cfr_embedded_signing#create' + end + # Login starts with POST'ing to: /auth/docusign + # /auth/docusign is an internal route created by OmniAuth and the docusign strategy from: /lib/docusign.rb + # Should be POST, see: https://nvd.nist.gov/vuln/detail/CVE-2015-9284 + # get '/ds/login' => redirect('/auth/docusign') + + # Handle OmniAuth OAuth2 login callback result that includes the AuthHash + get '/auth/:provider/callback', to: 'session#create' + + # Handle OmniAuth OAuth2 login exceptions + get '/auth/failure', to: 'session#omniauth_failure' + + get '/ds_common-return' => 'ds_common#index' + + get '/ds/mustAuthenticate' => 'ds_common#ds_must_authenticate' + post '/ds/mustAuthenticate' => 'ds_common#ds_must_authenticate' + + # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html +end diff --git a/quick_acg/lib/docusign.rb b/quick_acg/lib/docusign.rb new file mode 100644 index 0000000..3617969 --- /dev/null +++ b/quick_acg/lib/docusign.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +# https://github.com/omniauth/omniauth-oauth2 +require 'omniauth-oauth2' + +module OmniAuth + module Strategies + class Docusign < OmniAuth::Strategies::OAuth2 + # The name of the strategy, used in config/initializer/omniauth.rb + option :name, 'docusign' + + # These are called after the OAuth2 login authentication has succeeded and are part of the Docusign callback response message: + # transforms the Docusign login response from the raw_info https://github.com/omniauth/omniauth/wiki/Strategy-Contribution-Guide#defining-the-callback-phase + # into the standardized schema required by OmniAuth https://github.com/omniauth/omniauth/wiki/Auth-Hash-Schema + # and gets exposed through the "request.env[omniauth.auth]" to the SessionController#create + uid { raw_info['sub'] } + + info do + { + name: raw_info['name'], + email: raw_info['email'], + first_name: raw_info['given_name'], + last_name: raw_info['family_name'] + } + end + + extra do + { + sub: raw_info['sub'], + account_id: @account['account_id'], + account_name: @account['account_name'], + base_uri: @account['base_uri'] + } + end + + private + + # @returns a Hash with the keys: + # sub, name, given_name, family_name, created, email, accounts: [account_id, is_default, account_name, base_uri] + def raw_info + return @raw_info if @raw_info + + @raw_info = access_token.get(options.client_options.user_info_url.to_s).parsed || {} + fetch_account(@raw_info['accounts']) if @raw_info.present? + @raw_info + end + + # @param items is an array of Hash'es that has the keys: account_id, is_default, account_name, base_uri + def fetch_account(items) + @account = if options.target_account_id + items.find { |item| item['account_id'] == options.target_account_id } + else + items.find { |item| item['is_default'] } + end + + raise %(Could not find account information for the user in the "accounts" of raw_info: #{@raw_info}) if @account.blank? + end + end + end +end diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb deleted file mode 100644 index 652febb..0000000 --- a/test/application_system_test_case.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class ApplicationSystemTestCase < ActionDispatch::SystemTestCase - driven_by :selenium, using: :chrome, screen_size: [1400, 1400] -end diff --git a/test/controllers/.keep b/test/controllers/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/controllers/ds_controller_test.rb b/test/controllers/ds_controller_test.rb deleted file mode 100644 index fefb0f4..0000000 --- a/test/controllers/ds_controller_test.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class DsControllerTest < ActionDispatch::IntegrationTest - test 'should get ds_return' do - get ds_ds_return_url - assert_response :success - end -end diff --git a/test/controllers/eg002_signing_via_email_controller_test.rb b/test/controllers/eg002_signing_via_email_controller_test.rb deleted file mode 100644 index 40b54b4..0000000 --- a/test/controllers/eg002_signing_via_email_controller_test.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class Eg002SigningViaEmailControllerTest < ActionDispatch::IntegrationTest - test 'should get get' do - get eg002_signing_via_email_get_url - assert_response :success - end - - test 'should get create' do - get eg002_signing_via_email_create_url - assert_response :success - end -end diff --git a/test/controllers/eg004_envelope_info_controller_test.rb b/test/controllers/eg004_envelope_info_controller_test.rb deleted file mode 100644 index b822342..0000000 --- a/test/controllers/eg004_envelope_info_controller_test.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class Eg004EnvelopeInfoControllerTest < ActionDispatch::IntegrationTest - test 'should get get' do - get eg004_envelope_info_get_url - assert_response :success - end - - test 'should get create' do - get eg004_envelope_info_create_url - assert_response :success - end -end diff --git a/test/controllers/eg005_envelope_recipients_controller_test.rb b/test/controllers/eg005_envelope_recipients_controller_test.rb deleted file mode 100644 index 63265ee..0000000 --- a/test/controllers/eg005_envelope_recipients_controller_test.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class Eg005EnvelopeRecipientsControllerTest < ActionDispatch::IntegrationTest - test 'should get get' do - get eg005_envelope_recipients_get_url - assert_response :success - end - - test 'should get create' do - get eg005_envelope_recipients_create_url - assert_response :success - end -end diff --git a/test/controllers/eg010_send_binary_docs_controller_test.rb b/test/controllers/eg010_send_binary_docs_controller_test.rb deleted file mode 100644 index f791f1b..0000000 --- a/test/controllers/eg010_send_binary_docs_controller_test.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class Eg010SendBinaryDocsControllerTest < ActionDispatch::IntegrationTest - test 'should get get' do - get eg010_send_binary_docs_get_url - assert_response :success - end - - test 'should get create' do - get eg010_send_binary_docs_create_url - assert_response :success - end -end diff --git a/test/controllers/eg_controller_test.rb b/test/controllers/eg_controller_test.rb deleted file mode 100644 index 26c5fcb..0000000 --- a/test/controllers/eg_controller_test.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class EgControllerTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end diff --git a/test/controllers/embedded_signing_controller_test.rb b/test/controllers/embedded_signing_controller_test.rb deleted file mode 100644 index 4a682d2..0000000 --- a/test/controllers/embedded_signing_controller_test.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class EmbeddedSigningControllerTest < ActionDispatch::IntegrationTest - test 'should get get' do - get embedded_signing_get_url - assert_response :success - end - - test 'should get create' do - get embedded_signing_create_url - assert_response :success - end -end diff --git a/test/controllers/get_controller_test.rb b/test/controllers/get_controller_test.rb deleted file mode 100644 index 7d59028..0000000 --- a/test/controllers/get_controller_test.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class GetControllerTest < ActionDispatch::IntegrationTest - test 'should get create' do - get get_create_url - assert_response :success - end -end diff --git a/test/controllers/home_controller_test.rb b/test/controllers/home_controller_test.rb deleted file mode 100644 index 77f010f..0000000 --- a/test/controllers/home_controller_test.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class HomeControllerTest < ActionDispatch::IntegrationTest - test 'should get index' do - get home_index_url - assert_response :success - end -end diff --git a/test/controllers/list_envelopes_controller_test.rb b/test/controllers/list_envelopes_controller_test.rb deleted file mode 100644 index 375f5e1..0000000 --- a/test/controllers/list_envelopes_controller_test.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class ListEnvelopesControllerTest < ActionDispatch::IntegrationTest - test 'should get create' do - get list_envelopes_create_url - assert_response :success - end -end diff --git a/test/eg001_create_clickwrap_test.rb b/test/eg001_create_clickwrap_test.rb new file mode 100644 index 0000000..f502b36 --- /dev/null +++ b/test/eg001_create_clickwrap_test.rb @@ -0,0 +1,31 @@ +require 'date' +require 'rubygems' +require 'test/unit' +require_relative './test_helper' +require_relative '../app/services/api_creator' +require_relative '../app/services/clickwrap/eg001_create_clickwrap_service' + +class Eg001CreateClickwrapTest < TestHelper + setup do + setup_test_data [api_type[:click]] + + args = { + account_id: @account_id, + base_path: @base_path, + access_token: @access_token, + doc_pdf: @data[:term_of_service], + clickwrap_name: "#{@data[:clickwrap_name]}_#{Time.now.strftime("%s%L")}" + } + + @ceg001 = Clickwrap::Eg001CreateClickwrapService.new(args) + end + + test 'should correctly create clickwrap if correct data is provided' do + results = @ceg001.worker + + TestData.set_clickwrap_id(results.clickwrap_id) + + assert_not_nil results + assert_not_empty results.clickwrap_id + end +end diff --git a/test/eg001_embedded_signing_test.rb b/test/eg001_embedded_signing_test.rb new file mode 100644 index 0000000..95a4039 --- /dev/null +++ b/test/eg001_embedded_signing_test.rb @@ -0,0 +1,83 @@ +require 'rubygems' +require 'test/unit' +require 'json' +require_relative 'test_helper' +require_relative '../app/services/api_creator' +require_relative '../app/services/eg001_embedded_signing_service' + +class Eg001EmbeddedSigningTest < TestHelper + setup do + setup_test_data [api_type[:e_sign]] + + args = { + account_id: @account_id, + base_path: @base_path, + access_token: @access_token, + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + ds_ping_url: @data[:ds_ping_url], + signer_client_id: @data[:signer_client_id], + pdf_filename: @data[:doc_pdf] + } + + @eg001_embedded_signing_service = Eg001EmbeddedSigningService.new(args) + end + + test 'should create and send an envelope if correct data is provided' do + redirect_url = @eg001_embedded_signing_service.worker + + assert_not_nil redirect_url + assert_not_empty redirect_url + end + + test 'should create correct envelope definition if correct data is provided' do + expected_document = create_ds_document(@data[:doc_pdf], 'Lorem Ipsum', 'pdf', '1') + + expected_sign_here_tab = DocuSign_eSign::SignHere.new + expected_sign_here_tab.anchor_string = '/sn1/' + expected_sign_here_tab.anchor_units = 'pixels' + expected_sign_here_tab.anchor_x_offset = '20' + expected_sign_here_tab.anchor_y_offset = '10' + + expected_tabs = DocuSign_eSign::Tabs.new + expected_tabs.sign_here_tabs = [expected_sign_here_tab] + + expected_signer = DocuSign_eSign::Signer.new + expected_signer.email = @config['signer_email'] + expected_signer.name = @config['signer_name'] + expected_signer.client_user_id = @data[:signer_client_id] + expected_signer.recipient_id = 1 + + expected_signer.tabs = expected_tabs + + expected_recipient = DocuSign_eSign::Recipients.new + expected_recipient.signers = [expected_signer] + + expected_envelope = DocuSign_eSign::EnvelopeDefinition.new + expected_envelope.email_subject = 'Please sign this document sent from Ruby SDK' + expected_envelope.documents = [expected_document] + expected_envelope.recipients = expected_recipient + expected_envelope.status = 'sent' + + envelope = @eg001_embedded_signing_service.send(:make_envelope, @data[:signer_client_id], @data[:doc_pdf], @config['signer_email'], @config['signer_name']) + + assert_not_nil envelope + assert_equal expected_envelope, envelope + end + + test 'should create correct recipient view if correct data is provided' do + expected_view_request = DocuSign_eSign::RecipientViewRequest.new + expected_view_request.return_url = "#{@data[:ds_return_url]}?state=123" + expected_view_request.authentication_method = 'none' + expected_view_request.email = @config['signer_email'] + expected_view_request.user_name = @config['signer_name'] + expected_view_request.client_user_id = @data[:signer_client_id] + expected_view_request.ping_frequency = '600' + expected_view_request.ping_url = @data[:ds_ping_url] + + recipient_view = @eg001_embedded_signing_service.send(:make_recipient_view_request, @data[:signer_client_id], @data[:ds_return_url], @data[:ds_ping_url], @config['signer_email'], @config['signer_name']) + + assert_not_nil recipient_view + assert_equal expected_view_request, recipient_view + end +end diff --git a/test/eg002_activate_clickwrap_test.rb b/test/eg002_activate_clickwrap_test.rb new file mode 100644 index 0000000..0ae85ab --- /dev/null +++ b/test/eg002_activate_clickwrap_test.rb @@ -0,0 +1,39 @@ +require 'date' +require 'rubygems' +require 'test/unit' +require_relative './test_helper' +require_relative '../app/services/api_creator' +require_relative '../app/services/clickwrap/eg002_activate_clickwrap_service' + +class Eg002ActivateClickwrapTest < TestHelper + setup do + setup_test_data [api_type[:click]] + + args = { + account_id: @account_id, + base_path: @base_path, + access_token: @access_token, + + ds_account_id: @account_id, + ds_base_path: @base_path, + ds_access_token: @access_token, + + clickwrap_id: TestData.get_clickwrap_id + } + + @ceg002 = Clickwrap::Eg002ActivateClickwrapService.new(args) + end + + test 'should correctly activate clickwrap if correct data is provided' do + results = @ceg002.worker + + assert_not_nil results + end + + test 'should get the list of inactive clickwraps if correct data is provided' do + statuses = %w[inactive draft] + results = @ceg002.get_inactive_clickwraps statuses + + assert_not_nil results + end +end diff --git a/test/eg002_sign_via_email_test.rb b/test/eg002_sign_via_email_test.rb new file mode 100644 index 0000000..b07fbd3 --- /dev/null +++ b/test/eg002_sign_via_email_test.rb @@ -0,0 +1,139 @@ +require 'rubygems' +require 'test/unit' +require_relative './test_helper' +require_relative '../app/services/api_creator' +require_relative '../app/services/e_sign/eg002_signing_via_email_service' + +class Eg002SignViaEmailTest < TestHelper + setup do + setup_test_data [api_type[:e_sign]] + + envelope_args = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + cc_email: @data[:cc_email], + cc_name: @data[:cc_name], + status: 'sent', + doc_docx: @data[:doc_docx], + doc_pdf: @data[:doc_pdf] + } + @args = { + account_id: @account_id, + base_path: @base_path, + access_token: @access_token, + envelope_args: envelope_args + } + + @eg002 = ESign::Eg002SigningViaEmailService.new(@args) + end + + test 'should create and send an envelope if correct data is provided' do + results = @eg002.worker + + assert_not_nil results + assert_not_empty results + assert_not_nil results['envelope_id'] + assert_not_empty results['envelope_id'] + end + + test 'should create correct envelope definition if correct data is provided' do + expected_html_document = create_ds_document(expected_html_doc, 'Order acknowledgement', 'html', '1') + expected_docx_document = create_ds_document(@data[:doc_docx], 'Battle Plan', 'docx', '2') + expected_pdf_document = create_ds_document(@data[:doc_pdf], 'Lorem Ipsum', 'pdf', '3') + + expected_sign_here_tab1 = DocuSign_eSign::SignHere.new + expected_sign_here_tab1.anchor_string = '**signature_1**' + expected_sign_here_tab1.anchor_units = 'pixels' + expected_sign_here_tab1.anchor_x_offset = '20' + expected_sign_here_tab1.anchor_y_offset = '10' + + expected_sign_here_tab2 = DocuSign_eSign::SignHere.new + expected_sign_here_tab2.anchor_string = '/sn1/' + expected_sign_here_tab2.anchor_units = 'pixels' + expected_sign_here_tab2.anchor_x_offset = '20' + expected_sign_here_tab2.anchor_y_offset = '10' + + expected_tabs = DocuSign_eSign::Tabs.new + expected_tabs.sign_here_tabs = [expected_sign_here_tab1, expected_sign_here_tab2] + + expected_signer = DocuSign_eSign::Signer.new + expected_signer.email = @config['signer_email'] + expected_signer.name = @config['signer_name'] + expected_signer.recipient_id = '1' + expected_signer.routing_order = '1' + + expected_signer.tabs = expected_tabs + + expected_cc = DocuSign_eSign::CarbonCopy.new + expected_cc.email = @data[:cc_email] + expected_cc.name = @data[:cc_name] + expected_cc.routing_order = '2' + expected_cc.recipient_id = '2' + + expected_recipient = DocuSign_eSign::Recipients.new + expected_recipient.signers = [expected_signer] + expected_recipient.carbon_copies = [expected_cc] + + expected_envelope = DocuSign_eSign::EnvelopeDefinition.new + expected_envelope.email_subject = 'Please sign this document set' + expected_envelope.documents = [expected_html_document, expected_docx_document, expected_pdf_document] + expected_envelope.recipients = expected_recipient + expected_envelope.status = 'sent' + + envelope_args = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + cc_email: @data[:cc_email], + cc_name: @data[:cc_name], + status: 'sent', + doc_docx: @data[:doc_docx], + doc_pdf: @data[:doc_pdf] + } + + envelope = @eg002.send(:make_envelope, envelope_args) + + assert_not_nil envelope + assert_equal expected_envelope, envelope + end + + test 'should create correct html document if correct data is provided' do + envelope_args = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + cc_email: @data[:cc_email], + cc_name: @data[:cc_name] + } + + html_document = @eg002.send(:create_document1, envelope_args) + + assert_not_nil html_document + assert_not_empty html_document + assert_equal expected_html_doc, html_document + end + + def expected_html_doc + " + + + + + + +

World Wide Corp

+

Order Processing Division

+

Ordered by #{@config['signer_name']}

+

Email: #{@config['signer_email']}

+

Copy to: #{@data[:cc_name]}, #{@data[:cc_email]}

+

+ 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**/

+ + " + end +end + diff --git a/test/eg008_create_template_test.rb b/test/eg008_create_template_test.rb new file mode 100644 index 0000000..0d7e498 --- /dev/null +++ b/test/eg008_create_template_test.rb @@ -0,0 +1,144 @@ +require 'rubygems' +require 'test/unit' +require_relative './test_helper' +require_relative '../app/services/api_creator' +require_relative '../app/services/e_sign/eg008_create_template_service' + +class Eg008CreateTemplateTest < TestHelper + setup do + setup_test_data [api_type[:e_sign]] + + args = { + account_id: @account_id, + base_path: @base_path, + access_token: @access_token, + template_name: @data[:template_name] + } + + @eg008 = ESign::Eg008CreateTemplateService.new(args) + end + + test 'should create template if correct data is provided' do + results = @eg008.worker + + TestData.set_template_id results[:template_id] + + assert_not_nil results + assert_not_nil results[:template_id] + assert_not_nil results[:template_name] + assert_not_empty results[:template_id] + assert_not_empty results[:template_name] + end + + test 'should create correct template definition if correct data is provided' do + expected_document = create_ds_document(@data[:doc_for_template], 'Lorem Ipsum', 'pdf', '1') + + expected_sign_here = DocuSign_eSign::SignHere.new + expected_sign_here.document_id = '1' + expected_sign_here.page_number = '1' + expected_sign_here.x_position = '191' + expected_sign_here.y_position = '148' + + expected_check1 = DocuSign_eSign::Checkbox.new( + 'documentId' => '1', 'pageNumber' => '1', + 'xPosition' => '75', 'yPosition' => '417', 'tabLabel' => 'ckAuthorization' + ) + + expected_check2 = DocuSign_eSign::Checkbox.new( + 'documentId' => '1', 'pageNumber' => '1', + 'xPosition' => '75', 'yPosition' => '447', 'tabLabel' => 'ckAuthentication' + ) + expected_check3 = DocuSign_eSign::Checkbox.new( + 'documentId' => '1', 'pageNumber' => '1', + 'xPosition' => '75', 'yPosition' => '478', 'tabLabel' => 'ckAgreement' + ) + expected_check4 = DocuSign_eSign::Checkbox.new( + 'documentId' => '1', 'pageNumber' => '1', + 'xPosition' => '75', 'yPosition' => '508', 'tabLabel' => 'ckAcknowledgement' + ) + + expected_list1 = DocuSign_eSign::List.new( + 'documentId' => '1', + 'pageNumber' => '1', + 'xPosition' => '142', + 'yPosition' => '291', + 'font' => 'helvetica', + 'fontSize' => 'size14', + 'tabLabel' => 'list', + 'required' => 'false', + 'listItems' => [ + DocuSign_eSign::ListItem.new('text' => 'Red', 'value' => 'red'), + DocuSign_eSign::ListItem.new('text' => 'Orange', 'value' => 'orange'), + DocuSign_eSign::ListItem.new('text' => 'Yellow', 'value' => 'yellow'), + DocuSign_eSign::ListItem.new('text' => 'Green', 'value' => 'green'), + DocuSign_eSign::ListItem.new('text' => 'Blue', 'value' => 'blue'), + DocuSign_eSign::ListItem.new('text' => 'Indigo', 'value' => 'indigo'), + DocuSign_eSign::ListItem.new('text' => 'Violet', 'value' => 'violet') + ] + ) + + expected_numerical = DocuSign_eSign::Numerical.new( + 'documentId' => '1', 'pageNumber' => '1', + 'xPosition' => '163', 'yPosition' => '260', + 'font' => 'helvetica', 'fontSize' => 'size14', 'validationType' => 'Currency', + 'tabLabel' => 'numericalCurrency', 'width' => '84', 'required' => 'false' + ) + expected_radio_group = DocuSign_eSign::RadioGroup.new( + 'documentId' => '1', 'groupName' => 'radio1', + 'radios' => [ + DocuSign_eSign::Radio.new('pageNumber' => '1', 'xPosition' => '142', + 'yPosition' => '384', 'value' => 'white', + 'required' => 'false'), + DocuSign_eSign::Radio.new('pageNumber' => '1', 'xPosition' => '74', + 'yPosition' => '384', 'value' => 'red', + 'required' => 'false'), + DocuSign_eSign::Radio.new('pageNumber' => '1', 'xPosition' => '220', + 'yPosition' => '384', 'value' => 'blue', + 'required' => 'false') + ] + ) + + expected_text = DocuSign_eSign::Text.new( + 'documentId' => '1', 'pageNumber' => '1', + 'xPosition' => '153', 'yPosition' => '230', + 'font' => 'helvetica', 'fontSize' => 'size14', + 'tabLabel' => 'text', 'height' => '23', + 'width' => '84', 'required' => 'false' + ) + + expected_tabs = DocuSign_eSign::Tabs.new + expected_tabs.sign_here_tabs = [expected_sign_here] + expected_tabs.checkbox_tabs = [expected_check1, expected_check2, expected_check3, expected_check4] + expected_tabs.list_tabs = [expected_list1] + expected_tabs.numerical_tabs = [expected_numerical] + expected_tabs.radio_group_tabs = [expected_radio_group] + expected_tabs.text_tabs = [expected_text] + + expected_signer = DocuSign_eSign::Signer.new + expected_signer.role_name = 'signer' + expected_signer.recipient_id = '1' + expected_signer.routing_order = '1' + expected_signer.tabs = expected_tabs + + expected_cc = DocuSign_eSign::CarbonCopy.new + expected_cc.role_name = 'cc' + expected_cc.recipient_id = '2' + expected_cc.routing_order = '2' + + expected_recipients = DocuSign_eSign::Recipients.new + expected_recipients.signers = [expected_signer] + expected_recipients.carbon_copies = [expected_cc] + + expected_template = DocuSign_eSign::EnvelopeTemplate.new + expected_template.documents = [expected_document] + expected_template.name = @data[:template_name] + expected_template.email_subject = 'Please sign this document' + expected_template.recipients = expected_recipients + expected_template.status = 'created' + + template = @eg008.send(:make_template_req) + + assert_not_nil template + assert_equal expected_template, template + end +end diff --git a/test/eg009_use_template_test.rb b/test/eg009_use_template_test.rb new file mode 100644 index 0000000..f99afad --- /dev/null +++ b/test/eg009_use_template_test.rb @@ -0,0 +1,82 @@ +require 'rubygems' +require 'test/unit' +require_relative './test_helper' +require_relative '../app/services/api_creator' +require_relative '../app/services/e_sign/eg009_use_template_service' + +class Eg009UseTemplateTest < TestHelper + setup do + setup_test_data [api_type[:e_sign]] + + envelope_args = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + cc_email: @data[:cc_email], + cc_name: @data[:cc_name], + template_id: TestData.get_template_id + } + args = { + account_id: @account_id, + base_path: @base_path, + access_token: @access_token, + envelope_args: envelope_args + } + + @eg009 = ESign::Eg009UseTemplateService.new(args) + end + + test 'should create the envelope with template if correct data is provided' do + results = @eg009.worker + + assert_not_nil results + assert_not_nil results[:envelope_id] + assert_not_empty results[:envelope_id] + end + + test 'should create correct envelope definition if correct data is provided' do + expected_check1 = DocuSign_eSign::Checkbox.new + expected_check1.tab_label = 'ckAuthorization' + expected_check1.selected = true + + expected_check3 = DocuSign_eSign::Checkbox.new + expected_check3.tab_label = 'ckAgreement' + expected_check3.selected = true + + expected_text = DocuSign_eSign::Text.new + expected_text.tab_label = 'text' + expected_text.value = 'Jabberwocky!' + + expected_tabs = DocuSign_eSign::Tabs.new + expected_tabs.checkbox_tabs = [expected_check1, expected_check3] + expected_tabs.text_tabs = [expected_text] + + expected_signer = DocuSign_eSign::TemplateRole.new + expected_signer.email = @config['signer_email'] + expected_signer.name = @config['signer_name'] + expected_signer.role_name = 'signer' + expected_signer.tabs = expected_tabs + + expected_cc = DocuSign_eSign::TemplateRole.new + expected_cc.email = @data[:cc_email] + expected_cc.name = @data[:cc_name] + expected_cc.role_name = 'cc' + + expected_envelope = DocuSign_eSign::EnvelopeDefinition.new + expected_envelope.status = 'sent' + expected_envelope.template_id = TestData.get_template_id + expected_envelope.template_roles = [expected_signer, expected_cc] + + envelope_args = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + cc_email: @data[:cc_email], + cc_name: @data[:cc_name], + template_id: TestData.get_template_id + } + + envelope = @eg009.send(:make_envelope, envelope_args) + + assert_not_nil envelope + assert_equal expected_envelope, envelope + end +end diff --git a/test/eg013_add_doc_to_template_test.rb b/test/eg013_add_doc_to_template_test.rb new file mode 100644 index 0000000..7b9b71d --- /dev/null +++ b/test/eg013_add_doc_to_template_test.rb @@ -0,0 +1,169 @@ +require 'rubygems' +require 'test/unit' +require_relative './test_helper' +require_relative '../app/services/api_creator' +require_relative '../app/services/e_sign/eg013_add_doc_to_template_service' + +class Eg013AddDocToTemplateTest < TestHelper + setup do + setup_test_data [api_type[:e_sign]] + + envelope_args = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + cc_email: @data[:cc_email], + cc_name: @data[:cc_name], + item: @data[:item], + quantity: @data[:quantity], + signer_client_id: @data[:signer_client_id], + template_id: TestData.get_template_id, + ds_return_url: @data[:ds_return_url] + } + args = { + account_id: @account_id, + base_path: @base_path, + access_token: @access_token, + envelope_args: envelope_args + } + + @eg013 = ESign::Eg013AddDocToTemplateService.new(args) + end + + test 'should correctly add document to a template if correct data is provided' do + results = @eg013.worker + + assert_not_nil results + assert_not_nil results[:envelope_id] + assert_not_empty results[:envelope_id] + assert_not_nil results[:redirect_url] + assert_not_empty results[:redirect_url] + end + + test 'should create the correct envelope definition if correct data is provided' do + expected_signer1 = DocuSign_eSign::Signer.new + expected_signer1.email = @config['signer_email'] + expected_signer1.name = @config['signer_name'] + expected_signer1.role_name = 'signer' + expected_signer1.recipient_id = '1' + expected_signer1.client_user_id = @data[:signer_client_id] + + expected_cc1 = DocuSign_eSign::CarbonCopy.new + expected_cc1.email = @data[:cc_email] + expected_cc1.name = @data[:cc_name] + expected_cc1.role_name = 'cc' + expected_cc1.recipient_id = '2' + + expected_recipients_server_template = DocuSign_eSign::Recipients.new + expected_recipients_server_template.carbon_copies = [expected_cc1] + expected_recipients_server_template.signers = [expected_signer1] + + expected_server_template = DocuSign_eSign::ServerTemplate.new + expected_server_template.sequence = '1' + expected_server_template.template_id = TestData.get_template_id + + expected_inline_template1 = DocuSign_eSign::InlineTemplate.new + expected_inline_template1.sequence = '2' + expected_inline_template1.recipients = expected_recipients_server_template + + expected_comp_template1 = DocuSign_eSign::CompositeTemplate.new + expected_comp_template1.server_templates = [expected_server_template] + expected_comp_template1.inline_templates = [expected_inline_template1] + + expected_sign_here1 = DocuSign_eSign::SignHere.new + expected_sign_here1.anchor_string = '**signature_1**' + expected_sign_here1.anchor_y_offset = '10' + expected_sign_here1.anchor_units = 'pixels' + expected_sign_here1.anchor_x_offset = '20' + + expected_signer1_tabs = DocuSign_eSign::Tabs.new + expected_signer1_tabs.sign_here_tabs = [expected_sign_here1] + + expected_signer1_added_doc = DocuSign_eSign::Signer.new + expected_signer1_added_doc.email = @config['signer_email'] + expected_signer1_added_doc.name = @config['signer_name'] + expected_signer1_added_doc.role_name = 'signer' + expected_signer1_added_doc.recipient_id = '1' + expected_signer1_added_doc.client_user_id = @data[:signer_client_id] + expected_signer1_added_doc.tabs = expected_signer1_tabs + + expected_recipients_added_doc = DocuSign_eSign::Recipients.new + expected_recipients_added_doc.carbon_copies = [expected_cc1] + expected_recipients_added_doc.signers = [expected_signer1_added_doc] + + expected_doc = create_ds_document(expected_html_doc, 'Appendix 1--Sales order', 'html', '1') + + expected_inline_template2 = DocuSign_eSign::InlineTemplate.new + expected_inline_template2.sequence = '1' + expected_inline_template2.recipients = expected_recipients_added_doc + + expected_comp_template2 = DocuSign_eSign::CompositeTemplate.new + expected_comp_template2.composite_template_id = '2' + expected_comp_template2.inline_templates = [expected_inline_template2] + expected_comp_template2.document = expected_doc + + expected_envelope = DocuSign_eSign::EnvelopeDefinition.new + expected_envelope.status = 'sent' + expected_envelope.composite_templates = [expected_comp_template1, expected_comp_template2] + + envelope_args = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + cc_email: @data[:cc_email], + cc_name: @data[:cc_name], + item: @data[:item], + quantity: @data[:quantity], + signer_client_id: @data[:signer_client_id], + template_id: TestData.get_template_id, + ds_return_url: @data[:ds_return_url] + } + + envelope = @eg013.send(:make_envelope, envelope_args) + + assert_not_nil envelope + assert_equal expected_envelope, envelope + end + + test 'should return correct HTML document if correct data is provided' do + envelope_args = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + cc_email: @data[:cc_email], + cc_name: @data[:cc_name], + item: @data[:item], + quantity: @data[:quantity] + } + + html_doc = @eg013.send(:create_document1, envelope_args) + + assert_not_nil html_doc + assert_not_empty html_doc + assert_equal expected_html_doc, html_doc + end + + def expected_html_doc + <<~HEREDOC + + + + + + +

World Wide Corp

+

Order Processing Division

+

Ordered by #{@config['signer_name']}

+

Email: #{@config['signer_email']}

+

Copy to: #{@data[:cc_name]}, #{@data[:cc_email]}

+

Item: #{@data[:item]}, quantity: #{@data[: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**/

+ + + HEREDOC + end +end diff --git a/test/eg017_set_template_tab_values_test.rb b/test/eg017_set_template_tab_values_test.rb new file mode 100644 index 0000000..e818293 --- /dev/null +++ b/test/eg017_set_template_tab_values_test.rb @@ -0,0 +1,131 @@ +require 'rubygems' +require 'test/unit' +require_relative './test_helper' +require_relative '../app/services/api_creator' +require_relative '../app/services/e_sign/eg017_set_template_tab_values_service' + +class Eg017SetTemplateTabValuesTest < TestHelper + setup do + setup_test_data [api_type[:e_sign]] + + envelope_args = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + cc_email: @data[:cc_email], + cc_name: @data[:cc_name], + template_id: TestData.get_template_id, + ds_ping_url: @data[:ds_ping_url], + } + args = { + account_id: @account_id, + base_path: @base_path, + access_token: @access_token, + envelope_args: envelope_args + } + + @eg017 = ESign::Eg017SetTemplateTabValuesService.new(args) + end + + test 'should correctly set the tab values of template if correct data is provided' do + results = @eg017.worker + + assert_not_nil results + assert_not_empty results + end + + test 'should create correct envelope definition if correct data is provided' do + expected_list1 = DocuSign_eSign::List.new + expected_list1.value = 'Green' + expected_list1.document_id = '1' + expected_list1.page_number = '1' + expected_list1.tab_label = 'list' + + expected_check1 = DocuSign_eSign::Checkbox.new + expected_check1.tab_label = 'ckAuthorization' + expected_check1.selected = 'true' + + expected_check3 = DocuSign_eSign::Checkbox.new + expected_check3.tab_label = 'ckAgreement' + expected_check3.selected = 'true' + + expected_radio = DocuSign_eSign::Radio.new + expected_radio.value = 'white' + expected_radio.selected = 'true' + + expected_radio_group = DocuSign_eSign::RadioGroup.new + expected_radio_group.group_name = 'radio1' + expected_radio_group.radios = [expected_radio] + + expected_text = DocuSign_eSign::Text.new + expected_text.tab_label = 'text' + expected_text.value = 'Jabberwocky!' + + expected_text_extra = DocuSign_eSign::Text.new + expected_text_extra.document_id = '1' + expected_text_extra.page_number = '1' + expected_text_extra.x_position = '280' + expected_text_extra.y_position = '172' + expected_text_extra.font = 'helvetica' + expected_text_extra.font_size = 'size14' + expected_text_extra.tab_label = 'added text field' + expected_text_extra.height = '23' + expected_text_extra.width = '84' + expected_text_extra.required = 'false' + expected_text_extra.bold = 'true' + expected_text_extra.value = @config['signer_name'] + expected_text_extra.locked = 'false' + expected_text_extra.tab_id = 'name' + + expected_tabs = DocuSign_eSign::Tabs.new + expected_tabs.list_tabs = [expected_list1] + expected_tabs.checkbox_tabs = [expected_check1, expected_check3] + expected_tabs.radio_group_tabs = [expected_radio_group] + expected_tabs.text_tabs = [expected_text, expected_text_extra] + + expected_signer = DocuSign_eSign::TemplateRole.new + expected_signer.client_user_id = 1000 + expected_signer.email = @config['signer_email'] + expected_signer.name = @config['signer_name'] + expected_signer.role_name = 'signer' + expected_signer.tabs = expected_tabs + + expected_cc = DocuSign_eSign::TemplateRole.new + expected_cc.email = @data[:cc_email] + expected_cc.name = @data[:cc_name] + expected_cc.role_name = 'cc' + + expected_envelope = DocuSign_eSign::EnvelopeDefinition.new + expected_envelope.template_roles = [expected_signer, expected_cc] + expected_envelope.status = 'sent' + expected_envelope.template_id = TestData.get_template_id + + envelope_args = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + cc_email: @data[:cc_email], + cc_name: @data[:cc_name], + template_id: TestData.get_template_id + } + + envelope = @eg017.send(:make_envelope, envelope_args) + + assert_not_nil envelope + assert_equal expected_envelope, envelope + end + + test 'should create correct recipient view request if correct data is provided' do + expected_view_request = DocuSign_eSign::RecipientViewRequest.new + expected_view_request.return_url = "#{@data[:ds_return_url]}?state=123" + expected_view_request.authentication_method = 'none' + expected_view_request.email = @config['signer_email'] + expected_view_request.user_name = @config['signer_name'] + expected_view_request.client_user_id = @data[:signer_client_id] + expected_view_request.ping_frequency = '600' + expected_view_request.ping_url = @data[:ds_ping_url] + + view_request = @eg017.send(:make_recipient_view_request, @config['signer_email'], @config['signer_name'], @data[:signer_client_id], @data[:ds_return_url], @data[:ds_ping_url]) + + assert_not_nil view_request + assert_equal expected_view_request, view_request + end +end diff --git a/test/eg024_permission_create_test.rb b/test/eg024_permission_create_test.rb new file mode 100644 index 0000000..7e4d3ad --- /dev/null +++ b/test/eg024_permission_create_test.rb @@ -0,0 +1,62 @@ +require 'date' +require 'rubygems' +require 'test/unit' +require_relative './test_helper' +require_relative '../app/services/api_creator' +require_relative '../app/services/e_sign/eg024_permission_create_service' + +class Eg024PermissionCreateTest < TestHelper + setup do + setup_test_data [api_type[:e_sign]] + + args = { + account_id: @account_id, + base_path: @base_path, + access_token: @access_token, + permission_profile_name: "#{@data[:permission_profile_name]}_#{Time.now.strftime("%s%L")}" + } + + @eg024 = ESign::Eg024PermissionCreateService.new(args) + end + + test 'should correctly create permission profile if correct data is provided' do + results = @eg024.worker + + assert_not_nil results + end + + test 'should create correct permission profile settings if correct data is provided' do + expected_permission_profile_settings = { + useNewDocuSignExperienceInterface: 0, + allowBulkSending: 'true', + allowEnvelopeSending: 'true', + allowSignerAttachments: 'true', + allowTaggingInSendAndCorrect: 'true', + allowWetSigningOverride: 'true', + allowedAddressBookAccess: 'personalAndShared', + allowedTemplateAccess: 'share', + enableRecipientViewingNotifications: 'true', + enableSequentialSigningInterface: 'true', + receiveCompletedSelfSignedDocumentsAsEmailLinks: 'false', + signingUiVersion: 'v2', + useNewSendingInterface: 'true', + allowApiAccess: 'true', + allowApiAccessToAccount: 'true', + allowApiSendingOnBehalfOfOthers: 'true', + allowApiSequentialSigning: 'true', + enableApiRequestLogging: 'true', + allowDocuSignDesktopClient: 'false', + allowSendersToSetRecipientEmailLanguage: 'true', + allowVaulting: 'false', + allowedToBeEnvelopeTransferRecipient: 'true', + enableTransactionPointIntegration: 'false', + powerFormRole: 'admin', + vaultingMode: 'none' + } + + permission_profile_settings = @eg024.send(:make_permission_profile_settings) + + assert_not_nil permission_profile_settings + assert_equal expected_permission_profile_settings, permission_profile_settings + end +end diff --git a/test/eg028_brands_creating_test.rb b/test/eg028_brands_creating_test.rb new file mode 100644 index 0000000..5d67b81 --- /dev/null +++ b/test/eg028_brands_creating_test.rb @@ -0,0 +1,33 @@ +require 'date' +require 'rubygems' +require 'test/unit' +require_relative './test_helper' +require_relative '../app/services/api_creator' +require_relative '../app/services/e_sign/eg028_brands_creating_service' + +class Eg028BrandsCreatingTest < TestHelper + setup do + setup_test_data [api_type[:e_sign]] + + args = { + account_id: @account_id, + base_path: @base_path, + access_token: @access_token, + brandName: "#{@data[:brand_name]}_#{Time.now.strftime("%s%L")}", + defaultBrandLanguage: @data[:default_brand_language] + } + + @eg028 = ESign::Eg028BrandsCreatingService.new(args) + end + + test 'should correctly create brand if correct data is provided' do + results = @eg028.worker + + TestData.set_brand_id results.brands[0].brand_id + + assert_not_nil results + assert_not_nil results.brands + assert_not_nil results.brands[0] + assert_not_empty results.brands[0].brand_id + end +end diff --git a/test/eg029_brands_apply_to_envelope_test.rb b/test/eg029_brands_apply_to_envelope_test.rb new file mode 100644 index 0000000..a95deed --- /dev/null +++ b/test/eg029_brands_apply_to_envelope_test.rb @@ -0,0 +1,86 @@ +require 'date' +require 'rubygems' +require 'test/unit' +require_relative './test_helper' +require_relative '../app/services/api_creator' +require_relative '../app/services/e_sign/eg029_brands_apply_to_envelope_service' + +class Eg029BrandsApplyToEnvelopeTest < TestHelper + setup do + setup_test_data [api_type[:e_sign]] + + envelope_args = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + brand_id: TestData.get_brand_id, + status: 'sent' + } + args = { + account_id: @account_id, + base_path: @base_path, + access_token: @access_token, + envelope_args: envelope_args + } + + @eg029 = ESign::Eg029BrandsApplyToEnvelopeService.new(args) + end + + test 'should correctly apply brand to envelope if correct data is provided' do + results = @eg029.worker + + assert_not_nil results + end + + test 'should correctly create envelope definition if correct data is provided' do + args = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + brand_id: TestData.get_brand_id, + status: 'sent' + } + + expected_document = create_ds_document(@data[:doc_pdf], 'NDA', 'pdf', '1') + + expected_signer = DocuSign_eSign::Signer.new + expected_signer.name = @config['signer_name'] + expected_signer.email = @config['signer_email'] + expected_signer.role_name = 'signer' + expected_signer.note = '' + expected_signer.routing_order = '1' + expected_signer.status = args[:status] + expected_signer.delivery_method = 'email' + expected_signer.recipient_id = '1' + + expected_sign_here = DocuSign_eSign::SignHere.new + expected_sign_here.document_id = '1' + expected_sign_here.name = 'SignHereTab' + expected_sign_here.page_number = '1' + expected_sign_here.recipient_id = '1' + expected_sign_here.tab_label = 'SignHereTab' + expected_sign_here.x_position = '75' + expected_sign_here.y_position = '572' + + expected_signer_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [expected_sign_here] + }) + expected_signer.tabs = expected_signer_tabs + + expected_recipients = DocuSign_eSign::Recipients.new( + signers: [expected_signer] + ) + + expected_envelope = DocuSign_eSign::EnvelopeDefinition.new + expected_envelope.email_blurb = 'Sample text for email body' + expected_envelope.email_subject = 'Please Sign' + expected_envelope.envelope_id_stamping = true + expected_envelope.brand_id = args[:brands] + expected_envelope.documents = [expected_document] + expected_envelope.recipients = expected_recipients + expected_envelope.status = 'sent' + + envelope = @eg029.send(:make_envelope, args) + + assert_not_nil envelope + assert_equal expected_envelope, envelope + end +end diff --git a/test/eg031_bulk_sending_envelopes_test.rb b/test/eg031_bulk_sending_envelopes_test.rb new file mode 100644 index 0000000..f32042a --- /dev/null +++ b/test/eg031_bulk_sending_envelopes_test.rb @@ -0,0 +1,173 @@ +require 'date' +require 'rubygems' +require 'test/unit' +require_relative './test_helper' +require_relative '../app/services/api_creator' +require_relative '../app/services/e_sign/eg031_bulk_sending_envelopes_service' + +class Eg031BulkSendingEnvelopesTest < TestHelper + setup do + setup_test_data [api_type[:e_sign]] + + signers = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + cc_email: @data[:cc_email], + cc_name: @data[:cc_name], + status: 'created', + + signer_email1: @data[:signer1_email], + signer_name1: @data[:signer1_name], + cc_email1: @data[:cc1_email], + cc_name1: @data[:cc1_name], + } + args = { + account_id: @account_id, + base_path: @base_path, + access_token: @access_token, + } + + @eg031 = ESign::Eg031BulkSendingEnvelopesService.new(args, signers) + end + + test 'should correctly bulk send the envelope if correct data is provided' do + results = @eg031.worker + + assert_not_nil results + end + + test 'should create correct bulk sending list if correct data is provided' do + args = { + signer_email: @config['signer_email'], + signer_name: @config['signer_name'], + cc_email: @data[:cc_email], + cc_name: @data[:cc_name], + status: 'created', + + signer_email1: @data[:signer1_email], + signer_name1: @data[:signer1_name], + cc_email1: @data[:cc1_email], + cc_name1: @data[:cc1_name], + } + + expected_bulk_sending_list = DocuSign_eSign::BulkSendingList.new( + name: 'sample.csv', + bulkCopies: [ + DocuSign_eSign::BulkSendingCopy.new( + recipients: [ + DocuSign_eSign::BulkSendingCopyRecipient.new( + roleName: 'signer', + tabs: [], + name: @config['signer_name'], + email: @config['signer_email'] + ), + DocuSign_eSign::BulkSendingCopyRecipient.new( + roleName: 'cc', + tabs: [], + name: @data[:cc_name], + email: @data[:cc_email] + ) + ], + custom_fields: [] + ), + DocuSign_eSign::BulkSendingCopy.new( + recipients: [ + DocuSign_eSign::BulkSendingCopyRecipient.new( + roleName: 'signer', + tabs: [], + name: @data[:signer1_name], + email: @data[:signer1_email] + ), + DocuSign_eSign::BulkSendingCopyRecipient.new( + roleName: 'cc', + tabs: [], + name: @data[:cc1_name], + email: @data[:cc1_email] + ) + ], + custom_fields: [] + ) + ] + ) + + results = @eg031.send(:create_bulk_sending_list, args) + + assert_not_nil results + assert_equal expected_bulk_sending_list, results + end + + test 'should create correct custom fields if correct data is provided' do + bulk_list_id = 'bulk_list_id' + + expected_custom_fields = DocuSign_eSign::CustomFields.new( + listCustomFields: [], + textCustomFields: [ + DocuSign_eSign::TextCustomField.new( + name: 'mailingListId', + required: 'false', + show: 'false', + value: bulk_list_id + ) + ] + ) + + results = @eg031.send(:custom_fields, bulk_list_id) + + assert_not_nil results + assert_equal expected_custom_fields, results + end + + test 'should correctly create envelope definition if correct data is provided' do + expected_document = create_ds_document(@data[:doc_pdf], 'Lorem Ipsum', 'pdf', '2') + + expected_sign_here = DocuSign_eSign::SignHere.new + expected_sign_here.anchor_string = '/sn1/' + expected_sign_here.anchor_units = 'pixels' + expected_sign_here.anchor_x_offset = '20' + expected_sign_here.anchor_y_offset = '10' + + expected_signer_tabs = DocuSign_eSign::Tabs.new({ + signHereTabs: [expected_sign_here] + }) + + expected_signer = DocuSign_eSign::Signer.new + expected_signer.name = 'Multi Bulk Recipient::signer' + expected_signer.email = 'multiBulkRecipients-signer@docusign.com' + expected_signer.role_name = 'signer' + expected_signer.note = '' + expected_signer.routing_order = 1 + expected_signer.status = 'created' + expected_signer.delivery_method = 'email' + expected_signer.recipient_id = '1' + expected_signer.recipient_type = 'signer' + expected_signer.tabs = expected_signer_tabs + + expected_cc = DocuSign_eSign::CarbonCopy.new + expected_cc.name = 'Multi Bulk Recipient::cc' + expected_cc.email = 'multiBulkRecipients-cc@docusign.com' + expected_cc.role_name = 'cc' + expected_cc.note = '' + expected_cc.routing_order = 2 + expected_cc.status = 'created' + expected_cc.delivery_method = 'email' + expected_cc.recipient_id = '2' + expected_cc.recipient_type = 'cc' + + expected_recipients = DocuSign_eSign::Recipients.new( + signers: [expected_signer], + carbonCopies: [expected_cc] + ) + + expected_envelope = DocuSign_eSign::EnvelopeDefinition.new + expected_envelope.email_subject = 'Please sign this document set' + expected_envelope.envelope_id_stamping = 'true' + expected_envelope.documents = [expected_document] + expected_envelope.recipients = expected_recipients + expected_envelope.status = 'created' + + envelope = @eg031.send(:make_envelope) + + assert_not_nil envelope + assert_equal expected_envelope, envelope + end +end diff --git a/test/fixtures/.keep b/test/fixtures/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/fixtures/files/.keep b/test/fixtures/files/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/helpers/.keep b/test/helpers/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/integration/.keep b/test/integration/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/mailers/.keep b/test/mailers/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/models/.keep b/test/models/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/run_tests.rb b/test/run_tests.rb new file mode 100644 index 0000000..5d78783 --- /dev/null +++ b/test/run_tests.rb @@ -0,0 +1 @@ +Dir["#{File.dirname(File.absolute_path(__FILE__))}/**/*_test.rb"].sort.each { |file| require file } diff --git a/test/system/.keep b/test/system/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/test/test_helper.rb b/test/test_helper.rb index 2ee518e..6192f03 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,12 +1,208 @@ -# frozen_string_literal: true +require 'bundler/inline' -ENV['RAILS_ENV'] ||= 'test' -require_relative '../config/environment' -require 'rails/test_help' +require 'yaml' +require 'test/unit' +require 'docusign_esign' +require 'docusign_click' -class ActiveSupport::TestCase - # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. - fixtures :all +$_scopes = %w[signature impersonation] +$_click_scopes = %w[click.manage click.send] +$_rooms_scopes = %w[dtr.rooms.read dtr.rooms.write dtr.documents.read dtr.documents.write dtr.profile.read dtr.profile.write dtr.company.read dtr.company.write room_forms] +$_admin_scopes = %w[organization_read group_read permission_read user_read user_write account_read domain_read identity_provider_read user_data_redact asset_group_account_read asset_group_account_clone_write asset_group_account_clone_read] - # Add more helper methods to be used by all tests here... + +class TestHelper < Test::Unit::TestCase + def api_type + { + e_sign: 'ESign', + click: 'Click', + rooms: 'Rooms', + monitor: 'Monitor', + admin: 'Admin' + } + end + + def setup_test_data(api_types) + auth_results = authenticate api_types + + @config = get_config_data + @data = get_common_data + + @account_id = auth_results[:account_id] + @access_token = auth_results[:access_token] + @base_path = auth_results[:base_path] + end + + def authenticate(api_types) + ds_config = get_config_data + data = get_common_data + + configuration = DocuSign_eSign::Configuration.new + configuration.debugging = true + api_client = DocuSign_eSign::ApiClient.new(configuration) + api_client.set_oauth_base_path(data[:authorization_server]) + + rsa_pk = './config/docusign_private_key.txt' + unless File.exist? rsa_pk + rsa_pk = ENV['PRIVATE_KEY'] + end + + scopes = $_scopes + + if api_types.include?(api_type[:click]) + scopes = scopes.concat($_click_scopes) + end + if api_types.include?(api_type[:rooms]) + scopes = scopes.concat($_rooms_scopes) + end + if api_types.include?(api_type[:admin]) + scopes = scopes.concat($_admin_scopes) + end + + begin + token = api_client.request_jwt_user_token(ds_config['jwt_integration_key'], ds_config['impersonated_user_guid'], rsa_pk, 3600, scopes) + user_info_response = api_client.get_user_info(token.access_token) + account = user_info_response.accounts.find(&:is_default) + + { + access_token: token.access_token, + account_id: account.account_id, + base_path: account.base_uri + } + rescue OpenSSL::PKey::RSAError => e + Rails.logger.error e.inspect + + raise "Please add your private RSA key to: #{rsa_pk}" if File.read(rsa_pk).starts_with? '{RSA_PRIVATE_KEY}' + + raise + rescue DocuSign_eSign::ApiError => e + body = JSON.parse(e.response_body) + if body['error'] == 'consent_required' + authenticate api_types if get_consent + else + puts 'API Error' + puts body['error'] + puts body['message'] + exit + end + end + end + + def get_config_data + config_file_path = './config/appsettings.yml' + if File.exist? config_file_path + begin + config_file_contents = File.read(config_file_path) + rescue Errno::ENOENT + warn 'Missing config file' + raise + end + YAML.unsafe_load(config_file_contents)['default'] + else + config = {} + config['jwt_integration_key'] = ENV['CLIENT_ID'] + config['impersonated_user_guid'] = ENV['USER_ID'] + config['signer_email'] = ENV['SIGNER_EMAIL'] + config['signer_name'] = ENV['SIGNER_NAME'] + + config + end + end + + def get_common_data + { + cc_name: 'Test Name', + cc_email: 'test@mail.com', + signer1_email: 'test.signer2@mail.com', + signer1_name: 'Test signer2', + cc1_email: 'test.cc2@mail.com', + cc1_name: 'Test cc2', + authorization_server: 'account-d.docusign.com', + signer_client_id: 1000, + ds_ping_url: 'http://localhost:3000', + ds_return_url: 'http://localhost:3000/ds_common-return', + doc_docx: './data/World_Wide_Corp_Battle_Plan_Trafalgar.docx', + doc_pdf: './data/World_Wide_Corp_lorem.pdf', + term_of_service: './data/Term_Of_Service.pdf', + doc_for_template: './data/World_Wide_Corp_fields.pdf', + item: 'Item', + quantity: 5, + template_name: 'Example Signer and CC template v2', + permission_profile_name: 'Test_Permission_Profile', + brand_name: 'Test_Brand_Name', + default_brand_language: 'en' + } + end + + def create_ds_document(filepath_or_content, name, file_ext, doc_id) + content = File.exist?(filepath_or_content) ? File.binread(filepath_or_content) : filepath_or_content + + document = DocuSign_eSign::Document.new + document.document_base64 = Base64.encode64(content) + document.name = name + document.file_extension = file_ext + document.document_id = doc_id + + document + end + + private + + def get_consent + ds_config = get_config_data + + url_scopes = $_scopes.join('+') + # Construct consent URL + redirect_uri = 'https://developers.docusign.com/platform/auth/consent' + consent_url = "#{ds_config['authorization_server']}/oauth/auth?response_type=code&" \ + "scope=#{url_scopes}&client_id=#{ds_config['jwt_integration_key']}&" \ + "redirect_uri=#{redirect_uri}" + + puts 'Open the following URL in your browser to grant consent to the application:' + puts consent_url + puts "Consent granted? \n 1)Yes \n 2)No" + continue = gets + if continue.chomp == '1' + true + else + puts 'Please grant consent' + exit + end + end +end + +class TestData + @template_id + @brand_id + @clickwrap_id + + def self.get_template_id + @template_id + end + + def self.get_brand_id + @brand_id + end + + def self.get_clickwrap_id + @clickwrap_id + end + + def self.set_template_id(id) + @template_id = id + end + + def self.set_brand_id(id) + @brand_id = id + end + + def self.set_clickwrap_id(id) + @clickwrap_id = id + end +end + +class ESign +end + +class Clickwrap end