multiwoven-integrations 0.1.1 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.vscode/settings.json +5 -0
  3. data/lib/multiwoven/integrations/core/base_connector.rb +3 -3
  4. data/lib/multiwoven/integrations/core/constants.rb +2 -0
  5. data/lib/multiwoven/integrations/core/source_connector.rb +17 -0
  6. data/lib/multiwoven/integrations/core/utils.rb +8 -0
  7. data/lib/multiwoven/integrations/destination/facebook_custom_audience/client.rb +123 -0
  8. data/lib/multiwoven/integrations/destination/facebook_custom_audience/config/catalog.json +38 -0
  9. data/lib/multiwoven/integrations/destination/facebook_custom_audience/config/meta.json +15 -0
  10. data/lib/multiwoven/integrations/destination/facebook_custom_audience/config/spec.json +22 -0
  11. data/lib/multiwoven/integrations/destination/klaviyo/client.rb +11 -3
  12. data/lib/multiwoven/integrations/destination/klaviyo/config/catalog.json +86 -88
  13. data/lib/multiwoven/integrations/destination/klaviyo/config/meta.json +1 -1
  14. data/lib/multiwoven/integrations/destination/klaviyo/config/spec.json +2 -2
  15. data/lib/multiwoven/integrations/destination/salesforce_crm/client.rb +127 -0
  16. data/lib/multiwoven/integrations/destination/salesforce_crm/config/catalog.json +317 -0
  17. data/lib/multiwoven/integrations/destination/salesforce_crm/config/meta.json +15 -0
  18. data/lib/multiwoven/integrations/destination/salesforce_crm/config/spec.json +37 -0
  19. data/lib/multiwoven/integrations/destination/slack/client.rb +125 -0
  20. data/lib/multiwoven/integrations/destination/slack/config/catalog.json +71 -0
  21. data/lib/multiwoven/integrations/destination/slack/config/meta.json +14 -0
  22. data/lib/multiwoven/integrations/destination/slack/config/spec.json +22 -0
  23. data/lib/multiwoven/integrations/protocol/protocol.rb +2 -0
  24. data/lib/multiwoven/integrations/rollout.rb +4 -1
  25. data/lib/multiwoven/integrations/source/bigquery/client.rb +6 -0
  26. data/lib/multiwoven/integrations/source/bigquery/config/meta.json +1 -1
  27. data/lib/multiwoven/integrations/source/redshift/client.rb +5 -0
  28. data/lib/multiwoven/integrations/source/redshift/config/meta.json +1 -1
  29. data/lib/multiwoven/integrations/source/snowflake/client.rb +5 -0
  30. data/lib/multiwoven/integrations/source/snowflake/config/meta.json +1 -1
  31. data/lib/multiwoven/integrations.rb +6 -0
  32. data/multiwoven-integrations.gemspec +10 -6
  33. metadata +65 -8
