multiwoven-integrations 0.35.4 → 0.35.5

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/lib/multiwoven/integrations/core/destination_connector.rb +1 -1
  3. data/lib/multiwoven/integrations/core/utils.rb +4 -2
  4. data/lib/multiwoven/integrations/destination/airtable/client.rb +1 -1
  5. data/lib/multiwoven/integrations/destination/ais_data_store/client.rb +1 -1
  6. data/lib/multiwoven/integrations/destination/amazon_s3/client.rb +1 -1
  7. data/lib/multiwoven/integrations/destination/databricks_lakehouse/client.rb +1 -1
  8. data/lib/multiwoven/integrations/destination/facebook_custom_audience/client.rb +1 -1
  9. data/lib/multiwoven/integrations/destination/google_sheets/client.rb +1 -1
  10. data/lib/multiwoven/integrations/destination/http/client.rb +1 -1
  11. data/lib/multiwoven/integrations/destination/hubspot/client.rb +1 -1
  12. data/lib/multiwoven/integrations/destination/iterable/client.rb +1 -1
  13. data/lib/multiwoven/integrations/destination/klaviyo/client.rb +1 -1
  14. data/lib/multiwoven/integrations/destination/mailchimp/client.rb +1 -1
  15. data/lib/multiwoven/integrations/destination/maria_db/client.rb +1 -1
  16. data/lib/multiwoven/integrations/destination/microsoft_dynamics/client.rb +1 -1
  17. data/lib/multiwoven/integrations/destination/microsoft_excel/client.rb +1 -1
  18. data/lib/multiwoven/integrations/destination/microsoft_sql/client.rb +1 -1
  19. data/lib/multiwoven/integrations/destination/odoo/client.rb +1 -1
  20. data/lib/multiwoven/integrations/destination/oracle_db/client.rb +1 -1
  21. data/lib/multiwoven/integrations/destination/pinecone_db/client.rb +1 -1
  22. data/lib/multiwoven/integrations/destination/postgresql/client.rb +45 -22
  23. data/lib/multiwoven/integrations/destination/qdrant/client.rb +1 -1
  24. data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/client.rb +1 -1
  25. data/lib/multiwoven/integrations/destination/salesforce_crm/client.rb +1 -1
  26. data/lib/multiwoven/integrations/destination/sftp/client.rb +1 -1
  27. data/lib/multiwoven/integrations/destination/slack/client.rb +1 -1
  28. data/lib/multiwoven/integrations/destination/stripe/client.rb +1 -1
  29. data/lib/multiwoven/integrations/destination/weaviate/client.rb +1 -1
  30. data/lib/multiwoven/integrations/destination/zendesk/client.rb +1 -1
  31. data/lib/multiwoven/integrations/protocol/protocol.rb +1 -0
  32. data/lib/multiwoven/integrations/rollout.rb +1 -1
  33. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f03f909a2b711e29b6c9adb5ce81c3b5bcb12653d633c7dbd5cc7828ffce4121
4
- data.tar.gz: b244b5a203346665be128a7b24b0d82cd7431ed4480cb90059351489c97e1d02
3
+ metadata.gz: 05a855904763ae02e56fc52e11c7c0b1f87b1c140c771b16cd899e3056643d9b
4
+ data.tar.gz: 0de786c3a14408809cce44d422402b5325768edfbe7b1fe6ca28ffb991ca794d
5
5
  SHA512:
6
- metadata.gz: 61deca83fc498fc44c3d31b6fdbd5e68863dfdebc7449046bfe39d84ebf9ca49aeb4623bab3c4438130fb701d6286bd8f9832e32d7e1bc670509685ebe802b1e
7
- data.tar.gz: 5a1b1c6060ee16c5e6ee552c4eb63a9cdecf3d229d5e7dfb1f04b0117769d8b75925f23b3c210a220d6c5456e96243ce4197be17a3e8e336070c799479dce866
6
+ metadata.gz: 8a93957c5a3a671e11a5c1366158252255be5c5687d4638a44f966ebad5d592c5e5af9d03a067c8462985fb486edc4b360c649b4aeb35bd3f8432378b2d991a8
7
+ data.tar.gz: bae29e2583d2db39a248f38019c8cb4ee70f68514bc24efd73b057b18303c89ac5998d8c6f32167b0b32e9e2402b99e28ab233f11fe5a7288fca55da917caaf1
@@ -5,7 +5,7 @@ module Multiwoven
5
5
  class DestinationConnector < BaseConnector
