multiwoven-integrations 0.7.9 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 055227f9ec70d01b066063ca9d806514eb0cad47344a60e303b7a5bef9d0056d
4
- data.tar.gz: 54317d770824c8870ccb58f7b2f852731b8df591cf53fc22ebe80d1b4b1a886e
3
+ metadata.gz: 0d6c095f758588db92c0232cc771651405956eca4cb6c6ddcd76bf4076347e94
4
+ data.tar.gz: e42faebd684460ed4aeebdc09520c929a68b1757410598ccc0679e0cb1474849
5
5
  SHA512:
6
- metadata.gz: e4dea4975ecc8802be486334961579e680aab99a08ce96a330c4cd6aa14925c1d79e8140bffce057ff3dec8bcc1440f4eca1305dcf389694ea984d2e84fb2de8
7
- data.tar.gz: eb235f292d326f59c8d9b35e11076eb32baaaa3b02c40a3cf7db570b50eba15159e12c97b8ff65f1f24bfd5f9b47fadf2f67762c305f5b379a0e8dc97759d918
6
+ metadata.gz: 1d29ac63ac873aa8467ffa10e280353dec803ede7cc670d91233bf70e8e2fdd56b55894c1ca8beca6783b130e75cc68721a4a0e41ea4af75a8bdcaa2b008159c
7
+ data.tar.gz: 9bf9ae850bb8e2b75466e20afb9fa8ca769368af72ec7f6ffe76e8bf7469d744497694ffcf9a2464ae275b2cb1a7fd484d0b08a4017280f03b577a2ec795bb2a
@@ -63,7 +63,8 @@ module Multiwoven
63
63
  end
64
64
 
65
65
  def failure_status(error)
66
- ConnectionStatus.new(status: ConnectionStatusType["failed"], message: error.message).to_multiwoven_message
66
+ message = error&.message || "failed"
67
+ ConnectionStatus.new(status: ConnectionStatusType["failed"], message: message).to_multiwoven_message
67
68
  end
68
69
  end
69
70
  end
@@ -36,6 +36,17 @@ module Multiwoven
36
36
  AIRTABLE_BASES_ENDPOINT = "https://api.airtable.com/v0/meta/bases"
37
37
  AIRTABLE_GET_BASE_SCHEMA_ENDPOINT = "https://api.airtable.com/v0/meta/bases/{baseId}/tables"
38
38
 
39
+ MS_EXCEL_AUTH_ENDPOINT = "https://graph.microsoft.com/v1.0/me"
40
+ MS_EXCEL_TABLE_ROW_WRITE_API = "https://graph.microsoft.com/v1.0/drives/%<drive_id>s/items/%<item_id>s/"\
41
+ "workbook/worksheets/%<sheet_name>s/tables/%<table_name>s/rows"
42
+ MS_EXCEL_TABLE_API = "https://graph.microsoft.com/v1.0/drives/%<drive_id>s/items/%<item_id>s/workbook/"\
43
+ "worksheets/sheet/tables?$select=name"
44
+ MS_EXCEL_FILES_API = "https://graph.microsoft.com/v1.0/drives/%<drive_id>s/root/children"
45
+ MS_EXCEL_WORKSHEETS_API = "https://graph.microsoft.com/v1.0/drives/%<drive_id>s/items/%<item_id>s/"\
46
+ "workbook/worksheets"
47
+ MS_EXCEL_SHEET_RANGE_API = "https://graph.microsoft.com/v1.0/drives/%<drive_id>s/items/%<item_id>s/"\
48
+ "workbook/worksheets/%<sheet_name>s/range(address='A1:Z1')/usedRange?$select=values"
49
+
39
50
  AWS_ACCESS_KEY_ID = ENV["AWS_ACCESS_KEY_ID"]
40
51
  AWS_SECRET_ACCESS_KEY = ENV["AWS_SECRET_ACCESS_KEY"]
41
52
 
@@ -44,6 +55,7 @@ module Multiwoven
44
55
  HTTP_POST = "POST"
45
56
  HTTP_PUT = "PUT"
46
57
  HTTP_DELETE = "DELETE"
58
+ HTTP_PATCH = "PATCH"
47
59
 
48
60
  # google sheets
49
61
  GOOGLE_SHEETS_SCOPE = "https://www.googleapis.com/auth/drive"
@@ -15,6 +15,14 @@ module Multiwoven
15
15
  success: success, failed: failure, logs: log_message_array
16
16
  ).to_multiwoven_message
17
17
  end
18
+
19
+ def auth_headers(access_token)
20
+ {
21
+ "Accept" => "application/json",
22
+ "Authorization" => "Bearer #{access_token}",
23
+ "Content-Type" => "application/json"
24
+ }
25
+ end
18
26
  end
19
27
  end
20
28
  end
@@ -19,13 +19,14 @@ module Multiwoven
19
19
  when Constants::HTTP_GET then Net::HTTP::Get
20
20
  when Constants::HTTP_POST then Net::HTTP::Post
21
21
  when Constants::HTTP_PUT then Net::HTTP::Put
22
+ when Constants::HTTP_PATCH then Net::HTTP::Patch
22
23
  when Constants::HTTP_DELETE then Net::HTTP::Delete
23
24
  else raise ArgumentError, "Unsupported HTTP method: #{method}"
24
25
  end
25
26
 
26
27
  request = request_class.new(uri)
27
28
  headers.each { |key, value| request[key] = value }
28
- request.body = payload.to_json if payload && %w[POST PUT].include?(method.upcase)
29
+ request.body = payload.to_json if payload && %w[POST PUT PATCH].include?(method.upcase)
29
30
  request
30
31
  end
31
32
  end
@@ -109,14 +109,6 @@ module Multiwoven
109
109
  }
110
110
  end
111
111
 
112
- def auth_headers(access_token)
113
- {
114
- "Accept" => "application/json",
115
- "Authorization" => "Bearer #{access_token}",
116
- "Content-Type" => "application/json"
117
- }
118
- end
119
-
120
112
  def base_id_exists?(bases, base_id)
121
113
  return if extract_bases(bases).any? { |base| base["id"] == base_id }
122
114
 
@@ -110,14 +110,6 @@ module Multiwoven::Integrations::Destination
110
110
  [schema, data]
111
111
  end
112
112
 
113
- def auth_headers(access_token)
114
- {
115
- "Accept" => "application/json",
116
- "Authorization" => "Bearer #{access_token}",
117
- "Content-Type" => "application/json"
118
- }
119
- end
120
-
121
113
  def ad_account_exists?(response, ad_account_id)
122
114
  return if extract_data(response).any? { |ad_account| ad_account["id"] == "act_#{ad_account_id}" }
123
115
 
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Multiwoven::Integrations::Destination
4
+ module MicrosoftExcel
5
+ include Multiwoven::Integrations::Core
6
+ class Client < DestinationConnector
7
+ prepend Multiwoven::Integrations::Core::RateLimiter
8
+ def check_connection(connection_config)
9
+ connection_config = connection_config.with_indifferent_access
10
+ drive_id = create_connection(connection_config)
11
+ if drive_id
12
+ success_status
13
+ else
14
+ failure_status(nil)
15
+ end
16
+ rescue StandardError => e
17
+ handle_exception(e, {
18
+ context: "MICROSOFT:EXCEL:CHECK_CONNECTION:EXCEPTION",
19
+ type: "error"
20
+ })
21
+ failure_status(e)
22
+ end
23
+
24
+ def discover(connection_config)
25
+ catalog_json = read_json(CATALOG_SPEC_PATH)
26
+ connection_config = connection_config.with_indifferent_access
27
+ token = connection_config[:token]
28
+ drive_id = create_connection(connection_config)
29
+ records = get_file(token, drive_id)
30
+ records.each do |record|
31
+ file_id = record[:id]
32
+ record[:worksheets] = get_file_data(token, drive_id, file_id)
33
+ end
34
+ catalog = Catalog.new(streams: create_streams(records, catalog_json))
35
+ catalog.to_multiwoven_message
36
+ rescue StandardError => e
37
+ handle_exception(e, {
38
+ context: "MICROSOFT:EXCEL:DISCOVER:EXCEPTION",
39
+ type: "error"
40
+ })
41
+ end
42
+
43
+ def write(sync_config, records, _action = "destination_insert")
44
+ connection_config = sync_config.destination.connection_specification.with_indifferent_access
45
+ token = connection_config[:token]
46
+ file_name = sync_config.stream.name.split(", ").first
47
+ sheet_name = sync_config.stream.name.split(", ").last
48
+ drive_id = create_connection(connection_config)
49
+ excel_files = get_file(token, drive_id)
50
+ worksheet = excel_files.find { |file| file[:name] == file_name }
51
+ item_id = worksheet[:id]
52
+
53
+ table = get_table(token, drive_id, item_id)
54
+ write_url = format(MS_EXCEL_TABLE_ROW_WRITE_API, drive_id: drive_id, item_id: item_id, sheet_name: sheet_name,
55
+ table_name: table["name"])
56
+ payload = { values: records.map(&:values) }
57
+ process_write_request(write_url, payload, token, sync_config)
58
+ end
59
+
60
+ private
61
+
62
+ def create_connection(connection_config)
63
+ token = connection_config[:token]
64
+ response = Multiwoven::Integrations::Core::HttpClient.request(
65
+ MS_EXCEL_AUTH_ENDPOINT,
66
+ HTTP_GET,
67
+ headers: auth_headers(token)
68
+ )
69
+ JSON.parse(response.body)["id"]
70
+ end
71
+
72
+ def get_table(token, drive_id, item_id)
73
+ table_url = format(MS_EXCEL_TABLE_API, drive_id: drive_id, item_id: item_id)
74
+ response = Multiwoven::Integrations::Core::HttpClient.request(
75
+ table_url,
76
+ HTTP_GET,
77
+ headers: auth_headers(token)
78
+ )
79
+ JSON.parse(response.body)["value"].first
80
+ end
81
+
82
+ def get_file(token, drive_id)
83
+ url = format(MS_EXCEL_FILES_API, drive_id: drive_id)
84
+ response = Multiwoven::Integrations::Core::HttpClient.request(
85
+ url,
86
+ HTTP_GET,
87
+ headers: auth_headers(token)
88
+ )
89
+ files = JSON.parse(response.body)["value"]
90
+ excel_files = files.select { |file| file["name"].match(/\.(xlsx|xls|xlsm)$/) }
91
+ excel_files.map { |file| { name: file["name"], id: file["id"] } }
92
+ end
93
+
94
+ def get_all_sheets(token, drive_id, item_id)
95
+ base_url = format(MS_EXCEL_WORKSHEETS_API, drive_id: drive_id, item_id: item_id)
96
+ worksheet_response = Multiwoven::Integrations::Core::HttpClient.request(
97
+ base_url,
98
+ HTTP_GET,
99
+ headers: auth_headers(token)
100
+ )
101
+ JSON.parse(worksheet_response.body)["value"]
102
+ end
103
+
104
+ def get_file_data(token, drive_id, item_id)
105
+ result = []
106
+ worksheets_data = get_all_sheets(token, drive_id, item_id)
107
+ worksheets_data.each do |sheet|
108
+ sheet_name = sheet["name"]
109
+ sheet_url = format(MS_EXCEL_SHEET_RANGE_API, drive_id: drive_id, item_id: item_id, sheet_name: sheet_name)
110
+
111
+ sheet_response = Multiwoven::Integrations::Core::HttpClient.request(
112
+ sheet_url,
113
+ HTTP_GET,
114
+ headers: auth_headers(token)
115
+ )
116
+ sheets_data = JSON.parse(sheet_response.body)
117
+ result << {
118
+ sheet_name: sheet_name,
119
+ column_names: sheets_data["values"].first
120
+ }
121
+ end
122
+ result
123
+ end
124
+
125
+ def create_streams(records, catalog_json)
126
+ group_by_table(records).flat_map do |_, record|
127
+ record.map do |_, r|
128
+ Multiwoven::Integrations::Protocol::Stream.new(
129
+ name: r[:workbook],
130
+ action: StreamAction["fetch"],
131
+ json_schema: convert_to_json_schema(r[:columns]),
132
+ request_rate_limit: catalog_json["request_rate_limit"] || 60,
133
+ request_rate_limit_unit: catalog_json["request_rate_limit_unit"] || "minute",
134
+ request_rate_concurrency: catalog_json["request_rate_concurrency"] || 1
135
+ )
136
+ end
137
+ end
138
+ end
139
+
140
+ def group_by_table(records)
141
+ result = {}
142
+
143
+ records.each_with_index do |entries, entries_index|
144
+ entries[:worksheets].each_with_index do |sheet, entry_index|
145
+ workbook_sheet = "#{entries[:name]}, #{sheet[:sheet_name]}"
146
+ columns = sheet[:column_names].map do |column_name|
147
+ column_name = "empty column" if column_name.empty?
148
+ {
149
+ column_name: column_name,
150
+ data_type: "String",
151
+ is_nullable: true
152
+ }
153
+ end
154
+ result[entries_index] ||= {}
155
+ result[entries_index][entry_index] = { workbook: workbook_sheet, columns: columns }
156
+ end
157
+ end
158
+ result
159
+ end
160
+
161
+ def process_write_request(write_url, payload, token, sync_config)
162
+ write_success = 0
163
+ write_failure = 0
164
+ log_message_array = []
165
+
166
+ begin
167
+ response = Multiwoven::Integrations::Core::HttpClient.request(
168
+ write_url,
169
+ HTTP_POST,
170
+ payload: payload,
171
+ headers: auth_headers(token)
172
+ )
173
+ if success?(response)
174
+ write_success += 1
175
+ else
176
+ write_failure += 1
177
+ end
178
+ log_message_array << log_request_response("info", [HTTP_POST, write_url, payload], response)
179
+ rescue StandardError => e
180
+ handle_exception(e, {
181
+ context: "MICROSOFT:EXCEL:RECORD:WRITE:EXCEPTION",
182
+ type: "error",
183
+ sync_id: sync_config.sync_id,
184
+ sync_run_id: sync_config.sync_run_id
185
+ })
186
+ write_failure += 1
187
+ log_message_array << log_request_response("error", [HTTP_POST, write_url, payload], e.message)
188
+ end
189
+
190
+ tracking_message(write_success, write_failure, log_message_array)
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,7 @@
1
+ {
2
+ "request_rate_limit": 6000,
3
+ "request_rate_limit_unit": "minute",
4
+ "request_rate_concurrency": 10,
5
+ "streams": []
6
+ }
7
+
@@ -0,0 +1,15 @@
1
+ {
2
+ "data": {
3
+ "name": "MicrosoftExcel",
4
+ "title": "Microsoft Excel",
5
+ "connector_type": "destination",
6
+ "category": "Database",
7
+ "documentation_url": "https://docs.squared.ai/guides/data-integration/destination/microsoft_excel",
8
+ "github_issue_label": "destination-microsoft-excel",
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,19 @@
1
+ {
2
+ "documentation_url": "https://docs.squared.ai/guides/data-integration/destination/microsoft_excel",
3
+ "stream_type": "dynamic",
4
+ "connector_query_type": "raw_sql",
5
+ "connection_specification": {
6
+ "$schema": "http://json-schema.org/draft-07/schema#",
7
+ "title": "Microsoft Excel",
8
+ "type": "object",
9
+ "required": ["token"],
10
+ "properties": {
11
+ "token": {
12
+ "description": "Token from Microsoft Graph.",
13
+ "type": "string",
14
+ "title": "Token",
15
+ "order": 0
16
+ }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,18 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="800" width="1200" viewBox="-343.4625 -532.5 2976.675 3195">
2
+ <path d="M1437.75 1011.75L532.5 852v1180.393c0 53.907 43.7 97.607 97.607 97.607h1562.036c53.907 0 97.607-43.7 97.607-97.607V1597.5z" fill="#185C37"/>
3
+ <path d="M1437.75 0H630.107C576.2 0 532.5 43.7 532.5 97.607V532.5l905.25 532.5L1917 1224.75 2289.75 1065V532.5z" fill="#21A366"/>
4
+ <path d="M532.5 532.5h905.25V1065H532.5z" fill="#107C41"/>
5
+ <path d="M1180.393 426H532.5v1331.25h647.893c53.834-.175 97.432-43.773 97.607-97.607V523.607c-.175-53.834-43.773-97.432-97.607-97.607z" opacity=".1"/>
6
+ <path d="M1127.143 479.25H532.5V1810.5h594.643c53.834-.175 97.432-43.773 97.607-97.607V576.857c-.175-53.834-43.773-97.432-97.607-97.607z" opacity=".2"/>
7
+ <path d="M1127.143 479.25H532.5V1704h594.643c53.834-.175 97.432-43.773 97.607-97.607V576.857c-.175-53.834-43.773-97.432-97.607-97.607z" opacity=".2"/>
8
+ <path d="M1073.893 479.25H532.5V1704h541.393c53.834-.175 97.432-43.773 97.607-97.607V576.857c-.175-53.834-43.773-97.432-97.607-97.607z" opacity=".2"/>
9
+ <linearGradient gradientTransform="matrix(1 0 0 -1 0 2132)" y2="404.982" x2="967.987" y1="1729.018" x1="203.513" gradientUnits="userSpaceOnUse" id="a">
10
+ <stop offset="0" stop-color="#18884f"/>
11
+ <stop offset=".5" stop-color="#117e43"/>
12
+ <stop offset="1" stop-color="#0b6631"/>
13
+ </linearGradient>
14
+ <path d="M97.607 479.25h976.285c53.907 0 97.607 43.7 97.607 97.607v976.285c0 53.907-43.7 97.607-97.607 97.607H97.607C43.7 1650.75 0 1607.05 0 1553.143V576.857c0-53.907 43.7-97.607 97.607-97.607z" fill="url(#a)"/>
15
+ <path d="M302.3 1382.264l205.332-318.169L319.5 747.683h151.336l102.666 202.35c9.479 19.223 15.975 33.494 19.49 42.919h1.331a798.667 798.667 0 0121.3-44.677L725.371 747.79H864.3l-192.925 314.548L869.2 1382.263H721.378L602.79 1160.158a186.298 186.298 0 01-14.164-29.66h-1.757a140.458 140.458 0 01-13.739 28.755l-122.102 223.011z" fill="#FFF"/>
16
+ <path d="M2192.143 0H1437.75v532.5h852V97.607C2289.75 43.7 2246.05 0 2192.143 0z" fill="#33C481"/>
17
+ <path d="M1437.75 1065h852v532.5h-852z" fill="#107C41"/>
18
+ </svg>
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Multiwoven
4
4
  module Integrations
5
- VERSION = "0.7.9"
5
+ VERSION = "0.8.0"
6
6
 
7
7
  ENABLED_SOURCES = %w[
8
8
  Snowflake
@@ -36,6 +36,7 @@ module Multiwoven
36
36
  MariaDB
37
37
  DatabricksLakehouse
38
38
  Oracle
39
+ MicrosoftExcel
39
40
  ].freeze
40
41
  end
41
42
  end
@@ -81,6 +81,7 @@ require_relative "integrations/destination/iterable/client"
81
81
  require_relative "integrations/destination/maria_db/client"
82
82
  require_relative "integrations/destination/databricks_lakehouse/client"
83
83
  require_relative "integrations/destination/oracle_db/client"
84
+ require_relative "integrations/destination/microsoft_excel/client"
84
85
 
85
86
  module Multiwoven
86
87
  module Integrations
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.7.9
4
+ version: 0.8.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-08-09 00:00:00.000000000 Z
11
+ date: 2024-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -544,6 +544,11 @@ files:
544
544
  - lib/multiwoven/integrations/destination/maria_db/config/meta.json
545
545
  - lib/multiwoven/integrations/destination/maria_db/config/spec.json
546
546
  - lib/multiwoven/integrations/destination/maria_db/icon.svg
547
+ - lib/multiwoven/integrations/destination/microsoft_excel/client.rb
548
+ - lib/multiwoven/integrations/destination/microsoft_excel/config/catalog.json
549
+ - lib/multiwoven/integrations/destination/microsoft_excel/config/meta.json
550
+ - lib/multiwoven/integrations/destination/microsoft_excel/config/spec.json
551
+ - lib/multiwoven/integrations/destination/microsoft_excel/icon.svg
547
552
  - lib/multiwoven/integrations/destination/oracle_db/client.rb
548
553
  - lib/multiwoven/integrations/destination/oracle_db/config/meta.json
549
554
  - lib/multiwoven/integrations/destination/oracle_db/config/spec.json