@@ -0,0 +1,317 @@
1
+ {
2
+ "streams": [
3
+ {
4
+ "name": "Account",
5
+ "action": "create",
6
+ "json_schema": {
7
+ "type": "object",
8
+ "additionalProperties": true,
9
+ "properties": {
10
+ "Id": {
11
+ "type": "string"
12
+ },
13
+ "IsDeleted": {
14
+ "type": "boolean"
15
+ },
16
+ "MasterRecordId": {
17
+ "type": ["string", "null"]
18
+ },
19
+ "Name": {
20
+ "type": "string"
21
+ },
22
+ "Type": {
23
+ "type": ["string", "null"],
24
+ "enum": [
25
+ "Prospect",
26
+ "Customer - Direct",
27
+ "Customer - Channel",
28
+ "Channel Partner / Reseller",
29
+ "Installation Partner",
30
+ "Technology Partner",
31
+ "Other"
32
+ ]
33
+ },
34
+ "ParentId": {
35
+ "type": ["string", "null"]
36
+ },
37
+ "BillingStreet": {
38
+ "type": ["string", "null"]
39
+ },
40
+ "BillingCity": {
41
+ "type": ["string", "null"]
42
+ },
43
+ "BillingState": {
44
+ "type": ["string", "null"]
45
+ },
46
+ "BillingPostalCode": {
47
+ "type": ["string", "null"]
48
+ },
49
+ "BillingCountry": {
50
+ "type": ["string", "null"]
51
+ },
52
+ "BillingLatitude": {
53
+ "type": ["number", "null"]
54
+ },
55
+ "BillingLongitude": {
56
+ "type": ["number", "null"]
57
+ },
58
+ "BillingGeocodeAccuracy": {
59
+ "type": ["string", "null"],
60
+ "enum": [
61
+ "Address",
62
+ "NearAddress",
63
+ "Block",
64
+ "Street",
65
+ "ExtendedZip",
66
+ "Zip",
67
+ "Neighborhood",
68
+ "City",
69
+ "County",
70
+ "State",
71
+ "Unknown"
72
+ ]
73
+ },
74
+ "BillingAddress": {
75
+ "type": ["string", "null"],
76
+ "enum": [
77
+ "Address",
78
+ "NearAddress",
79
+ "Block",
80
+ "Street",
81
+ "ExtendedZip",
82
+ "Zip",
83
+ "Neighborhood",
84
+ "City",
85
+ "County",
86
+ "State",
87
+ "Unknown"
88
+ ]
89
+ },
90
+ "ShippingStreet": {
91
+ "type": ["string", "null"]
92
+ },
93
+ "ShippingCity": {
94
+ "type": ["string", "null"]
95
+ },
96
+ "ShippingState": {
97
+ "type": ["string", "null"]
98
+ },
99
+ "ShippingPostalCode": {
100
+ "type": ["string", "null"]
101
+ },
102
+ "ShippingCountry": {
103
+ "type": ["string", "null"]
104
+ },
105
+ "ShippingLatitude": {
106
+ "type": ["number", "null"]
107
+ },
108
+ "ShippingLongitude": {
109
+ "type": ["number", "null"]
110
+ },
111
+ "ShippingGeocodeAccuracy": {
112
+ "type": ["string", "null"]
113
+ },
114
+ "ShippingAddress": {
115
+ "type": ["string", "null"]
116
+ },
117
+ "Phone": {
118
+ "type": ["string", "null"]
119
+ },
120
+ "Fax": {
121
+ "type": ["string", "null"]
122
+ },
123
+ "AccountNumber": {
124
+ "type": ["string", "null"]
125
+ },
126
+ "Website": {
127
+ "type": ["string", "null"],
128
+ "format": "uri"
129
+ },
130
+ "PhotoUrl": {
131
+ "type": ["string", "null"],
132
+ "format": "uri"
133
+ },
134
+ "Sic": {
135
+ "type": ["string", "null"]
136
+ },
137
+ "Industry": {
138
+ "type": ["string", "null"],
139
+ "enum": [
140
+ "Agriculture",
141
+ "Apparel",
142
+ "Banking",
143
+ "Biotechnology",
144
+ "Chemicals",
145
+ "Communications",
146
+ "Construction",
147
+ "Consulting",
148
+ "Education",
149
+ "Electronics",
150
+ "Energy",
151
+ "Engineering",
152
+ "Entertainment",
153
+ "Environmental",
154
+ "Finance",
155
+ "Food & Beverage",
156
+ "Government",
157
+ "Healthcare",
158
+ "Hospitality",
159
+ "Insurance",
160
+ "Machinery",
161
+ "Manufacturing",
162
+ "Media",
163
+ "Not For Profit",
164
+ "Recreation",
165
+ "Retail",
166
+ "Shipping",
167
+ "Technology",
168
+ "Telecommunications",
169
+ "Transportation",
170
+ "Utilities",
171
+ "Other"
172
+ ]
173
+ },
174
+ "AnnualRevenue": {
175
+ "type": ["number", "null"]
176
+ },
177
+ "NumberOfEmployees": {
178
+ "type": ["integer", "null"]
179
+ },
180
+ "Ownership": {
181
+ "type": ["string", "null"],
182
+ "enum": ["Public", "Private", "Subsidiary", "Other"]
183
+ },
184
+ "TickerSymbol": {
185
+ "type": ["string", "null"]
186
+ },
187
+ "Description": {
188
+ "type": ["string", "null"]
189
+ },
190
+ "Rating": {
191
+ "type": ["string", "null"],
192
+ "enum": ["Hot", "Warm", "Cold"]
193
+ },
194
+ "Site": {
195
+ "type": ["string", "null"]
196
+ },
197
+ "OwnerId": {
198
+ "type": "string"
199
+ },
200
+ "CreatedDate": {
201
+ "type": "string",
202
+ "format": "date-time"
203
+ },
204
+ "CreatedById": {
205
+ "type": "string"
206
+ },
207
+ "LastModifiedDate": {
208
+ "type": "string",
209
+ "format": "date-time"
210
+ },
211
+ "LastModifiedById": {
212
+ "type": "string"
213
+ },
214
+ "SystemModstamp": {
215
+ "type": "string",
216
+ "format": "date-time"
217
+ },
218
+ "LastActivityDate": {
219
+ "type": ["string", "null"],
220
+ "format": "date"
221
+ },
222
+ "LastViewedDate": {
223
+ "type": ["string", "null"],
224
+ "format": "date-time"
225
+ },
226
+ "LastReferencedDate": {
227
+ "type": ["string", "null"],
228
+ "format": "date-time"
229
+ },
230
+ "Jigsaw": {
231
+ "type": ["string", "null"]
232
+ },
233
+ "JigsawCompanyId": {
234
+ "type": ["string", "null"]
235
+ },
236
+ "CleanStatus": {
237
+ "type": ["string", "null"],
238
+ "enum": [
239
+ "In Sync",
240
+ "Different",
241
+ "Reviewed",
242
+ "Not Found",
243
+ "Inactive",
244
+ "Not Compared",
245
+ "Select Match",
246
+ "Skipped"
247
+ ]
248
+ },
249
+ "AccountSource": {
250
+ "type": ["string", "null"],
251
+ "enum": [
252
+ "Web",
253
+ "Phone Inquiry",
254
+ "Partner Referral",
255
+ "Purchased List",
256
+ "Other"
257
+ ]
258
+ },
259
+ "DunsNumber": {
260
+ "type": ["string", "null"]
261
+ },
262
+ "Tradestyle": {
263
+ "type": ["string", "null"]
264
+ },
265
+ "NaicsCode": {
266
+ "type": ["string", "null"]
267
+ },
268
+ "NaicsDesc": {
269
+ "type": ["string", "null"]
270
+ },
271
+ "YearStarted": {
272
+ "type": ["string", "null"]
273
+ },
274
+ "SicDesc": {
275
+ "type": ["string", "null"]
276
+ },
277
+ "DandbCompanyId": {
278
+ "type": ["string", "null"]
279
+ },
280
+ "OperatingHoursId": {
281
+ "type": ["string", "null"]
282
+ },
283
+ "CustomerPriority__c": {
284
+ "type": ["string", "null"],
285
+ "enum": ["High", "Low", "Medium"]
286
+ },
287
+ "SLA__c": {
288
+ "type": ["string", "null"],
289
+ "enum": ["Gold", "Silver", "Platinum", "Bronze"]
290
+ },
291
+ "Active__c": {
292
+ "type": ["string", "null"],
293
+ "enum": ["Yes", "No"]
294
+ },
295
+ "NumberofLocations__c": {
296
+ "type": ["number", "null"]
297
+ },
298
+ "UpsellOpportunity__c": {
299
+ "type": ["string", "null"],
300
+ "enum": ["Yes", "No", "Maybe"]
301
+ },
302
+ "SLASerialNumber__c": {
303
+ "type": ["string", "null"]
304
+ },
305
+ "SLAExpirationDate__c": {
306
+ "type": ["string", "null"],
307
+ "format": "date"
308
+ }
309
+ }
310
+ },
311
+ "supported_sync_modes": ["full_refresh", "incremental"],
312
+ "source_defined_cursor": true,
313
+ "default_cursor_field": ["updated"],
314
+ "source_defined_primary_key": [["Id"]]
315
+ }
316
+ ]
317
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "data":
3
+ {
4
+ "name": "Salesforce CRM",
5
+ "connector_type": "destination",
6
+ "category": "CRM",
7
+ "documentation_url": "https://docs.mutliwoven.com",
8
+ "github_issue_label": "destination-salesforce-crm",
9
+ "icon": "salesforce.svg",
10
+ "license": "MIT",
11
+ "release_stage": "alpha",
12
+ "support_level": "community",
13
+ "tags": ["language:ruby", "multiwoven"]
14
+ }
15
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "documentation_url": "https://docs.multiwoven.com/integrations/destination/salesforce_crm",
3
+ "stream_type": "static",
4
+ "connection_specification": {
5
+ "$schema": "http://json-schema.org/draft-07/schema#",
6
+ "title": "Salesforce Destination Spec",
7
+ "type": "object",
8
+ "required": ["access_token", "refresh_token", "instance_url", "client_id", "client_secret"],
9
+ "properties": {
10
+ "access_token": {
11
+ "type": "string",
12
+ "title": "Access Token",
13
+ "order": 0
14
+ },
15
+ "refresh_token": {
16
+ "type": "string",
17
+ "title": "Refresh Token",
18
+ "order": 1
19
+ },
20
+ "instance_url": {
21
+ "type": "string",
22
+ "title": "Instance URL",
23
+ "order": 2
24
+ },
25
+ "client_id": {
26
+ "type": "string",
27
+ "title": "Client ID",
28
+ "order": 3
29
+ },
30
+ "client_secret": {
31
+ "type": "string",
32
+ "title": "Client Secret",
33
+ "order": 4
34
+ }
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Multiwoven
4
+ module Integrations
5
+ module Destination
6
+ module Slack
7
+ include Multiwoven::Integrations::Core
8
+
9
+ class Client < DestinationConnector
10
+ attr_accessor :channel_id
11
+
12
+ def check_connection(connection_config)
13
+ configure_slack(connection_config[:api_token])
14
+ client = ::Slack::Web::Client.new
15
+ client.auth_test
16
+ success_status
17
+ rescue StandardError => e
18
+ failure_status(e)
19
+ end
20
+
21
+ def discover(_connection_config = nil)
22
+ catalog = build_catalog(load_catalog_streams)
23
+ catalog.to_multiwoven_message
24
+ rescue StandardError => e
25
+ handle_exception("SLACK:DISCOVER:EXCEPTION", "error", e)
26
+ end
27
+
28
+ def write(sync_config, records, action = "create")
29
+ # Currently as we only create a message for each record in slack, we are not using actions.
30
+ # This will be changed in future.
31
+
32
+ @action = sync_config.stream.action || action
33
+ connection_config = sync_config.destination.connection_specification.with_indifferent_access
34
+ configure_slack(connection_config[:api_token])
35
+ @client = ::Slack::Web::Client.new
36
+ @channel_id = connection_config[:channel_id]
37
+ process_records(records, sync_config.stream)
38
+ rescue StandardError => e
39
+ handle_exception("SLACK:WRITE:EXCEPTION", "error", e)
40
+ end
41
+
42
+ private
43
+
44
+ def configure_slack(api_token)
45
+ ::Slack.configure do |config|
46
+ config.token = api_token
47
+ end
48
+ end
49
+
50
+ def process_records(records, stream)
51
+ write_success = 0
52
+ write_failure = 0
53
+ records.each do |record_object|
54
+ process_record(stream, record_object.with_indifferent_access)
55
+ write_success += 1
56
+ rescue StandardError => e
57
+ write_failure += 1
58
+ handle_exception("SLACK:CRM:WRITE:EXCEPTION", "error", e)
59
+ end
60
+ tracking_message(write_success, write_failure)
61
+ end
62
+
63
+ def process_record(stream, record)
64
+ send_data_to_slack(stream[:name], record)
65
+ end
66
+
67
+ def send_data_to_slack(stream_name, record = {})
68
+ args = build_args(stream_name, record)
69
+ @client.send(stream_name, **args)
70
+ end
71
+
72
+ def build_args(stream_name, record)
73
+ case stream_name
74
+ when "chat_postMessage"
75
+ { channel: channel_id, text: slack_code_block(record[:data]) }
76
+ else
77
+ raise "Stream name not found: #{stream_name}"
78
+ end
79
+ end
80
+
81
+ def slack_code_block(data)
82
+ longest_key = data.keys.map(&:to_s).max_by(&:length).length
83
+ table_str = "```\n"
84
+ data.each do |key, value|
85
+ table_str += "#{key.to_s.ljust(longest_key)} : #{value}\n"
86
+ end
87
+ table_str += "```"
88
+
89
+ table_str
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_streams
101
+ catalog_json = read_json(CATALOG_SPEC_PATH)
102
+ catalog_json["streams"].map { |stream| build_stream(stream) }
103
+ end
104
+
105
+ def build_stream(stream)
106
+ Multiwoven::Integrations::Protocol::Stream.new(
107
+ name: stream["name"], json_schema: stream["json_schema"],
108
+ action: stream["action"]
109
+ )
110
+ end
111
+
112
+ def build_catalog(streams)
113
+ Multiwoven::Integrations::Protocol::Catalog.new(streams: streams)
114
+ end
115
+
116
+ def tracking_message(success, failure)
117
+ Multiwoven::Integrations::Protocol::TrackingMessage.new(
118
+ success: success, failed: failure
119
+ ).to_multiwoven_message
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,71 @@
1
+ {
2
+ "streams": [
3
+ {
4
+ "name": "chat_postMessage",
5
+ "action": "create",
6
+ "json_schema": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "text": {
11
+ "type": ["string", "null"]
12
+ },
13
+ "attachments": {
14
+ "type": ["string", "null"]
15
+ },
16
+ "blocks": {
17
+ "type": ["array", "null"],
18
+ "items": {
19
+ "type": "string"
20
+ }
21
+ },
22
+ "as_user": {
23
+ "type": ["boolean", "null"]
24
+ },
25
+ "icon_emoji": {
26
+ "type": ["string", "null"]
27
+ },
28
+ "icon_url": {
29
+ "type": ["string", "null"]
30
+ },
31
+ "link_names": {
32
+ "type": ["boolean", "null"]
33
+ },
34
+ "metadata": {
35
+ "type": ["string", "null"]
36
+ },
37
+ "mrkdwn": {
38
+ "type": ["boolean", "null"]
39
+ },
40
+ "parse": {
41
+ "type": ["string", "null"]
42
+ },
43
+ "reply_broadcast": {
44
+ "type": ["boolean", "null"]
45
+ },
46
+ "thread_ts": {
47
+ "type": ["string", "null"]
48
+ },
49
+ "unfurl_links": {
50
+ "type": ["boolean", "null"]
51
+ },
52
+ "unfurl_media": {
53
+ "type": ["boolean", "null"]
54
+ },
55
+ "username": {
56
+ "type": ["string", "null"]
57
+ }
58
+ },
59
+ "oneOf": [
60
+ { "required": ["text"] },
61
+ { "required": ["attachments"] },
62
+ { "required": ["blocks"] }
63
+ ]
64
+ },
65
+ "supported_sync_modes": ["full_refresh", "incremental"],
66
+ "source_defined_cursor": true,
67
+ "default_cursor_field": ["updated"],
68
+ "source_defined_primary_key": [["Id"]]
69
+ }
70
+ ]
71
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "data": {
3
+ "name": "Slack",
4
+ "connector_type": "destination",
5
+ "category": "Team Collaboration",
6
+ "documentation_url": "https://docs.mutliwoven.com",
7
+ "github_issue_label": "destination-slack",
8
+ "icon": "slack.svg",
9
+ "license": "MIT",
10
+ "release_stage": "alpha",
11
+ "support_level": "community",
12
+ "tags": ["language:ruby", "multiwoven"]
13
+ }
14
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "documentation_url": "https://docs.multiwoven.com/integrations/destination/slack",
3
+ "stream_type": "static",
4
+ "connection_specification": {
5
+ "$schema": "http://json-schema.org/draft-07/schema#",
6
+ "title": "Slack Destination Spec",
7
+ "type": "object",
8
+ "required": ["api_token"],
9
+ "properties": {
10
+ "api_token": {
11
+ "type": "string",
12
+ "title": "API Token",
13
+ "order": 0
14
+ },
15
+ "channel_id": {
16
+ "type": "string",
17
+ "title": "Channel ID",
18
+ "order": 1
19
+ }
20
+ }
21
+ }
22
+ }
@@ -130,6 +130,8 @@ module Multiwoven
130
130
  end
131
131
 
132
132
  class SyncConfig < ProtocolModel
133
+ attr_accessor :offset, :limit
134
+
133
135
  attribute :source, Connector
134
136
  attribute :destination, Connector
135
137
  attribute :model, Model
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Multiwoven
4
4
  module Integrations
5
- VERSION = "0.1.1"
5
+ VERSION = "0.1.6"
6
6
 
7
7
  ENABLED_SOURCES = %w[
8
8
  Snowflake
@@ -12,6 +12,9 @@ module Multiwoven
12
12
 
13
13
  ENABLED_DESTINATIONS = %w[
14
14
  Klaviyo
15
+ SalesforceCrm
16
+ FacebookCustomAudience
17
+ Slack
15
18
  ].freeze
16
19
  end
17
20
  end
@@ -7,6 +7,7 @@ module Multiwoven::Integrations::Source
7
7
  include Multiwoven::Integrations::Core
8
8
  class Client < SourceConnector
9
9
  def check_connection(connection_config)
10
+ connection_config = connection_config.with_indifferent_access
10
11
  bigquery = create_connection(connection_config)
11
12
  bigquery.datasets
12
13
  ConnectionStatus.new(status: ConnectionStatusType["succeeded"]).to_multiwoven_message
@@ -15,6 +16,7 @@ module Multiwoven::Integrations::Source
15
16
  end
16
17
 
17
18
  def discover(connection_config)
19
+ connection_config = connection_config.with_indifferent_access
18
20
  bigquery = create_connection(connection_config)
19
21
  target_dataset_id = connection_config["dataset_id"]
20
22
  records = bigquery.datasets.flat_map do |dataset|
@@ -43,7 +45,11 @@ module Multiwoven::Integrations::Source
43
45
 
44
46
  def read(sync_config)
45
47
  connection_config = sync_config.source.connection_specification
48
+ connection_config = connection_config.with_indifferent_access
46
49
  query = sync_config.model.query
50
+
51
+ query = batched_query(query, sync_config.limit, sync_config.offset) unless sync_config.limit.nil? && sync_config.offset.nil?
52
+
47
53
  bigquery = create_connection(connection_config)
48
54
  records = []
49
55
  results = bigquery.query query
@@ -3,7 +3,7 @@
3
3
  {
4
4
  "name": "BigQuery",
5
5
  "connector_type": "source",
6
- "connector_subtype": "database",
6
+ "category": "Data Warehouse",
7
7
  "documentation_url": "https://docs.mutliwoven.com",
8
8
  "github_issue_label": "source-bigquery",
9
9
  "icon": "bigquery.svg",