etl-integrations 0.1.81

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +34 -0
  4. data/.ruby-version +1 -0
  5. data/.vscode/settings.json +5 -0
  6. data/CHANGELOG.md +38 -0
  7. data/CODE_OF_CONDUCT.md +84 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +105 -0
  10. data/Rakefile +12 -0
  11. data/lib/multiwoven/integrations/config.rb +13 -0
  12. data/lib/multiwoven/integrations/core/base_connector.rb +70 -0
  13. data/lib/multiwoven/integrations/core/constants.rb +46 -0
  14. data/lib/multiwoven/integrations/core/destination_connector.rb +14 -0
  15. data/lib/multiwoven/integrations/core/fullrefresher.rb +19 -0
  16. data/lib/multiwoven/integrations/core/http_client.rb +34 -0
  17. data/lib/multiwoven/integrations/core/query_builder.rb +27 -0
  18. data/lib/multiwoven/integrations/core/rate_limiter.rb +19 -0
  19. data/lib/multiwoven/integrations/core/source_connector.rb +38 -0
  20. data/lib/multiwoven/integrations/core/utils.rb +104 -0
  21. data/lib/multiwoven/integrations/destination/airtable/client.rb +153 -0
  22. data/lib/multiwoven/integrations/destination/airtable/config/catalog.json +6 -0
  23. data/lib/multiwoven/integrations/destination/airtable/config/meta.json +15 -0
  24. data/lib/multiwoven/integrations/destination/airtable/config/spec.json +22 -0
  25. data/lib/multiwoven/integrations/destination/airtable/icon.svg +6 -0
  26. data/lib/multiwoven/integrations/destination/airtable/schema_helper.rb +141 -0
  27. data/lib/multiwoven/integrations/destination/facebook_custom_audience/client.rb +124 -0
  28. data/lib/multiwoven/integrations/destination/facebook_custom_audience/config/catalog.json +42 -0
  29. data/lib/multiwoven/integrations/destination/facebook_custom_audience/config/meta.json +15 -0
  30. data/lib/multiwoven/integrations/destination/facebook_custom_audience/config/spec.json +27 -0
  31. data/lib/multiwoven/integrations/destination/facebook_custom_audience/icon.svg +23 -0
  32. data/lib/multiwoven/integrations/destination/google_sheets/client.rb +231 -0
  33. data/lib/multiwoven/integrations/destination/google_sheets/config/catalog.json +6 -0
  34. data/lib/multiwoven/integrations/destination/google_sheets/config/meta.json +15 -0
  35. data/lib/multiwoven/integrations/destination/google_sheets/config/spec.json +74 -0
  36. data/lib/multiwoven/integrations/destination/google_sheets/icon.svg +1 -0
  37. data/lib/multiwoven/integrations/destination/hubspot/client.rb +107 -0
  38. data/lib/multiwoven/integrations/destination/hubspot/config/catalog.json +351 -0
  39. data/lib/multiwoven/integrations/destination/hubspot/config/meta.json +15 -0
  40. data/lib/multiwoven/integrations/destination/hubspot/config/spec.json +17 -0
  41. data/lib/multiwoven/integrations/destination/hubspot/icon.svg +5 -0
  42. data/lib/multiwoven/integrations/destination/klaviyo/client.rb +116 -0
  43. data/lib/multiwoven/integrations/destination/klaviyo/config/catalog.json +103 -0
  44. data/lib/multiwoven/integrations/destination/klaviyo/config/meta.json +15 -0
  45. data/lib/multiwoven/integrations/destination/klaviyo/config/spec.json +22 -0
  46. data/lib/multiwoven/integrations/destination/klaviyo/icon.svg +6 -0
  47. data/lib/multiwoven/integrations/destination/postgresql/client.rb +123 -0
  48. data/lib/multiwoven/integrations/destination/postgresql/config/meta.json +15 -0
  49. data/lib/multiwoven/integrations/destination/postgresql/config/spec.json +68 -0
  50. data/lib/multiwoven/integrations/destination/postgresql/icon.svg +20 -0
  51. data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/client.rb +114 -0
  52. data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/config/catalog.json +6 -0
  53. data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/config/meta.json +16 -0
  54. data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/config/spec.json +49 -0
  55. data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/icon.svg +16 -0
  56. data/lib/multiwoven/integrations/destination/salesforce_consumer_goods_cloud/schema_helper.rb +132 -0
  57. data/lib/multiwoven/integrations/destination/salesforce_crm/client.rb +117 -0
  58. data/lib/multiwoven/integrations/destination/salesforce_crm/config/catalog.json +320 -0
  59. data/lib/multiwoven/integrations/destination/salesforce_crm/config/meta.json +15 -0
  60. data/lib/multiwoven/integrations/destination/salesforce_crm/config/spec.json +43 -0
  61. data/lib/multiwoven/integrations/destination/salesforce_crm/icon.svg +16 -0
  62. data/lib/multiwoven/integrations/destination/sftp/client.rb +133 -0
  63. data/lib/multiwoven/integrations/destination/sftp/config/catalog.json +16 -0
  64. data/lib/multiwoven/integrations/destination/sftp/config/meta.json +16 -0
  65. data/lib/multiwoven/integrations/destination/sftp/config/spec.json +50 -0
  66. data/lib/multiwoven/integrations/destination/sftp/icon.svg +1 -0
  67. data/lib/multiwoven/integrations/destination/slack/client.rb +114 -0
  68. data/lib/multiwoven/integrations/destination/slack/config/catalog.json +22 -0
  69. data/lib/multiwoven/integrations/destination/slack/config/meta.json +15 -0
  70. data/lib/multiwoven/integrations/destination/slack/config/spec.json +22 -0
  71. data/lib/multiwoven/integrations/destination/slack/icon.svg +26 -0
  72. data/lib/multiwoven/integrations/destination/stripe/client.rb +82 -0
  73. data/lib/multiwoven/integrations/destination/stripe/config/catalog.json +128 -0
  74. data/lib/multiwoven/integrations/destination/stripe/config/meta.json +15 -0
  75. data/lib/multiwoven/integrations/destination/stripe/config/spec.json +17 -0
  76. data/lib/multiwoven/integrations/destination/stripe/icon.svg +10 -0
  77. data/lib/multiwoven/integrations/destination/tally/client.rb +151 -0
  78. data/lib/multiwoven/integrations/destination/tally/config/catalog.json +62 -0
  79. data/lib/multiwoven/integrations/destination/tally/config/meta.json +15 -0
  80. data/lib/multiwoven/integrations/destination/tally/config/spec.json +45 -0
  81. data/lib/multiwoven/integrations/destination/tally/icon.svg +7 -0
  82. data/lib/multiwoven/integrations/protocol/protocol.json +189 -0
  83. data/lib/multiwoven/integrations/protocol/protocol.rb +216 -0
  84. data/lib/multiwoven/integrations/rollout.rb +32 -0
  85. data/lib/multiwoven/integrations/service.rb +79 -0
  86. data/lib/multiwoven/integrations/source/bigquery/client.rb +98 -0
  87. data/lib/multiwoven/integrations/source/bigquery/config/meta.json +15 -0
  88. data/lib/multiwoven/integrations/source/bigquery/config/spec.json +82 -0
  89. data/lib/multiwoven/integrations/source/bigquery/icon.svg +1 -0
  90. data/lib/multiwoven/integrations/source/databricks/client.rb +98 -0
  91. data/lib/multiwoven/integrations/source/databricks/config/meta.json +16 -0
  92. data/lib/multiwoven/integrations/source/databricks/config/spec.json +56 -0
  93. data/lib/multiwoven/integrations/source/databricks/icon.svg +19 -0
  94. data/lib/multiwoven/integrations/source/postgresql/client.rb +109 -0
  95. data/lib/multiwoven/integrations/source/postgresql/config/meta.json +15 -0
  96. data/lib/multiwoven/integrations/source/postgresql/config/spec.json +69 -0
  97. data/lib/multiwoven/integrations/source/postgresql/icon.svg +20 -0
  98. data/lib/multiwoven/integrations/source/redshift/client.rb +109 -0
  99. data/lib/multiwoven/integrations/source/redshift/config/meta.json +15 -0
  100. data/lib/multiwoven/integrations/source/redshift/config/spec.json +71 -0
  101. data/lib/multiwoven/integrations/source/redshift/icon.svg +15 -0
  102. data/lib/multiwoven/integrations/source/salesforce_consumer_goods_cloud/client.rb +123 -0
  103. data/lib/multiwoven/integrations/source/salesforce_consumer_goods_cloud/config/catalog.json +6 -0
  104. data/lib/multiwoven/integrations/source/salesforce_consumer_goods_cloud/config/meta.json +17 -0
  105. data/lib/multiwoven/integrations/source/salesforce_consumer_goods_cloud/config/spec.json +50 -0
  106. data/lib/multiwoven/integrations/source/salesforce_consumer_goods_cloud/icon.svg +16 -0
  107. data/lib/multiwoven/integrations/source/salesforce_consumer_goods_cloud/schema_helper.rb +130 -0
  108. data/lib/multiwoven/integrations/source/snowflake/client.rb +92 -0
  109. data/lib/multiwoven/integrations/source/snowflake/config/meta.json +15 -0
  110. data/lib/multiwoven/integrations/source/snowflake/config/spec.json +82 -0
  111. data/lib/multiwoven/integrations/source/snowflake/icon.svg +10 -0
  112. data/lib/multiwoven/integrations/source/zoho_books/client.rb +155 -0
  113. data/lib/multiwoven/integrations/source/zoho_books/config/meta.json +16 -0
  114. data/lib/multiwoven/integrations/source/zoho_books/config/spec.json +43 -0
  115. data/lib/multiwoven/integrations/source/zoho_books/icon.svg +16 -0
  116. data/lib/multiwoven/integrations.rb +71 -0
  117. data/multiwoven-integrations-0.1.68.gem +0 -0
  118. data/sig/multiwoven/integrations.rbs +6 -0
  119. 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