multiwoven-integrations 0.32.2 → 0.33.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 +4 -4
- data/lib/multiwoven/integrations/core/constants.rb +16 -0
- data/lib/multiwoven/integrations/destination/pinecone_db/client.rb +7 -7
- data/lib/multiwoven/integrations/rollout.rb +2 -1
- data/lib/multiwoven/integrations/source/google_drive/client.rb +215 -0
- data/lib/multiwoven/integrations/source/google_drive/config/catalog.json +63 -0
- data/lib/multiwoven/integrations/source/google_drive/config/meta.json +15 -0
- data/lib/multiwoven/integrations/source/google_drive/config/spec.json +120 -0
- data/lib/multiwoven/integrations/source/google_drive/icon.svg +8 -0
- data/lib/multiwoven/integrations.rb +4 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6be57be6129595ce97d90e8ae2458d9acf46f263d6385e19ab8d5e54f89bcc3a
|
4
|
+
data.tar.gz: 6054e513e6cd67226c5f35e9ab5f85dfe39ad3a13cab12ed2e1f6f33c08b68be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa601444cd95bd8a8b6404ff5c352c4d44a45a5a4d50af0a28a3bcb0a2a90163e1ee84ffb71939e575086821a54d88159fef1cbec260c7037ff423132b0840e7
|
7
|
+
data.tar.gz: b70732578f3dc563519db07e7ecb10df0022281e781099820a90f585791ca786c8eb23523fa93d2417acb1a51657e0f021b326822b5ca9845569ba57c288bef4
|
@@ -98,6 +98,22 @@ module Multiwoven
|
|
98
98
|
FIRECRAWL_CRAWL_ACTIVE_URL = "https://api.firecrawl.dev/v1/crawl/active"
|
99
99
|
FIRECRAWL_GET_CRAWL_URL = "https://api.firecrawl.dev/v1/crawl/%<id>s"
|
100
100
|
FIRECRAWL_REQUEST_RATE_LIMIT = 5
|
101
|
+
|
102
|
+
# Amazon Textract (analyze_expense) fields
|
103
|
+
TEXTRACT_SUMMARY_FIELDS = {
|
104
|
+
"invoice_number" => "INVOICE_RECEIPT_ID",
|
105
|
+
"invoice_date" => "INVOICE_RECEIPT_DATE",
|
106
|
+
"purchase_order" => "PO_NUMBER",
|
107
|
+
"invoice_total" => "TOTAL",
|
108
|
+
"vendor_name" => "VENDOR_NAME"
|
109
|
+
}.freeze
|
110
|
+
TEXTRACT_LINE_ITEMS_FIELDS = {
|
111
|
+
"item_number" => "PRODUCT_CODE",
|
112
|
+
"item_description" => "ITEM",
|
113
|
+
"item_quantity" => "QUANTITY",
|
114
|
+
"item_price" => "UNIT_PRICE",
|
115
|
+
"line_total" => "PRICE"
|
116
|
+
}.freeze
|
101
117
|
end
|
102
118
|
end
|
103
119
|
end
|
@@ -104,15 +104,15 @@ module Multiwoven::Integrations::Destination
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def parse_meta_data(vector_meta_data)
|
107
|
-
return {}
|
107
|
+
return {} unless vector_meta_data.is_a?(String)
|
108
108
|
|
109
|
-
metadata =
|
110
|
-
|
111
|
-
|
109
|
+
metadata = {}
|
110
|
+
# Use regex to parse the metadata string into a hash
|
111
|
+
vector_meta_data.scan(/([A-Za-z]+):\s*(.*?)(?=(?:\n?[A-Za-z]+:)|\z)/m).each do |key, value|
|
112
|
+
metadata[key.strip] = value.strip
|
113
|
+
end
|
112
114
|
|
113
|
-
|
114
|
-
rescue JSON::ParserError
|
115
|
-
{}
|
115
|
+
metadata
|
116
116
|
end
|
117
117
|
|
118
118
|
def send_to_pinecone(pinecone_index, record)
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Multiwoven
|
4
4
|
module Integrations
|
5
|
-
VERSION = "0.
|
5
|
+
VERSION = "0.33.0"
|
6
6
|
|
7
7
|
ENABLED_SOURCES = %w[
|
8
8
|
Snowflake
|
@@ -32,6 +32,7 @@ module Multiwoven
|
|
32
32
|
Qdrant
|
33
33
|
Firecrawl
|
34
34
|
Odoo
|
35
|
+
GoogleDrive
|
35
36
|
].freeze
|
36
37
|
|
37
38
|
ENABLED_DESTINATIONS = %w[
|
@@ -0,0 +1,215 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Multiwoven::Integrations::Source
|
4
|
+
module GoogleDrive
|
5
|
+
include Multiwoven::Integrations::Core
|
6
|
+
|
7
|
+
FIELDS = "files(id, name, parents, mimeType), nextPageToken"
|
8
|
+
MAX_PER_PAGE = 1000
|
9
|
+
MIMETYPE_GOOGLE_DRIVE_FOLDER = "mimeType = 'application/vnd.google-apps.folder'"
|
10
|
+
|
11
|
+
class Client < SourceConnector
|
12
|
+
def check_connection(connection_config)
|
13
|
+
connection_config = connection_config.with_indifferent_access
|
14
|
+
client = create_connection(connection_config)
|
15
|
+
build_query(client)
|
16
|
+
success_status
|
17
|
+
rescue StandardError => e
|
18
|
+
failure_status(e)
|
19
|
+
end
|
20
|
+
|
21
|
+
def discover(_connection_config)
|
22
|
+
catalog_json = read_json(CATALOG_SPEC_PATH)
|
23
|
+
catalog = build_catalog(catalog_json)
|
24
|
+
catalog.to_multiwoven_message
|
25
|
+
rescue StandardError => e
|
26
|
+
handle_exception(e, {
|
27
|
+
context: "GOOGLE_DRIVE:DISCOVER:EXCEPTION",
|
28
|
+
type: "error"
|
29
|
+
})
|
30
|
+
end
|
31
|
+
|
32
|
+
def read(sync_config)
|
33
|
+
connection_config = sync_config.source.connection_specification.with_indifferent_access
|
34
|
+
client = create_connection(connection_config)
|
35
|
+
query = sync_config.model.query
|
36
|
+
query = batched_query(query, sync_config.limit, sync_config.offset) unless sync_config.limit.nil? && sync_config.offset.nil?
|
37
|
+
records = query(client, query)
|
38
|
+
analyze_expenses(client, records)
|
39
|
+
rescue StandardError => e
|
40
|
+
handle_exception(e, {
|
41
|
+
context: "GOOGLE_DRIVE:READ:EXCEPTION",
|
42
|
+
type: "error",
|
43
|
+
sync_id: sync_config.sync_id,
|
44
|
+
sync_run_id: sync_config.sync_run_id
|
45
|
+
})
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def query(client, query)
|
51
|
+
limit = 0
|
52
|
+
offset = 0
|
53
|
+
query = query.gsub("\n", " ").gsub(/\s+/, " ")
|
54
|
+
limit = query.match(/LIMIT (\d+)/)[1].to_i if query.include? "LIMIT"
|
55
|
+
offset = query.match(/OFFSET (\d+)/)[1].to_i if query.include? "OFFSET"
|
56
|
+
query = query.match(/\((.*)\) AS/)[1] if query.include? "AS subquery"
|
57
|
+
columns = select_columns(query)
|
58
|
+
|
59
|
+
google_drive_query = build_query(client)
|
60
|
+
files = get_files(client, google_drive_query, limit, offset)
|
61
|
+
files.map do |file|
|
62
|
+
RecordMessage.new(data: prepare_invoice(file, columns), emitted_at: Time.now.to_i).to_multiwoven_message
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Reads files from Google Drive and sends them to Amazon Textract for analysis
|
67
|
+
def analyze_expenses(client, records)
|
68
|
+
textract = create_aws_connection
|
69
|
+
results = []
|
70
|
+
records.each do |record|
|
71
|
+
invoice = record.record.data
|
72
|
+
begin
|
73
|
+
byte_stream = StringIO.new
|
74
|
+
client.get_file(invoice["id"], download_dest: byte_stream)
|
75
|
+
byte_stream.rewind
|
76
|
+
analysis = textract.analyze_expense(document: { bytes: byte_stream.read })
|
77
|
+
invoice = extract_invoice_data(invoice, analysis)
|
78
|
+
rescue Aws::Textract::Errors::UnsupportedDocumentException => e
|
79
|
+
invoice[:exception] = "Document format not supported." if invoice.key?(:exception)
|
80
|
+
handle_exception(e, {
|
81
|
+
context: "GOOGLE_DRIVE:READ:EXTRACT:EXCEPTION",
|
82
|
+
type: "error"
|
83
|
+
})
|
84
|
+
rescue StandardError => e
|
85
|
+
invoice[:exception] = e.message if invoice.key?(:exception)
|
86
|
+
handle_exception(e, {
|
87
|
+
context: "GOOGLE_DRIVE:READ:EXTRACT:EXCEPTION",
|
88
|
+
type: "error"
|
89
|
+
})
|
90
|
+
end
|
91
|
+
results.append(RecordMessage.new(data: invoice, emitted_at: Time.now.to_i).to_multiwoven_message)
|
92
|
+
end
|
93
|
+
results
|
94
|
+
end
|
95
|
+
|
96
|
+
def build_query(client)
|
97
|
+
query = "mimeType != 'application/vnd.google-apps.folder'"
|
98
|
+
|
99
|
+
if @options[:folder]
|
100
|
+
folder_query = "#{MIMETYPE_GOOGLE_DRIVE_FOLDER} and (name = '#{@options[:folder]}')"
|
101
|
+
response = client.list_files(include_items_from_all_drives: true, supports_all_drives: true, q: folder_query, fields: FIELDS)
|
102
|
+
raise "Specified folder does not exist" if response.files.empty?
|
103
|
+
|
104
|
+
parent_id = response.files.first.id
|
105
|
+
parents_query = "'#{parent_id}' in parents"
|
106
|
+
end
|
107
|
+
|
108
|
+
if @options[:subfolders]
|
109
|
+
subfolders_query = MIMETYPE_GOOGLE_DRIVE_FOLDER
|
110
|
+
subfolders_query += "and #{parents_query}" if parents_query
|
111
|
+
response = client.list_files(include_items_from_all_drives: true, supports_all_drives: true, q: subfolders_query, fields: FIELDS)
|
112
|
+
subfolders_ids = response.files.map { |file| "'#{file.id}'" }
|
113
|
+
parents_query = "(#{subfolders_ids.join(" in parents or ")} in parents)"
|
114
|
+
end
|
115
|
+
|
116
|
+
query += " and mimeType = '#{@options[:file_type]}'" if @options[:file_type]
|
117
|
+
query += " and #{parents_query}" if parents_query
|
118
|
+
query
|
119
|
+
end
|
120
|
+
|
121
|
+
def get_files(client, query, limit, offset)
|
122
|
+
total_fetched = 0
|
123
|
+
result = []
|
124
|
+
|
125
|
+
return result if offset.positive? && !@next_page_token
|
126
|
+
|
127
|
+
while total_fetched < limit
|
128
|
+
batch_limit = [MAX_PER_PAGE, limit - total_fetched].min
|
129
|
+
response = if @next_page_token
|
130
|
+
client.list_files(include_items_from_all_drives: true, supports_all_drives: true, q: query, fields: FIELDS, page_size: batch_limit, page_token: @next_page_token)
|
131
|
+
else
|
132
|
+
client.list_files(include_items_from_all_drives: true, supports_all_drives: true, q: query, fields: FIELDS, page_size: batch_limit)
|
133
|
+
end
|
134
|
+
break if response.files.empty?
|
135
|
+
|
136
|
+
result.push(*response.files)
|
137
|
+
@next_page_token = response.next_page_token
|
138
|
+
break unless response.next_page_token
|
139
|
+
|
140
|
+
total_fetched += response.files.size
|
141
|
+
end
|
142
|
+
|
143
|
+
result
|
144
|
+
end
|
145
|
+
|
146
|
+
def select_columns(query)
|
147
|
+
columns = query.match(/SELECT (.*) FROM/)[1]
|
148
|
+
all_columns = %w[line_items id file_name] + TEXTRACT_SUMMARY_FIELDS.keys
|
149
|
+
@options[:fields] = all_columns if @options[:fields].empty?
|
150
|
+
|
151
|
+
return @options[:fields] if columns.include?("*")
|
152
|
+
|
153
|
+
columns = columns.split(",").map(&:strip)
|
154
|
+
raise "Column(s) #{(columns - all_columns).join(", ")} not valid." if (columns - all_columns).length.positive?
|
155
|
+
|
156
|
+
columns & all_columns
|
157
|
+
end
|
158
|
+
|
159
|
+
def prepare_invoice(file, columns)
|
160
|
+
invoice = {}
|
161
|
+
columns.each { |column| invoice[column] = "" if TEXTRACT_SUMMARY_FIELDS.key?(column) }
|
162
|
+
invoice["line_items"] = [] if columns.any?("line_items")
|
163
|
+
invoice["id"] = file.id if columns.any?("id")
|
164
|
+
invoice["file_name"] = file.id if columns.any?("file_name")
|
165
|
+
invoice["exception"] = "" if columns.any?("exception")
|
166
|
+
invoice
|
167
|
+
end
|
168
|
+
|
169
|
+
def create_connection(connection_config)
|
170
|
+
@options = connection_config[:options]
|
171
|
+
credentials = connection_config[:credentials_json]
|
172
|
+
client = Google::Apis::DriveV3::DriveService.new
|
173
|
+
client.authorization = Google::Auth::ServiceAccountCredentials.make_creds(
|
174
|
+
json_key_io: StringIO.new(credentials.to_json),
|
175
|
+
scope: GOOGLE_SHEETS_SCOPE
|
176
|
+
)
|
177
|
+
client
|
178
|
+
end
|
179
|
+
|
180
|
+
def create_aws_connection
|
181
|
+
region = ENV["AWS_REGION"]
|
182
|
+
access_key_id = ENV["AWS_ACCESS_KEY_ID"]
|
183
|
+
secret_access_key = ENV["AWS_SECRET_ACCESS_KEY"]
|
184
|
+
credentials = Aws::Credentials.new(access_key_id, secret_access_key)
|
185
|
+
Aws::Textract::Client.new(region: region, credentials: credentials)
|
186
|
+
end
|
187
|
+
|
188
|
+
def extract_invoice_data(invoice, results)
|
189
|
+
expense_document = results.expense_documents[0]
|
190
|
+
(invoice.keys & TEXTRACT_SUMMARY_FIELDS.keys).each do |key|
|
191
|
+
invoice[key] = extract_field_value(expense_document.summary_fields, TEXTRACT_SUMMARY_FIELDS[key])
|
192
|
+
end
|
193
|
+
|
194
|
+
if invoice.key?("line_items")
|
195
|
+
expense_document.line_item_groups.each do |line_item_group|
|
196
|
+
line_item_group.line_items.each do |line_item|
|
197
|
+
extracted_line_item = {}
|
198
|
+
TEXTRACT_LINE_ITEMS_FIELDS.each_key do |key|
|
199
|
+
extracted_line_item[key] = extract_field_value(line_item.line_item_expense_fields, TEXTRACT_LINE_ITEMS_FIELDS[key])
|
200
|
+
end
|
201
|
+
invoice["line_items"] << extracted_line_item
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
invoice["line_items"] = invoice["line_items"].to_json
|
206
|
+
invoice.transform_keys(&:to_sym)
|
207
|
+
end
|
208
|
+
|
209
|
+
def extract_field_value(fields, selector)
|
210
|
+
selected_field = fields.select { |field| field.type.text == selector }.first
|
211
|
+
selected_field ? selected_field.value_detection.text : ""
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
{
|
2
|
+
"request_rate_limit": 6000,
|
3
|
+
"request_rate_limit_unit": "minute",
|
4
|
+
"request_rate_concurrency": 10,
|
5
|
+
"streams": [
|
6
|
+
{
|
7
|
+
"name": "invoices",
|
8
|
+
"action": "fetch",
|
9
|
+
"json_schema": {
|
10
|
+
"type": "object",
|
11
|
+
"properties": {
|
12
|
+
"id": {
|
13
|
+
"type": "string"
|
14
|
+
},
|
15
|
+
"vendor_name": {
|
16
|
+
"type": "string"
|
17
|
+
},
|
18
|
+
"file_name": {
|
19
|
+
"type": "string"
|
20
|
+
},
|
21
|
+
"exception": {
|
22
|
+
"type": "string"
|
23
|
+
},
|
24
|
+
"invoice_number": {
|
25
|
+
"type": "string"
|
26
|
+
},
|
27
|
+
"purchase_order": {
|
28
|
+
"type": "string"
|
29
|
+
},
|
30
|
+
"invoice_date": {
|
31
|
+
"type": "string"
|
32
|
+
},
|
33
|
+
"invoice_total": {
|
34
|
+
"type": "string"
|
35
|
+
},
|
36
|
+
"line_items": {
|
37
|
+
"type": "array",
|
38
|
+
"items": {
|
39
|
+
"type": "object",
|
40
|
+
"properties": {
|
41
|
+
"item_number": {
|
42
|
+
"type": "string"
|
43
|
+
},
|
44
|
+
"item_description": {
|
45
|
+
"type": "string"
|
46
|
+
},
|
47
|
+
"item_quantity": {
|
48
|
+
"type": "string"
|
49
|
+
},
|
50
|
+
"item_price": {
|
51
|
+
"type": "string"
|
52
|
+
},
|
53
|
+
"line_total": {
|
54
|
+
"type": "string"
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
]
|
63
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
{
|
2
|
+
"data": {
|
3
|
+
"name": "GoogleDrive",
|
4
|
+
"title": "Google Drive",
|
5
|
+
"connector_type": "source",
|
6
|
+
"category": "File Storage",
|
7
|
+
"documentation_url": "https://docs.squared.ai/guides/sources/data-sources/google_drive",
|
8
|
+
"github_issue_label": "source-google-drive",
|
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,120 @@
|
|
1
|
+
{
|
2
|
+
"documentation_url": "https://docs.squared.ai/guides/sources/data-sources/google_drive",
|
3
|
+
"stream_type": "dynamic",
|
4
|
+
"connector_query_type": "raw_sql",
|
5
|
+
"connection_specification": {
|
6
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
7
|
+
"title": "GoogleDrive",
|
8
|
+
"type": "object",
|
9
|
+
"required": ["credentials_json"],
|
10
|
+
"properties": {
|
11
|
+
"credentials_json": {
|
12
|
+
"type": "object",
|
13
|
+
"description": "You can get the keys from the Google Cloud web console. First, go to the IAM page and select Service Accounts from the left menu. Next, locate your service account in the list, click on its Keys tab, and then click Add Key. Lastly, click Create new key and select JSON.",
|
14
|
+
"title": "",
|
15
|
+
"properties": {
|
16
|
+
"type": {
|
17
|
+
"type": "string",
|
18
|
+
"enum": ["service_account"]
|
19
|
+
},
|
20
|
+
"project_id": {
|
21
|
+
"type": "string"
|
22
|
+
},
|
23
|
+
"private_key_id": {
|
24
|
+
"type": "string",
|
25
|
+
"multiwoven_secret": true
|
26
|
+
},
|
27
|
+
"private_key": {
|
28
|
+
"type": "string",
|
29
|
+
"multiwoven_secret": true
|
30
|
+
},
|
31
|
+
"client_email": {
|
32
|
+
"type": "string",
|
33
|
+
"format": "email"
|
34
|
+
},
|
35
|
+
"client_id": {
|
36
|
+
"type": "string",
|
37
|
+
"multiwoven_secret": true
|
38
|
+
},
|
39
|
+
"auth_uri": {
|
40
|
+
"type": "string"
|
41
|
+
},
|
42
|
+
"token_uri": {
|
43
|
+
"type": "string"
|
44
|
+
},
|
45
|
+
"auth_provider_x509_cert_url": {
|
46
|
+
"type": "string"
|
47
|
+
},
|
48
|
+
"client_x509_cert_url": {
|
49
|
+
"type": "string",
|
50
|
+
"format": "uri"
|
51
|
+
},
|
52
|
+
"universe_domain": {
|
53
|
+
"type": "string"
|
54
|
+
}
|
55
|
+
},
|
56
|
+
"required": [
|
57
|
+
"type",
|
58
|
+
"project_id",
|
59
|
+
"private_key_id",
|
60
|
+
"private_key",
|
61
|
+
"client_email",
|
62
|
+
"client_id",
|
63
|
+
"auth_uri",
|
64
|
+
"token_uri",
|
65
|
+
"auth_provider_x509_cert_url",
|
66
|
+
"client_x509_cert_url",
|
67
|
+
"universe_domain"
|
68
|
+
]
|
69
|
+
},
|
70
|
+
"options": {
|
71
|
+
"type": "object",
|
72
|
+
"description": "When the subfolders option is set to true, files will be read from the subfolders of the root or specified folder.",
|
73
|
+
"title": "Options",
|
74
|
+
"required": ["document_type"],
|
75
|
+
"properties": {
|
76
|
+
"folder": {
|
77
|
+
"type": "string",
|
78
|
+
"description": "When specified, reads files contained in the folder.",
|
79
|
+
"title": "Folder"
|
80
|
+
},
|
81
|
+
"subfolders": {
|
82
|
+
"type": "boolean",
|
83
|
+
"default": false,
|
84
|
+
"title": "Read from subfolders"
|
85
|
+
},
|
86
|
+
"file_type": {
|
87
|
+
"description": "The type of file to read",
|
88
|
+
"type": "string",
|
89
|
+
"title": "File Type",
|
90
|
+
"enum": [
|
91
|
+
"application/pdf"
|
92
|
+
]
|
93
|
+
},
|
94
|
+
"document_type": {
|
95
|
+
"type": "string",
|
96
|
+
"enum": [ "invoices" ]
|
97
|
+
},
|
98
|
+
"fields": {
|
99
|
+
"type": "array",
|
100
|
+
"description": "Leave blank to extract all fields.",
|
101
|
+
"items": {
|
102
|
+
"type": "string",
|
103
|
+
"anyOf": [
|
104
|
+
{ "const": "id", "title": "File Id" },
|
105
|
+
{ "const": "file_name", "title": "File Name" },
|
106
|
+
{ "const": "exception", "title": "Parsing Exception" },
|
107
|
+
{ "const": "invoice_number", "title": "Invoice/Receipt Number" },
|
108
|
+
{ "const": "invoice_date", "title": "Invoice/Receipt Date" },
|
109
|
+
{ "const": "invoice_total", "title": "Invoice/Receipt Total" },
|
110
|
+
{ "const": "purchase_order", "title": "Purchase Order Number" },
|
111
|
+
{ "const": "line_items", "title": "Invoice Line Items (Code, Description, Quantity, Unit Price, Total Price)"}
|
112
|
+
]
|
113
|
+
},
|
114
|
+
"uniqueItems": true
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<svg viewBox="0 0 87.3 78" xmlns="http://www.w3.org/2000/svg">
|
2
|
+
<path d="m6.6 66.85 3.85 6.65c.8 1.4 1.95 2.5 3.3 3.3l13.75-23.8h-27.5c0 1.55.4 3.1 1.2 4.5z" fill="#0066da"/>
|
3
|
+
<path d="m43.65 25-13.75-23.8c-1.35.8-2.5 1.9-3.3 3.3l-25.4 44a9.06 9.06 0 0 0 -1.2 4.5h27.5z" fill="#00ac47"/>
|
4
|
+
<path d="m73.55 76.8c1.35-.8 2.5-1.9 3.3-3.3l1.6-2.75 7.65-13.25c.8-1.4 1.2-2.95 1.2-4.5h-27.502l5.852 11.5z" fill="#ea4335"/>
|
5
|
+
<path d="m43.65 25 13.75-23.8c-1.35-.8-2.9-1.2-4.5-1.2h-18.5c-1.6 0-3.15.45-4.5 1.2z" fill="#00832d"/>
|
6
|
+
<path d="m59.8 53h-32.3l-13.75 23.8c1.35.8 2.9 1.2 4.5 1.2h50.8c1.6 0 3.15-.45 4.5-1.2z" fill="#2684fc"/>
|
7
|
+
<path d="m73.4 26.5-12.7-22c-.8-1.4-1.95-2.5-3.3-3.3l-13.75 23.8 16.15 28h27.45c0-1.55-.4-3.1-1.2-4.5z" fill="#ffba00"/>
|
8
|
+
</svg>
|
@@ -42,6 +42,9 @@ require "pinecone"
|
|
42
42
|
require "intuit-oauth"
|
43
43
|
require "nokogiri"
|
44
44
|
require "xmlrpc/client"
|
45
|
+
require "googleauth"
|
46
|
+
require "google/apis/drive_v3"
|
47
|
+
require "aws-sdk-textract"
|
45
48
|
|
46
49
|
# Service
|
47
50
|
require_relative "integrations/config"
|
@@ -92,6 +95,7 @@ require_relative "integrations/source/pinecone_db/client"
|
|
92
95
|
require_relative "integrations/source/qdrant/client"
|
93
96
|
require_relative "integrations/source/firecrawl/client"
|
94
97
|
require_relative "integrations/source/odoo/client"
|
98
|
+
require_relative "integrations/source/google_drive/client"
|
95
99
|
|
96
100
|
# Destination
|
97
101
|
require_relative "integrations/destination/klaviyo/client"
|
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.
|
4
|
+
version: 0.33.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: 2025-07-
|
11
|
+
date: 2025-07-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -765,6 +765,11 @@ files:
|
|
765
765
|
- lib/multiwoven/integrations/source/generic_open_ai/config/meta.json
|
766
766
|
- lib/multiwoven/integrations/source/generic_open_ai/config/spec.json
|
767
767
|
- lib/multiwoven/integrations/source/generic_open_ai/icon.svg
|
768
|
+
- lib/multiwoven/integrations/source/google_drive/client.rb
|
769
|
+
- lib/multiwoven/integrations/source/google_drive/config/catalog.json
|
770
|
+
- lib/multiwoven/integrations/source/google_drive/config/meta.json
|
771
|
+
- lib/multiwoven/integrations/source/google_drive/config/spec.json
|
772
|
+
- lib/multiwoven/integrations/source/google_drive/icon.svg
|
768
773
|
- lib/multiwoven/integrations/source/google_vertex_model/client.rb
|
769
774
|
- lib/multiwoven/integrations/source/google_vertex_model/config/catalog.json
|
770
775
|
- lib/multiwoven/integrations/source/google_vertex_model/config/meta.json
|