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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e33f303a54e624644de0dbaa80056632e61c7834f0b192ba2e041e6c3d131e6
4
- data.tar.gz: dd3e29285e988665a08b6e71b1986810af068916bd2ebdd4f74a3bacab6b1385
3
+ metadata.gz: 2ca3845d4b648bffe8c785fdd1cc0c206099deb7d7414700d528a25ec2fb72f9
4
+ data.tar.gz: 319139931a5c9a723a379c3aa116d3d6a825235fc6b80359abf6c19104fcc815
5
5
  SHA512:
6
- metadata.gz: f538a941eae0f2d00d754d494d70c319f4bf44444ff4463e36010dbd53e25633b114f7399edd5a1199720eb736d20f64c8e956df4b349776e173ed1f6bc537c1
7
- data.tar.gz: 56fe3d7902f215f67403d21bcb857f8d18d87f6be0082dd875489e4d60d74a753178a2436eacbc4405f7c3baa8b1fbf267bfe3ab09cc9a292137c413e5432bd3
6
+ metadata.gz: f5a96c27ef09fb593a0f000bbefa835fb79ec2c7876a7d84635013f757d8a31a52f7f5f32b791a9539dc6494af0092faf28ca8c13cccc1b4465e94eeba49490a
7
+ data.tar.gz: a9ddefdc6259d2d160ea118e1571ed1facc8cb39d533f490692e085a12d160b2d8ec8138ab1801353c0e31eb172111ffa51d1c49af9b1b54c9a1645b7a9315f1
@@ -0,0 +1,5 @@
1
+ {
2
+ "cSpell.words": [
3
+ "multiwoven"
4
+ ]
5
+ }
@@ -30,10 +30,10 @@ module Multiwoven
30
30
  private
31
31
 
32
32
  def read_json(file_path)
33
- # TODO: Rethink. Hack to find connector folder
34
- connector_folder = self.class.to_s.split("::")[2..3].map(&:downcase).join("/")
33
+ path = Object.const_source_location(self.class.to_s)[0]
34
+ connector_folder = File.dirname(path)
35
35
  file_path = File.join(
36
- INTEGRATIONS_PATH, "#{connector_folder}/",
36
+ "#{connector_folder}/",
37
37
  file_path
38
38
  )
39
39
  file_contents = File.read(file_path)
@@ -24,6 +24,8 @@ module Multiwoven
24
24
  }
25
25
  }.freeze
26
26
 
27
+ FACEBOOK_AUDIENCE_GET_ALL_ACCOUNTS = "https://graph.facebook.com/v18.0/me/adaccounts?fields=id,name"
28
+
27
29
  # HTTP
28
30
  HTTP_GET = "GET"
29
31
  HTTP_POST = "POST"
@@ -7,6 +7,23 @@ module Multiwoven
7
7
  raise "Not implemented"
8
8
  # return list of RecordMessage
9
9
  end
10
+
11
+ private
12
+
13
+ def batched_query(sql_query, limit, offset)
14
+ offset = offset.to_i
15
+ limit = limit.to_i
16
+ raise ArgumentError, "Offset and limit must be non-negative" if offset.negative? || limit.negative?
17
+
18
+ # Removing any trailing semicolons
19
+ sql_query.chomp!(";")
20
+
21
+ # Checking if the query already has a LIMIT clause
22
+ raise ArgumentError, "Query already contains a LIMIT clause" if sql_query.match?(/LIMIT \d+/i)
23
+
24
+ # Appending the LIMIT and OFFSET clauses to the SQL query
25
+ "#{sql_query} LIMIT #{limit} OFFSET #{offset}"
26
+ end
10
27
  end
11
28
  end
12
29
  end
@@ -63,6 +63,14 @@ module Multiwoven
63
63
 
64
64
  create_log_message(context, type, exception)
65
65
  end
66
+
67
+ def extract_data(record_object, properties)
68
+ record_object.data.select { |key, _| properties.key?(key.to_s) }
69
+ end
70
+
71
+ def success?(response)
72
+ response && %w[200 201].include?(response.code.to_s)
73
+ end
66
74
  end
67
75
  end
68
76
  end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Multiwoven::Integrations::Destination
