multiwoven-integrations 0.42.0 → 0.43.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2aafe56dba0839f408ce9b67bd4a5a3e0e7b06081cea437da55299fb83236aa5
4
- data.tar.gz: 73de5d0ebc7c416a889f40c6e1131fb3b8d4fb15b8414b0ecfea0d068745dc52
3
+ metadata.gz: 4fa94a603b7e80f97d72f8d40959925b4f7fa434b1870773011561e2e11300b0
4
+ data.tar.gz: 71586d86bae1182eaddbeadc983f0745a1b2c32aaa61b09bc2fb0c47a7c10381
5
5
  SHA512:
6
- metadata.gz: 9fe49be29f5706af6f8cc6df96394082143c419c56d82cde9222969fd1b45b949d00e1023317fa54e8b3171c649967b0ca8aa70aff8515745ef6cd15d0f8e323
7
- data.tar.gz: 80a9c368b79c7251c88692c30f17325690ebc6e96384ae4010ad8a8f878a091f523e50fe1d5a4a7ffaf6ca32922d20711609905b53fb70791ebda2fb5e2db2cc
6
+ metadata.gz: c7797375fb072c56132d1509a3fd6cb8a2432fca25bbb62461d5497bd9bd4bc9927ea7c0b87a362c61919cf1539e6cd9d151e76484a7b690d56ec0d6e774f4c7
7
+ data.tar.gz: e19a77ece6aa7b4fa10550ed62a1e3dc5414802845f3add6d7588f5b151a886a5cda66534b63f1b9857add61adb8a1214d56c861b224c2ae92a9d87fa3d21bba
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.2.0
1
+ 3.4.11
@@ -9,7 +9,7 @@ module Multiwoven
9
9
  case action.downcase
10
10
  when "destination_insert"
11
11
  columns = record.keys.join(", ")
12
- values = record.values.map { |value| "'#{value}'" }.join(", ")
12
+ values = record.values.map { |value| "'#{Utils.serialize_value(value)}'" }.join(", ")
13
13
  # TODO: support bulk insert
14
14
  "INSERT INTO #{table} (#{columns}) VALUES (#{values});"
15
15
  when "destination_update"
@@ -21,7 +21,7 @@ module Multiwoven
21
21
  end
22
22
 
23
23
  primary_key_value = record.delete(primary_key) # Remove and return the primary key value
24
- set_clause = record.map { |key, value| "#{key} = '#{value}'" }.join(", ")
24
+ set_clause = record.map { |key, value| "#{key} = '#{Utils.serialize_value(value)}'" }.join(", ")
25
25
  where_clause = "#{primary_key} = '#{primary_key_value}'"
26
26
  "UPDATE #{table} SET #{set_clause} WHERE #{where_clause};"
27
27
  else
@@ -3,6 +3,11 @@
3
3
  module Multiwoven
4
4
  module Integrations::Core
5
5
  module Utils
6
+ def serialize_value(value)
7
+ value.is_a?(Hash) || value.is_a?(Array) ? value.to_json : value
8
+ end
9
+ module_function :serialize_value
10
+
6
11
  def keys_to_symbols(hash)
7
12
  if hash.is_a?(Hash)
8
13
  hash.each_with_object({}) do |(key, value), result|
@@ -96,7 +96,9 @@ module Multiwoven::Integrations::Destination
96
96
  CSV.generate do |csv|
97
97
  headers = records.first.keys
98
98
  csv << headers
99
- records.each { |record| csv << record.values_at(*headers) }
99
+ records.each do |record|
100
+ csv << record.values_at(*headers).map { |v| serialize_value(v) }
101
+ end
100
102
  end
101
103
  end
102
104
 
@@ -153,7 +153,7 @@ module Multiwoven::Integrations::Destination
153
153
  def escape_value(db, value)
154
154
  return "NULL" if value.nil?
155
155
 
156
- "'#{db.escape_string(value.to_s)}'"
156
+ "'#{db.escape_string(serialize_value(value).to_s)}'"
157
157
  end
158
158
 
159
159
  def quote_ident(name)
@@ -149,7 +149,9 @@ module Multiwoven::Integrations::Destination
149
149
  CSV.generate do |csv|
150
150
  headers = records.first.keys
151
151
  csv << headers