6
6
  # Records are transformed json payload send it to the destination
7
7
  # SyncConfig is the Protocol::SyncConfig object
8
- def write(_sync_config, _records, _action = "destination_insert")
8
+ def write(_sync_config, _records, _action = "destination_insert", _identifier_key = nil)
9
9
  raise "Not implemented"
10
10
  # return Protocol::TrackingMessage
11
11
  end
@@ -55,16 +55,18 @@ module Multiwoven
55
55
  reporter&.report(exception, meta)
56
56
  end
57
57
 
58
- def log_request_response(level, request, response)
58
+ def log_request_response(level, request, response, record_identifier = nil)
59
59
  Integrations::Protocol::LogMessage.new(
60
+ record_identifier: record_identifier,
60
61
  name: self.class.name,
61
62
  level: level,
62
63
  message: { request: request.to_s, response: response.to_s, level: level }.to_json
63
64
  )
64
65
  end
65
66
 
66
- def create_log_message(context, type, message)
67
+ def create_log_message(context, type, message, record_identifier = nil)
67
68
  Integrations::Protocol::LogMessage.new(
69
+ record_identifier: record_identifier,
68
70
  name: context,
69
71
  level: type,
70
72
  message: message
@@ -55,7 +55,7 @@ module Multiwoven
55
55
  })
56
56
  end
57
57
 
58
- def write(sync_config, records, _action = "create")
58
+ def write(sync_config, records, _action = "create", _identifier_key = nil)
59
59
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
60
60
  api_key = connection_config[:api_key]
61
61
  url = sync_config.stream.url
@@ -45,7 +45,7 @@ module Multiwoven::Integrations::Destination
45
45
  db&.close
46
46
  end
47
47
 
48
- def write(sync_config, records, action = "destination_insert")
48
+ def write(sync_config, records, action = "destination_insert", _identifier_key = nil)
49
49
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
50
50
  table_name = sync_config.stream.name
51
51
  primary_key = sync_config.model.primary_key
@@ -27,7 +27,7 @@ module Multiwoven::Integrations::Destination
27
27
  })
28
28
  end
29
29
 
30
- def write(sync_config, records, _action = "destination_insert")
30
+ def write(sync_config, records, _action = "destination_insert", _identifier_key = nil)
31
31
  records_size = records.size
32
32
  log_message_array = []
33
33
  write_success = upload_csv_content(sync_config, records)
@@ -48,7 +48,7 @@ module Multiwoven
48
48
  )
49
49
  end
50
50
 
51
- def write(sync_config, records, action = "destination_insert")
51
+ def write(sync_config, records, action = "destination_insert", _identifier_key = nil)
52
52
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
53
53
  table_name = "#{connection_config[:catalog]}.#{connection_config[:schema]}.#{sync_config.stream.name}"
54
54
  primary_key = sync_config.model.primary_key
@@ -35,7 +35,7 @@ module Multiwoven::Integrations::Destination
35
35
  })
36
36
  end
37
37
 
38
- def write(sync_config, records, _action = "destination_insert")
38
+ def write(sync_config, records, _action = "destination_insert", _identifier_key = nil)
39
39
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
40
40
  access_token = connection_config[:access_token]
41
41
  url = generate_url(sync_config, connection_config)
@@ -33,7 +33,7 @@ module Multiwoven
33
33
  })
34
34
  end
35
35
 
36
- def write(sync_config, records, action = "create")
36
+ def write(sync_config, records, action = "create", _identifier_key = nil)
37
37
  setup_write_environment(sync_config, action)
38
38
  process_record_chunks(records, sync_config)
39
39
  rescue StandardError => e
@@ -41,7 +41,7 @@ module Multiwoven
41
41
  })
42
42
  end
43
43
 
44
- def write(sync_config, records, _action = "create")
44
+ def write(sync_config, records, _action = "create", _identifier_key = nil)
45
45
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
46
46
  url = connection_config[:destination_url]
47
47
  headers = connection_config[:headers]
@@ -33,7 +33,7 @@ module Multiwoven
33
33
  })
34
34
  end
35
35
 
36
- def write(sync_config, records, action = "create")
36
+ def write(sync_config, records, action = "create", _identifier_key = nil)
37
37
  @action = sync_config.stream.action || action