4
+ module FacebookCustomAudience
5
+ include Multiwoven::Integrations::Core
6
+ class Client < DestinationConnector # rubocop:disable Metrics/ClassLength
7
+ def check_connection(connection_config)
8
+ connection_config = connection_config.with_indifferent_access
9
+ access_token = connection_config[:access_token]
10
+ response = Multiwoven::Integrations::Core::HttpClient.request(
11
+ FACEBOOK_AUDIENCE_GET_ALL_ACCOUNTS,
12
+ HTTP_GET,
13
+ headers: auth_headers(access_token)
14
+ )
15
+ if success?(response)
16
+ ad_account_exists?(response, connection_config[:ad_account_id])
17
+ ConnectionStatus.new(status: ConnectionStatusType["succeeded"]).to_multiwoven_message
18
+ else
19
+ ConnectionStatus.new(status: ConnectionStatusType["failed"]).to_multiwoven_message
20
+ end
21
+ rescue StandardError => e
22
+ ConnectionStatus.new(status: ConnectionStatusType["failed"], message: e.message).to_multiwoven_message
23
+ end
24
+
25
+ def discover(_connection_config = nil)
26
+ catalog_json = read_json(CATALOG_SPEC_PATH)
27
+
28
+ streams = catalog_json["streams"].map do |stream|
29
+ Multiwoven::Integrations::Protocol::Stream.new(
30
+ audience_id: stream["audience_id"],
31
+ url: stream["url"],
32
+ name: stream["name"],
33
+ json_schema: stream["json_schema"],
34
+ request_method: stream["method"],
35
+ action: stream["action"]
36
+ )
37
+ end
38
+
39
+ catalog = Multiwoven::Integrations::Protocol::Catalog.new(
40
+ streams: streams
41
+ )
42
+
43
+ catalog.to_multiwoven_message
44
+ rescue StandardError => e
45
+ handle_exception(
46
+ "FACEBOOK AUDIENCE:DISCOVER:EXCEPTION",
47
+ "error",
48
+ e
49
+ )
50
+ end
51
+
52
+ def write(sync_config, records, _action = "insert")
53
+ url = sync_config.stream.url
54
+ connection_config = sync_config.destination.connection_specification.with_indifferent_access
55
+ connection_config = connection_config.with_indifferent_access
56
+ access_token = connection_config[:access_token]
57
+
58
+ write_success = 0
59
+ write_failure = 0
60
+ records.each do |record|
61
+ schema, data = extract_schema_and_data(record.with_indifferent_access[:data])
62
+ payload = {
63
+ "payload" => {
64
+ "schema" => schema,
65
+ "data" => [data]
66
+ }
67
+ }
68
+ response = Multiwoven::Integrations::Core::HttpClient.request(
69
+ url,
70
+ sync_config.stream.request_method,
71
+ payload: payload,
72
+ headers: auth_headers(access_token)
73
+ )
74
+ if success?(response)
75
+ write_success += 1
76
+ else
77
+ write_failure += 1
78
+ end
79
+ rescue StandardError => e
80
+ logger.error(
81
+ "FACEBOOK:RECORD:WRITE:FAILURE: #{e.message}"
82
+ )
83
+ write_failure += 1
84
+ end
85
+ tracker = Multiwoven::Integrations::Protocol::TrackingMessage.new(
86
+ success: write_success,
87
+ failed: write_failure
88
+ )
89
+ tracker.to_multiwoven_message
90
+ rescue StandardError => e
91
+ # TODO: Handle rate limiting seperately
92
+ handle_exception(
93
+ "FACEBOOK:WRITE:EXCEPTION",
94
+ "error",
95
+ e
96
+ )
97
+ end
98
+
99
+ def extract_schema_and_data(data)
100
+ [data.keys, data.values]
101
+ end
102
+
103
+ def auth_headers(access_token)
104
+ {
105
+ "Accept" => "application/json",
106
+ "Authorization" => "Bearer #{access_token}",
107
+ "Content-Type" => "application/json"
108
+ }
109
+ end
110
+
111
+ def ad_account_exists?(response, ad_account_id)
112
+ return if extract_data(response).any? { |ad_account| ad_account["id"] == "act_#{ad_account_id}" }
113
+
114
+ raise ArgumentError, "Ad account not found in business account"
115
+ end
116
+
117
+ def extract_data(response)
118
+ response_body = response.body
119
+ JSON.parse(response_body)["data"] if response_body
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,38 @@
1
+ {
2
+ "streams": [
3
+ {
4
+ "name": "audience",
5
+ "action": "create",
6
+ "method": "POST",
7
+ "audience_id": "audience_id",
8
+ "url": "https://graph.facebook.com/v18.0/{audience_id}/users",
9
+ "json_schema": {
10
+ "type": "object",
11
+ "additionalProperties": true,
12
+ "properties": {
13
+ "CT": { "type": ["string", "null"], "default": null },
14
+ "COUNTRY": { "type": ["string", "null"], "default": null },
15
+ "DOBD": { "type": ["string", "null"], "default": null },
16
+ "DOBM": { "type": ["string", "null"], "default": null },
17
+ "DOBY": { "type": ["string", "null"], "default": null },
18
+ "EMAIL": { "type": ["string", "null"], "default": null },
19
+ "EXTERN_ID": { "type": ["string", "null"], "default": null },
20
+ "FI": { "type": ["string", "null"], "default": null },
21
+ "FN": { "type": ["string", "null"], "default": null },
22
+ "GEN": { "type": ["string", "null"], "default": null },
23
+ "LN": { "type": ["string", "null"], "default": null },
24
+ "LOOKALIKE_VALUE": { "type": ["number", "null"], "default": null },
25
+ "MADID": { "type": ["string", "null"], "default": null },
26
+ "PAGEUID": { "type": ["string", "null"], "default": null },
27
+ "PHONE": { "type": ["string", "null"], "default": null },
28
+ "ST": { "type": ["string", "null"], "default": null },
29
+ "ZIP": { "type": ["string", "null"], "default": null }
30
+ }
31
+ },
32
+ "supported_sync_modes": ["full_refresh", "incremental"],
33
+ "source_defined_cursor": true,
34
+ "default_cursor_field": ["updated"],
35
+ "source_defined_primary_key": [["email"]]
36
+ }
37
+ ]
38
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "data":
3
+ {
4
+ "name": "Facebook",
5
+ "connector_type": "destination",
6
+ "category": "Adtech",
7
+ "documentation_url": "https://docs.mutliwoven.com",
8
+ "github_issue_label": "destination-facebook",
9
+ "icon": "facebook.svg",
10
+ "license": "MIT",
11
+ "release_stage": "alpha",
12
+ "support_level": "community",
13
+ "tags": ["language:ruby", "multiwoven"]
14
+ }
15
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "documentation_url": "https://docs.multiwoven.com/integrations/destination/facebook",
3
+ "stream_type": "static",
4
+ "connection_specification": {
5
+ "$schema": "http://json-schema.org/draft-07/schema#",
6
+ "title": "Facebook Destination Spec",
7
+ "type": "object",
8
+ "required": ["access_token","ad_account_id"],
9
+ "properties": {
10
+ "access_token": {
11
+ "type": "string",
12
+ "title": "Access Token",
13
+ "order": 0
14
+ },
15
+ "ad_account_id": {
16
+ "type": "string",
17
+ "title": "Ad Account Id",
18
+ "order": 1
19
+ }
20
+ }
21
+ }
22
+ }
@@ -3,8 +3,9 @@
3
3
  module Multiwoven::Integrations::Destination
