Skip to content

Commit

Permalink
Merge branch 'main' into cherry-pick-ce-commit-966eb99716a638cea4b033…
Browse files Browse the repository at this point in the history
…66fdc377e37d4150e3
  • Loading branch information
macintushar committed Sep 5, 2024
2 parents c840e86 + 0cf9c09 commit 2accf47
Show file tree
Hide file tree
Showing 256 changed files with 7,177 additions and 1,920 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/integrations-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ jobs:
sudo mv libduckdb/libduckdb.so /usr/local/lib
sudo ldconfig /usr/local/lib
- name: Download and Install Oracle Instant Client
run: |
sudo apt-get install -y libaio1 alien
wget http://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/x86_64/getPackage/oracle-instantclient19.6-basic-19.6.0.0.0-1.x86_64.rpm
wget http://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/x86_64/getPackage/oracle-instantclient19.6-devel-19.6.0.0.0-1.x86_64.rpm
sudo alien -i --scripts oracle-instantclient*.rpm
rm -f oracle-instantclient*.rpm
- name: Install dependencies
run: |
gem install bundler
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/integrations-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ jobs:
sudo mv libduckdb/libduckdb.so /usr/local/lib
sudo ldconfig /usr/local/lib
- name: Download and Install Oracle Instant Client
run: |
sudo apt-get install -y libaio1 alien
wget http://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/x86_64/getPackage/oracle-instantclient19.6-basic-19.6.0.0.0-1.x86_64.rpm
wget http://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/x86_64/getPackage/oracle-instantclient19.6-devel-19.6.0.0.0-1.x86_64.rpm
sudo alien -i --scripts oracle-instantclient*.rpm
rm -f oracle-instantclient*.rpm
- name: Install dependencies
run: bundle install
working-directory: ./integrations
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/server-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ jobs:
sudo mv libduckdb/libduckdb.so /usr/local/lib
sudo ldconfig /usr/local/lib
- name: Download and Install Oracle Instant Client
run: |
sudo apt-get install -y libaio1 alien
wget http://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/x86_64/getPackage/oracle-instantclient19.6-basic-19.6.0.0.0-1.x86_64.rpm
wget http://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/x86_64/getPackage/oracle-instantclient19.6-devel-19.6.0.0.0-1.x86_64.rpm
sudo alien -i --scripts oracle-instantclient*.rpm
rm -f oracle-instantclient*.rpm
- name: Bundle Install
run: bundle install
working-directory: ./server
Expand Down
6 changes: 6 additions & 0 deletions integrations/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ gem "mysql2"

gem "aws-sdk-sts"

gem "ruby-oci8", "~> 2.2.12"

gem "aws-sdk-sagemaker"

gem "aws-sdk-sagemakerruntime"

group :development, :test do
gem "simplecov", require: false
gem "simplecov_json_formatter", require: false
Expand Down
19 changes: 18 additions & 1 deletion integrations/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ GIT
PATH
remote: .
specs:
multiwoven-integrations (0.5.2)
multiwoven-integrations (0.10.0)
activesupport
async-websocket
aws-sdk-athena
aws-sdk-cloudwatchlogs
aws-sdk-s3
aws-sdk-sts
aws-sigv4
csv
dry-schema
dry-struct
Expand All @@ -28,6 +30,7 @@ PATH
rake
restforce
ruby-limiter
ruby-oci8
ruby-odbc
rubyzip
sequel
Expand Down Expand Up @@ -66,6 +69,9 @@ GEM
aws-sdk-athena (1.83.0)
aws-sdk-core (~> 3, >= 3.193.0)
aws-sigv4 (~> 1.1)
aws-sdk-cloudwatchlogs (1.82.0)
aws-sdk-core (~> 3, >= 3.193.0)
aws-sigv4 (~> 1.1)
aws-sdk-core (3.196.1)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0)
Expand All @@ -78,6 +84,12 @@ GEM
aws-sdk-core (~> 3, >= 3.194.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.8)
aws-sdk-sagemaker (1.229.0)
aws-sdk-core (~> 3, >= 3.188.0)
aws-sigv4 (~> 1.1)
aws-sdk-sagemakerruntime (1.63.0)
aws-sdk-core (~> 3, >= 3.193.0)
aws-sigv4 (~> 1.1)
aws-sdk-sts (1.11.0)
aws-sdk-core (~> 3, >= 3.110.0)
aws-sigv4 (~> 1.1)
Expand Down Expand Up @@ -275,6 +287,8 @@ GEM
rubocop-ast (1.31.3)
parser (>= 3.3.1.0)
ruby-limiter (2.3.0)
ruby-oci8 (2.2.12)
ruby-oci8 (2.2.12-x64-mingw-ucrt)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
Expand Down Expand Up @@ -335,6 +349,8 @@ DEPENDENCIES
async-websocket (~> 0.8.0)
aws-sdk-athena
aws-sdk-s3
aws-sdk-sagemaker
aws-sdk-sagemakerruntime
aws-sdk-sts
byebug
csv
Expand All @@ -357,6 +373,7 @@ DEPENDENCIES
rspec (~> 3.0)
rubocop (~> 1.21)
ruby-limiter
ruby-oci8 (~> 2.2.12)
ruby-odbc!
rubyzip
sequel
Expand Down
4 changes: 4 additions & 0 deletions integrations/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ Before you begin the installation, ensure you have the following dependencies in
- Command: `brew install openssl@3`
- Description: Essential for secure communication.

- **Oracle Instant Client**
- Download Link: https://www.oracle.com/database/technologies/instant-client/downloads.html
- Description: Required for database interactions.


### Installation

Expand Down
8 changes: 8 additions & 0 deletions integrations/lib/multiwoven/integrations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
require "duckdb"
require "iterable-api-client"
require "aws-sdk-sts"
require "ruby-oci8"
require "aws-sdk-sagemaker"
require "aws-sdk-sagemakerruntime"

# Service
require_relative "integrations/config"
Expand Down Expand Up @@ -60,6 +63,9 @@
require_relative "integrations/source/clickhouse/client"
require_relative "integrations/source/amazon_s3/client"
require_relative "integrations/source/maria_db/client"
require_relative "integrations/source/oracle_db/client"
require_relative "integrations/source/databrics_model/client"
require_relative "integrations/source/aws_sagemaker_model/client"

# Destination
require_relative "integrations/destination/klaviyo/client"
Expand All @@ -78,6 +84,8 @@
require_relative "integrations/destination/iterable/client"
require_relative "integrations/destination/maria_db/client"
require_relative "integrations/destination/databricks_lakehouse/client"
require_relative "integrations/destination/oracle_db/client"
require_relative "integrations/destination/microsoft_excel/client"

module Multiwoven
module Integrations
Expand Down
11 changes: 10 additions & 1 deletion integrations/lib/multiwoven/integrations/core/base_connector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,16 @@ def success_status
end

def failure_status(error)
ConnectionStatus.new(status: ConnectionStatusType["failed"], message: error.message).to_multiwoven_message
message = error&.message || "failed"
ConnectionStatus.new(status: ConnectionStatusType["failed"], message: message).to_multiwoven_message
end

def auth_headers(access_token)
{
"Accept" => "application/json",
"Authorization" => "Bearer #{access_token}",
"Content-Type" => "application/json"
}
end
end
end
Expand Down
16 changes: 14 additions & 2 deletions integrations/lib/multiwoven/integrations/core/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,26 @@ module Constants
AIRTABLE_BASES_ENDPOINT = "https://api.airtable.com/v0/meta/bases"
AIRTABLE_GET_BASE_SCHEMA_ENDPOINT = "https://api.airtable.com/v0/meta/bases/{baseId}/tables"