38
38
  @sync_config = sync_config
39
39
  initialize_client(sync_config.destination.connection_specification)
@@ -36,7 +36,7 @@ module Multiwoven
36
36
  )
37
37
  end
38
38
 
39
- def write(sync_config, records, action = "create")
39
+ def write(sync_config, records, action = "create", _identifier_key = nil)
40
40
  @action = sync_config.stream.action || action
41
41
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
42
42
  initialize_client(connection_config)
@@ -31,7 +31,7 @@ module Multiwoven::Integrations::Destination
31
31
  })
32
32
  end
33
33
 
34
- def write(sync_config, records, _action = "destination_insert")
34
+ def write(sync_config, records, _action = "destination_insert", _identifier_key = nil)
35
35
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
36
36
  connection_config = connection_config.with_indifferent_access
37
37
  url = sync_config.stream.url
@@ -30,7 +30,7 @@ module Multiwoven
30
30
  })
31
31
  end
32
32
 
33
- def write(sync_config, records, _action = "create")
33
+ def write(sync_config, records, _action = "create", _identifier_key = nil)
34
34
  @sync_config = sync_config
35
35
  initialize_client(sync_config.destination.connection_specification)
36
36
  process_records(records, sync_config.stream)
@@ -35,7 +35,7 @@ module Multiwoven::Integrations::Destination
35
35
  )
36
36
  end
37
37
 
38
- def write(sync_config, records, action = "destination_insert")
38
+ def write(sync_config, records, action = "destination_insert", _identifier_key = nil)
39
39
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
40
40
  table_name = sync_config.stream.name
41
41
  primary_key = sync_config.model.primary_key
@@ -41,7 +41,7 @@ module Multiwoven::Integrations::Destination
41
41
  })
42
42
  end
43
43
 
44
- def write(sync_config, records, _action = "upsert")
44
+ def write(sync_config, records, _action = "upsert", _identifier_key = nil)
45
45
  @sync_config = sync_config
46
46
  stream = @sync_config.stream
47
47
  connection_config = @sync_config.destination.connection_specification.with_indifferent_access
@@ -40,7 +40,7 @@ module Multiwoven::Integrations::Destination
40
40
  })
41
41
  end
42
42
 
43
- def write(sync_config, records, _action = "destination_insert")
43
+ def write(sync_config, records, _action = "destination_insert", _identifier_key = nil)
44
44
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
45
45
  token = connection_config[:token]
46
46
  file_name = sync_config.stream.name.split(", ").first
@@ -49,7 +49,7 @@ module Multiwoven::Integrations::Destination
49
49
  db_client&.close
50
50
  end
51
51
 
52
- def write(sync_config, records, action = "destination_insert")
52
+ def write(sync_config, records, action = "destination_insert", _identifier_key = nil)
53
53
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
54
54
  table_name = sync_config.stream.name
55
55
  primary_key = sync_config.model.primary_key
@@ -26,7 +26,7 @@ module Multiwoven::Integrations::Destination
26
26
  })
27
27
  end
28
28
 
29
- def write(sync_config, records, _action = "destination_insert")
29
+ def write(sync_config, records, _action = "destination_insert", _identifier_key = nil)
30
30
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
31
31
  create_connection(connection_config)
32
32
  model = sync_config.stream.name
@@ -38,7 +38,7 @@ module Multiwoven::Integrations::Destination
38
38
  )
39
39
  end
40
40
 
41
- def write(sync_config, records, action = "destination_insert")
41
+ def write(sync_config, records, action = "destination_insert", _identifier_key = nil)
42
42
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
43
43
  table_name = sync_config.stream.name
44
44
  primary_key = sync_config.model.primary_key
@@ -39,7 +39,7 @@ module Multiwoven::Integrations::Destination
39
39
  })
40
40
  end
41
41
 
42
- def write(sync_config, records, _action = "upsert")
42
+ def write(sync_config, records, _action = "upsert", _identifier_key = nil)
43
43
  @sync_config = sync_config
44
44
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
45
45
  create_connection(connection_config)
@@ -47,37 +47,28 @@ module Multiwoven::Integrations::Destination
47
47
  db&.close
48
48
  end
49
49
 
50
- def write(sync_config, records, action = "destination_insert")
50
+ def write(sync_config, records, action = "destination_insert", identifier_key = nil)
51
51
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
52
52
  raw_table = sync_config.stream.name
