multiwoven-integrations 0.1.66 → 0.1.68

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d41f10c87a8d9a6b6d5b3f04952afcc4c906266d79a2c0935b4468d6ccb4592e
4
- data.tar.gz: 5a09773b98f94d2ecf401ee8c46affe0684704b65a5d0dd73838418dbcf2d78d
3
+ metadata.gz: 77816f54f13495e303382ef4c1e2e932472475d31e0f486d3dcc0535e561f36d
4
+ data.tar.gz: 854a6871e65a3ad4764548ea14793a04c02d42cde0ed31712c252aff960134ab
5
5
  SHA512:
6
- metadata.gz: 3cff2743efecd6b06a1d7470d11ef3410adea1bd81ee5f028468d964a6630f8f3bafb4f5b7729aaa514afbb4608cacd0ee44723cb27863facc88dd0c5143c773
7
- data.tar.gz: bbfc548d9a8e0df2efcf466a728fd3d13e6eb3a8fe49ed7844d7c55249764b515e57f5c1a915601ec3e570195835b8a3162d5f3b9bd08b092c652c0990e8e015
6
+ metadata.gz: 0de8475fa0b27194c1a61b58d56f11fe3a5df8058c6065664a2c67c7af5c5d4c709aab352c0be4c7f53b9f62fda1031a47f1590844755b54897b38a652173306
7
+ data.tar.gz: dce5ca510e72bccf871075a1a5dd523db56961454b8d4ba987e6037b219f07683a79b1dabc03e51e9b533b18af52ce048ad60684fb2f9ee6e619c9fac272316b
@@ -3,10 +3,11 @@
3
3
  module Multiwoven
4
4
  module Integrations
5
5
  class Config
6
- attr_accessor :logger
6
+ attr_accessor :logger, :exception_reporter
7
7
 
8
8
  def initialize(params = {})
9
9
  @logger = params[:logger]
10
+ @exception_reporter = params[:exception_reporter]
10
11
  end
11
12
  end
12
13
  end
@@ -48,6 +48,11 @@ module Multiwoven
48
48
  Integrations::Service.logger
49
49
  end
50
50
 
51
+ def report_exception(exception)
52
+ reporter = Integrations::Service.exception_reporter
53
+ reporter&.report(exception)
54
+ end
55
+
51
56
  def create_log_message(context, type, exception)
