multiwoven-integrations 0.5.2 → 0.7.0

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: fdecb7677ac11971794080ac898200969ee1b243e917ccba7f1c7ce254ebdcb5
4
- data.tar.gz: f609d9a9cfcef3644b07352d6704c713070046c5b8e405017e3e7f8c11d112e6
3
+ metadata.gz: 697c4972edd603a75aa7fd96653ce86c5946f90d90acf859d2577e8a1314608f
4
+ data.tar.gz: 6295ce58e8cdbe4c6f562202362a8e33c3dc0dd9db1dc368ec4be5cfce6ee3a9
5
5
  SHA512:
6
- metadata.gz: 98d9e5c0028a6e6d3b45584ba8cbd01778040ec0a2a7cf77a1dc2e1a326735df427021300e3b7cf412ee270e8bbe67101c9148cf203518d6504f1af6fd32b2cd
7
- data.tar.gz: f4aece39d2bdcb2901b2c9981e1ad4ca6ddb24333b8ab057b2fdd275c3496404569ac830abcd8d690f48ac39d493c661b305e119eee5a286d4dd211e8ba23d65
6
+ metadata.gz: 4868f70099d7fd8f8454e50371ab29ae622749175df9bcfa29f992223ef6b88946723442653f6ec82837326e506b86b583382478bd5fc5c4af94fa93eeccb594
7
+ data.tar.gz: 7a02dab7bf70873cf95455cf1906a7357c156803c4037c9adeb1e0dfb47ebe55cc0b24f219a0ba71152eb996b6e11ab244139d2267b2db5b8998dcd3dbe99dd0
@@ -23,7 +23,8 @@
23
23
  "type": "string"
24
24
  },
25
25
  "private_key_id": {
26
- "type": "string"
26
+ "type": "string",
27
+ "multiwoven_secret": true
27
28
  },
28
29
  "private_key": {
29
30
  "type": "string",
@@ -34,19 +35,17 @@
34
35
  "format": "email"
35
36
  },
36
37
  "client_id": {
37
- "type": "string"
38
+ "type": "string",
39
+ "multiwoven_secret": true
38
40
  },
39
41
  "auth_uri": {
40
- "type": "string",
41
- "format": "uri"
42
+ "type": "string"
42
43
  },
43
44
  "token_uri": {
44
- "type": "string",
45
- "format": "uri"
45
+ "type": "string"
46
46
  },
47
47
  "auth_provider_x509_cert_url": {
48
- "type": "string",
49
- "format": "uri"
48
+ "type": "string"
50
49
  },