4
4
  module Klaviyo
5
5
  include Multiwoven::Integrations::Core
6
- class Client < DestinationConnector
6
+ class Client < DestinationConnector # rubocop:disable Metrics/ClassLength
7
7
  def check_connection(connection_config)
8
+ connection_config = connection_config.with_indifferent_access
8
9
  api_key = connection_config[:private_api_key]
9
10
 
10
11
  response = Multiwoven::Integrations::Core::HttpClient.request(
@@ -16,7 +17,7 @@ module Multiwoven::Integrations::Destination
16
17
  parse_response(response)
17
18
  end
18
19
 
19
- def discover
20
+ def discover(_connection_config = nil)
20
21
  catalog_json = read_json(CATALOG_SPEC_PATH)
21
22
 
22
23
  streams = catalog_json["streams"].map do |stream|
@@ -24,7 +25,7 @@ module Multiwoven::Integrations::Destination
24
25
  name: stream["name"],
25
26
  json_schema: stream["json_schema"],
26
27
  url: stream["url"],
27
- method: stream["method"],
28
+ request_method: stream["method"],
28
29
  action: stream["action"]
29
30
  )
30
31
  end
@@ -44,12 +45,19 @@ module Multiwoven::Integrations::Destination
44
45
 
45
46
  def write(sync_config, records, _action = "insert")
46
47
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
48
+ connection_config = connection_config.with_indifferent_access
47
49
  url = sync_config.stream.url
50
+
48
51
  request_method = sync_config.stream.request_method
49
52
 
50
53
  write_success = 0
51
54
  write_failure = 0
52
55
  records.each do |record|