52
57
  Integrations::Protocol::LogMessage.new(
53
58
  name: context,
@@ -60,7 +65,7 @@ module Multiwoven
60
65
  logger.error(
61
66
  "#{context}: #{exception.message}"
62
67
  )
63
-
68
+ report_exception(exception)
64
69
  create_log_message(context, type, exception)
65
70
  end
66
71
 
@@ -1,110 +1,110 @@
1
1
  {
2
- "request_rate_limit": 700,
3
- "request_rate_limit_unit": "minute",
4
- "request_rate_concurrency": 10,
5
- "streams": [
6
- {
7
- "name": "tickets",
8
- "action": "create",
9
- "json_schema": {
10
- "type": "object",
11
- "additionalProperties": true,
12
- "properties": {
13
- "id": {
14
- "type": "integer"
15
- },
16
- "subject": {
17
- "type": "string"
18
- },
19
- "description": {
20
- "type": "string"
21
- },
22
- "status": {
23
- "type": "string"
24
- },
25
- "priority": {
26
- "type": "string"
27
- },
28
- "requester_id": {
29
- "type": "integer"
30
- },
31
- "assignee_id": {
32
- "type": "integer"
33
- },
34
- "tags": {
35
- "type": "array",
36
- "items": {
37
- "type": "string"
38
- }
39
- },
40
- "created_at": {
41
- "type": "string",
42
- "format": "date-time"
43
- },
44
- "updated_at": {
45
- "type": "string",
46
- "format": "date-time"
47
- }
48
- }
49
- },
50
- "supported_sync_modes": [
51
- "incremental"
52
- ],
53
- "source_defined_cursor": true,
54
- "default_cursor_field": [
55
- "updated_at"
56
- ],
57
- "source_defined_primary_key": [
58
- [
59
- "id"
60
- ]
61
- ]
62
- },
63
- {
64
- "name": "users",
65
- "action": "create",
66
- "json_schema": {
67
- "type": "object",
68
- "additionalProperties": true,
69
- "properties": {
70
- "id": {
71
- "type": "integer"
72
- },
73
- "name": {
74
- "type": "string"
75
- },
76
- "email": {
77
- "type": "string"
78
- },
79
- "role": {
80
- "type": "string"
81
- },
82
- "last_login_at": {
83
- "type": "string",
84
- "format": "date-time"
85
- },
86
- "created_at": {
87
- "type": "string",
88
- "format": "date-time"
89
- },
90
- "updated_at": {
91
- "type": "string",
92
- "format": "date-time"
93
- }
94
- }
95
- },
96
- "supported_sync_modes": [
97
- "incremental"
98
- ],
99
- "source_defined_cursor": true,
100
- "default_cursor_field": [
101
- "updated_at"
102
- ],
103
- "source_defined_primary_key": [
104
- [
105
- "id"
106
- ]
107
- ]
2
+ "request_rate_limit": 700,
3
+ "request_rate_limit_unit": "minute",
4
+ "request_rate_concurrency": 10,
5
+ "streams": [
6
+ {
7
+ "name": "tickets",
8
+ "action": "create",
9
+ "json_schema": {
10
+ "type": "object",
11
+ "additionalProperties": true,
12
+ "properties": {
13
+ "id": {
14
+ "type": "integer"
15
+ },
16
+ "subject": {
17
+ "type": "string"
18
+ },
19
+ "description": {
20
+ "type": "string"
21
+ },
22
+ "status": {
23
+ "type": "string"
24
+ },
25
+ "priority": {
26
+ "type": "string"
27
+ },
28
+ "requester_id": {
29
+ "type": "integer"
30
+ },
31
+ "assignee_id": {
32
+ "type": "integer"
33
+ },
34
+ "tags": {
35
+ "type": "array",
36
+ "items": {
37
+ "type": "string"
38
+ }
39
+ },
40
+ "created_at": {
41
+ "type": "string",
42
+ "format": "date-time"
43
+ },
44
+ "updated_at": {
45
+ "type": "string",
46
+ "format": "date-time"
47
+ }
108
48
  }
109
- ]
49
+ },
50
+ "supported_sync_modes": [
51
+ "incremental"
52
+ ],
53
+ "source_defined_cursor": true,
54
+ "default_cursor_field": [
55
+ "updated_at"
56
+ ],
57
+ "source_defined_primary_key": [
58
+ [
59
+ "id"
60
+ ]
61
+ ]
62
+ },
63
+ {
64
+ "name": "users",
65
+ "action": "create",
66
+ "json_schema": {
67
+ "type": "object",
68
+ "additionalProperties": true,
69
+ "properties": {
70
+ "id": {
71
+ "type": "integer"
72
+ },
73
+ "name": {
74
+ "type": "string"
75
+ },
76
+ "email": {
77
+ "type": "string"
78
+ },
79
+ "role": {
80
+ "type": "string"
81
+ },
82
+ "last_login_at": {
83
+ "type": "string",
84
+ "format": "date-time"
85
+ },
86
+ "created_at": {
87
+ "type": "string",
88
+ "format": "date-time"
89
+ },
90
+ "updated_at": {
91
+ "type": "string",
92
+ "format": "date-time"
93
+ }
94
+ }
95
+ },
96
+ "supported_sync_modes": [
97
+ "incremental"
98
+ ],
99
+ "source_defined_cursor": true,
100
+ "default_cursor_field": [
101
+ "updated_at"
102
+ ],
103
+ "source_defined_primary_key": [
104
+ [
105
+ "id"
106
+ ]
107
+ ]
108
+ }
109
+ ]
110
110
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Multiwoven
4
4
  module Integrations
5
- VERSION = "0.1.66"
5
+ VERSION = "0.1.68"
6
6
 
7
7
  ENABLED_SOURCES = %w[
8
8
  Snowflake
@@ -11,6 +11,7 @@ module Multiwoven
11
11
  Postgresql
12
12
  Databricks
13
13
  SalesforceConsumerGoodsCloud
14
+ AwsAthena
14
15
  ].freeze
15
16
 
16
17
  ENABLED_DESTINATIONS = %w[
@@ -25,8 +26,8 @@ module Multiwoven
25
26
  SalesforceConsumerGoodsCloud
26
27
  Sftp
27
28
  Postgresql
28
- Zendesk
29
29
  Http
30
+ Zendesk
30
31
  ].freeze
31
32
  end
32
33
  end
@@ -28,6 +28,10 @@ module Multiwoven
28
28
  config.logger || default_logger
29
29
  end
30
30
 
31
+ def exception_reporter
32
+ config.exception_reporter
33
+ end
34
+
31
35
  def config
32
36
  @config ||= Config.new
33
37
  end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Multiwoven::Integrations::Source
4
+ module AwsAthena
5
+ include Multiwoven::Integrations::Core
6
+ class Client < SourceConnector
7
+ def check_connection(connection_config)
8
+ connection_config = connection_config.with_indifferent_access
9
+ athena_client = create_connection(connection_config)
10
+ athena_client.list_work_groups
11
+ ConnectionStatus.new(status: ConnectionStatusType["succeeded"]).to_multiwoven_message
12
+ rescue StandardError => e
13
+ ConnectionStatus.new(status: ConnectionStatusType["failed"], message: e.message).to_multiwoven_message
14
+ end
15
+
16
+ def discover(connection_config)
17
+ connection_config = connection_config.with_indifferent_access
18
+ query = "SELECT table_name, column_name, data_type, is_nullable FROM information_schema.columns WHERE table_schema = '#{connection_config[:schema]}' ORDER BY table_name, ordinal_position;"
19
+ db = create_connection(connection_config)
20
+ results = query_execution(db, query)
21
+ catalog = Catalog.new(streams: create_streams(results))
22
+ catalog.to_multiwoven_message
23
+ rescue StandardError => e
24
+ handle_exception(
25
+ "AWS:ATHENA:DISCOVER:EXCEPTION",
26
+ "error",
27
+ e
28
+ )
29
+ end
30
+
31
+ def read(sync_config)
32
+ connection_config = sync_config.source.connection_specification
33
+ connection_config = connection_config.with_indifferent_access
34
+ query = sync_config.model.query
35
+ query = batched_query(query, sync_config.limit, sync_config.offset) unless sync_config.limit.nil? && sync_config.offset.nil?
36
+ db = create_connection(connection_config)
37
+ query(db, query)
38
+ rescue StandardError => e
39
+ handle_exception(
40
+ "AWS:ATHENA:READ:EXCEPTION",
41
+ "error",
42
+ e
43
+ )
44
+ end
45
+
46
+ private
47
+
48
+ def create_connection(connection_config)
49
+ Aws.config.update({ credentials: Aws::Credentials.new(connection_config[:access_key], connection_config[:secret_access_key]), region: connection_config[:region] })
50
+ @database = connection_config[:schema]
51
+ @output_location = connection_config[:output_location]
52
+ Aws::Athena::Client.new
53
+ end
54
+
55
+ def query_execution(db, query)
56
+ response = db.start_query_execution(
57
+ query_string: query,
58
+ query_execution_context: { database: @database },
59
+ result_configuration: { output_location: @output_location }
60
+ )
61
+ query_execution_id = response[:query_execution_id]
62
+ loop do
63
+ response = db.get_query_execution(query_execution_id: query_execution_id)
64
+ status = response.query_execution.status.state
65
+ break if %w[SUCCEEDED FAILED CANCELLED].include?(status)
66
+ end
67
+ transform_results(db.get_query_results(query_execution_id: query_execution_id))
68
+ end
69
+
70
+ def create_streams(records)
71
+ group_by_table(records).map do |_, r|
72
+ Multiwoven::Integrations::Protocol::Stream.new(name: r[:tablename], action: StreamAction["fetch"], json_schema: convert_to_json_schema(r[:columns]))
73
+ end
74
+ end
75
+
76
+ def transform_results(results)
77
+ columns = results.result_set.result_set_metadata.column_info.map(&:name)
78
+ rows = results.result_set.rows.map do |row|
79
+ row.data.map(&:var_char_value)
80
+ end
81
+ rows.map { |row| columns.zip(row).to_h }
82
+ end
83
+
84
+ def query(db, query)
85
+ records = []
86
+ query_execution(db, query).map do |row|
87
+ records << RecordMessage.new(data: row, emitted_at: Time.now.to_i).to_multiwoven_message
88
+ end
89
+ records
90
+ end
91
+
92
+ def group_by_table(records)
93
+ result = {}
94
+ records.each_with_index do |entry, index|
95
+ table_name = entry["table_name"]
96
+ column_data = {
97
+ column_name: entry["column_name"],
98
+ data_type: entry["data_type"],
99
+ is_nullable: entry["is_nullable"] == "YES"
100
+ }
101
+ result[index] ||= {}
102
+ result[index][:tablename] = table_name
103
+ result[index][:columns] = [column_data]
104
+ end
105
+ result
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,15 @@
1
+ {
2
+ "data": {
3
+ "name": "AwsAthena",
4
+ "title": "AWS Athena",
5
+ "connector_type": "source",
6
+ "category": "Data Warehouse",
7
+ "documentation_url": "https://docs.mutliwoven.com",
8
+ "github_issue_label": "source-aws-athena",
9
+ "icon": "icon.svg",
10
+ "license": "MIT",
11
+ "release_stage": "alpha",
12
+ "support_level": "community",
13
+ "tags": ["language:ruby", "multiwoven"]
14
+ }
15
+ }
@@ -0,0 +1,63 @@
1
+ {
2
+ "documentation_url": "https://docs.multiwoven.com/integrations/sources/athena",
3
+ "stream_type": "dynamic",
4
+ "connector_query_type": "raw_sql",
5
+ "connection_specification": {
6
+ "$schema": "http://json-schema.org/draft-07/schema#",
7
+ "title": "AWS Athena",
8
+ "type": "object",
9
+ "required": ["access_key", "secret_access_key", "region", "workgroup", "catalog", "output_location"],
10
+ "properties": {
11
+ "access_key": {
12
+ "description": "The AWS Access Key ID to use for authentication.",
13
+ "examples": ["AWSATHENAACCESS"],
14
+ "type": "string",
15
+ "title": "Personal Access Key",
16
+ "order": 0
17
+ },
18
+ "secret_access_key": {
19
+ "description": "The AWS Secret Access Key to use for authentication.",
20
+ "examples": ["AWSATHENASECRET"],
21
+ "type": "string",
22
+ "multiwoven_secret": true,
23
+ "title": "Secret Access Key",
24
+ "order": 1
25
+ },
26
+ "region": {
27
+ "description": "AWS region where Athena is located.",
28
+ "examples": ["ATHENA_REGION"],
29
+ "type": "string",
30
+ "title": "Secret Access Key",
31
+ "order": 2
32
+ },
33
+ "workgroup": {
34
+ "description": "The Athena workgroup you previously set up in AWS.",
35
+ "examples": ["ATHENA_WORKGROUP"],
36
+ "type": "string",
37
+ "title": "Workgroup",
38
+ "order": 3
39
+ },
40
+ "catalog": {
41
+ "description": "The Data catalog name within Athena.",
42
+ "examples": ["ATHENA_CATALOG"],
43
+ "type": "string",
44
+ "title": "Catalog",
45
+ "order": 4
46
+ },
47
+ "schema": {
48
+ "description": "The specific Athena database/schema to connect to.",
49
+ "examples": ["ATHENA_DB"],
50
+ "type": "string",
51
+ "title": "Database",
52
+ "order": 5
53
+ },
54
+ "output_location": {
55
+ "description": "S3 path for query output.",
56
+ "examples": ["s3://example-bucket-name/query-results/"],
57
+ "type": "string",
58
+ "title": "Query",
59
+ "order": 6
60
+ }
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,22 @@
1
+ <svg height="100" viewBox="0 0 100 100" width="100" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="m84 64.68-34-4.19-34 4.19 34 26.62z" fill="#fcbf92"/>
3
+ <path d="m16 64.68 34 10.17v16.45l-34-17.05z" fill="#9d5025"/>
4
+ <path d="m84 64.68-34 10.17v16.45l34-17.05z" fill="#f58534"/>
5
+ <path d="m20.39 50.22h-4.39v9.67l4.39.8 4.88-5.18z" fill="#9d5025"/>
6
+ <path d="m25.27 60.29-4.88.4v-10.47h4.88z" fill="#f58534"/>
7
+ <path d="m28.56 38.16-5.78 1.09v21.94l5.78 1.2 5.29-12.17z" fill="#9d5025"/>
8
+ <path d="m33.85 61.79-5.29.6v-24.23l5.29.6z" fill="#f58534"/>
9
+ <path d="m39.83 43.04-7.98.8v19.24l7.98 1.6 8.47-10.87z" fill="#9d5025"/>
10
+ <path d="m45.21 63.78-5.38.9v-21.64l5.38.4z" fill="#f58534"/>
11
+ <g fill="#9d5025">
12
+ <path d="m50 25.59-5.38 1.7v38.29l5.38 1.09 5.38-20.54z"/>
13
+ <path d="m54.98 63.78 5.19.9 7.98-14.46-7.98-14.36-5.19.8z"/>
14
+ <path d="m68.15 50.22-7.98-14.36-5.19.8"/>
15
+ <path d="m66.05 61.79 5.39.6 5.78-18.25-5.78-18.15-5.39 1.3z"/>
16
+ <path d="m74.73 60.29 4.88.4 4.39-18.05-4.39-18.64-4.88 1.2z"/>
17
+ </g>
18
+ <path d="m50 25.59 5.38 1.7v38.29l-5.38 1.09z" fill="#f58534"/>
19
+ <path d="m68.15 37.46-7.98-1.6v28.82l7.98-1.6z" fill="#f58534"/>
20
+ <path d="m77.22 28.29-5.78-2.3v36.4l5.78-1.2z" fill="#f58534"/>
21
+ <path d="m84 26.19-4.39-2.19v36.69l4.39-.8z" fill="#f58534"/>
22
+ </svg>
@@ -22,6 +22,7 @@ require "stripe"
22
22
  require "net/sftp"
23
23
  require "csv"
24
24
  require "securerandom"
25
+ require "aws-sdk-athena"
25
26
  require "zip"
26
27
  require "zendesk_api"
27
28
 
@@ -49,6 +50,7 @@ require_relative "integrations/source/bigquery/client"
49
50
  require_relative "integrations/source/postgresql/client"
50
51
  require_relative "integrations/source/databricks/client"
51
52
  require_relative "integrations/source/salesforce_consumer_goods_cloud/client"
53
+ require_relative "integrations/source/aws_athena/client"
52
54
 
53
55
  # Destination
54
56
  require_relative "integrations/destination/klaviyo/client"
@@ -62,8 +64,8 @@ require_relative "integrations/destination/stripe/client"
62
64
  require_relative "integrations/destination/salesforce_consumer_goods_cloud/client"
63
65
  require_relative "integrations/destination/sftp/client"
64
66
  require_relative "integrations/destination/postgresql/client"
65
- require_relative "integrations/destination/zendesk/client"
66
67
  require_relative "integrations/destination/http/client"
68
+ require_relative "integrations/destination/zendesk/client"
67
69
 
68
70
  module Multiwoven
69
71
  module Integrations
@@ -35,6 +35,7 @@ Gem::Specification.new do |spec|
35
35
 
36
36
  spec.add_runtime_dependency "activesupport"
37
37
  spec.add_runtime_dependency "async-websocket"
38
+ spec.add_runtime_dependency "aws-sdk-athena"
38
39
  spec.add_runtime_dependency "csv"
39
40
  spec.add_runtime_dependency "dry-schema"
40
41
  spec.add_runtime_dependency "dry-struct"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: multiwoven-integrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.66
4
+ version: 0.1.68
5
5
  platform: ruby
6
6
  authors:
7
7
  - Subin T P
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-05-14 00:00:00.000000000 Z
11
+ date: 2024-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk-athena
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: csv
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -486,6 +500,10 @@ files:
486
500
  - lib/multiwoven/integrations/protocol/protocol.rb
487
501
  - lib/multiwoven/integrations/rollout.rb
488
502
  - lib/multiwoven/integrations/service.rb
503
+ - lib/multiwoven/integrations/source/aws_athena/client.rb
504
+ - lib/multiwoven/integrations/source/aws_athena/config/meta.json
505
+ - lib/multiwoven/integrations/source/aws_athena/config/spec.json
506
+ - lib/multiwoven/integrations/source/aws_athena/icon.svg
489
507
  - lib/multiwoven/integrations/source/bigquery/client.rb
490
508
  - lib/multiwoven/integrations/source/bigquery/config/meta.json
491
509
  - lib/multiwoven/integrations/source/bigquery/config/spec.json