multiwoven-integrations 0.18.5 → 0.19.0

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: 6f5f6890969a89be17fe50cb2fd9ff8f863b185de80b6153837ef8a031edf293
4
- data.tar.gz: f76b6b9d309d6336ea4650b8b27e9752a646a78d1a501d74537ffafae7752bc6
3
+ metadata.gz: 74c92fb99f1531d7844d52474ab8b105cce604bd450f8ca82c5975f7bea093af
4
+ data.tar.gz: 8cf59706729c7d94e49546f51455f062ef21829f226f743d25da01e1f41a9305
5
5
  SHA512:
6
- metadata.gz: b3eb67d238ea710d230a6a41404046864eab190dadf4341035441196fc7637c1c70ebe390806808a09d4ccef95cfd53e90e6e9bb33e7cc8f778b481c8a34ce83
7
- data.tar.gz: e30a2fa6adb0293bd07e467b475df793a38bf271e9b48d41c7ff3d41a9f1ba2d9b791acb9f51582c7f3fbfe123e8a8592d9758bf7a384cc309762c94e97f8dc9
6
+ metadata.gz: 9a18cdc18d37c33ca2737ec9a8a91a462f5205761d7bb7012398f679b7e56e3581e594f38ca909e8acd8a4b9c527225ba8b22301b69bcf5aa9108ed704b083f6
7
+ data.tar.gz: 579fa2afb8f1f9616f1e290a0321f0ad9a082a3f34f2607bb40c3124d219d80e17304c6e69eb713a743e4596c38a428b686478190342b638a8efb29304ee274e
@@ -46,6 +46,8 @@ module Multiwoven
46
46
  "workbook/worksheets"
47
47
  MS_EXCEL_SHEET_RANGE_API = "https://graph.microsoft.com/v1.0/drives/%<drive_id>s/items/%<item_id>s/"\
48
48
  "workbook/worksheets/%<sheet_name>s/range(address='A1:Z1')/usedRange?$select=values"
49
+ MS_DYNAMICS_WHOAMI_API = "https://%<instance_url>s.crm.dynamics.com/api/data/v%<api_version>s/WhoAmI"
50
+ MS_DYNAMICS_REST_API = "https://%<instance_url>s.crm.dynamics.com/api/data/v%<api_version>s/%<entity>s"
49
51
 
50
52
  DATABRICKS_HEALTH_URL = "https://%<databricks_host>s/api/2.0/serving-endpoints/%<endpoint_name>s"