56
+ # pre process payload
57
+ # Add hardcode values into payload
58
+ record["data"] ||= {}
59
+ record["data"]["type"] = sync_config.stream.name
60
+
53
61
  response = Multiwoven::Integrations::Core::HttpClient.request(
54
62
  url,
55
63
  request_method,
@@ -6,103 +6,101 @@
6
6
  "url": "https://a.klaviyo.com/api/profiles",
7
7
  "method": "POST",
8
8
  "json_schema": {
9
+ "$schema": "http://json-schema.org/draft-07/schema#",
9
10
  "type": "object",
10
- "additionalProperties": true,
11
11
  "properties": {
12
- "type": {
13
- "type": [
14
- "null",
15
- "string"
16
- ]
17
- },
18
- "id": {
19
- "type": "string"
20
- },
21
- "updated": {
22
- "type": [
23
- "null",
24
- "string"
25
- ],
26
- "format": "date-time"
27
- },
28
- "attributes": {
29
- "type": [
30
- "null",
31
- "object"
32
- ],
33
- "additionalProperties": true,
12
+ "data": {
13
+ "type": "object",
34
14
  "properties": {
35
- "email": {
36
- "type": [
37
- "null",
38
- "string"
39
- ]
40
- },
41
- "phone_number": {
42
- "type": [
43
- "null",
44
- "string"
45
- ]
46
- },
47
- "first_name": {
48
- "type": [
49
- "null",
50
- "string"
51
- ]
52
- },
53
- "last_name": {
54
- "type": [
55
- "null",
56
- "string"
15
+ "type": {
16
+ "type": "string",
17
+ "enum": [
18
+ "profile"
57
19
  ]
58
20
  },
59
- "properties": {
60
- "type": [
61
- "null",
62
- "object"
63
- ],
64
- "additionalProperties": true
65
- },
66
- "organization": {
67
- "type": [
68
- "null",
69
- "string"
70
- ]
71
- },
72
- "title": {
73
- "type": [
74
- "null",
75
- "string"
21
+ "attributes": {
22
+ "type": "object",
23
+ "properties": {
24
+ "email": {
25
+ "type": "string",
26
+ "format": "email"
27
+ },
28
+ "phone_number": {
29
+ "type": "string"
30
+ },
31
+ "external_id": {
32
+ "type": "string",
33
+ "format": "uuid"
34
+ },
35
+ "first_name": {
36
+ "type": "string"
37
+ },
38
+ "last_name": {
39
+ "type": "string"
40
+ },
41
+ "organization": {
42
+ "type": "string"
43
+ },
44
+ "title": {
45
+ "type": "string"
46
+ },
47
+ "image": {
48
+ "type": "string",
49
+ "format": "uri"
50
+ },
51
+ "location": {
52
+ "type": "object",
53
+ "properties": {
54
+ "address1": {
55
+ "type": "string"
56
+ },
57
+ "address2": {
58
+ "type": "string"
59
+ },
60
+ "city": {
61
+ "type": "string"
62
+ },
63
+ "country": {
64
+ "type": "string"
65
+ },
66
+ "region": {
67
+ "type": "string"
68
+ },
69
+ "zip": {
70
+ "type": "string"
71
+ },
72
+ "timezone": {
73
+ "type": "string"
74
+ },
75
+ "ip": {
76
+ "type": "string",
77
+ "format": "ipv4"
78
+ }
79
+ }
80
+
81
+ },
82
+ "properties": {
83
+ "type": "object",
84
+ "additionalProperties": {
85
+ "type": "string"
86
+ }
87
+ }
88
+ },
89
+ "required": [
90
+ "email",
91
+ "phone_number"
76
92
  ]
77
- },
78
- "last_event_date": {
79
- "type": [
80
- "null",
81
- "string"
82
- ],
83
- "format": "date-time"
84
93
  }
85
- }
86
- },
87
- "links": {
88
- "type": [
89
- "null",
90
- "object"
91
- ]
92
- },
93
- "relationships": {
94
- "type": [
95
- "null",
96
- "object"
97
- ]
98
- },
99
- "segments": {
100
- "type": [
101
- "null",
102
- "object"
94
+ },
95
+ "required": [
96
+ "type",
97
+ "attributes"
103
98
  ]
104
99
  }
105
- }
100
+ },
101
+ "required": [
102
+ "data"
103
+ ]
106
104
  },
107
105
  "supported_sync_modes": [
108
106
  "full_refresh",
@@ -3,7 +3,7 @@
3
3
  {
4
4
  "name": "Klaviyo",
5
5
  "connector_type": "destination",
6
- "connector_subtype": "API",
6
+ "category": "Marketing Automation",
7
7
  "documentation_url": "https://docs.mutliwoven.com",
8
8
  "github_issue_label": "destination-klaviyo",
9
9
  "icon": "klaviyo.svg",
@@ -7,7 +7,7 @@
7
7
  "type": "object",
8
8
  "required": ["public_api_key", "private_api_key"],
9
9
  "properties": {
10
- "public_api_keyhost": {
10
+ "public_api_key": {
11
11
  "type": "string",
12
12
  "title": "Public API Key",
13
13
  "order": 0
@@ -19,4 +19,4 @@
19
19
  }
20
20
  }
21
21
  }
22
- }
22
+ }
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ module Multiwoven
6
+ module Integrations
7
+ module Destination
8
+ module SalesforceCrm
9
+ include Multiwoven::Integrations::Core
10
+
11
+ API_VERSION = "59.0"
12
+
13
+ class Client < DestinationConnector
14
+ def check_connection(connection_config)
15
+ connection_config = connection_config.with_indifferent_access
16
+ initialize_client(connection_config)
17
+ authenticate_client
18
+ success_status
19
+ rescue StandardError => e
20
+ failure_status(e)
21
+ end
22
+
23
+ def discover(_connection_config = nil)
24
+ catalog = build_catalog(load_catalog_streams)
25
+ catalog.to_multiwoven_message
26
+ rescue StandardError => e
27
+ handle_exception("SALESFORCE:CRM:DISCOVER:EXCEPTION", "error", e)
28
+ end
29
+
30
+ def write(sync_config, records, _action = "create")
31
+ initialize_client(sync_config[:destination][:connection_specification])
32
+ process_records(records, sync_config[:stream])
33
+ rescue StandardError => e
34
+ handle_exception("SALESFORCE:CRM:WRITE:EXCEPTION", "error", e)
35
+ end
36
+
37
+ private
38
+
39
+ def initialize_client(config)
40
+ config = config.with_indifferent_access
41
+ @client = Restforce.new(oauth_token: config[:access_token],
42
+ refresh_token: config[:refresh_token],
43
+ instance_url: config[:instance_url],
44
+ client_id: config[:client_id],
45
+ client_secret: config[:client_secret],
46
+ authentication_callback: proc { |x| log_debug(x.to_s) },
47
+ api_version: API_VERSION)
48
+ end
49
+
50
+ def process_records(records, stream)
51
+ write_success = 0
52
+ write_failure = 0
53
+ properties = stream[:json_schema][:properties]
54
+ records.each do |record_object|
55
+ record = extract_data(record_object, properties)
56
+ process_record(stream, record)
57
+ write_success += 1
58
+ rescue StandardError => e
59
+ handle_exception("SALESFORCE:CRM:WRITE:EXCEPTION", "error", e)
60
+ write_failure += 1
61
+ end
62
+ tracking_message(write_success, write_failure)
63
+ end
64
+
65
+ def process_record(stream, record)
66
+ send_data_to_salesforce(stream[:action], stream[:name], record)
67
+ end
68
+
69
+ def send_data_to_salesforce(action, stream_name, record = {})
70
+ method_name = "#{action}!"
71
+ args = build_args(action, stream_name, record)
72
+ @client.send(method_name, *args)
73
+ end
74
+
75
+ def build_args(action, stream_name, record)
76
+ case action
77
+ when :upsert
78
+ [stream_name, record[:external_key], record]
79
+ when :destroy
80
+ [stream_name, record[:id]]
81
+ else
82
+ [stream_name, record]
83
+ end
84
+ end
85
+
86
+ def authenticate_client
87
+ @client.authenticate!
88
+ end
89
+
90
+ def success_status
91
+ ConnectionStatus.new(status: ConnectionStatusType["succeeded"]).to_multiwoven_message
92
+ end
93
+
94
+ def failure_status(error)
95
+ ConnectionStatus.new(status: ConnectionStatusType["failed"], message: error.message).to_multiwoven_message
96
+ end
97
+
98
+ def load_catalog_streams
99
+ catalog_json = read_json(CATALOG_SPEC_PATH)
100
+ catalog_json["streams"].map { |stream| build_stream(stream) }
101
+ end
102
+
103
+ def build_stream(stream)
104
+ Multiwoven::Integrations::Protocol::Stream.new(
105
+ name: stream["name"], json_schema: stream["json_schema"],
106
+ action: stream["action"]
107
+ )
108
+ end
109
+
110
+ def build_catalog(streams)
111
+ Multiwoven::Integrations::Protocol::Catalog.new(streams: streams)
112
+ end
113
+
114
+ def tracking_message(success, failure)
115
+ Multiwoven::Integrations::Protocol::TrackingMessage.new(
116
+ success: success, failed: failure
117
+ ).to_multiwoven_message
118
+ end
119
+
120
+ def log_debug(message)
121
+ Multiwoven::Integrations::Service.logger.debug(message)
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end