workarea-salesforce_esp 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  3. data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/workflows/ci.yml +54 -0
  6. data/.gitignore +24 -0
  7. data/.rubocop.yml +2 -0
  8. data/CHANGELOG.md +9 -0
  9. data/Gemfile +17 -0
  10. data/LICENSE +52 -0
  11. data/README.md +60 -0
  12. data/Rakefile +57 -0
  13. data/app/assets/config/salesforce_esp_manifest.js +0 -0
  14. data/app/assets/images/salesforce_esp/.keep +0 -0
  15. data/app/assets/javascripts/salesforce_esp/.keep +0 -0
  16. data/app/assets/stylesheets/salesforce_esp/.keep +0 -0
  17. data/app/controllers/.keep +0 -0
  18. data/app/helpers/.keep +0 -0
  19. data/app/mailers/.keep +0 -0
  20. data/app/models/.keep +0 -0
  21. data/app/views/.keep +0 -0
  22. data/app/workers/workarea/salesforce_esp/save_email_signup.rb +28 -0
  23. data/bin/rails +25 -0
  24. data/config/initializers/workarea.rb +6 -0
  25. data/config/routes.rb +2 -0
  26. data/lib/workarea/salesforce_esp.rb +55 -0
  27. data/lib/workarea/salesforce_esp/bogus_gateway.rb +33 -0
  28. data/lib/workarea/salesforce_esp/engine.rb +10 -0
  29. data/lib/workarea/salesforce_esp/gateway.rb +116 -0
  30. data/lib/workarea/salesforce_esp/response.rb +34 -0
  31. data/lib/workarea/salesforce_esp/subscriber.rb +15 -0
  32. data/lib/workarea/salesforce_esp/version.rb +5 -0
  33. data/test/dummy/.ruby-version +1 -0
  34. data/test/dummy/Rakefile +6 -0
  35. data/test/dummy/app/assets/config/manifest.js +3 -0
  36. data/test/dummy/app/assets/images/.keep +0 -0
  37. data/test/dummy/app/assets/javascripts/application.js +14 -0
  38. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  39. data/test/dummy/app/controllers/application_controller.rb +2 -0
  40. data/test/dummy/app/controllers/concerns/.keep +0 -0
  41. data/test/dummy/app/helpers/application_helper.rb +2 -0
  42. data/test/dummy/app/jobs/application_job.rb +2 -0
  43. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  44. data/test/dummy/app/models/concerns/.keep +0 -0
  45. data/test/dummy/app/views/layouts/application.html.erb +15 -0
  46. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  47. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  48. data/test/dummy/bin/bundle +3 -0
  49. data/test/dummy/bin/rails +4 -0
  50. data/test/dummy/bin/rake +4 -0
  51. data/test/dummy/bin/setup +28 -0
  52. data/test/dummy/bin/update +28 -0
  53. data/test/dummy/bin/yarn +11 -0
  54. data/test/dummy/config.ru +5 -0
  55. data/test/dummy/config/application.rb +34 -0
  56. data/test/dummy/config/boot.rb +5 -0
  57. data/test/dummy/config/environment.rb +5 -0
  58. data/test/dummy/config/environments/development.rb +52 -0
  59. data/test/dummy/config/environments/production.rb +83 -0
  60. data/test/dummy/config/environments/test.rb +45 -0
  61. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  62. data/test/dummy/config/initializers/assets.rb +14 -0
  63. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  64. data/test/dummy/config/initializers/content_security_policy.rb +25 -0
  65. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  66. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  67. data/test/dummy/config/initializers/inflections.rb +16 -0
  68. data/test/dummy/config/initializers/mime_types.rb +4 -0
  69. data/test/dummy/config/initializers/workarea.rb +5 -0
  70. data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
  71. data/test/dummy/config/locales/en.yml +33 -0
  72. data/test/dummy/config/puma.rb +37 -0
  73. data/test/dummy/config/routes.rb +5 -0
  74. data/test/dummy/config/spring.rb +6 -0
  75. data/test/dummy/db/seeds.rb +2 -0
  76. data/test/dummy/lib/assets/.keep +0 -0
  77. data/test/dummy/log/.keep +0 -0
  78. data/test/dummy/package.json +5 -0
  79. data/test/gateways/gateway_test.rb +38 -0
  80. data/test/teaspoon_env.rb +6 -0
  81. data/test/test_helper.rb +10 -0
  82. data/workarea-salesforce_esp.gemspec +22 -0
  83. metadata +152 -0