53
53
  table_name = qualify_table(connection_config[:schema], raw_table)
54
54
  db = create_connection(connection_config)
55
55
  primary_key = fetch_primary_key(db, connection_config[:schema], raw_table)
56
+ opts = { action: action, identifier_key: identifier_key, sync_config: sync_config }
56
57
 
57
58
  write_success = 0
58
59
  write_failure = 0
59
60
  log_message_array = []
60
61
 
61
62
  records.each_slice(MAX_CHUNK_SIZE) do |chunk|
62
- bulk_write(db, table_name, chunk, primary_key, action)
63
+ bulk_write(db, table_name, chunk, primary_key, opts)
63
64
  write_success += chunk.size
64
65
  log_message_array << log_request_response("info", "bulk_#{action}", "#{chunk.size} rows")
65
66
  rescue StandardError => e
66
67
  logger.warn("POSTGRESQL:BULK_WRITE:FALLBACK chunk_size=#{chunk.size} error=#{e.message}")
67
- chunk.each do |record|
68
- response = bulk_write(db, table_name, [record], primary_key, action)
69
- write_success += 1
70
- log_message_array << log_request_response("info", "fallback_#{action}", response)
71
- rescue StandardError => individual_error
72
- handle_exception(individual_error, {
73
- context: "POSTGRESQL:RECORD:WRITE:EXCEPTION",
74
- type: "error",
75
- sync_id: sync_config.sync_id,
76
- sync_run_id: sync_config.sync_run_id
77
- })
78
- write_failure += 1
79
- log_message_array << log_request_response("error", "fallback_#{action}", individual_error.message)
80
- end
68
+ success, failure, logs = write_chunk_fallback(db, table_name, chunk, primary_key, opts)
69
+ write_success += success
70
+ write_failure += failure
71
+ log_message_array.concat(logs)
81
72
  end
82
73
 
83
74
  tracking_message(write_success, write_failure, log_message_array)
@@ -94,10 +85,39 @@ module Multiwoven::Integrations::Destination
94
85
 
95
86
  private
96
87
 
97
- def bulk_write(db, table_name, records, primary_key, action)
88
+ def write_chunk_fallback(db, table_name, chunk, primary_key, opts)
89
+ action = opts[:action]
90
+ identifier_key = opts[:identifier_key]
91
+ sync_config = opts[:sync_config]
92
+ success = 0
93
+ failure = 0
94
+ logs = []
95
+
96
+ chunk.each do |record|
97
+ response = bulk_write(db, table_name, [record], primary_key, opts)
98
+ success += 1
99
+ logs << log_request_response("info", "fallback_#{action}", response, record[identifier_key])
100
+ rescue StandardError => individual_error # rubocop:disable Naming/RescuedExceptionsVariableName
101
+ handle_exception(individual_error, {
102
+ context: "POSTGRESQL:RECORD:WRITE:EXCEPTION",
103
+ type: "error",
104
+ sync_id: sync_config.sync_id,
105
+ sync_run_id: sync_config.sync_run_id
106
+ })
107
+ failure += 1
108
+ logs << log_request_response("error", "fallback_#{action}", individual_error.message, record[identifier_key])
109
+ end
110
+
111
+ [success, failure, logs]
112
+ end
113
+
114
+ def bulk_write(db, table_name, records, primary_key, opts)
115
+ action = opts[:action]
116
+ identifier_key = opts[:identifier_key]
98
117
  return if records.empty?
99
118
 
100
119
  columns = records.flat_map(&:keys).uniq
120
+ columns -= [identifier_key] if identifier_key
101
121
  col_list = columns.map { |c| quote_ident(c) }.join(", ")
102
122
 
103
123
  values_clauses = records.map do |record|
@@ -105,11 +125,14 @@ module Multiwoven::Integrations::Destination
105
125
  "(#{vals.join(", ")})"
106
126
  end
107
127
 
108
- sql = "INSERT INTO #{table_name} (#{col_list}) VALUES #{values_clauses.join(", ")}"
109
- if primary_key.present?
110
- sql += action.to_s == "destination_insert" ? build_safe_insert_clause(primary_key) : build_upsert_clause(columns, primary_key)
111
- end
112
- db.exec(sql)
128
+ conflict = build_conflict_clause(primary_key, action, columns)
129
+ db.exec("INSERT INTO #{table_name} (#{col_list}) VALUES #{values_clauses.join(", ")}#{conflict}")
130
+ end
131
+
132
+ def build_conflict_clause(primary_key, action, columns)
133
+ return "" unless primary_key.present?
134
+
135
+ action.to_s == "destination_insert" ? build_safe_insert_clause(primary_key) : build_upsert_clause(columns, primary_key)
113
136
  end