152
- records.each { |record| csv << record.values_at(*headers) }
152
+ records.each do |record|
153
+ csv << record.values_at(*headers).map { |v| serialize_value(v) }
154
+ end
153
155
  end
154
156
  end
155
157
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Multiwoven
4
4
  module Integrations
5
- VERSION = "0.42.0"
5
+ VERSION = "0.43.1"
6
6
 
7
7
  ENABLED_SOURCES = %w[
8
8
  Snowflake
@@ -45,6 +45,7 @@ module Multiwoven
45
45
  OneDrive
46
46
  MicrosoftDynamics
47
47
  EpicFhir
48
+ SqlServer
48
49
  ].freeze
49
50
 
50
51
  ENABLED_DESTINATIONS = %w[
@@ -0,0 +1,280 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tiny_tds"
4
+
5
+ module Multiwoven::Integrations::Source
6
+ module SqlServer
7
+ include Multiwoven::Integrations::Core
8
+ class Client < SourceConnector
9
+ def check_connection(connection_config)
10
+ connection_config = connection_config.with_indifferent_access
11
+ db = create_connection(connection_config)
12
+ ConnectionStatus.new(
13
+ status: ConnectionStatusType["succeeded"]
14
+ ).to_multiwoven_message
15
+ rescue TinyTds::Error => e
16
+ ConnectionStatus.new(
17
+ status: ConnectionStatusType["failed"],
18
+ message: e.message
19
+ ).to_multiwoven_message
20
+ ensure
21
+ db&.close
22
+ end
23
+
24
+ def discover(connection_config)
25
+ connection_config = connection_config.with_indifferent_access
26
+ schema = connection_config[:schema].presence || "dbo"
27
+ query = "SELECT table_name, column_name, data_type, is_nullable
28
+ FROM information_schema.columns
29
+ WHERE table_schema = '#{escape(schema)}'
30
+ ORDER BY table_name, ordinal_position;"
31
+
32
+ db = create_connection(connection_config)
33
+ records = db.execute(query).map do |row|
34
+ row.transform_keys { |key| key.to_s.downcase }
35
+ end
36
+ catalog = Catalog.new(streams: create_streams(records))
37
+ catalog.to_multiwoven_message
38
+ rescue StandardError => e
39
+ handle_exception(e, {
40
+ context: "SQLSERVER:DISCOVER:EXCEPTION",
41
+ type: "error"
42
+ })
43
+ ensure
44
+ db&.close
45
+ end
46
+
47
+ def read(sync_config)
48
+ connection_config = sync_config.source.connection_specification.with_indifferent_access
49
+ @pagination_primary_key = sync_config.model.primary_key
50
+ query = sync_config.model.query
51
+ query = batched_query(query, sync_config.limit, sync_config.offset) unless sync_config.limit.nil? && sync_config.offset.nil?
52
+
53
+ db = create_connection(connection_config)
54
+
55
+ query(db, query)
56
+ rescue StandardError => e
57
+ handle_exception(e, {
58
+ context: "SQLSERVER:READ:EXCEPTION",
59
+ type: "error",
60
+ sync_id: sync_config.sync_id,
61
+ sync_run_id: sync_config.sync_run_id
62
+ })
63
+ ensure
64
+ @pagination_primary_key = nil
65
+ db&.close
66
+ end
67
+
68
+ def search(vector_search_config)
69
+ connection_config = vector_search_config.source.connection_specification.with_indifferent_access
70
+ query = vector_search_config[:vector]
71
+ limit = vector_search_config[:limit]
72
+ query = batched_query(query, limit, 0) unless limit.nil?
73
+
74
+ db = create_connection(connection_config)
75
+ query(db, query)
76
+ rescue StandardError => e
77
+ handle_exception(e, {
78
+ context: "SQLSERVER:SEARCH:EXCEPTION",
79
+ type: "error"
80
+ })
81
+ ensure
82
+ db&.close
83
+ end
84
+
85
+ private
86
+
87
+ def query(connection, sql)
88
+ connection.execute(reformat_query(sql)).map do |row|
89
+ RecordMessage.new(data: row, emitted_at: Time.now.to_i).to_multiwoven_message
90
+ end
91
+ end
92
+
93
+ # SQL Server does not support LIMIT/OFFSET; use OFFSET/FETCH NEXT.
94
+ def batched_query(sql_query, limit, offset)
95
+ offset = offset.to_i
96
+ limit = limit&.to_i
97
+ raise ArgumentError, "Offset and limit must be non-negative" if offset.negative? || (!limit.nil? && limit.negative?)
98
+
99
+ sql_query = strip_trailing_terminator(sql_query)
100
+ raise ArgumentError, "Query already contains a LIMIT clause" if clause_outside_literals?(sql_query, /\bLIMIT\s+\d+\b/i)
101
+ raise ArgumentError, "Query already contains an OFFSET clause" if clause_outside_literals?(sql_query, /\bOFFSET\s+\d+\b/i)
102
+
103
+ apply_pagination(sql_query, limit, offset)
104
+ end
105
+
106
+ # Convert Postgres/MySQL-style LIMIT/OFFSET (from upstream, including query_source) into SQL Server pagination.
107
+ def reformat_query(sql_query)
108
+ sql_query = strip_trailing_terminator(sql_query)
109
+
110
+ return sql_query if clause_outside_literals?(sql_query, /\bOFFSET\s+\d+\s+ROWS\b/i)
111
+
112
+ limit = nil
113
+ offset = nil
114
+
115
+ normalized = with_masked_literals(sql_query) do |masked|
116
+ if (match = masked.match(/\bLIMIT\s+(\d+)\b/i))
117
+ limit = match[1].to_i
118
+ masked = masked.sub(/\bLIMIT\s+\d+\b/i, "")
119
+ end
120
+
121
+ if (match = masked.match(/\bOFFSET\s+(\d+)\b(?!\s+ROWS)/i))
122
+ offset = match[1].to_i
123
+ masked = masked.sub(/\bOFFSET\s+\d+\b(?!\s+ROWS)/i, "")
124
+ end
125
+
126
+ masked.gsub(/[^\S\n]+/, " ").strip
127
+ end
128
+
129
+ return normalized if limit.nil? && offset.nil?
130
+
131
+ apply_pagination(normalized, limit, offset || 0)
132
+ end
133
+
134
+ def apply_pagination(sql_query, limit, offset)
135
+ raise ArgumentError, "Limit must be at least 1" if !limit.nil? && limit.to_i < 1
136
+
137
+ keys = normalize_primary_keys(@pagination_primary_key)
138
+ has_order = clause_outside_literals?(sql_query, /\bORDER\s+BY\b/i)
139
+
140
+ # query_source appends LIMIT without ORDER BY or primary_key metadata.
141
+ # TOP does not require ORDER BY; OFFSET/FETCH does.
142
+ return apply_top(sql_query, limit) if offset.to_i.zero? && !limit.nil? && !has_order && keys.empty?
143
+
144
+ sql_query = ensure_stable_order(sql_query, keys: keys, has_order: has_order)
145
+
146
+ clause = "OFFSET #{offset.to_i} ROWS"
147
+ clause = "#{clause} FETCH NEXT #{limit.to_i} ROWS ONLY" unless limit.nil?
148
+
149
+ "#{sql_query} #{clause}"
150
+ end
151
+
152
+ def apply_top(sql_query, limit)
153
+ with_masked_literals(sql_query) do |masked|
154
+ raise ArgumentError, "Query already contains TOP" if masked.match?(/\A\s*SELECT\s+(?:(?:DISTINCT|ALL)\s+)?TOP\b/i)
155
+
156
+ pattern = /\A\s*SELECT\s+((?:DISTINCT|ALL)\s+)?/i
157
+ raise ArgumentError, "Cannot apply TOP to this query; add ORDER BY" unless masked.match?(pattern)
158
+
159
+ masked.sub(pattern) { "SELECT #{Regexp.last_match(1)}TOP #{limit.to_i} " }
160
+ end
161
+ end
162
+
163
+ def ensure_stable_order(sql_query, keys:, has_order:)
164
+ return append_order_tie_breakers(sql_query, keys) if has_order
165
+
166
+ if keys.empty?
167
+ raise ArgumentError,
168
+ "Paginated SQL Server queries require an ORDER BY with a unique key, or a model primary_key"
169
+ end
170
+
171
+ "#{sql_query} ORDER BY #{order_by_list(keys)}"
172
+ end
173
+
174
+ def append_order_tie_breakers(sql_query, keys)
175
+ missing = keys.reject { |key| order_by_includes_key?(sql_query, key) }
176
+ return sql_query if missing.empty?
177
+
178
+ "#{sql_query}, #{order_by_list(missing)}"
179
+ end
180
+
181
+ def order_by_includes_key?(sql_query, key)
182
+ included = false
183
+ with_masked_literals(sql_query) do |masked|
184
+ order_clause = masked[/\bORDER\s+BY\b(.+)\z/im, 1]
185
+ if order_clause
186
+ quoted = Regexp.escape(quote_identifier(key))
187
+ bare = Regexp.escape(key.to_s)
188
+ included = order_clause.match?(/#{quoted}|\b#{bare}\b/i)
189
+ end
190
+ masked
191
+ end
192
+ included
193
+ end
194
+
195
+ def normalize_primary_keys(primary_key)
196
+ Array(primary_key).flat_map { |key| key.to_s.split(",") }.map(&:strip).reject(&:empty?).uniq
197
+ end
198
+
199
+ def order_by_list(keys)
200
+ keys.map { |key| quote_identifier(key) }.join(", ")
201
+ end
202
+
203
+ def quote_identifier(name)
204
+ "[#{name.to_s.gsub("]", "]]")}]"
205
+ end
206
+
207
+ def strip_trailing_terminator(sql_query)
208
+ with_masked_literals(sql_query.to_s.strip) do |masked|
209
+ masked = masked.rstrip
210
+ masked = masked.chomp(";") while masked.end_with?(";")
211
+ masked
212
+ end
213
+ end
214
+
215
+ def clause_outside_literals?(sql_query, pattern)
216
+ matched = false
217
+ with_masked_literals(sql_query) do |masked|
218
+ matched = masked.match?(pattern)
219
+ masked
220
+ end
221
+ matched
222
+ end
223
+
224
+ # Mask single-quoted SQL literals (including N'...' and escaped '') so rewrites
225
+ # do not touch values like 'a;b' or 'LIMIT 1'.
226
+ def with_masked_literals(sql_query)
227
+ literals = []
228
+ masked = sql_query.to_s.gsub(/(?:N)?'(?:''|[^'])*'/i) do |literal|
229
+ literals << literal
230
+ "__SQL_LITERAL_#{literals.length - 1}__"
231
+ end
232
+
233
+ result = yield(masked)
234
+ literals.each_with_index do |literal, index|
235
+ result = result.gsub("__SQL_LITERAL_#{index}__", literal)
236
+ end
237
+ result
238
+ end
239
+
240
+ def create_connection(connection_config)
241
+ raise "Unsupported Auth type" unless connection_config[:credentials][:auth_type] == "username/password"
242
+
243
+ TinyTds::Client.new(
244
+ username: connection_config[:credentials][:username],
245
+ password: connection_config[:credentials][:password],
246
+ host: connection_config[:host],
247
+ port: connection_config[:port].presence || 1433,
248
+ database: connection_config[:database],
249
+ timeout: 10,
250
+ azure: connection_config[:azure].presence || false
251
+ )
252
+ end
253
+
254
+ def create_streams(records)
255
+ group_by_table(records).map do |r|
256
+ Multiwoven::Integrations::Protocol::Stream.new(name: r[:tablename], action: StreamAction["fetch"], json_schema: convert_to_json_schema(r[:columns]))
257
+ end
258
+ end
259
+
260
+ def group_by_table(records)
261
+ records.group_by { |entry| entry["table_name"] }.map do |table_name, columns|
262
+ {
263
+ tablename: table_name,
264
+ columns: columns.map do |column|
265
+ {
266
+ column_name: column["column_name"],
267
+ type: column["data_type"],
268
+ optional: column["is_nullable"] == "YES"
269
+ }
270
+ end
271
+ }
272
+ end
273
+ end
274
+
275
+ def escape(value)
276
+ value.to_s.gsub("'", "''")
277
+ end
278
+ end
279
+ end
280
+ end
@@ -0,0 +1,16 @@
1
+ {
2
+ "data": {
3
+ "name": "SqlServer",
4
+ "title": "SQL Server",
5
+ "connector_type": "source",
6
+ "category": "Data Warehouse",
7
+ "sub_category": "Relational Database",
8
+ "documentation_url": "https://docs.squared.ai/guides/sources/data-sources/sql-server",
9
+ "github_issue_label": "source-sql-server",
10
+ "icon": "https://res.cloudinary.com/dspflukeu/image/upload/v1790358609/Multiwoven/connectors/sql_server/icon.svg",
11
+ "license": "MIT",
12
+ "release_stage": "alpha",
13
+ "support_level": "community",
14
+ "tags": ["language:ruby", "multiwoven"]
15
+ }
16
+ }
@@ -0,0 +1,94 @@
1
+ {
2
+ "documentation_url": "https://docs.squared.ai/guides/sources/data-sources/sql-server",
3
+ "stream_type": "dynamic",
4
+ "connector_query_type": "raw_sql",
5
+ "connection_specification": {
6
+ "$schema": "http://json-schema.org/draft-07/schema#",
7
+ "title": "SqlServer",
8
+ "type": "object",
9
+ "required": ["data_type", "credentials", "host", "database", "schema"],
10
+ "properties": {
11
+ "data_type": {
12
+ "description": "Type of data in the database",
13
+ "type": "string",
14
+ "title": "Data Format Type",
15
+ "oneOf": [
16
+ {
17
+ "const": "structured",
18
+ "title": "Tables & Records (Structured)"
19
+ },
20
+ {
21
+ "const": "vector",
22
+ "title": "Tables & Records containing Vector fields"
23
+ }
24
+ ],
25
+ "default": "structured",
26
+ "order": 0
27
+ },
28
+ "credentials": {
29
+ "title": "",
30
+ "type": "object",
31
+ "required": ["auth_type", "username", "password"],
32
+ "properties": {
33
+ "auth_type": {
34
+ "type": "string",
35
+ "default": "username/password",
36
+ "order": 0,
37
+ "readOnly": true
38
+ },
39
+ "username": {
40
+ "description": "Username refers to your individual SQL Server login credentials. At a minimum, the user associated with these credentials must be granted read access to the data intended for synchronization.",
41
+ "examples": ["SQLSERVER_USER"],
42
+ "type": "string",
43
+ "title": "Username",
44
+ "order": 1
45
+ },
46
+ "password": {
47
+ "description": "This field requires the password associated with the SQL Server login specified in the preceding section.",
48
+ "type": "string",
49
+ "multiwoven_secret": true,
50
+ "title": "Password",
51
+ "order": 2
52
+ }
53
+ },
54
+ "order": 1
55
+ },
56
+ "host": {
57
+ "description": "The hostname or IP address of your SQL Server instance.",
58
+ "examples": ["127.0.0.1"],
59
+ "type": "string",
60
+ "title": "Host",
61
+ "order": 2
62
+ },
63
+ "port": {
64
+ "description": "The port number for your SQL Server instance, which defaults to 1433 and may vary based on your configuration.",
65
+ "examples": ["1433"],
66
+ "type": "string",
67
+ "title": "Port",
68
+ "order": 3
69
+ },
70
+ "database": {
71
+ "description": "The specific SQL Server database to connect to.",
72
+ "examples": ["SQLSERVER_DB"],
73
+ "type": "string",
74
+ "title": "Database",
75
+ "order": 4
76
+ },
77
+ "schema": {
78
+ "description": "The schema within the SQL Server database, typically 'dbo'.",
79
+ "examples": ["dbo"],
80
+ "default": "dbo",
81
+ "type": "string",
82
+ "title": "Schema",
83
+ "order": 5
84
+ },
85
+ "azure": {
86
+ "description": "Enable when connecting to Azure SQL Database (required for Azure encryption/login behaviour).",
87
+ "type": "boolean",
88
+ "title": "Azure SQL",
89
+ "default": false,
90
+ "order": 6
91
+ }
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+
3
+ <svg width="800px" height="800px" viewBox="0 -141.54 1478.201 1478.201" xmlns="http://www.w3.org/2000/svg">
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
@@ -113,6 +113,7 @@ require_relative "integrations/source/aisquared/client"
113
113
  require_relative "integrations/source/one_drive/client"
114
114
  require_relative "integrations/source/microsoft_dynamics/client"
115
115
  require_relative "integrations/source/epic_fhir/client"
116
+ require_relative "integrations/source/sql_server/client"
116
117
 
117
118
  # Destination
118
119
  require_relative "integrations/destination/klaviyo/client"
@@ -39,6 +39,8 @@ Gem::Specification.new do |spec|
39
39
  spec.add_runtime_dependency "aws-sdk-s3"
40
40
  spec.add_runtime_dependency "aws-sdk-sts"
41
41
  spec.add_runtime_dependency "aws-sigv4"
42
+ spec.add_runtime_dependency "base64"
43
+ spec.add_runtime_dependency "bigdecimal"
42
44
  spec.add_runtime_dependency "csv"
43
45
  spec.add_runtime_dependency "dry-schema"
44
46
  spec.add_runtime_dependency "dry-struct"
@@ -52,6 +54,7 @@ Gem::Specification.new do |spec|
52
54
  spec.add_runtime_dependency "hubspot-api-client"
53
55
  spec.add_runtime_dependency "iterable-api-client"
54
56
  spec.add_runtime_dependency "jwt"
57
+ spec.add_runtime_dependency "logger"
55
58
  spec.add_runtime_dependency "MailchimpMarketing"
56
59
  spec.add_runtime_dependency "net-sftp"
57
60
  spec.add_runtime_dependency "pg"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: multiwoven-integrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.42.0
4
+ version: 0.43.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Subin T P
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2026-09-24 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -94,6 +93,34 @@ dependencies:
94
93
  - - ">="
95
94
  - !ruby/object:Gem::Version
96
95
  version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: base64
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: bigdecimal
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
97
124
  - !ruby/object:Gem::Dependency
98
125
  name: csv
99
126
  requirement: !ruby/object:Gem::Requirement
@@ -276,6 +303,20 @@ dependencies:
276
303
  - - ">="
277
304
  - !ruby/object:Gem::Version
278
305
  version: '0'
306
+ - !ruby/object:Gem::Dependency
307
+ name: logger
308
+ requirement: !ruby/object:Gem::Requirement
309
+ requirements:
310
+ - - ">="
311
+ - !ruby/object:Gem::Version
312
+ version: '0'
313
+ type: :runtime
314
+ prerelease: false
315
+ version_requirements: !ruby/object:Gem::Requirement
316
+ requirements:
317
+ - - ">="
318
+ - !ruby/object:Gem::Version
319
+ version: '0'
279
320
  - !ruby/object:Gem::Dependency
280
321
  name: MailchimpMarketing
281
322
  requirement: !ruby/object:Gem::Requirement
@@ -913,6 +954,10 @@ files:
913
954
  - lib/multiwoven/integrations/source/snowflake/config/meta.json
914
955
  - lib/multiwoven/integrations/source/snowflake/config/spec.json
915
956
  - lib/multiwoven/integrations/source/snowflake/icon.svg
957
+ - lib/multiwoven/integrations/source/sql_server/client.rb
958
+ - lib/multiwoven/integrations/source/sql_server/config/meta.json
959
+ - lib/multiwoven/integrations/source/sql_server/config/spec.json
960
+ - lib/multiwoven/integrations/source/sql_server/icon.svg
916
961
  - lib/multiwoven/integrations/source/watsonx_ai/client.rb
917
962
  - lib/multiwoven/integrations/source/watsonx_ai/config/catalog.json
918
963
  - lib/multiwoven/integrations/source/watsonx_ai/config/meta.json
@@ -939,7 +984,6 @@ metadata:
939
984
  homepage_uri: https://www.multiwoven.com/
940
985
  source_code_uri: https://github.com/Multiwoven/multiwoven/tree/main/integrations
941
986
  changelog_uri: https://github.com/Multiwoven/multiwoven/blob/main/integrations/CHANGELOG.md
942
- post_install_message:
943
987
  rdoc_options: []
944
988
  require_paths:
945
989
  - lib
@@ -954,8 +998,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
954
998
  - !ruby/object:Gem::Version
955
999
  version: '0'
956
1000
  requirements: []
957
- rubygems_version: 3.4.1
958
- signing_key:
1001
+ rubygems_version: 3.6.9
959
1002
  specification_version: 4
960
1003
  summary: Integration suite for open source reverse ETL platform
961
1004
  test_files: []