@@ -0,0 +1,6 @@
1
+ Workarea.configure do |config|
2
+ config.salesforce = ActiveSupport::Configurable::Configuration.new
3
+ config.salesforce.default_list = '' # required, default list to add new email signups to.
4
+ config.salesforce.token_endpoint = nil # required, URL endpoint to get an auth token.
5
+ config.salesforce.account_id = nil # optional, target account ID, optional
6
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ Rails.application.routes.draw do
2
+ end
@@ -0,0 +1,55 @@
1
+ require 'workarea'
2
+ require 'workarea/storefront'
3
+ require 'workarea/admin'
4
+
5
+ require 'workarea/salesforce_esp/version'
6
+ require 'workarea/salesforce_esp/engine'
7
+ require 'workarea/salesforce_esp/gateway'
8
+ require 'workarea/salesforce_esp/bogus_gateway'
9
+ require 'workarea/salesforce_esp/response'
10
+ require 'workarea/salesforce_esp/subscriber'
11
+
12
+ require 'marketingcloudsdk'
13
+
14
+ module Workarea
15
+ module SalesforceEsp
16
+ def self.gateway
17
+ if credentials.present?
18
+ options = {
19
+ client_id: client_id,
20
+ secret: secret,
21
+ account_id: account_id,
22
+ request_token_url: token_endpoint
23
+ }
24
+
25
+ Workarea::SalesforceEsp::Gateway.new(options)
26
+ else
27
+ Workarea::SalesforceEsp::BogusGateway.new
28
+ end
29
+ end
30
+
31
+ def self.credentials
32
+ (Rails.application.secrets.salesforce || {}).deep_symbolize_keys
33
+ end
34
+
35
+ def self.config
36
+ Workarea.config.salesforce
37
+ end
38
+
39
+ def self.account_id
40
+ Workarea.config.salesforce.account_id
41
+ end
42
+
43
+ def self.token_endpoint
44
+ Workarea.config.salesforce.token_endpoint
45
+ end
46
+
47
+ def self.client_id
48
+ credentials[:client_id]
49
+ end
50
+
51
+ def self.secret
52
+ credentials[:secret]
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,33 @@
1
+ module Workarea
2
+ module SalesforceEsp
3
+ class BogusGateway
4
+ @@supported_methods = SalesforceEsp::Gateway.public_instance_methods
5
+
6
+
7
+ def subscribe(email, attrs, list_id)
8
+ bogus_response
9
+ end
10
+
11
+ def unsubscribe(email, list_id)
12
+ bogus_response
13
+ end
14
+
15
+ def send_triggered_email(email_key, email, attrs = {})
16
+ bogus_response
17
+ end
18
+
19
+ def method_missing(method, *args)
20
+ return true if @@supported_methods.include? method
21
+ super
22
+ end
23
+
24
+ private
25
+
26
+ def bogus_response
27
+ response = OpenStruct.new()
28
+ response["success?"] = true
29
+ Workarea::SalesforceEsp::Response.new(response)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ require 'workarea/salesforce_esp'
2
+
3
+ module Workarea
4
+ module SalesforceEsp
5
+ class Engine < ::Rails::Engine
6
+ include Workarea::Plugin
7
+ isolate_namespace Workarea::SalesforceEsp
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,116 @@
1
+ module Workarea
2
+ module SalesforceEsp
3
+ class Gateway
4
+ class SalesforceEspSubscriptionError < StandardError; end
5
+ class SalesforceEspEmailError < StandardError; end
6
+ class SalesforceEspListError < StandardError; end
7
+
8
+ def initialize(options = {})
9
+ @client = MarketingCloudSDK::Client.new(
10
+ 'client' => {
11
+ 'id' => options[:client_id],
12
+ 'secret' => options[:secret],
13
+ 'account_id' => options[:account_id],
14
+ 'use_oAuth2_authentication' => true,
15
+ 'request_token_url' => options[:request_token_url]
16
+ }
17
+ )
18
+ end
19
+
20
+ def subscribe(email, attrs, list_id)
21
+ raise SalesforceEspListError, 'No List ID error' unless list_id.present?
22
+
23
+ subscriber = build_subscriber(email, "Active", attrs, list_id)
24
+ response = perform_api_call(subscriber, "post")
25
+
26
+ if response.failure?
27
+ if response.subscriber_already_exists?
28
+ response = update_subscriber(email, "Active", attrs, list_id)
29
+ end
30
+ end
31
+
32
+ response
33
+ end
34
+
35
+ def unsubscribe(email, list_id)
36
+ subscriber = build_subscriber(email, "Unsubscribed", {}, list_id)
37
+ response = perform_api_call(subscriber, "patch")
38
+
39
+ if response.failure? && !response.user_not_found?
40
+ raise SalesforceEspSubscriptionError, response.status_message
41
+ end
42
+
43
+ response
44
+ end
45
+
46
+ def update_subscriber(email, status, attrs, list_id)
47
+ subscriber = build_subscriber(email, status, attrs, list_id)
48
+ response = perform_api_call(subscriber, "patch")
49
+
50
+ if response.failure? && !response.subscriber_already_exists?
51
+ raise SalesforceEspSubscriptionError, response.status_message
52
+ end
53
+
54
+ response
55
+ end
56
+
57
+ def write_to_data_extension(data_extension, attrs)
58
+ ex = ET_DataExtension::Row.new
59
+ ex.authStub = @client
60
+ ex.Name = data_extension
61
+ ex.props = attrs
62
+
63
+ perform_api_call(ex, 'post')
64
+ end
65
+
66
+ def send_triggered_email(email_key, email, attrs = {})
67
+ triggered_send = MarketingCloudSDK::TriggeredSend.new
68
+ triggered_send.authStub = @client
69
+ triggered_send.props = {
70
+ "CustomerKey" => email_key
71
+ }
72
+ triggered_send.subscribers = [
73
+ {
74
+ "EmailAddress" => email,
75
+ "SubscriberKey" => email,
76
+ "Attributes" => build_subscriber_attributes(attrs)
77
+ }
78
+ ]
79
+
80
+ response = Response.new(triggered_send.send)
81
+
82
+ raise SalesforceEspEmailError, response.status_message unless response.success?
83
+
84
+ response
85
+ end
86
+
87
+ private
88
+
89
+ def build_subscriber(email, status, attrs, list_id)
90
+ subscriber = MarketingCloudSDK::Subscriber.new
91
+ subscriber.authStub = @client
92
+ subscriber.props = {
93
+ "EmailAddress" => email,
94
+ "SubscriberKey" => email,
95
+ "Status" => "Active",
96
+ "Attributes" => build_subscriber_attributes(attrs),
97
+ "Lists" => { "ID" => list_id, "Status" => status }
98
+ }
99
+ subscriber
100
+ end
101
+
102
+ def build_subscriber_attributes(attrs)
103
+ attributes = []
104
+ attrs.each do |key, value|
105
+ attributes.push({ "Name" => key, "Value" => value })
106
+ end
107
+ attributes
108
+ end
109
+
110
+ def perform_api_call(obj, method)
111
+ results = obj.send(method.to_sym)
112
+ SalesforceEsp::Response.new(results)
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,34 @@
1
+ module Workarea
2
+ module SalesforceEsp
3
+ class Response
4
+ attr_reader :success, :message, :status_message, :results
5
+
6
+ def initialize(response)
7
+ @success = response.success?
8
+ @message = response.message
9
+ @results = response.results
10
+ @status_message = results.first[:status_message] if results.present?
11
+ end
12
+
13
+ def subscriber_already_exists?
14
+ message == 'Error' && status_message == 'The subscriber is already on the list'
15
+ end
16
+
17
+ def user_not_found?
18
+ message == 'Error' && status_message == 'The subscriber was not found.'
19
+ end
20
+
21
+ def excluded_by_list_detective?
22
+ message == 'Error' && results.first[:subscriber_failures][:error_description] == 'Error Code: 24 - Subscriber was excluded by List Detective.'
23
+ end
24
+
25
+ def success?
26
+ success
27
+ end
28
+
29
+ def failure?
30
+ !success
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,15 @@
1
+ module Workarea
2
+ module SalesforceEsp
3
+ class Subscriber
4
+ attr_reader :data
5
+
6
+ def initialize(data)
7
+ @data = data
8
+ end
9
+
10
+ def subscriber_key
11
+ data[:subscriber_key]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module Workarea
2
+ module SalesforceEsp
3
+ VERSION = "1.0.0".freeze
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ 2.4.2
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative 'config/application'
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../javascripts .js
3
+ //= link_directory ../stylesheets .css
File without changes
@@ -0,0 +1,14 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require rails-ujs
14
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,2 @@
1
+ class ApplicationController < ActionController::Base
2
+ end
File without changes
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ class ApplicationJob < ActiveJob::Base
2
+ end
@@ -0,0 +1,4 @@
1
+ class ApplicationMailer < ActionMailer::Base
2
+ default from: 'from@example.com'
3
+ layout 'mailer'
4
+ end
File without changes
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Dummy</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag 'application', media: 'all' %>
9
+ <%= javascript_include_tag 'application' %>
10
+ </head>
11
+
12
+ <body>
13
+ <%= yield %>
14
+ </body>
15
+ </html>
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5
+ <style>
6
+ /* Email styles need to be inline */
7
+ </style>
8
+ </head>
9
+
10
+ <body>
11
+ <%= yield %>
12
+ </body>
13
+ </html>
@@ -0,0 +1 @@
1
+ <%= yield %>
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3
+ load Gem.bin_path('bundler', 'bundle')
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path('../config/application', __dir__)
3
+ require_relative '../config/boot'
4
+ require 'rails/commands'
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../config/boot'
3
+ require 'rake'
4
+ Rake.application.run
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ require 'fileutils'
3
+ include FileUtils
4
+
5
+ # path to your application root.
6
+ APP_ROOT = File.expand_path('..', __dir__)
7
+
8
+ def system!(*args)
9
+ system(*args) || abort("\n== Command #{args} failed ==")
10
+ end
11
+
12
+ chdir APP_ROOT do
13
+ # This script is a starting point to setup your application.
14
+ # Add necessary setup steps to this file.
15
+
16
+ puts '== Installing dependencies =='
17
+ system! 'gem install bundler --conservative'
18
+ system('bundle check') || system!('bundle install')
19
+
20
+ # Install JavaScript dependencies if using Yarn
21
+ # system('bin/yarn')
22
+
23
+ puts "\n== Removing old logs and tempfiles =="
24
+ system! 'bin/rails log:clear tmp:clear'
25
+
26
+ puts "\n== Restarting application server =="
27
+ system! 'bin/rails restart'
28
+ end