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 +4 -4
- data/.ruby-version +1 -1
- data/lib/multiwoven/integrations/core/query_builder.rb +2 -2
- data/lib/multiwoven/integrations/core/utils.rb +5 -0
- data/lib/multiwoven/integrations/destination/amazon_s3/client.rb +3 -1
- data/lib/multiwoven/integrations/destination/postgresql/client.rb +1 -1
- data/lib/multiwoven/integrations/destination/sftp/client.rb +3 -1
- data/lib/multiwoven/integrations/rollout.rb +2 -1
- data/lib/multiwoven/integrations/source/sql_server/client.rb +280 -0
- data/lib/multiwoven/integrations/source/sql_server/config/meta.json +16 -0
- data/lib/multiwoven/integrations/source/sql_server/config/spec.json +94 -0
- data/lib/multiwoven/integrations/source/sql_server/icon.svg +22 -0
- data/lib/multiwoven/integrations.rb +1 -0
- data/multiwoven-integrations.gemspec +3 -0
- metadata +49 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4fa94a603b7e80f97d72f8d40959925b4f7fa434b1870773011561e2e11300b0
|
|
4
|
+
data.tar.gz: 71586d86bae1182eaddbeadc983f0745a1b2c32aaa61b09bc2fb0c47a7c10381
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c7797375fb072c56132d1509a3fd6cb8a2432fca25bbb62461d5497bd9bd4bc9927ea7c0b87a362c61919cf1539e6cd9d151e76484a7b690d56ec0d6e774f4c7
|
|
7
|
+
data.tar.gz: e19a77ece6aa7b4fa10550ed62a1e3dc5414802845f3add6d7588f5b151a886a5cda66534b63f1b9857add61adb8a1214d56c861b224c2ae92a9d87fa3d21bba
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
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
|
|
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
|
|
|
@@ -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
|
|
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.
|
|
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
|
+
}
|
|
@@ -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.
|
|
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:
|
|
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.
|
|
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: []
|