114
137
 
115
138
  def build_safe_insert_clause(primary_key)
@@ -48,7 +48,7 @@ module Multiwoven::Integrations::Destination
48
48
  })
49
49
  end
50
50
 
51
- def write(sync_config, records, _action = "upsert")
51
+ def write(sync_config, records, _action = "upsert", _identifier_key = nil)
52
52
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
53
53
  collection_name = sync_config.stream.name
54
54
  primary_key = sync_config.model.primary_key
@@ -52,7 +52,7 @@ module Multiwoven
52
52
  })
53
53
  end
54
54
 
55
- def write(sync_config, records, action = "create")
55
+ def write(sync_config, records, action = "create", _identifier_key = nil)
56
56
  @action = sync_config.stream.action || action
57
57
  @sync_config = sync_config
58
58
  initialize_client(sync_config.destination.connection_specification)
@@ -31,7 +31,7 @@ module Multiwoven
31
31
  })
32
32
  end
33
33
 
34
- def write(sync_config, records, action = "create")
34
+ def write(sync_config, records, action = "create", _identifier_key = nil)
35
35
  @action = sync_config.stream.action || action
36
36
  @sync_config = sync_config
37
37
  initialize_client(sync_config.destination.connection_specification)
@@ -36,7 +36,7 @@ module Multiwoven::Integrations::Destination
36
36
  })
37
37
  end
38
38
 
39
- def write(sync_config, records, _action = "destination_insert")
39
+ def write(sync_config, records, _action = "destination_insert", _identifier_key = nil)
40
40
  @sync_config = sync_config
41
41
  connection_config = sync_config.destination.connection_specification.with_indifferent_access
42
42
  file_path = generate_file_path(sync_config)
@@ -29,7 +29,7 @@ module Multiwoven
29
29
  })
30
30
  end
31
31
 
32
- def write(sync_config, records, action = "create")
32
+ def write(sync_config, records, action = "create", _identifier_key = nil)
33
33
  # Currently as we only create a message for each record in slack, we are not using actions.
34
34
  # This will be changed in future.
35
35
  @sync_config = sync_config
@@ -29,7 +29,7 @@ module Multiwoven
29
29
  })
30
30
  end
31
31
 
32
- def write(sync_config, records, action = "create")
32
+ def write(sync_config, records, action = "create", _identifier_key = nil)
33
33
  @sync_config = sync_config
34
34
  @action = sync_config.stream.action || action
35
35
  initialize_client(sync_config.destination.connection_specification)
@@ -39,7 +39,7 @@ module Multiwoven::Integrations::Destination
39
39
  })
40
40
  end
41
41
 
42
- def write(sync_config, records, _action = "destination_insert")
42
+ def write(sync_config, records, _action = "destination_insert", _identifier_key = nil)
43
43
  write_success = 0
44
44
  write_failure = 0
45
45
  log_message_array = []
@@ -31,7 +31,7 @@ module Multiwoven
31
31
  failure_status(e)
32
32
  end
33
33
 
34
- def write(sync_config, records, action = "create")
34
+ def write(sync_config, records, action = "create", _identifier_key = nil)
35
35
  @sync_config = sync_config
36
36
  @action = sync_config.stream.action || action
37
37
  initialize_client(sync_config.destination.connection_specification)
@@ -79,6 +79,7 @@ module Multiwoven
79
79
 
80
80
  class LogMessage < ProtocolModel
81
81
  attribute :level, LogLevel
82
+ attribute? :record_identifier, Types::String.optional
82
83
  attribute :message, Types::String
83
84
  attribute? :name, Types::String.optional
84
85
  attribute? :stack_trace, Types::String.optional
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Multiwoven
4
4
  module Integrations
5
- VERSION = "0.35.4"
5
+ VERSION = "0.35.5"
6
6
 
7
7
  ENABLED_SOURCES = %w[
8
8
  Snowflake
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: multiwoven-integrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.35.4
4
+ version: 0.35.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Subin T P
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-23 00:00:00.000000000 Z
11
+ date: 2026-05-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport