etl-integrations 0.1.81
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +34 -0
- data/.ruby-version +1 -0
- data/.vscode/settings.json +5 -0
- data/CHANGELOG.md +38 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +105 -0
- data/Rakefile +12 -0
- data/lib/multiwoven/integrations/config.rb +13 -0
- data/lib/multiwoven/integrations/core/base_connector.rb +70 -0
- data/lib/multiwoven/integrations/core/constants.rb +46 -0
- data/lib/multiwoven/integrations/core/destination_connector.rb +14 -0
- data/lib/multiwoven/integrations/core/fullrefresher.rb +19 -0
- data/lib/multiwoven/integrations/core/http_client.rb +34 -0
- data/lib/multiwoven/integrations/core/query_builder.rb +27 -0
- data/lib/multiwoven/integrations/core/rate_limiter.rb +19 -0
- data/lib/multiwoven/integrations/core/source_connector.rb +38 -0
- data/lib/multiwoven/integrations/core/utils.rb +104 -0
- data/lib/multiwoven/integrations/destination/airtable/client.rb +153 -0
- data/lib/multiwoven/integrations/destination/airtable/config/catalog.json +6 -0
- data/lib/multiwoven/integrations/destination/airtable/config/meta.json +15 -0
- data/lib/multiwoven/integrations/destination/airtable/config/spec.json +22 -0
- data/lib/multiwoven/integrations/destination/airtable/icon.svg +6 -0
- data/lib/multiwoven/integrations/destination/airtable/schema_helper.rb +141 -0
- data/lib/multiwoven/integrations/destination/facebook_custom_audience/client.rb +124 -0
- data/lib/multiwoven/integrations/destination/facebook_custom_audience/config/catalog.json +42 -0
- data/lib/multiwoven/integrations/destination/facebook_custom_audience/config/meta.json +15 -0
- data/lib/multiwoven/integrations/destination/facebook_custom_audience/config/spec.json +27 -0
- data/lib/multiwoven/integrations/destination/facebook_custom_audience/icon.svg +23 -0
- data/lib/multiwoven/integrations/destination/google_sheets/client.rb +231 -0
- data/lib/multiwoven/integrations/destination/google_sheets/config/catalog.json +6 -0
- data/lib/multiwoven/integrations/destination/google_sheets/config/meta.json +15 -0
- data/lib/multiwoven/integrations/destination/google_sheets/config/spec.json +74 -0
- data/lib/multiwoven/integrations/destination/google_sheets/icon.svg +1 -0
- data/lib/multiwoven/integrations/destination/hubspot/client.rb +107 -0
- data/lib/multiwoven/integrations/destination/hubspot/config/catalog.json +351 -0
- data/lib/multiwoven/integrations/destination/hubspot/config/meta.json +15 -0
- data/lib/multiwoven/integrations/destination/hubspot/config/spec.json +17 -0
- data/lib/multiwoven/integrations/destination/hubspot/icon.svg +5 -0
- data/lib/multiwoven/integrations/destination/klaviyo/client.rb +116 -0
- data/lib/multiwoven/integrations/destination/klaviyo/config/catalog.json +103 -0
- data/lib/multiwoven/integrations/destination/klaviyo/config/meta.json +15 -0
- data/lib/multiwoven/integrations/destination/klaviyo/config/spec.json +22 -0
- data/lib/multiwoven/integrations/destination/klaviyo/icon.svg +6 -0
- data/lib/multiwoven/integrations/destination/postgresql/client.rb +123 -0
- data/lib/multiwoven/integrations/destination/postgresql/config/meta.json +15 -0
- data/lib/multiwoven/integrations/destination/postgresql/config/spec.json +68 -0
- data/lib/multiwoven/integrations/destination/postgresql/icon.svg +20 -0
- data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/client.rb +114 -0
- data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/config/catalog.json +6 -0
- data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/config/meta.json +16 -0
- data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/config/spec.json +49 -0
- data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/icon.svg +16 -0
- data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/schema_helper.rb +132 -0
- data/lib/multiwoven/integrations/destination/salesforce_crm/client.rb +117 -0
- data/lib/multiwoven/integrations/destination/salesforce_crm/config/catalog.json +320 -0
- data/lib/multiwoven/integrations/destination/salesforce_crm/config/meta.json +15 -0
- data/lib/multiwoven/integrations/destination/salesforce_crm/config/spec.json +43 -0
- data/lib/multiwoven/integrations/destination/salesforce_crm/icon.svg +16 -0
- data/lib/multiwoven/integrations/destination/sftp/client.rb +133 -0
- data/lib/multiwoven/integrations/destination/sftp/config/catalog.json +16 -0
- data/lib/multiwoven/integrations/destination/sftp/config/meta.json +16 -0
- data/lib/multiwoven/integrations/destination/sftp/config/spec.json +50 -0
- data/lib/multiwoven/integrations/destination/sftp/icon.svg +1 -0
- data/lib/multiwoven/integrations/destination/slack/client.rb +114 -0
- data/lib/multiwoven/integrations/destination/slack/config/catalog.json +22 -0
- data/lib/multiwoven/integrations/destination/slack/config/meta.json +15 -0
- data/lib/multiwoven/integrations/destination/slack/config/spec.json +22 -0
- data/lib/multiwoven/integrations/destination/slack/icon.svg +26 -0
- data/lib/multiwoven/integrations/destination/stripe/client.rb +82 -0
- data/lib/multiwoven/integrations/destination/stripe/config/catalog.json +128 -0
- data/lib/multiwoven/integrations/destination/stripe/config/meta.json +15 -0
- data/lib/multiwoven/integrations/destination/stripe/config/spec.json +17 -0
- data/lib/multiwoven/integrations/destination/stripe/icon.svg +10 -0
- data/lib/multiwoven/integrations/destination/tally/client.rb +151 -0
- data/lib/multiwoven/integrations/destination/tally/config/catalog.json +62 -0
- data/lib/multiwoven/integrations/destination/tally/config/meta.json +15 -0
- data/lib/multiwoven/integrations/destination/tally/config/spec.json +45 -0
- data/lib/multiwoven/integrations/destination/tally/icon.svg +7 -0
- data/lib/multiwoven/integrations/protocol/protocol.json +189 -0
- data/lib/multiwoven/integrations/protocol/protocol.rb +216 -0
- data/lib/multiwoven/integrations/rollout.rb +32 -0
- data/lib/multiwoven/integrations/service.rb +79 -0
- data/lib/multiwoven/integrations/source/bigquery/client.rb +98 -0
- data/lib/multiwoven/integrations/source/bigquery/config/meta.json +15 -0
- data/lib/multiwoven/integrations/source/bigquery/config/spec.json +82 -0
- data/lib/multiwoven/integrations/source/bigquery/icon.svg +1 -0
- data/lib/multiwoven/integrations/source/databricks/client.rb +98 -0
- data/lib/multiwoven/integrations/source/databricks/config/meta.json +16 -0
- data/lib/multiwoven/integrations/source/databricks/config/spec.json +56 -0
- data/lib/multiwoven/integrations/source/databricks/icon.svg +19 -0
- data/lib/multiwoven/integrations/source/postgresql/client.rb +109 -0
- data/lib/multiwoven/integrations/source/postgresql/config/meta.json +15 -0
- data/lib/multiwoven/integrations/source/postgresql/config/spec.json +69 -0
- data/lib/multiwoven/integrations/source/postgresql/icon.svg +20 -0
- data/lib/multiwoven/integrations/source/redshift/client.rb +109 -0
- data/lib/multiwoven/integrations/source/redshift/config/meta.json +15 -0
- data/lib/multiwoven/integrations/source/redshift/config/spec.json +71 -0
- data/lib/multiwoven/integrations/source/redshift/icon.svg +15 -0
- data/lib/multiwoven/integrations/source/salesforce_consumer_goods_cloud/client.rb +123 -0
- data/lib/multiwoven/integrations/source/salesforce_consumer_goods_cloud/config/catalog.json +6 -0
- data/lib/multiwoven/integrations/source/salesforce_consumer_goods_cloud/config/meta.json +17 -0
- data/lib/multiwoven/integrations/source/salesforce_consumer_goods_cloud/config/spec.json +50 -0
- data/lib/multiwoven/integrations/source/salesforce_consumer_goods_cloud/icon.svg +16 -0
- data/lib/multiwoven/integrations/source/salesforce_consumer_goods_cloud/schema_helper.rb +130 -0
- data/lib/multiwoven/integrations/source/snowflake/client.rb +92 -0
- data/lib/multiwoven/integrations/source/snowflake/config/meta.json +15 -0
- data/lib/multiwoven/integrations/source/snowflake/config/spec.json +82 -0
- data/lib/multiwoven/integrations/source/snowflake/icon.svg +10 -0
- data/lib/multiwoven/integrations/source/zoho_books/client.rb +155 -0
- data/lib/multiwoven/integrations/source/zoho_books/config/meta.json +16 -0
- data/lib/multiwoven/integrations/source/zoho_books/config/spec.json +43 -0
- data/lib/multiwoven/integrations/source/zoho_books/icon.svg +16 -0
- data/lib/multiwoven/integrations.rb +71 -0
- data/multiwoven-integrations-0.1.68.gem +0 -0
- data/sig/multiwoven/integrations.rbs +6 -0
- metadata +515 -0
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "stringio"
|
4
|
+
require "net/http"
|
5
|
+
require "uri"
|
6
|
+
require "json"
|
7
|
+
require 'builder' # Add builder gem for XML generation
|
8
|
+
|
9
|
+
module Multiwoven
|
10
|
+
module Integrations
|
11
|
+
module Destination
|
12
|
+
module Tally
|
13
|
+
include Multiwoven::Integrations::Core
|
14
|
+
|
15
|
+
API_VERSION = "1.0"
|
16
|
+
|
17
|
+
class Client < DestinationConnector
|
18
|
+
prepend Multiwoven::Integrations::Core::RateLimiter
|
19
|
+
|
20
|
+
def check_connection(connection_config)
|
21
|
+
connection_config = connection_config.with_indifferent_access
|
22
|
+
initialize_client(connection_config)
|
23
|
+
authenticate_client
|
24
|
+
success_status
|
25
|
+
rescue StandardError => e
|
26
|
+
failure_status(e)
|
27
|
+
end
|
28
|
+
|
29
|
+
def discover(_connection_config = nil)
|
30
|
+
catalog = build_catalog(load_catalog)
|
31
|
+
catalog.to_multiwoven_message
|
32
|
+
rescue StandardError => e
|
33
|
+
handle_exception("TALLY:DISCOVER:EXCEPTION", "error", e)
|
34
|
+
end
|
35
|
+
|
36
|
+
def write(sync_config, records, action = "create")
|
37
|
+
@action = sync_config.stream.action || action
|
38
|
+
initialize_client(sync_config.destination.connection_specification)
|
39
|
+
process_records(records, sync_config.stream)
|
40
|
+
rescue StandardError => e
|
41
|
+
handle_exception("TALLY:WRITE:EXCEPTION", "error", e)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def initialize_client(config)
|
47
|
+
config = config.with_indifferent_access
|
48
|
+
@tally_url = config[:tally_url]
|
49
|
+
@company_name = config[:company_name]
|
50
|
+
end
|
51
|
+
|
52
|
+
def process_records(records, stream)
|
53
|
+
write_success = 0
|
54
|
+
write_failure = 0
|
55
|
+
properties = stream.json_schema[:properties]
|
56
|
+
records.each do |record_object|
|
57
|
+
record = extract_data(record_object, properties)
|
58
|
+
xml_payload = convert_to_xml(record) # Convert record to XML
|
59
|
+
process_record(xml_payload)
|
60
|
+
write_success += 1
|
61
|
+
rescue StandardError => e
|
62
|
+
handle_exception("TALLY:WRITE:EXCEPTION", "error", e)
|
63
|
+
write_failure += 1
|
64
|
+
end
|
65
|
+
tracking_message(write_success, write_failure)
|
66
|
+
end
|
67
|
+
|
68
|
+
def process_record(xml_payload)
|
69
|
+
send_data_to_tally(xml_payload)
|
70
|
+
end
|
71
|
+
|
72
|
+
def send_data_to_tally(xml_payload = {})
|
73
|
+
uri = URI.parse("#{@tally_url}/#{@company_name}")
|
74
|
+
request = Net::HTTP::Post.new(uri)
|
75
|
+
request.content_type = 'application/xml' # Tally uses XML format
|
76
|
+
request.body = xml_payload
|
77
|
+
response = Net::HTTP.start(uri.hostname, uri.port) { |http| http.request(request) }
|
78
|
+
|
79
|
+
raise StandardError, response.body unless response.is_a?(Net::HTTPSuccess)
|
80
|
+
end
|
81
|
+
|
82
|
+
def convert_to_xml(record)
|
83
|
+
# Using Builder gem to create XML structure
|
84
|
+
xml = Builder::XmlMarkup.new
|
85
|
+
xml.instruct! :xml, version: '1.0', encoding: 'UTF-8'
|
86
|
+
xml.ENVELOPE do
|
87
|
+
xml.HEADER do
|
88
|
+
xml.TALLYREQUEST 'Import Data'
|
89
|
+
end
|
90
|
+
xml.BODY do
|
91
|
+
xml.IMPORTDATA do
|
92
|
+
xml.REQUESTDESC do
|
93
|
+
xml.REPORTNAME 'Vouchers'
|
94
|
+
xml.STATICVARIABLES do
|
95
|
+
xml.SVCURRENTCOMPANY @company_name
|
96
|
+
end
|
97
|
+
end
|
98
|
+
xml.REQUESTDATA do
|
99
|
+
xml.TALLYMESSAGE('xmlns:UDF' => 'TallyUDF') do
|
100
|
+
xml.VOUCHER do
|
101
|
+
xml.DATE '20240915' # Static or dynamic date
|
102
|
+
xml.VOUCHERTYPENAME 'Sales'
|
103
|
+
xml.NARRATION 'Test invoice from ETL Tool'
|
104
|
+
xml.VOUCHERNUMBER record[:VOUCHERNUMBER] || 'INV-1002'
|
105
|
+
xml.PARTYNAME record[:PARTYNAME] || 'XYZ Ltd'
|
106
|
+
xml.AMOUNT record[:AMOUNT] || 10000
|
107
|
+
xml.LEDGERENTRIES__LIST do
|
108
|
+
xml.LEDGERNAME 'Sales Account'
|
109
|
+
xml.AMOUNT record[:AMOUNT] || 10000
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
xml.target! # Returns the generated XML string
|
118
|
+
end
|
119
|
+
|
120
|
+
def authenticate_client
|
121
|
+
# Tally may not need specific authentication steps like OAuth;
|
122
|
+
# skipping authentication or implementing basic connection validation.
|
123
|
+
true
|
124
|
+
end
|
125
|
+
|
126
|
+
def success_status
|
127
|
+
ConnectionStatus.new(status: ConnectionStatusType["succeeded"]).to_multiwoven_message
|
128
|
+
end
|
129
|
+
|
130
|
+
def failure_status(error)
|
131
|
+
ConnectionStatus.new(status: ConnectionStatusType["failed"], message: error.message).to_multiwoven_message
|
132
|
+
end
|
133
|
+
|
134
|
+
def load_catalog
|
135
|
+
read_json(CATALOG_SPEC_PATH)
|
136
|
+
end
|
137
|
+
|
138
|
+
def tracking_message(success, failure)
|
139
|
+
Multiwoven::Integrations::Protocol::TrackingMessage.new(
|
140
|
+
success: success, failed: failure
|
141
|
+
).to_multiwoven_message
|
142
|
+
end
|
143
|
+
|
144
|
+
def log_debug(message)
|
145
|
+
Multiwoven::Integrations::Service.logger.debug(message)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
{
|
2
|
+
"request_rate_limit": 100000,
|
3
|
+
"request_rate_limit_unit": "day",
|
4
|
+
"request_rate_concurrency": 10,
|
5
|
+
"streams": [
|
6
|
+
{
|
7
|
+
"name": "Voucher",
|
8
|
+
"action": "create",
|
9
|
+
"json_schema": {
|
10
|
+
"type": "object",
|
11
|
+
"additionalProperties": true,
|
12
|
+
"properties": {
|
13
|
+
"VoucherNumber": {
|
14
|
+
"type": "string"
|
15
|
+
},
|
16
|
+
"VoucherType": {
|
17
|
+
"type": "string",
|
18
|
+
"enum": [
|
19
|
+
"Payment",
|
20
|
+
"Receipt",
|
21
|
+
"Journal",
|
22
|
+
"Contra",
|
23
|
+
"Sales",
|
24
|
+
"Purchase",
|
25
|
+
"Credit Note",
|
26
|
+
"Debit Note"
|
27
|
+
]
|
28
|
+
},
|
29
|
+
"Date": {
|
30
|
+
"type": "string",
|
31
|
+
"format": "date"
|
32
|
+
},
|
33
|
+
"LedgerName": {
|
34
|
+
"type": "string"
|
35
|
+
},
|
36
|
+
"Amount": {
|
37
|
+
"type": "number"
|
38
|
+
},
|
39
|
+
"Narration": {
|
40
|
+
"type": ["string", "null"]
|
41
|
+
},
|
42
|
+
"PartyLedgerName": {
|
43
|
+
"type": ["string", "null"]
|
44
|
+
},
|
45
|
+
"InvoiceNumber": {
|
46
|
+
"type": ["string", "null"]
|
47
|
+
},
|
48
|
+
"CostCenter": {
|
49
|
+
"type": ["string", "null"]
|
50
|
+
},
|
51
|
+
"CostCategory": {
|
52
|
+
"type": ["string", "null"]
|
53
|
+
}
|
54
|
+
}
|
55
|
+
},
|
56
|
+
"supported_sync_modes": ["incremental"],
|
57
|
+
"source_defined_cursor": true,
|
58
|
+
"default_cursor_field": ["Date"],
|
59
|
+
"source_defined_primary_key": [["VoucherNumber"]]
|
60
|
+
}
|
61
|
+
]
|
62
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
{
|
2
|
+
"data": {
|
3
|
+
"name": "Tally",
|
4
|
+
"title": "Tally",
|
5
|
+
"connector_type": "destination",
|
6
|
+
"category": "Accounting",
|
7
|
+
"documentation_url": "https://docs.multiwoven.com/tally",
|
8
|
+
"github_issue_label": "destination-tally",
|
9
|
+
"icon": "icon.svg",
|
10
|
+
"license": "MIT",
|
11
|
+
"release_stage": "alpha",
|
12
|
+
"support_level": "community",
|
13
|
+
"tags": ["language:ruby", "multiwoven", "accounting", "tally"]
|
14
|
+
}
|
15
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
{
|
2
|
+
"documentation_url": "https://docs.multiwoven.com/integrations/destination/tally",
|
3
|
+
"stream_type": "static",
|
4
|
+
"connection_specification": {
|
5
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
6
|
+
"title": "Tally",
|
7
|
+
"type": "object",
|
8
|
+
"required": [
|
9
|
+
"server_url",
|
10
|
+
"company_name"
|
11
|
+
],
|
12
|
+
"properties": {
|
13
|
+
"server_url": {
|
14
|
+
"type": "string",
|
15
|
+
"title": "Server URL",
|
16
|
+
"order": 0,
|
17
|
+
"description": "The URL or IP address of the Tally server."
|
18
|
+
},
|
19
|
+
"company_name": {
|
20
|
+
"type": "string",
|
21
|
+
"title": "Company Name",
|
22
|
+
"order": 1,
|
23
|
+
"description": "The company name in Tally to connect with."
|
24
|
+
},
|
25
|
+
"license_key": {
|
26
|
+
"type": "string",
|
27
|
+
"title": "Tally License Key",
|
28
|
+
"order": 2,
|
29
|
+
"description": "Tally license key (if applicable)."
|
30
|
+
},
|
31
|
+
"username": {
|
32
|
+
"type": "string",
|
33
|
+
"title": "Tally Username",
|
34
|
+
"order": 3,
|
35
|
+
"description": "Username for authentication (if required)."
|
36
|
+
},
|
37
|
+
"password": {
|
38
|
+
"type": "string",
|
39
|
+
"title": "Tally Password",
|
40
|
+
"order": 4,
|
41
|
+
"description": "Password for authentication (if required)."
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
@@ -0,0 +1,7 @@
|
|
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" fill="none" xmlns="http://www.w3.org/2000/svg">
|
3
|
+
<path d="M3.661 2.67105C3.71317 2.39988 3.97931 2.18005 4.25546 2.18005H6.25546C6.5316 2.18005 6.71317 2.39988 6.661 2.67105L5.33853 9.54499C5.28636 9.81616 5.02021 10.036 4.74406 10.036H2.74406C2.46792 10.036 2.28636 9.81616 2.33853 9.54499L3.661 2.67105Z" fill="#000000"/>
|
4
|
+
<path d="M8.661 2.67105C8.71317 2.39988 8.97931 2.18005 9.25546 2.18005H11.2555C11.5316 2.18005 11.7132 2.39988 11.661 2.67105L8.07144 21.3289C8.01927 21.6001 7.75312 21.8199 7.47698 21.8199H5.47698C5.20083 21.8199 5.01927 21.6001 5.07144 21.3289L8.661 2.67105Z" fill="#000000"/>
|
5
|
+
<path d="M13.661 2.67105C13.7132 2.39988 13.9793 2.18005 14.2555 2.18005H16.2555C16.5316 2.18005 16.7132 2.39988 16.661 2.67105L13.0714 21.3289C13.0193 21.6001 12.7531 21.8199 12.477 21.8199H10.477C10.2008 21.8199 10.0193 21.6001 10.0714 21.3289L13.661 2.67105Z" fill="#000000"/>
|
6
|
+
<path d="M18.661 2.67105C18.7132 2.39988 18.9793 2.18005 19.2555 2.18005H21.2555C21.5316 2.18005 21.7132 2.39988 21.661 2.67105L20.3385 9.54499C20.2864 9.81616 20.0202 10.036 19.7441 10.036H17.7441C17.4679 10.036 17.2864 9.81616 17.3385 9.54499L18.661 2.67105Z" fill="#000000"/>
|
7
|
+
</svg>
|
@@ -0,0 +1,189 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
3
|
+
"title": "Mutliwoven Protocol",
|
4
|
+
"type": "object",
|
5
|
+
"description": "Mutliwoven protocol schema",
|
6
|
+
"version": "1.0.0",
|
7
|
+
"definitions": {
|
8
|
+
"SyncMode": {
|
9
|
+
"type": "string",
|
10
|
+
"enum": [
|
11
|
+
"full_refresh",
|
12
|
+
"incremental"
|
13
|
+
]
|
14
|
+
},
|
15
|
+
"SyncStatus": {
|
16
|
+
"type": "string",
|
17
|
+
"enum": [
|
18
|
+
"STARTED",
|
19
|
+
"RUNNING",
|
20
|
+
"COMPLETE",
|
21
|
+
"INCOMPLETE"
|
22
|
+
]
|
23
|
+
},
|
24
|
+
"DestinationSyncMode": {
|
25
|
+
"type": "string",
|
26
|
+
"enum": [
|
27
|
+
"append",
|
28
|
+
"overwrite",
|
29
|
+
"append_dedup"
|
30
|
+
]
|
31
|
+
},
|
32
|
+
"ProtocolModel": {
|
33
|
+
"type": "object",
|
34
|
+
"additionalProperties": true
|
35
|
+
},
|
36
|
+
"ConnectionStatus": {
|
37
|
+
"type": "object",
|
38
|
+
"properties": {
|
39
|
+
"status": {
|
40
|
+
"$ref": "#/definitions/Types/String.enum(SUCCEEDED,FAILED)"
|
41
|
+
},
|
42
|
+
"message": {
|
43
|
+
"type": "string"
|
44
|
+
}
|
45
|
+
},
|
46
|
+
"additionalProperties": true
|
47
|
+
},
|
48
|
+
"ConnectorSpecification": {
|
49
|
+
"type": "object",
|
50
|
+
"properties": {
|
51
|
+
"documentation_url": {
|
52
|
+
"type": "string"
|
53
|
+
},
|
54
|
+
"changelog_url": {
|
55
|
+
"type": "string"
|
56
|
+
},
|
57
|
+
"connection_specification": {
|
58
|
+
"type": "object"
|
59
|
+
},
|
60
|
+
"supports_normalization": {
|
61
|
+
"type": "boolean",
|
62
|
+
"default": false
|
63
|
+
},
|
64
|
+
"supports_dbt": {
|
65
|
+
"type": "boolean",
|
66
|
+
"default": false
|
67
|
+
},
|
68
|
+
"supported_destination_sync_modes": {
|
69
|
+
"type": "array",
|
70
|
+
"items": {
|
71
|
+
"$ref": "#/definitions/DestinationSyncMode"
|
72
|
+
}
|
73
|
+
}
|
74
|
+
},
|
75
|
+
"additionalProperties": true
|
76
|
+
},
|
77
|
+
"LogMessage": {
|
78
|
+
"type": "object",
|
79
|
+
"properties": {
|
80
|
+
"level": {
|
81
|
+
"$ref": "#/definitions/Types/String.enum(FATAL,ERROR,WARN,INFO,DEBUG,TRACE)"
|
82
|
+
},
|
83
|
+
"message": {
|
84
|
+
"type": "string"
|
85
|
+
},
|
86
|
+
"stack_trace": {
|
87
|
+
"type": "string"
|
88
|
+
}
|
89
|
+
},
|
90
|
+
"additionalProperties": true
|
91
|
+
},
|
92
|
+
"RecordMessage": {
|
93
|
+
"type": "object",
|
94
|
+
"properties": {
|
95
|
+
"stream": {
|
96
|
+
"type": "string"
|
97
|
+
},
|
98
|
+
"data": {
|
99
|
+
"type": "object"
|
100
|
+
},
|
101
|
+
"emitted_at": {
|
102
|
+
"type": "integer"
|
103
|
+
}
|
104
|
+
},
|
105
|
+
"additionalProperties": true
|
106
|
+
},
|
107
|
+
"Stream": {
|
108
|
+
"type": "object",
|
109
|
+
"properties": {
|
110
|
+
"name": {
|
111
|
+
"type": "string"
|
112
|
+
},
|
113
|
+
"json_schema": {
|
114
|
+
"type": "object"
|
115
|
+
},
|
116
|
+
"supported_sync_modes": {
|
117
|
+
"type": "array",
|
118
|
+
"items": {
|
119
|
+
"$ref": "#/definitions/SyncMode"
|
120
|
+
}
|
121
|
+
},
|
122
|
+
"source_defined_cursor": {
|
123
|
+
"type": "boolean"
|
124
|
+
},
|
125
|
+
"default_cursor_field": {
|
126
|
+
"type": "array",
|
127
|
+
"items": {
|
128
|
+
"type": "string"
|
129
|
+
}
|
130
|
+
},
|
131
|
+
"source_defined_primary_key": {
|
132
|
+
"type": "array",
|
133
|
+
"items": {
|
134
|
+
"type": "array",
|
135
|
+
"items": {
|
136
|
+
"type": "string"
|
137
|
+
}
|
138
|
+
}
|
139
|
+
},
|
140
|
+
"namespace": {
|
141
|
+
"type": "string"
|
142
|
+
}
|
143
|
+
},
|
144
|
+
"additionalProperties": true
|
145
|
+
},
|
146
|
+
"Catalog": {
|
147
|
+
"type": "object",
|
148
|
+
"properties": {
|
149
|
+
"streams": {
|
150
|
+
"type": "array",
|
151
|
+
"items": {
|
152
|
+
"$ref": "#/definitions/Stream"
|
153
|
+
}
|
154
|
+
}
|
155
|
+
},
|
156
|
+
"additionalProperties": true
|
157
|
+
},
|
158
|
+
"SyncConfig": {
|
159
|
+
"type": "object",
|
160
|
+
"properties": {
|
161
|
+
"stream": {
|
162
|
+
"$ref": "#/definitions/Stream"
|
163
|
+
},
|
164
|
+
"sync_mode": {
|
165
|
+
"$ref": "#/definitions/SyncMode"
|
166
|
+
},
|
167
|
+
"cursor_field": {
|
168
|
+
"type": "array",
|
169
|
+
"items": {
|
170
|
+
"type": "string"
|
171
|
+
}
|
172
|
+
},
|
173
|
+
"destination_sync_mode": {
|
174
|
+
"$ref": "#/definitions/DestinationSyncMode"
|
175
|
+
},
|
176
|
+
"primary_key": {
|
177
|
+
"type": "array",
|
178
|
+
"items": {
|
179
|
+
"type": "array",
|
180
|
+
"items": {
|
181
|
+
"type": "string"
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
},
|
186
|
+
"additionalProperties": true
|
187
|
+
}
|
188
|
+
}
|
189
|
+
}
|
@@ -0,0 +1,216 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Multiwoven
|
4
|
+
module Integrations::Protocol
|
5
|
+
module Types
|
6
|
+
include Dry.Types()
|
7
|
+
end
|
8
|
+
|
9
|
+
SyncMode = Types::String.enum("full_refresh", "incremental")
|
10
|
+
SyncStatus = Types::String.enum("started", "running", "complete", "incomplete")
|
11
|
+
DestinationSyncMode = Types::String.enum("insert", "upsert")
|
12
|
+
ConnectorType = Types::String.enum("source", "destination")
|
13
|
+
ConnectorQueryType = Types::String.enum("raw_sql", "soql")
|
14
|
+
ModelQueryType = Types::String.enum("raw_sql", "dbt", "soql")
|
15
|
+
ConnectionStatusType = Types::String.enum("succeeded", "failed")
|
16
|
+
StreamType = Types::String.enum("static", "dynamic")
|
17
|
+
StreamAction = Types::String.enum("fetch", "create", "update", "delete")
|
18
|
+
MultiwovenMessageType = Types::String.enum(
|
19
|
+
"record", "log", "connector_spec",
|
20
|
+
"connection_status", "catalog", "control",
|
21
|
+
"tracking"
|
22
|
+
)
|
23
|
+
ControlMessageType = Types::String.enum(
|
24
|
+
"rate_limit", "connection_config", "full_refresh"
|
25
|
+
)
|
26
|
+
LogLevel = Types::String.enum("fatal", "error", "warn", "info", "debug", "trace")
|
27
|
+
RequestRateLimitingUnit = Types::String.default("minute").enum("minute", "hour", "day")
|
28
|
+
SchemaMode = Types::String.enum("schema", "schemaless")
|
29
|
+
|
30
|
+
class ProtocolModel < Dry::Struct
|
31
|
+
extend Multiwoven::Integrations::Core::Utils
|
32
|
+
class << self
|
33
|
+
def from_json(json_string)
|
34
|
+
data = JSON.parse(json_string)
|
35
|
+
new(keys_to_symbols(data))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class ConnectionStatus < ProtocolModel
|
41
|
+
attribute :status, ConnectionStatusType
|
42
|
+
attribute? :message, Types::String.optional
|
43
|
+
|
44
|
+
def to_multiwoven_message
|
45
|
+
MultiwovenMessage.new(
|
46
|
+
type: MultiwovenMessageType["connection_status"],
|
47
|
+
connection_status: self
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class ConnectorSpecification < ProtocolModel
|
53
|
+
attribute? :documentation_url, Types::String.optional
|
54
|
+
attribute? :changelog_url, Types::String.optional
|
55
|
+
attribute :connection_specification, Types::Hash
|
56
|
+
attribute :supports_normalization, Types::Bool.default(false)
|
57
|
+
attribute :supports_dbt, Types::Bool.default(false)
|
58
|
+
attribute :stream_type, StreamType
|
59
|
+
attribute? :supported_destination_sync_modes, Types::Array.of(DestinationSyncMode).optional
|
60
|
+
attribute? :connector_query_type, ConnectorQueryType
|
61
|
+
|
62
|
+
def to_multiwoven_message
|
63
|
+
MultiwovenMessage.new(
|
64
|
+
type: MultiwovenMessageType["connector_spec"],
|
65
|
+
connector_spec: self
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Connector < ProtocolModel
|
71
|
+
attribute :name, Types::String
|
72
|
+
attribute :type, ConnectorType
|
73
|
+
attribute :connection_specification, Types::Hash
|
74
|
+
attribute :query_type, Types::String.default("raw_sql").enum(*ConnectorQueryType.values)
|
75
|
+
end
|
76
|
+
|
77
|
+
class LogMessage < ProtocolModel
|
78
|
+
attribute :level, LogLevel
|
79
|
+
attribute :message, Types::String
|
80
|
+
attribute? :name, Types::String.optional
|
81
|
+
attribute? :stack_trace, Types::String.optional
|
82
|
+
|
83
|
+
def to_multiwoven_message
|
84
|
+
MultiwovenMessage.new(
|
85
|
+
type: MultiwovenMessageType["log"],
|
86
|
+
log: self
|
87
|
+
)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Model < ProtocolModel
|
92
|
+
attribute? :name, Types::String.optional
|
93
|
+
attribute :query, Types::String
|
94
|
+
attribute :query_type, ModelQueryType
|
95
|
+
attribute :primary_key, Types::String
|
96
|
+
end
|
97
|
+
|
98
|
+
class RecordMessage < ProtocolModel
|
99
|
+
attribute :data, Types::Hash
|
100
|
+
attribute :emitted_at, Types::Integer
|
101
|
+
|
102
|
+
def to_multiwoven_message
|
103
|
+
MultiwovenMessage.new(
|
104
|
+
type: MultiwovenMessageType["record"],
|
105
|
+
record: self
|
106
|
+
)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class Stream < ProtocolModel
|
111
|
+
# Common
|
112
|
+
attribute :name, Types::String
|
113
|
+
attribute? :action, StreamAction
|
114
|
+
attribute :json_schema, Types::Hash
|
115
|
+
attribute? :supported_sync_modes, Types::Array.of(SyncMode).optional.default(["incremental"])
|
116
|
+
|
117
|
+
# Applicable for database streams
|
118
|
+
attribute :source_defined_cursor, Types::Bool.default(false)
|
119
|
+
attribute? :default_cursor_field, Types::Array.of(Types::String).optional
|
120
|
+
attribute? :source_defined_primary_key, Types::Array.of(Types::Array.of(Types::String)).optional
|
121
|
+
attribute? :namespace, Types::String.optional
|
122
|
+
# Applicable for API streams
|
123
|
+
attribute? :url, Types::String.optional
|
124
|
+
attribute? :request_method, Types::String.optional
|
125
|
+
attribute :batch_support, Types::Bool.default(false)
|
126
|
+
attribute :batch_size, Types::Integer.default(1)
|
127
|
+
# Rate limits
|
128
|
+
attribute? :request_rate_limit, Types::Integer
|
129
|
+
attribute? :request_rate_limit_unit, RequestRateLimitingUnit
|
130
|
+
attribute? :request_rate_concurrency, Types::Integer
|
131
|
+
|
132
|
+
def rate_limit_unit_seconds
|
133
|
+
case request_rate_limit_unit
|
134
|
+
when "minute"
|
135
|
+
60 # Seconds in a minute
|
136
|
+
when "hour"
|
137
|
+
3600 # Seconds in an hour
|
138
|
+
when "day"
|
139
|
+
86_400 # Seconds in a day
|
140
|
+
else
|
141
|
+
1 # Default case, consider as seconds or handle as error
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class Catalog < ProtocolModel
|
147
|
+
attribute :streams, Types::Array.of(Stream)
|
148
|
+
|
149
|
+
# Rate limits
|
150
|
+
attribute? :request_rate_limit, Types::Integer.default(60)
|
151
|
+
attribute? :request_rate_limit_unit, RequestRateLimitingUnit
|
152
|
+
attribute? :request_rate_concurrency, Types::Integer.default(10)
|
153
|
+
attribute? :schema_mode, Types::String.optional.default("schema")
|
154
|
+
attribute :source_defined_cursor, Types::Bool.default(false)
|
155
|
+
attribute? :default_cursor_field, Types::Array.of(Types::String).optional
|
156
|
+
|
157
|
+
def to_multiwoven_message
|
158
|
+
MultiwovenMessage.new(
|
159
|
+
type: MultiwovenMessageType["catalog"],
|
160
|
+
catalog: self
|
161
|
+
)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
class SyncConfig < ProtocolModel
|
166
|
+
attr_accessor :offset, :limit
|
167
|
+
|
168
|
+
attribute :source, Connector
|
169
|
+
attribute :destination, Connector
|
170
|
+
attribute :model, Model
|
171
|
+
attribute :stream, Stream
|
172
|
+
attribute :sync_mode, SyncMode
|
173
|
+
attribute? :cursor_field, Types::String.optional
|
174
|
+
attribute? :current_cursor_field, Types::String.optional
|
175
|
+
attribute :destination_sync_mode, DestinationSyncMode
|
176
|
+
end
|
177
|
+
|
178
|
+
class ControlMessage < ProtocolModel
|
179
|
+
attribute :type, ControlMessageType
|
180
|
+
attribute :emitted_at, Types::Integer
|
181
|
+
attribute? :status, ConnectionStatusType.optional
|
182
|
+
attribute? :meta, Types::Hash
|
183
|
+
|
184
|
+
def to_multiwoven_message
|
185
|
+
MultiwovenMessage.new(
|
186
|
+
type: MultiwovenMessageType["control"],
|
187
|
+
control: self
|
188
|
+
)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class TrackingMessage < ProtocolModel
|
193
|
+
attribute :success, Types::Integer.default(0)
|
194
|
+
attribute :failed, Types::Integer.default(0)
|
195
|
+
attribute? :meta, Types::Hash
|
196
|
+
|
197
|
+
def to_multiwoven_message
|
198
|
+
MultiwovenMessage.new(
|
199
|
+
type: MultiwovenMessageType["tracking"],
|
200
|
+
tracking: self
|
201
|
+
)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
class MultiwovenMessage < ProtocolModel
|
206
|
+
attribute :type, MultiwovenMessageType
|
207
|
+
attribute? :log, LogMessage.optional
|
208
|
+
attribute? :connection_status, ConnectionStatus.optional
|
209
|
+
attribute? :connector_spec, ConnectorSpecification.optional
|
210
|
+
attribute? :catalog, Catalog.optional
|
211
|
+
attribute? :record, RecordMessage.optional
|
212
|
+
attribute? :control, ControlMessage.optional
|
213
|
+
attribute? :tracking, TrackingMessage.optional
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|