51
53
  DATABRICKS_SERVING_URL = "https://%<databricks_host>s/serving-endpoints/%<endpoint_name>s/invocations"
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Multiwoven::Integrations::Destination
4
+ module MicrosoftDynamics
5
+ include Multiwoven::Integrations::Core
6
+ API_VERSION = "9.2"
7
+ class Client < DestinationConnector
8
+ def check_connection(connection_config)
9
+ connection_config = connection_config.with_indifferent_access
10
+ initialize_client(connection_config)
11
+ token_response = create_access_token
12
+ uri = URI.parse(format(MS_DYNAMICS_WHOAMI_API, instance_url: @instance_url, api_version: API_VERSION))
13
+ http = Net::HTTP.new(uri.host, uri.port)
14
+ http.use_ssl = (uri.scheme == "https")
15
+ request = Net::HTTP::Get.new(uri)
16
+ auth_headers(token_response["access_token"]).each { |key, value| request[key] = value }
17
+ response = http.request(request)
18
+ response_body = JSON.parse(response.body)
19
+
20
+ if success?(response) && response_body.key?("UserId")
21
+ success_status
22
+ else
23
+ failure_status(nil)
24
+ end
25
+ rescue StandardError => e
26
+ handle_exception(e, {
27
+ context: "MICROSOFT:DYNAMICS:CHECK_CONNECTION:EXCEPTION",
28
+ type: "error"
29
+ })
30
+ failure_status(e)
31
+ end
32
+
33
+ def discover(_connection_config = nil)
34
+ catalog_json = read_json(CATALOG_SPEC_PATH)
35
+ catalog = build_catalog(catalog_json)
36
+ catalog.to_multiwoven_message
37
+ rescue StandardError => e
38
+ handle_exception(e, {
39
+ context: "MICROSOFT:DYNAMICS:DISCOVER:EXCEPTION",
40
+ type: "error"
41
+ })
42
+ end
43
+
44
+ def write(sync_config, records, _action = "upsert")
45
+ @sync_config = sync_config
46
+ stream = @sync_config.stream
47
+ connection_config = @sync_config.destination.connection_specification.with_indifferent_access
48
+ create_connection(connection_config)
49
+ build_url(stream)
50
+ process_records(records, stream)
51
+ rescue StandardError => e
52
+ handle_exception(e, {
53
+ context: "MICROSOFT:DYNAMICS:WRITE:EXCEPTION",
54
+ type: "error",
55
+ sync_id: @sync_config.sync_id,
56
+ sync_run_id: @sync_config.sync_run_id
57
+ })
58
+ end
59
+
60
+ private
61
+
62
+ def create_access_token
63
+ uri = URI.parse("https://login.microsoftonline.com/#{@tenant_id}/oauth2/v2.0/token")
64
+
65
+ payload = {
66
+ client_id: @client_id,
67
+ client_secret: @client_secret,
68
+ scope: "https://#{@instance_url}.crm.dynamics.com/.default",
69
+ grant_type: "client_credentials"
70
+ }
71
+
72
+ response = Net::HTTP.post_form(uri, payload)
73
+ JSON.parse(response.body)
74
+ end
75
+
76
+ def get_access_token(cache)
77
+ cache_key = "dynamics_#{@instance_url}_#{@tenant_id}_#{@client_id}"
78
+ cached_token = cache.read(cache_key)
79
+ if cached_token
80
+ @access_token = cached_token
81
+ else
82
+ new_token = create_access_token
83
+ # max expiration is 3 minutes. No way to make it higher
84
+ cache.write(cache_key, new_token["access_token"], expires_in: 180)
85
+ @access_token = new_token["access_token"]
86
+ end
87
+ end
88
+
89
+ def create_connection(connection_config)
90
+ cache = defined?(Rails) && Rails.respond_to?(:cache) ? Rails.cache : ActiveSupport::Cache::MemoryStore.new
91
+ initialize_client(connection_config)
92
+ get_access_token(cache)
93
+ end
94
+
95
+ def initialize_client(connection_config)
96
+ @tenant_id = connection_config[:tenant_id]
97
+ @client_id = connection_config[:application_id]
98
+ @instance_url = connection_config[:instance_url]
99
+ @client_secret = connection_config[:client_secret]
100
+ end
101
+
102
+ def process_records(records, stream)
103
+ write_success = 0
104
+ write_failure = 0
105
+ properties = stream.json_schema[:properties]
106
+ log_message_array = []
107
+
108
+ records.each do |record_object|
109
+ record = extract_data(record_object, properties)
110
+ response = send_data_to_dynamics(record)
111
+ response_code = response.code.to_i
112
+ if response_code >= 200 && response_code < 300
113
+ write_success += 1
114
+ log_message_array << log_request_response("info", record, response["location"])
115
+ else
116
+ write_failure += 1
117
+ log_message_array << log_request_response("error", record, response.body)
118
+ end
119
+ rescue StandardError => e
120
+ # TODO: add sync_id and sync run id to the logs
121
+ handle_exception(e, {
122
+ context: "MICROSOFT:DYNAMICS:WRITE:EXCEPTION",
123
+ type: "error",
124
+ sync_id: @sync_config.sync_id,
125
+ sync_run_id: @sync_config.sync_run_id
126
+ })
127
+ write_failure += 1
128
+ log_message_array << log_request_response("error", record, e.message)
129
+ end
130
+ tracking_message(write_success, write_failure, log_message_array)
131
+ end
132
+
133
+ def build_url(stream)
134
+ @destination_url = format(MS_DYNAMICS_REST_API, instance_url: @instance_url, api_version: API_VERSION, entity: stream.name)
135
+ end
136
+
137
+ def send_data_to_dynamics(payload)
138
+ uri = URI.parse(@destination_url)
139
+
140
+ http = Net::HTTP.new(uri.host, uri.port)
141
+ http.use_ssl = (uri.scheme == "https")
142
+
143
+ request = Net::HTTP::Post.new(uri)
144
+ auth_headers(@access_token).each { |key, value| request[key] = value }
145
+ request.body = payload.to_json
146
+ http.request(request)
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,161 @@
1
+ {
2
+ "request_rate_limit": 40000,
3
+ "request_rate_limit_unit": "day",
4
+ "request_rate_concurrency": 10,
5
+ "streams": [
6
+ {
7
+ "name": "accounts",
8
+ "action": "create",
9
+ "json_schema": {
10
+ "type": "object",
11
+ "additionalProperties": true,
12
+ "required": ["name"],
13
+ "properties": {
14
+ "name": {
15
+ "type": "string"
16
+ },
17
+ "account_number": {
18
+ "type": "string"
19
+ },
20
+ "telephone1": {
21
+ "type": "string"
22
+ },
23
+ "emailaddress1": {
24
+ "type": "string",
25
+ "format": "email"
26
+ },
27
+ "websiteurl": {
28
+ "type": "string",
29
+ "format": "uri"
30
+ },
31
+ "address1_line1": {
32
+ "type": "string"
33
+ },
34
+ "address1_city": {
35
+ "type": "string"
36
+ },
37
+ "address1_stateorprovince": {
38
+ "type": "string"
39
+ },
40
+ "address1_postalcode": {
41
+ "type": "string"
42
+ },
43
+ "industrycode": {
44
+ "type": "integer"
45
+ },
46
+ "revenue": {
47
+ "type": "number",
48
+ "format": "float"
49
+ },
50
+ "numberofemployees": {
51
+ "type": "integer"
52
+ }
53
+ }
54
+ },
55
+ "supported_sync_modes": ["incremental"],
56
+ "source_defined_cursor": false,
57
+ "source_defined_primary_key": ["account_number"]
58
+ },
59
+ {
60
+ "name": "contacts",
61
+ "action": "create",
62
+ "json_schema": {
63
+ "type": "object",
64
+ "additionalProperties": true,
65
+ "required": ["emailaddress1", "firstname", "lastname"],
66
+ "properties": {
67
+ "firstname": {
68
+ "type": "string"
69
+ },
70
+ "lastname": {
71
+ "type": "string"
72
+ },
73
+ "emailaddress1": {
74
+ "type": "string",
75
+ "format": "email"
76
+ },
77
+ "mobilephone": {
78
+ "type": "string"
79
+ },
80
+ "telephone1": {
81
+ "type": "string"
82
+ },
83
+ "address1_line1": {
84
+ "type": "string"
85
+ },
86
+ "address1_city": {
87
+ "type": "string"
88
+ },
89
+ "address1_stateorprovince": {
90
+ "type": "string"
91
+ },
92
+ "address1_postalcode": {
93
+ "type": "string"
94
+ },
95
+ "jobtitle": {
96
+ "type": "string"
97
+ },
98
+ "parentcustomerid": {
99
+ "type": "string",
100
+ "description": "ID of the associated Account"
101
+ }
102
+ }
103
+ },
104
+ "supported_sync_modes": ["incremental"],
105
+ "source_defined_cursor": false,
106
+ "source_defined_primary_key": ["emailaddress1"]
107
+ },
108
+ {
109
+ "name": "opportunities",
110
+ "action": "create",
111
+ "json_schema": {
112
+ "type": "object",
113
+ "additionalProperties": true,
114
+ "required": ["name", "customerid", "estimatedvalue"],
115
+ "properties": {
116
+ "name": {
117
+ "type": "string"
118
+ },
119
+ "customerid": {
120
+ "type": "string",
121
+ "description": "ID of the associated Account or Contact"
122
+ },
123
+ "estimatedvalue": {
124
+ "type": "number",
125
+ "format": "float"
126
+ },
127
+ "description": {
128
+ "type": "string"
129
+ },
130
+ "closeprobability": {
131
+ "type": "integer",
132
+ "minimum": 0,
133
+ "maximum": 100
134
+ },
135
+ "estimatedclosedate": {
136
+ "type": "string",
137
+ "format": "date"
138
+ },
139
+ "actualclosedate": {
140
+ "type": "string",
141
+ "format": "date"
142
+ },
143
+ "opportunityratingcode": {
144
+ "type": "string",
145
+ "enum": ["Hot", "Warm", "Cold"]
146
+ },
147
+ "stageid": {
148
+ "type": "string"
149
+ },
150
+ "ownerid": {
151
+ "type": "string",
152
+ "description": "ID of the owner (user or team)"
153
+ }
154
+ }
155
+ },
156
+ "supported_sync_modes": ["incremental"],
157
+ "source_defined_cursor": false,
158
+ "source_defined_primary_key": ["customerid"]
159
+ }
160
+ ]
161
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "data": {
3
+ "name": "MicrosoftDynamics",
4
+ "title": "Microsoft Dynamics",
5
+ "connector_type": "destination",
6
+ "category": "CRM",
7
+ "documentation_url": "https://docs.mutliwoven.com",
8
+ "github_issue_label": "destination-microsoft-dynamics",
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,35 @@
1
+ {
2
+ "documentation_url": "https://docs.multiwoven.com/integrations/destination/microsoft_dynamics",
3
+ "stream_type": "dynamic",
4
+ "connector_query_type": "raw_sql",
5
+ "connection_specification": {
6
+ "$schema": "http://json-schema.org/draft-07/schema#",
7
+ "title": "Microsoft Dynamics",
8
+ "type": "object",
9
+ "required": ["instance_url", "tenant_id", "application_id", "client_secret"],
10
+ "properties": {
11
+ "instance_url": {
12
+ "type": "string",
13
+ "title": "Organization Name",
14
+ "order": 0
15
+ },
16
+ "tenant_id": {
17
+ "type": "string",
18
+ "multiwoven_secret": true,
19
+ "title": "Tenant ID",
20
+ "order": 1
21
+ },
22
+ "application_id": {
23
+ "type": "string",
24
+ "title": "Application ID",
25
+ "order": 2
26
+ },
27
+ "client_secret": {
28
+ "type": "string",
29
+ "multiwoven_secret": true,
30
+ "title": "Client Secret",
31
+ "order": 3
32
+ }
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2
+ <svg fill="#000000" width="800px" height="800px" viewBox="0 0 24 24" role="img" xmlns="http://www.w3.org/2000/svg"><title>Dynamics 365 icon</title><path d="M4.59 7.41l4.94 3.54L4.59 24zm0-7.41v6.36l9.53 5.29 4.59-3.52zm0 24l14.82-8.47v-6.7Z"/></svg>
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Multiwoven
4
4
  module Integrations
5
- VERSION = "0.18.5"
5
+ VERSION = "0.19.0"
6
6
 
7
7
  ENABLED_SOURCES = %w[
8
8
  Snowflake
@@ -47,6 +47,7 @@ module Multiwoven
47
47
  Mailchimp
48
48
  AISDataStore
49
49
  AmazonS3
50
+ MicrosoftDynamics
50
51
  ].freeze
51
52
  end
52
53
  end
@@ -99,6 +99,7 @@ require_relative "integrations/destination/microsoft_sql/client"
99
99
  require_relative "integrations/destination/mailchimp/client"
100
100
  require_relative "integrations/destination/ais_data_store/client"
101
101
  require_relative "integrations/destination/amazon_s3/client"
102
+ require_relative "integrations/destination/microsoft_dynamics/client"
102
103
 
103
104
  module Multiwoven
104
105
  module Integrations
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.18.5
4
+ version: 0.19.0
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: 2025-01-22 00:00:00.000000000 Z
11
+ date: 2025-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -644,6 +644,11 @@ files:
644
644
  - lib/multiwoven/integrations/destination/maria_db/config/meta.json
645
645
  - lib/multiwoven/integrations/destination/maria_db/config/spec.json
646
646
  - lib/multiwoven/integrations/destination/maria_db/icon.svg
647
+ - lib/multiwoven/integrations/destination/microsoft_dynamics/client.rb
648
+ - lib/multiwoven/integrations/destination/microsoft_dynamics/config/catalog.json
649
+ - lib/multiwoven/integrations/destination/microsoft_dynamics/config/meta.json
650
+ - lib/multiwoven/integrations/destination/microsoft_dynamics/config/spec.json
651
+ - lib/multiwoven/integrations/destination/microsoft_dynamics/icon.svg
647
652
  - lib/multiwoven/integrations/destination/microsoft_excel/client.rb
648
653
  - lib/multiwoven/integrations/destination/microsoft_excel/config/catalog.json
649
654
  - lib/multiwoven/integrations/destination/microsoft_excel/config/meta.json