51
50
  "client_x509_cert_url": {
52
51
  "type": "string",
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Multiwoven::Integrations::Destination
4
+ module Oracle
5
+ include Multiwoven::Integrations::Core
6
+ class Client < DestinationConnector
7
+ def check_connection(connection_config)
8
+ connection_config = connection_config.with_indifferent_access
9
+ create_connection(connection_config)
10
+ ConnectionStatus.new(
11
+ status: ConnectionStatusType["succeeded"]
12
+ ).to_multiwoven_message
13
+ rescue StandardError => e
14
+ ConnectionStatus.new(
15
+ status: ConnectionStatusType["failed"], message: e.message
16
+ ).to_multiwoven_message
17
+ end
18
+
19
+ def discover(connection_config)
20
+ records = []
21
+ connection_config = connection_config.with_indifferent_access
22
+ query = "SELECT table_name, column_name, data_type, nullable
23
+ FROM all_tab_columns
24
+ WHERE owner = '#{connection_config[:username].upcase}'
25
+ ORDER BY table_name, column_id"
26
+ conn = create_connection(connection_config)
27
+ cursor = conn.exec(query)
28
+ while (row = cursor.fetch)
29
+ records << row
30
+ end
31
+ catalog = Catalog.new(streams: create_streams(records))
32
+ catalog.to_multiwoven_message
33
+ rescue StandardError => e
34
+ handle_exception(
35
+ "ORACLE:DISCOVER:EXCEPTION",
36
+ "error",
37
+ e
38
+ )
39
+ end
40
+
41
+ def write(sync_config, records, action = "destination_insert")
42
+ connection_config = sync_config.destination.connection_specification.with_indifferent_access
43
+ table_name = sync_config.stream.name
44
+ primary_key = sync_config.model.primary_key
45
+ conn = create_connection(connection_config)
46
+
47
+ write_success = 0
48
+ write_failure = 0
49
+ log_message_array = []
50
+
51
+ records.each do |record|
52
+ query = Multiwoven::Integrations::Core::QueryBuilder.perform(action, table_name, record, primary_key)
53
+ query = query.gsub(";", "")
54
+ logger.debug("ORACLE:WRITE:QUERY query = #{query} sync_id = #{sync_config.sync_id} sync_run_id = #{sync_config.sync_run_id}")
55
+ begin
56
+ response = conn.exec(query)
57
+ conn.exec("COMMIT")
58
+ write_success += 1
59
+ log_message_array << log_request_response("info", query, response)
60
+ rescue StandardError => e
61
+ handle_exception(e, {
62
+ context: "ORACLE:RECORD:WRITE:EXCEPTION",
63
+ type: "error",
64
+ sync_id: sync_config.sync_id,
65
+ sync_run_id: sync_config.sync_run_id
66
+ })
67
+ write_failure += 1
68
+ log_message_array << log_request_response("error", query, e.message)
69
+ end
70
+ end
71
+ tracking_message(write_success, write_failure, log_message_array)
72
+ rescue StandardError => e
73
+ handle_exception(e, {
74
+ context: "ORACLE:RECORD:WRITE:EXCEPTION",
75
+ type: "error",
76
+ sync_id: sync_config.sync_id,
77
+ sync_run_id: sync_config.sync_run_id
78
+ })
79
+ end
80
+
81
+ private
82
+
83
+ def create_connection(connection_config)
84
+ OCI8.new(connection_config[:username], connection_config[:password], "#{connection_config[:host]}:#{connection_config[:port]}/#{connection_config[:sid]}")
85
+ end
86
+
87
+ def create_streams(records)
88
+ group_by_table(records).map do |_, r|
89
+ Multiwoven::Integrations::Protocol::Stream.new(name: r[:tablename], action: StreamAction["fetch"], json_schema: convert_to_json_schema(r[:columns]))
90
+ end
91
+ end
92
+
93
+ def group_by_table(records)
94
+ result = {}
95
+ records.each_with_index do |entry, index|
96
+ table_name = entry[0]
97
+ column_data = {
98
+ column_name: entry[1],
99
+ data_type: entry[2],
100
+ is_nullable: entry[3] == "Y"
101
+ }
102
+ result[index] ||= {}
103
+ result[index][:tablename] = table_name
104
+ result[index][:columns] = [column_data]
105
+ end
106
+ result.values.group_by { |entry| entry[:tablename] }.transform_values do |entries|
107
+ { tablename: entries.first[:tablename], columns: entries.flat_map { |entry| entry[:columns] } }
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,15 @@
1
+ {
2
+ "data": {
3
+ "name": "Oracle",
4
+ "title": "Oracle",
5
+ "connector_type": "destination",
6
+ "category": "Database",
7
+ "documentation_url": "https://docs.squared.ai/guides/data-integration/destination/oracle",
8
+ "github_issue_label": "destination-oracle",
9
+ "icon": "icon.svg",
10
+ "license": "MIT",
11
+ "release_stage": "alpha",
12
+ "support_level": "community",
13
+ "tags": ["language:ruby", "multiwoven"]
14
+ }
15
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "documentation_url": "https://docs.squared.ai/guides/data-integration/destination/oracle",
3
+ "stream_type": "dynamic",
4
+ "connector_query_type": "raw_sql",
5
+ "connection_specification": {
6
+ "$schema": "http://json-schema.org/draft-07/schema#",
7
+ "title": "Oracle",
8
+ "type": "object",
9
+ "required": ["host", "port", "sid", "username", "password"],
10
+ "properties": {
11
+ "host": {
12
+ "description": "The Oracle host.",
13
+ "examples": ["localhost"],
14
+ "type": "string",
15
+ "title": "Host",
16
+ "order": 0
17
+ },
18
+ "port": {
19
+ "description": "The Oracle port number.",
20
+ "examples": ["1521"],
21
+ "type": "string",
22
+ "title": "Port",
23
+ "order": 1
24
+ },
25
+ "sid": {
26
+ "description": "The name of your service in Oracle.",
27
+ "examples": ["ORCLPDB1"],
28
+ "type": "string",
29
+ "title": "SID",
30
+ "order": 2
31
+ },
32
+ "username": {
33
+ "description": "The username used to authenticate and connect.",
34
+ "type": "string",
35
+ "title": "Username",
36
+ "order": 3
37
+ },
38
+ "password": {
39
+ "description": "The password corresponding to the username used for authentication.",
40
+ "type": "string",
41
+ "multiwoven_secret": true,
42
+ "title": "Password",
43
+ "order": 4
44
+ }
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2
+ <svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
3
+ <path fill="#F00" fill-rule="evenodd" d="M7.957359,18.9123664 C4.11670252,18.9123664 1,15.803458 1,11.9617373 C1,8.12000773 4.11670252,5 7.957359,5 L16.0437948,5 C19.8855156,5 23,8.12000773 23,11.9617373 C23,15.803458 19.8855156,18.9123664 16.0437948,18.9123664 L7.957359,18.9123664 L7.957359,18.9123664 Z M15.8639176,16.4585488 C18.352201,16.4585488 20.3674397,14.448858 20.3674397,11.9617373 C20.3674397,9.47460595 18.352201,7.45381934 15.8639176,7.45381934 L8.1360824,7.45381934 C5.64895285,7.45381934 3.63255855,9.47460595 3.63255855,11.9617373 C3.63255855,14.448858 5.64895285,16.4585488 8.1360824,16.4585488 L15.8639176,16.4585488 L15.8639176,16.4585488 Z"/>
4
+ </svg>
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Multiwoven
4
4
  module Integrations
5
- VERSION = "0.5.2"
5
+ VERSION = "0.7.0"
6
6
 
7
7
  ENABLED_SOURCES = %w[
8
8
  Snowflake
@@ -15,6 +15,7 @@ module Multiwoven
15
15
  Clickhouse
16
16
  AmazonS3
17
17
  MariaDB
18
+ Oracle
18
19
  ].freeze
19
20
 
20
21
  ENABLED_DESTINATIONS = %w[
@@ -34,6 +35,7 @@ module Multiwoven
34
35
  Iterable
35
36
  MariaDB
36
37
  DatabricksLakehouse
38
+ Oracle
37
39
  ].freeze
38
40
  end
39
41
  end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Multiwoven::Integrations::Source
4
+ module Oracle
5
+ include Multiwoven::Integrations::Core
6
+ class Client < SourceConnector
7
+ def check_connection(connection_config)
8
+ connection_config = connection_config.with_indifferent_access
9
+ create_connection(connection_config)
10
+ ConnectionStatus.new(
11
+ status: ConnectionStatusType["succeeded"]
12
+ ).to_multiwoven_message
13
+ rescue StandardError => e
14
+ ConnectionStatus.new(
15
+ status: ConnectionStatusType["failed"], message: e.message
16
+ ).to_multiwoven_message
17
+ end
18
+
19
+ def discover(connection_config)
20
+ records = []
21
+ connection_config = connection_config.with_indifferent_access
22
+ query = "SELECT table_name, column_name, data_type, nullable
23
+ FROM all_tab_columns
24
+ WHERE owner = '#{connection_config[:username].upcase}'
25
+ ORDER BY table_name, column_id"
26
+ conn = create_connection(connection_config)
27
+ cursor = conn.exec(query)
28
+ while (row = cursor.fetch)
29
+ records << row
30
+ end
31
+ catalog = Catalog.new(streams: create_streams(records))
32
+ catalog.to_multiwoven_message
33
+ rescue StandardError => e
34
+ handle_exception(
35
+ "ORACLE:DISCOVER:EXCEPTION",
36
+ "error",
37
+ e
38
+ )
39
+ end
40
+
41
+ def read(sync_config)
42
+ connection_config = sync_config.source.connection_specification.with_indifferent_access
43
+ query = sync_config.model.query
44
+ db = create_connection(connection_config)
45
+ query(db, query)
46
+ rescue StandardError => e
47
+ handle_exception(e, {
48
+ context: "ORACLE:READ:EXCEPTION",
49
+ type: "error",
50
+ sync_id: sync_config.sync_id,
51
+ sync_run_id: sync_config.sync_run_id
52
+ })
53
+ end
54
+
55
+ private
56
+
57
+ def create_connection(connection_config)
58
+ OCI8.new(connection_config[:username], connection_config[:password], "#{connection_config[:host]}:#{connection_config[:port]}/#{connection_config[:sid]}")
59
+ end
60
+
61
+ def create_streams(records)
62
+ group_by_table(records).map do |_, r|
63
+ Multiwoven::Integrations::Protocol::Stream.new(name: r[:tablename], action: StreamAction["fetch"], json_schema: convert_to_json_schema(r[:columns]))
64
+ end
65
+ end
66
+
67
+ def query(connection, query)
68
+ records = []
69
+ query = reformat_query(query)
70
+ cursor = connection.exec(query)
71
+ columns = cursor.get_col_names
72
+ while (row = cursor.fetch)
73
+ data_hash = columns.zip(row).to_h
74
+ records << RecordMessage.new(data: data_hash, emitted_at: Time.now.to_i).to_multiwoven_message
75
+ end
76
+ records
77
+ end
78
+
79
+ def group_by_table(records)
80
+ result = {}
81
+ records.each_with_index do |entry, index|
82
+ table_name = entry[0]
83
+ column_data = {
84
+ column_name: entry[1],
85
+ data_type: entry[2],
86
+ is_nullable: entry[3] == "Y"
87
+ }
88
+ result[index] ||= {}
89
+ result[index][:tablename] = table_name
90
+ result[index][:columns] = [column_data]
91
+ end
92
+ result.values.group_by { |entry| entry[:tablename] }.transform_values do |entries|
93
+ { tablename: entries.first[:tablename], columns: entries.flat_map { |entry| entry[:columns] } }
94
+ end
95
+ end
96
+
97
+ def reformat_query(sql_query)
98
+ offset = nil
99
+ limit = nil
100
+
101
+ sql_query = sql_query.gsub(";", "")
102
+
103
+ if sql_query.match?(/LIMIT (\d+)/i)
104
+ limit = sql_query.match(/LIMIT (\d+)/i)[1].to_i
105
+ sql_query.sub!(/LIMIT \d+/i, "")
106
+ end
107
+
108
+ if sql_query.match?(/OFFSET (\d+)/i)
109
+ offset = sql_query.match(/OFFSET (\d+)/i)[1].to_i
110
+ sql_query.sub!(/OFFSET \d+/i, "")
111
+ end
112
+
113
+ sql_query.strip!
114
+
115
+ if offset && limit
116
+ "#{sql_query} OFFSET #{offset} ROWS FETCH NEXT #{limit} ROWS ONLY"
117
+ elsif offset
118
+ "#{sql_query} OFFSET #{offset} ROWS"
119
+ elsif limit
120
+ "#{sql_query} FETCH NEXT #{limit} ROWS ONLY"
121
+ else
122
+ sql_query
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,15 @@
1
+ {
2
+ "data": {
3
+ "name": "Oracle",
4
+ "title": "Oracle",
5
+ "connector_type": "source",
6
+ "category": "Database",
7
+ "documentation_url": "https://docs.squared.ai/guides/data-integration/source/oracle",
8
+ "github_issue_label": "source-oracle",
9
+ "icon": "icon.svg",
10
+ "license": "MIT",
11
+ "release_stage": "alpha",
12
+ "support_level": "community",
13
+ "tags": ["language:ruby", "multiwoven"]
14
+ }
15
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "documentation_url": "https://docs.squared.ai/guides/data-integration/source/oracle",
3
+ "stream_type": "dynamic",
4
+ "connector_query_type": "raw_sql",
5
+ "connection_specification": {
6
+ "$schema": "http://json-schema.org/draft-07/schema#",
7
+ "title": "Oracle",
8
+ "type": "object",
9
+ "required": ["host", "port", "sid", "username", "password"],
10
+ "properties": {
11
+ "host": {
12
+ "description": "The Oracle host.",
13
+ "examples": ["localhost"],
14
+ "type": "string",
15
+ "title": "Host",
16
+ "order": 0
17
+ },
18
+ "port": {
19
+ "description": "The Oracle port number.",
20
+ "examples": ["1521"],
21
+ "type": "string",
22
+ "title": "Port",
23
+ "order": 1
24
+ },
25
+ "sid": {
26
+ "description": "The name of your service in Oracle.",
27
+ "examples": ["ORCLPDB1"],
28
+ "type": "string",
29
+ "title": "SID",
30
+ "order": 2
31
+ },
32
+ "username": {
33
+ "description": "The username used to authenticate and connect.",
34
+ "type": "string",
35
+ "title": "Username",
36
+ "order": 3
37
+ },
38
+ "password": {
39
+ "description": "The password corresponding to the username used for authentication.",
40
+ "type": "string",
41
+ "multiwoven_secret": true,
42
+ "title": "Password",
43
+ "order": 4
44
+ }
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2
+ <svg width="800px" height="800px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
3
+ <path fill="#F00" fill-rule="evenodd" d="M7.957359,18.9123664 C4.11670252,18.9123664 1,15.803458 1,11.9617373 C1,8.12000773 4.11670252,5 7.957359,5 L16.0437948,5 C19.8855156,5 23,8.12000773 23,11.9617373 C23,15.803458 19.8855156,18.9123664 16.0437948,18.9123664 L7.957359,18.9123664 L7.957359,18.9123664 Z M15.8639176,16.4585488 C18.352201,16.4585488 20.3674397,14.448858 20.3674397,11.9617373 C20.3674397,9.47460595 18.352201,7.45381934 15.8639176,7.45381934 L8.1360824,7.45381934 C5.64895285,7.45381934 3.63255855,9.47460595 3.63255855,11.9617373 C3.63255855,14.448858 5.64895285,16.4585488 8.1360824,16.4585488 L15.8639176,16.4585488 L15.8639176,16.4585488 Z"/>
4
+ </svg>
@@ -31,6 +31,7 @@ require "aws-sdk-s3"
31
31
  require "duckdb"
32
32
  require "iterable-api-client"
33
33
  require "aws-sdk-sts"
34
+ require "ruby-oci8"
34
35
 
35
36
  # Service
36
37
  require_relative "integrations/config"
@@ -60,6 +61,7 @@ require_relative "integrations/source/aws_athena/client"
60
61
  require_relative "integrations/source/clickhouse/client"
61
62
  require_relative "integrations/source/amazon_s3/client"
62
63
  require_relative "integrations/source/maria_db/client"
64
+ require_relative "integrations/source/oracle_db/client"
63
65
 
64
66
  # Destination
65
67
  require_relative "integrations/destination/klaviyo/client"
@@ -78,6 +80,7 @@ require_relative "integrations/destination/zendesk/client"
78
80
  require_relative "integrations/destination/iterable/client"
79
81
  require_relative "integrations/destination/maria_db/client"
80
82
  require_relative "integrations/destination/databricks_lakehouse/client"
83
+ require_relative "integrations/destination/oracle_db/client"
81
84
 
82
85
  module Multiwoven
83
86
  module Integrations
@@ -53,6 +53,7 @@ Gem::Specification.new do |spec|
53
53
  spec.add_runtime_dependency "rake"
54
54
  spec.add_runtime_dependency "restforce"
55
55
  spec.add_runtime_dependency "ruby-limiter"
56
+ spec.add_runtime_dependency "ruby-oci8"
56
57
  spec.add_runtime_dependency "ruby-odbc"
57
58
  spec.add_runtime_dependency "rubyzip"
58
59
  spec.add_runtime_dependency "sequel"
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.5.2
4
+ version: 0.7.0
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: 2024-07-16 00:00:00.000000000 Z
11
+ date: 2024-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -290,6 +290,20 @@ dependencies:
290
290
  - - ">="
291
291
  - !ruby/object:Gem::Version
292
292
  version: '0'
293
+ - !ruby/object:Gem::Dependency
294
+ name: ruby-oci8
295
+ requirement: !ruby/object:Gem::Requirement
296
+ requirements:
297
+ - - ">="
298
+ - !ruby/object:Gem::Version
299
+ version: '0'
300
+ type: :runtime
301
+ prerelease: false
302
+ version_requirements: !ruby/object:Gem::Requirement
303
+ requirements:
304
+ - - ">="
305
+ - !ruby/object:Gem::Version
306
+ version: '0'
293
307
  - !ruby/object:Gem::Dependency
294
308
  name: ruby-odbc
295
309
  requirement: !ruby/object:Gem::Requirement
@@ -530,6 +544,10 @@ files:
530
544
  - lib/multiwoven/integrations/destination/maria_db/config/meta.json
531
545
  - lib/multiwoven/integrations/destination/maria_db/config/spec.json
532
546
  - lib/multiwoven/integrations/destination/maria_db/icon.svg
547
+ - lib/multiwoven/integrations/destination/oracle_db/client.rb
548
+ - lib/multiwoven/integrations/destination/oracle_db/config/meta.json
549
+ - lib/multiwoven/integrations/destination/oracle_db/config/spec.json
550
+ - lib/multiwoven/integrations/destination/oracle_db/icon.svg
533
551
  - lib/multiwoven/integrations/destination/postgresql/client.rb
534
552
  - lib/multiwoven/integrations/destination/postgresql/config/meta.json
535
553
  - lib/multiwoven/integrations/destination/postgresql/config/spec.json
@@ -593,6 +611,10 @@ files:
593
611
  - lib/multiwoven/integrations/source/maria_db/config/meta.json
594
612
  - lib/multiwoven/integrations/source/maria_db/config/spec.json
595
613
  - lib/multiwoven/integrations/source/maria_db/icon.svg
614
+ - lib/multiwoven/integrations/source/oracle_db/client.rb
615
+ - lib/multiwoven/integrations/source/oracle_db/config/meta.json
616
+ - lib/multiwoven/integrations/source/oracle_db/config/spec.json
617
+ - lib/multiwoven/integrations/source/oracle_db/icon.svg
596
618
  - lib/multiwoven/integrations/source/postgresql/client.rb
597
619
  - lib/multiwoven/integrations/source/postgresql/config/meta.json
598
620
  - lib/multiwoven/integrations/source/postgresql/config/spec.json