multiwoven-integrations 0.1.36 → 0.1.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/multiwoven/integrations/core/destination_connector.rb +0 -2
- data/lib/multiwoven/integrations/core/fullrefresher.rb +19 -0
- data/lib/multiwoven/integrations/destination/airtable/client.rb +1 -0
- data/lib/multiwoven/integrations/destination/facebook_custom_audience/client.rb +1 -0
- data/lib/multiwoven/integrations/destination/facebook_custom_audience/config/catalog.json +1 -1
- data/lib/multiwoven/integrations/destination/google_sheets/client.rb +51 -3
- data/lib/multiwoven/integrations/destination/hubspot/client.rb +1 -0
- data/lib/multiwoven/integrations/destination/klaviyo/client.rb +1 -0
- data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/client.rb +117 -0
- data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/config/catalog.json +2042 -0
- data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/config/meta.json +16 -0
- data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/config/spec.json +43 -0
- data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/icon.svg +16 -0
- data/lib/multiwoven/integrations/destination/salesforce_crm/client.rb +1 -0
- data/lib/multiwoven/integrations/destination/slack/client.rb +1 -0
- data/lib/multiwoven/integrations/destination/stripe/client.rb +1 -0
- data/lib/multiwoven/integrations/protocol/protocol.rb +2 -1
- data/lib/multiwoven/integrations/rollout.rb +2 -1
- data/lib/multiwoven/integrations.rb +2 -0
- metadata +36 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be7368d653a73843a7d204b4dd7793c2ce7d5ba7f7e1e3b49818e15aff85367a
|
4
|
+
data.tar.gz: 21c792560111def1edad270dfcba87627818fbf4763c159053abf0114438e035
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b294140a41825ef76941958c46ec86a976e9073813b8a641663d249d518dd77d208d1c229bc20b7e76336196aa8abef7504ee92ac8a4100681b76fef05a2251e
|
7
|
+
data.tar.gz: f72981b9d03124e508d4cc1087ef13b876ee731d037bfaf4c4142bbc13c9a3227f248b0bdf07579f95591549c0800ce6439fdcd94a36510537589186512f9018
|
@@ -3,8 +3,6 @@
|
|
3
3
|
module Multiwoven
|
4
4
|
module Integrations::Core
|
5
5
|
class DestinationConnector < BaseConnector
|
6
|
-
prepend RateLimiter
|
7
|
-
|
8
6
|
# Records are transformed json payload send it to the destination
|
9
7
|
# SyncConfig is the Protocol::SyncConfig object
|
10
8
|
def write(_sync_config, _records, _action = "insert")
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Multiwoven
|
4
|
+
module Integrations::Core
|
5
|
+
module Fullrefresher
|
6
|
+
def write(sync_config, records, action = "insert")
|
7
|
+
if sync_config && sync_config.sync_mode == "full_refresh" && !@full_refreshed
|
8
|
+
response = clear_all_records(sync_config)
|
9
|
+
return response unless response &&
|
10
|
+
response.control.status == Multiwoven::Integrations::Protocol::ConnectionStatusType["succeeded"]
|
11
|
+
|
12
|
+
@full_refreshed = true
|
13
|
+
end
|
14
|
+
|
15
|
+
super(sync_config, records, action)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -7,6 +7,7 @@ module Multiwoven
|
|
7
7
|
module Airtable
|
8
8
|
include Multiwoven::Integrations::Core
|
9
9
|
class Client < DestinationConnector # rubocop:disable Metrics/ClassLength
|
10
|
+
prepend Multiwoven::Integrations::Core::RateLimiter
|
10
11
|
MAX_CHUNK_SIZE = 10
|
11
12
|
def check_connection(connection_config)
|
12
13
|
connection_config = connection_config.with_indifferent_access
|
@@ -4,6 +4,7 @@ module Multiwoven::Integrations::Destination
|
|
4
4
|
module FacebookCustomAudience
|
5
5
|
include Multiwoven::Integrations::Core
|
6
6
|
class Client < DestinationConnector # rubocop:disable Metrics/ClassLength
|
7
|
+
prepend Multiwoven::Integrations::Core::RateLimiter
|
7
8
|
MAX_CHUNK_SIZE = 10_000
|
8
9
|
def check_connection(connection_config)
|
9
10
|
connection_config = connection_config.with_indifferent_access
|
@@ -33,7 +33,7 @@
|
|
33
33
|
"PAGEUID": { "type": ["string", "null"], "default": null, "title": "Page-Scoped ID", "x-hashRequired": false }
|
34
34
|
}
|
35
35
|
},
|
36
|
-
"supported_sync_modes": ["
|
36
|
+
"supported_sync_modes": ["incremental"],
|
37
37
|
"source_defined_cursor": true,
|
38
38
|
"default_cursor_field": ["updated"],
|
39
39
|
"source_defined_primary_key": [["email"]]
|
@@ -7,9 +7,12 @@ module Multiwoven
|
|
7
7
|
include Multiwoven::Integrations::Core
|
8
8
|
|
9
9
|
class Client < DestinationConnector # rubocop:disable Metrics/ClassLength
|
10
|
+
prepend Multiwoven::Integrations::Core::Fullrefresher
|
11
|
+
prepend Multiwoven::Integrations::Core::RateLimiter
|
10
12
|
MAX_CHUNK_SIZE = 10_000
|
11
13
|
|
12
14
|
def check_connection(connection_config)
|
15
|
+
connection_config = connection_config.with_indifferent_access
|
13
16
|
authorize_client(connection_config)
|
14
17
|
fetch_google_spread_sheets(connection_config)
|
15
18
|
success_status
|
@@ -19,6 +22,7 @@ module Multiwoven
|
|
19
22
|
end
|
20
23
|
|
21
24
|
def discover(connection_config)
|
25
|
+
connection_config = connection_config.with_indifferent_access
|
22
26
|
authorize_client(connection_config)
|
23
27
|
spreadsheets = fetch_google_spread_sheets(connection_config)
|
24
28
|
catalog = build_catalog_from_spreadsheets(spreadsheets, connection_config)
|
@@ -34,6 +38,24 @@ module Multiwoven
|
|
34
38
|
handle_exception("GOOGLE_SHEETS:CRM:WRITE:EXCEPTION", "error", e)
|
35
39
|
end
|
36
40
|
|
41
|
+
def clear_all_records(sync_config)
|
42
|
+
setup_write_environment(sync_config, "clear")
|
43
|
+
connection_specification = sync_config.destination.connection_specification.with_indifferent_access
|
44
|
+
spreadsheet = fetch_google_spread_sheets(connection_specification)
|
45
|
+
sheet_ids = spreadsheet.sheets.map(&:properties).map(&:sheet_id)
|
46
|
+
|
47
|
+
delete_extra_sheets(sheet_ids)
|
48
|
+
|
49
|
+
unless sheet_ids.empty?
|
50
|
+
clear_response = clear_sheet_data(spreadsheet.sheets.first.properties.title)
|
51
|
+
return control_message("Successfully cleared data.", "succeeded") if clear_response&.cleared_range
|
52
|
+
end
|
53
|
+
|
54
|
+
control_message("Failed to clear data.", "failed")
|
55
|
+
rescue StandardError => e
|
56
|
+
control_message(e.message, "failed")
|
57
|
+
end
|
58
|
+
|
37
59
|
private
|
38
60
|
|
39
61
|
# To define the level of access granted to your app, you need to identify and declare authorization scopes which is provided by google scopse https://developers.google.com/sheets/api/scopes
|
@@ -99,7 +121,7 @@ module Multiwoven
|
|
99
121
|
batch_support: true,
|
100
122
|
batch_size: 10_000,
|
101
123
|
json_schema: generate_properties_schema(column_names),
|
102
|
-
supported_sync_modes: %w[incremental]
|
124
|
+
supported_sync_modes: %w[incremental full_refresh]
|
103
125
|
}.with_indifferent_access
|
104
126
|
end
|
105
127
|
|
@@ -113,8 +135,9 @@ module Multiwoven
|
|
113
135
|
|
114
136
|
def setup_write_environment(sync_config, action)
|
115
137
|
@action = sync_config.stream.action || action
|
116
|
-
|
117
|
-
|
138
|
+
connection_specification = sync_config.destination.connection_specification.with_indifferent_access
|
139
|
+
@spreadsheet_id = extract_spreadsheet_id(connection_specification[:spreadsheet_link])
|
140
|
+
authorize_client(connection_specification)
|
118
141
|
end
|
119
142
|
|
120
143
|
def extract_spreadsheet_id(link)
|
@@ -176,6 +199,31 @@ module Multiwoven
|
|
176
199
|
success: success, failed: failure
|
177
200
|
).to_multiwoven_message
|
178
201
|
end
|
202
|
+
|
203
|
+
def delete_extra_sheets(sheet_ids)
|
204
|
+
# Leave one sheet intact as a spreadsheet must have at least one sheet.
|
205
|
+
# Delete all other sheets.
|
206
|
+
(sheet_ids.length - 1).times do |i|
|
207
|
+
request = Google::Apis::SheetsV4::BatchUpdateSpreadsheetRequest.new(
|
208
|
+
requests: [{ delete_sheet: { sheet_id: sheet_ids[i + 1] } }]
|
209
|
+
)
|
210
|
+
@client.batch_update_spreadsheet(@spreadsheet_id, request)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def clear_sheet_data(sheet_title)
|
215
|
+
clear_request = Google::Apis::SheetsV4::ClearValuesRequest.new
|
216
|
+
@client&.clear_values(@spreadsheet_id, "#{sheet_title}!A2:Z", clear_request)
|
217
|
+
end
|
218
|
+
|
219
|
+
def control_message(message, status)
|
220
|
+
ControlMessage.new(
|
221
|
+
type: "full_refresh",
|
222
|
+
emitted_at: Time.now.to_i,
|
223
|
+
status: ConnectionStatusType[status],
|
224
|
+
meta: { detail: message }
|
225
|
+
).to_multiwoven_message
|
226
|
+
end
|
179
227
|
end
|
180
228
|
end
|
181
229
|
end
|
@@ -9,6 +9,7 @@ module Multiwoven
|
|
9
9
|
include Multiwoven::Integrations::Core
|
10
10
|
|
11
11
|
class Client < DestinationConnector
|
12
|
+
prepend Multiwoven::Integrations::Core::RateLimiter
|
12
13
|
def check_connection(connection_config)
|
13
14
|
connection_config = connection_config.with_indifferent_access
|
14
15
|
initialize_client(connection_config)
|
@@ -4,6 +4,7 @@ module Multiwoven::Integrations::Destination
|
|
4
4
|
module Klaviyo
|
5
5
|
include Multiwoven::Integrations::Core
|
6
6
|
class Client < DestinationConnector
|
7
|
+
prepend Multiwoven::Integrations::Core::RateLimiter
|
7
8
|
def check_connection(connection_config)
|
8
9
|
connection_config = connection_config.with_indifferent_access
|
9
10
|
api_key = connection_config[:private_api_key]
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "stringio"
|
4
|
+
|
5
|
+
module Multiwoven
|
6
|
+
module Integrations
|
7
|
+
module Destination
|
8
|
+
module SalesforceConsumerGoodsCloud
|
9
|
+
include Multiwoven::Integrations::Core
|
10
|
+
|
11
|
+
API_VERSION = "59.0"
|
12
|
+
|
13
|
+
class Client < DestinationConnector
|
14
|
+
prepend Multiwoven::Integrations::Core::RateLimiter
|
15
|
+
def check_connection(connection_config)
|
16
|
+
connection_config = connection_config.with_indifferent_access
|
17
|
+
initialize_client(connection_config)
|
18
|
+
authenticate_client
|
19
|
+
success_status
|
20
|
+
rescue StandardError => e
|
21
|
+
failure_status(e)
|
22
|
+
end
|
23
|
+
|
24
|
+
def discover(_connection_config = nil)
|
25
|
+
catalog = build_catalog(load_catalog)
|
26
|
+
catalog.to_multiwoven_message
|
27
|
+
rescue StandardError => e
|
28
|
+
handle_exception("SALESFORCE:CONSUMER:GOODS:ClOUD:DISCOVER:EXCEPTION", "error", e)
|
29
|
+
end
|
30
|
+
|
31
|
+
def write(sync_config, records, action = "create")
|
32
|
+
@action = sync_config.stream.action || action
|
33
|
+
initialize_client(sync_config.destination.connection_specification)
|
34
|
+
process_records(records, sync_config.stream)
|
35
|
+
rescue StandardError => e
|
36
|
+
handle_exception("SALESFORCE:CONSUMER:GOODS:ClOUD:WRITE:EXCEPTION", "error", e)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def initialize_client(config)
|
42
|
+
config = config.with_indifferent_access
|
43
|
+
@client = Restforce.new(oauth_token: config[:access_token],
|
44
|
+
refresh_token: config[:refresh_token],
|
45
|
+
instance_url: config[:instance_url],
|
46
|
+
client_id: config[:client_id],
|
47
|
+
client_secret: config[:client_secret],
|
48
|
+
authentication_callback: proc { |x| log_debug(x.to_s) },
|
49
|
+
api_version: API_VERSION)
|
50
|
+
end
|
51
|
+
|
52
|
+
def process_records(records, stream)
|
53
|
+
write_success = 0
|
54
|
+
write_failure = 0
|
55
|
+
properties = stream.json_schema[:properties]
|
56
|
+
records.each do |record_object|
|
57
|
+
record = extract_data(record_object, properties)
|
58
|
+
process_record(stream, record)
|
59
|
+
write_success += 1
|
60
|
+
rescue StandardError => e
|
61
|
+
handle_exception("SALESFORCE:CONSUMER:GOODS:ClOUD:WRITE:EXCEPTION", "error", e)
|
62
|
+
write_failure += 1
|
63
|
+
end
|
64
|
+
tracking_message(write_success, write_failure)
|
65
|
+
end
|
66
|
+
|
67
|
+
def process_record(stream, record)
|
68
|
+
send_data_to_salesforce(stream.name, record)
|
69
|
+
end
|
70
|
+
|
71
|
+
def send_data_to_salesforce(stream_name, record = {})
|
72
|
+
method_name = "#{@action}!"
|
73
|
+
args = build_args(@action, stream_name, record)
|
74
|
+
@client.send(method_name, *args)
|
75
|
+
end
|
76
|
+
|
77
|
+
def build_args(action, stream_name, record)
|
78
|
+
case action
|
79
|
+
when :upsert
|
80
|
+
[stream_name, record[:external_key], record]
|
81
|
+
when :destroy
|
82
|
+
[stream_name, record[:id]]
|
83
|
+
else
|
84
|
+
[stream_name, record]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def authenticate_client
|
89
|
+
@client.authenticate!
|
90
|
+
end
|
91
|
+
|
92
|
+
def success_status
|
93
|
+
ConnectionStatus.new(status: ConnectionStatusType["succeeded"]).to_multiwoven_message
|
94
|
+
end
|
95
|
+
|
96
|
+
def failure_status(error)
|
97
|
+
ConnectionStatus.new(status: ConnectionStatusType["failed"], message: error.message).to_multiwoven_message
|
98
|
+
end
|
99
|
+
|
100
|
+
def load_catalog
|
101
|
+
read_json(CATALOG_SPEC_PATH)
|
102
|
+
end
|
103
|
+
|
104
|
+
def tracking_message(success, failure)
|
105
|
+
Multiwoven::Integrations::Protocol::TrackingMessage.new(
|
106
|
+
success: success, failed: failure
|
107
|
+
).to_multiwoven_message
|
108
|
+
end
|
109
|
+
|
110
|
+
def log_debug(message)
|
111
|
+
Multiwoven::Integrations::Service.logger.debug(message)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|