AWS_ACCESS_KEY_ID = ENV["AWS_ACCESS_KEY_ID"]
AWS_SECRET_ACCESS_KEY = ENV["AWS_SECRET_ACCESS_KEY"]
MS_EXCEL_AUTH_ENDPOINT = "https://graph.microsoft.com/v1.0/me"
MS_EXCEL_TABLE_ROW_WRITE_API = "https://graph.microsoft.com/v1.0/drives/%<drive_id>s/items/%<item_id>s/"\
"workbook/worksheets/%<sheet_name>s/tables/%<table_name>s/rows"
MS_EXCEL_TABLE_API = "https://graph.microsoft.com/v1.0/drives/%<drive_id>s/items/%<item_id>s/workbook/"\
"worksheets/%<sheet_name>s/tables?$select=name"
MS_EXCEL_FILES_API = "https://graph.microsoft.com/v1.0/drives/%<drive_id>s/root/children"
MS_EXCEL_WORKSHEETS_API = "https://graph.microsoft.com/v1.0/drives/%<drive_id>s/items/%<item_id>s/"\
"workbook/worksheets"
MS_EXCEL_SHEET_RANGE_API = "https://graph.microsoft.com/v1.0/drives/%<drive_id>s/items/%<item_id>s/"\
"workbook/worksheets/%<sheet_name>s/range(address='A1:Z1')/usedRange?$select=values"

DATABRICKS_HEALTH_URL = "https://%<databricks_host>s/api/2.0/serving-endpoints/%<endpoint_name>s"
DATABRICKS_SERVING_URL = "https://%<databricks_host>s/serving-endpoints/%<endpoint_name>s/invocations"

# HTTP
HTTP_GET = "GET"
HTTP_POST = "POST"
HTTP_PUT = "PUT"
HTTP_DELETE = "DELETE"
HTTP_PATCH = "PATCH"

# google sheets
GOOGLE_SHEETS_SCOPE = "https://www.googleapis.com/auth/drive"
Expand Down
3 changes: 2 additions & 1 deletion integrations/lib/multiwoven/integrations/core/http_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ def build_request(method, uri, payload, headers)
when Constants::HTTP_GET then Net::HTTP::Get
when Constants::HTTP_POST then Net::HTTP::Post
when Constants::HTTP_PUT then Net::HTTP::Put
when Constants::HTTP_PATCH then Net::HTTP::Patch
when Constants::HTTP_DELETE then Net::HTTP::Delete
else raise ArgumentError, "Unsupported HTTP method: #{method}"
end

request = request_class.new(uri)
headers.each { |key, value| request[key] = value }
request.body = payload.to_json if payload && %w[POST PUT].include?(method.upcase)
request.body = payload.to_json if payload && %w[POST PUT PATCH].include?(method.upcase)
request
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,12 @@ def write(sync_config, records, _action = "create")
connection_config = sync_config.destination.connection_specification.with_indifferent_access
api_key = connection_config[:api_key]
url = sync_config.stream.url
log_message_array = []
write_success = 0
write_failure = 0
records.each_slice(MAX_CHUNK_SIZE) do |chunk|
payload = create_payload(chunk)
args = [sync_config.stream.request_method, url, payload]
response = Multiwoven::Integrations::Core::HttpClient.request(
url,
sync_config.stream.request_method,
Expand All @@ -74,6 +76,7 @@ def write(sync_config, records, _action = "create")
else
write_failure += chunk.size
end
log_message_array << log_request_response("info", args, response)
rescue StandardError => e
handle_exception(e, {
context: "AIRTABLE:RECORD:WRITE:EXCEPTION",
Expand All @@ -82,13 +85,9 @@ def write(sync_config, records, _action = "create")
sync_run_id: sync_config.sync_run_id
})
write_failure += chunk.size
log_message_array << log_request_response("error", args, e.message)
end

tracker = Multiwoven::Integrations::Protocol::TrackingMessage.new(
success: write_success,
failed: write_failure
)
tracker.to_multiwoven_message
tracking_message(write_success, write_failure, log_message_array)
rescue StandardError => e
handle_exception(e, {
context: "AIRTABLE:RECORD:WRITE:EXCEPTION",
Expand All @@ -110,16 +109,8 @@ def create_payload(records)
}
end

def auth_headers(access_token)
{
"Accept" => "application/json",
"Authorization" => "Bearer #{access_token}",
"Content-Type" => "application/json"
}
end

def base_id_exists?(bases, base_id)
return if extract_data(bases).any? { |base| base["id"] == base_id }
return if extract_bases(bases).any? { |base| base["id"] == base_id }

raise ArgumentError, "base_id not found"
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"data": {
"name": "Airtable",
"title": "airtable",
"title": "Airtable",
"connector_type": "destination",
"category": "Productivity Tools",
"documentation_url": "https://docs.multiwoven.com/destinations/productivity-tools/airtable",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"data": {
"name": "DatabricksLakehouse",
"title": "Databricks Lakehouse",
"title": "Databricks Data Warehouse",
"connector_type": "destination",
"category": "Marketing Automation",
"category": "Database",
"documentation_url": "https://docs.multiwoven.com/destinations/databricks_lakehouse",
"github_issue_label": "destination-databricks-lakehouse",
"icon": "icon.svg",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"stream_type": "static",
"connection_specification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Databricks Lakehouse",
"title": "Databricks Datawarehouse",
"type": "object",
"required": ["host", "api_token", "warehouse_id", "catalog", "schema"],
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,6 @@ def extract_schema_and_data(records, json_schema)
[schema, data]
end

def auth_headers(access_token)
{
"Accept" => "application/json",
"Authorization" => "Bearer #{access_token}",
"Content-Type" => "application/json"
}
end

def ad_account_exists?(response, ad_account_id)
return if extract_data(response).any? { |ad_account| ad_account["id"] == "act_#{ad_account_id}" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "FacebookCustomAudience",
"title": "Facebook Custom Audiences",
"connector_type": "destination",
"category": "Adtech",
"category": "Ad-Tech",
"documentation_url": "https://docs.mutliwoven.com",
"github_issue_label": "destination-facebook",
"icon": "icon.svg",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,15 @@ def extract_spreadsheet_id(link)

# Batch has a limit of sending 2MB data. So creating a chunk of records to meet that limit
def process_record_chunks(records, sync_config)
log_message_array = []
write_success = 0
write_failure = 0

records.each_slice(MAX_CHUNK_SIZE) do |chunk|
values = prepare_chunk_values(chunk, sync_config.stream)
update_sheet_values(values, sync_config.stream.name)
request, response = *update_sheet_values(values, sync_config.stream.name)
write_success += values.size
log_message_array << log_request_response("info", request, response)
rescue StandardError => e
handle_exception(e, {
context: "GOOGLE_SHEETS:RECORD:WRITE:EXCEPTION",
Expand All @@ -168,9 +170,9 @@ def process_record_chunks(records, sync_config)
sync_run_id: sync_config.sync_run_id
})
write_failure += chunk.size
log_message_array << log_request_response("error", request, e.message)
end

tracking_message(write_success, write_failure)
tracking_message(write_success, write_failure, log_message_array)
end

# We need to format the data to adhere to google sheets API format. This converts the sync mapped data to 2D array format expected by google sheets API
Expand Down Expand Up @@ -199,19 +201,14 @@ def update_sheet_values(values, stream_name)
)

# TODO: Remove & this is added for the test to pass we need
@client&.batch_update_values(@spreadsheet_id, batch_update_request)
response = @client&.batch_update_values(@spreadsheet_id, batch_update_request)
[batch_update_request, response]
end

def load_catalog
read_json(CATALOG_SPEC_PATH)
end

def tracking_message(success, failure)
Multiwoven::Integrations::Protocol::TrackingMessage.new(
success: success, failed: failure
).to_multiwoven_message
end

def delete_extra_sheets(sheet_ids)
# Leave one sheet intact as a spreadsheet must have at least one sheet.
# Delete all other sheets.
Expand Down
Loading

0 comments on commit 2accf47

Please sign in to comment.