etl-integrations 0.1.81

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.
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