multiwoven-integrations 0.31.2 → 0.32.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d4ab3e1e7f8cc3237943176323a9dc3b7f5bb7783dda9a712d6e4f90b009d5c
4
- data.tar.gz: 1ca74881258ef5c71e265298f3af9e557829b3614c9ebab109183564d4d4db11
3
+ metadata.gz: 9455dc8be1de31368a3f1020d07ac58815e7a9658ac51c12981c86d7ca390496
4
+ data.tar.gz: 8601b4ca19210ed1aeca50aaeeb6e5b261d4ee5df268f97240d6f9d2b6b8a413
5
5
  SHA512:
6
- metadata.gz: 69a506a64d4ddccbe2792dc819132427e37243926e69e56fb3d1b1dca9a1bab3664666fd3dfbe9c2b5374c0bc9c8c31eb163f58e0e5ccf383ff153b8a8584481
7
- data.tar.gz: acecd84a3e20931608c312160e0de8510a6b6e51dd82e9f670d1d36f1a568429ba0b3ccdc2158656f76fc8ab4f4f19e629e928770d2b90b5bc0b5aa3dd0f554f
6
+ metadata.gz: dfdff633b5152d44508879be3ab350473680843957df08d6bb7fa4b9df69e517bac5906a58f435734a983cbf56702c9a4e1732f702010e8218ba9f49ed69aafa
7
+ data.tar.gz: e11cf12ce58a056a809a6946afa8f19d7834138682b6243f07629f8cb638bd4e3d88846e9859aaa039118fcedd6746e9a830b817b67d1bed571b25a40640be48
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Multiwoven::Integrations::Destination
4
+ module Odoo
5
+ include Multiwoven::Integrations::Core
6
+ class Client < DestinationConnector
7
+ def check_connection(connection_config)
8
+ connection_config = connection_config.with_indifferent_access
9
+ create_connection(connection_config)
10
+ success_status
11
+ rescue StandardError => e
12
+ failure_status(e)
13
+ end
14
+
15
+ def discover(connection_config)
16
+ connection_config = connection_config.with_indifferent_access
17
+ create_connection(connection_config)
18
+
19
+ models = @client.execute_kw(connection_config[:database], @uid, connection_config[:password],
20
+ "ir.model", "search_read", [[["transient", "=", false], ["abstract", "=", false]]], { 'fields': %w[name model] })
21
+
22
+ catalog = Catalog.new(streams: create_streams(connection_config, models))
23
+ catalog.to_multiwoven_message
24
+ rescue StandardError => e
25
+ handle_exception(e, {
26
+ context: "ODOO:DISCOVER:EXCEPTION",
27
+ type: "error"
28
+ })
29
+ end
30
+
31
+ def write(sync_config, records, _action = "destination_insert")
32
+ connection_config = sync_config.destination.connection_specification.with_indifferent_access
33
+ create_connection(connection_config)
34
+ model = sync_config.stream.name
35
+
36
+ write_success = 0
37
+ write_failure = 0
38
+ log_message_array = []
39
+
40
+ records.each do |record|
41
+ logger.debug("ODOO:WRITE:#{model} sync_id = #{sync_config.sync_id} sync_run_id = #{sync_config.sync_run_id}")
42
+ begin
43
+ response = @client.execute_kw(connection_config[:database], @uid, connection_config[:password],
44
+ model, "create", [record])
45
+ write_success += 1
46
+ log_message_array << log_request_response("info", model, response)
47
+ rescue StandardError => e
48
+ handle_exception(e, {
49
+ context: "ODOO:WRITE:#{model}",
50
+ type: "error",
51
+ sync_id: sync_config.sync_id,
52
+ sync_run_id: sync_config.sync_run_id
53
+ })
54
+ write_failure += 1
55
+ log_message_array << log_request_response("error", model, e.message)
56
+ end
57
+ end
58
+ tracking_message(write_success, write_failure, log_message_array)
59
+ rescue StandardError => e
60
+ handle_exception(e, {
61
+ context: "ODOO:WRITE:EXCEPTION",
62
+ type: "error",
63
+ sync_id: sync_config.sync_id,
64
+ sync_run_id: sync_config.sync_run_id
65
+ })
66
+ end
67
+
68
+ private
69
+
70
+ def create_streams(connection_config, models)
71
+ models.map do |model|
72
+ fields = @client.execute_kw(connection_config[:database], @uid, connection_config[:password],
73
+ model["model"], "fields_get", [], { 'attributes': %w[name type] })
74
+ Multiwoven::Integrations::Protocol::Stream.new(name: model["model"], action: StreamAction["fetch"],
75
+ supported_sync_modes: %w[incremental], json_schema: convert_to_json_schema(fields))
76
+ end
77
+ end
78
+
79
+ def convert_to_json_schema(fields)
80
+ json_schema = {
81
+ "type" => "object",
82
+ "properties" => {}
83
+ }
84
+ required_fields = []
85
+ fields.each do |field|
86
+ next if field[1]["readonly"]
87
+
88
+ column_name = field[1]["name"]
89
+ required = field[1]["required"]
90
+ type = field[1]["type"]
91
+ json_schema["properties"][column_name] = {
92
+ "type" => type
93
+ }
94
+ required_fields.push(column_name) if required
95
+ end
96
+ json_schema["required"] = required_fields
97
+ json_schema
98
+ end
99
+
100
+ def create_connection(connection_config)
101
+ common = XMLRPC::Client.new2("#{connection_config[:url]}/xmlrpc/2/common")
102
+ common.call("version")
103
+ @uid = common.call("authenticate", connection_config[:database], connection_config[:username],
104
+ connection_config[:password], { 'raise_exception': true })
105
+ @client = XMLRPC::Client.new2("#{connection_config[:url]}/xmlrpc/2/object").proxy
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,15 @@
1
+ {
2
+ "data": {
3
+ "name": "Odoo",
4
+ "title": "Odoo",
5
+ "connector_type": "destination",
6
+ "category": "Database",
7
+ "documentation_url": "https://docs.squared.ai/guides/destinations/retl-destinations/database/odoo",
8
+ "github_issue_label": "destination-odoo",
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,39 @@
1
+
2
+ {
3
+ "documentation_url": "https://docs.squared.ai/guides/destinations/retl-destinations/database/odoo",
4
+ "stream_type": "static",
5
+ "connector_query_type": "raw_sql",
6
+ "connection_specification": {
7
+ "$schema": "http://json-schema.org/draft-07/schema#",
8
+ "title": "Odoo",
9
+ "type": "object",
10
+ "required": ["url","database","username","password"],
11
+ "properties": {
12
+ "url": {
13
+ "description": "The Odoo host url.",
14
+ "type": "string",
15
+ "title": "URL",
16
+ "order": 0
17
+ },
18
+ "database": {
19
+ "description": "The Odoo database.",
20
+ "type": "string",
21
+ "title": "Database",
22
+ "order": 1
23
+ },
24
+ "username": {
25
+ "description": "The Odoo username.",
26
+ "type": "string",
27
+ "title": "Username",
28
+ "order": 2
29
+ },
30
+ "password": {
31
+ "description": "The Odoo password.",
32
+ "type": "string",
33
+ "multiwoven_secret": true,
34
+ "title": "Password",
35
+ "order": 3
36
+ }
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="0 0 126.2 40" style="enable-background:new 0 0 126.2 40;" xml:space="preserve">
5
+ <style type="text/css">
6
+ .st0{fill:#8F8F8F;}
7
+ .st1{fill:#714B67;}
8
+ </style>
9
+ <g id="Group_982" transform="translate(-13.729 -4.35)">
10
+ <path id="Path_172" class="st0" d="M60.9,38c4.9,0,8.9-4,8.9-8.9c0-4.9-4-8.9-8.9-8.9c-4.9,0-8.9,4-8.9,8.9c0,0,0,0,0,0
11
+ C51.9,34,55.9,38,60.9,38 M76.1,28.8c0.1,8.4-6.6,15.4-15,15.5c-8.4,0.1-15.4-6.6-15.5-15c0-0.2,0-0.3,0-0.5
12
+ c0.3-8.6,7.6-15.4,16.2-15.1c2.9,0.1,5.6,1,8,2.6V7.4c0.1-1.7,1.5-3.1,3.3-3.1c1.7,0,3,1.4,3.1,3.1L76.1,28.8z M92.7,38
13
+ c4.9,0,8.9-4,8.9-8.9c0-4.9-4-8.9-8.9-8.9c-4.9,0-8.9,4-8.9,8.9c0,0,0,0,0,0C83.8,34,87.8,38,92.7,38L92.7,38 M92.7,44.3
14
+ c-8.4,0-15.2-6.8-15.2-15.2c0-8.4,6.8-15.2,15.2-15.2c8.4,0,15.2,6.8,15.2,15.2c0,0,0,0,0,0C108,37.4,101.2,44.3,92.7,44.3
15
+ M124.6,38c4.9,0,8.9-4,8.9-8.9s-4-8.9-8.9-8.9c-4.9,0-8.9,4-8.9,8.9c0,0,0,0,0,0C115.7,34,119.7,38,124.6,38 M124.6,44.3
16
+ c-8.4,0-15.2-6.8-15.2-15.2c0-8.4,6.8-15.2,15.2-15.2c8.4,0,15.2,6.8,15.2,15.2c0,0,0,0,0,0C139.9,37.4,133,44.3,124.6,44.3"/>
17
+ <path id="Path_173" class="st1" d="M29,38c4.9,0,8.9-4,8.9-8.9c0-4.9-4-8.9-8.9-8.9c-4.9,0-8.9,4-8.9,8.9c0,0,0,0,0,0
18
+ C20,34,24,38,29,38 M29,44.3c-8.4,0-15.2-6.8-15.2-15.2S20.5,13.8,29,13.8S44.2,20.6,44.2,29c0,0,0,0,0,0
19
+ C44.2,37.4,37.4,44.3,29,44.3C29,44.3,29,44.3,29,44.3"/>
20
+ </g>
21
+ </svg>
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Multiwoven
4
4
  module Integrations
5
- VERSION = "0.31.2"
5
+ VERSION = "0.32.0"
6
6
 
7
7
  ENABLED_SOURCES = %w[
8
8
  Snowflake
@@ -60,6 +60,7 @@ module Multiwoven
60
60
  MicrosoftDynamics
61
61
  Qdrant
62
62
  PineconeDB
63
+ Odoo
63
64
  ].freeze
64
65
  end
65
66
  end
@@ -32,6 +32,7 @@ module Multiwoven::Integrations::Source
32
32
  connection_config = sync_config.source.connection_specification.with_indifferent_access
33
33
  create_connection(connection_config)
34
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?
35
36
  query(connection_config, query)
36
37
  rescue StandardError => e
37
38
  handle_exception(e, {
@@ -79,9 +80,14 @@ module Multiwoven::Integrations::Source
79
80
 
80
81
  def query(connection, query)
81
82
  limit = 0
83
+ offset = 0
84
+ order = "id DESC"
85
+
82
86
  limit = query.match(/LIMIT (\d+)/)[1].to_i if query.include? "LIMIT"
87
+ offset = query.match(/OFFSET (\d+)/)[1].to_i if query.include? "OFFSET"
88
+ order = query.match(/ORDER BY (.*) LIMIT/)[1] if query.include? "ORDER BY"
89
+ model = query.match(/FROM ([aA-zZ.aA-zZ]*)/i)[1]
83
90
 
84
- model = query.gsub(/LIMIT\s+\d+/i, "").gsub(/SELECT (.*) FROM/, "").strip
85
91
  columns = if query.include? "SELECT *"
86
92
  []
87
93
  else
@@ -89,9 +95,9 @@ module Multiwoven::Integrations::Source
89
95
  end
90
96
 
91
97
  records = @client.execute_kw(connection[:database], @uid, connection[:password],
92
- model, "search_read", [], { limit: limit, 'fields': columns })
98
+ model, "search_read", [], { limit: limit,
99
+ offset: offset, order: order, 'fields': columns })
93
100
  records.map do |row|
94
- puts row
95
101
  RecordMessage.new(data: row, emitted_at: Time.now.to_i).to_multiwoven_message
96
102
  end
97
103
  end
@@ -119,6 +119,7 @@ require_relative "integrations/destination/amazon_s3/client"
119
119
  require_relative "integrations/destination/microsoft_dynamics/client"
120
120
  require_relative "integrations/destination/qdrant/client"
121
121
  require_relative "integrations/destination/pinecone_db/client"
122
+ require_relative "integrations/destination/odoo/client"
122
123
 
123
124
  module Multiwoven
124
125
  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.31.2
4
+ version: 0.32.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-07-11 00:00:00.000000000 Z
11
+ date: 2025-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -660,6 +660,10 @@ files:
660
660
  - lib/multiwoven/integrations/destination/microsoft_sql/config/meta.json
661
661
  - lib/multiwoven/integrations/destination/microsoft_sql/config/spec.json
662
662
  - lib/multiwoven/integrations/destination/microsoft_sql/icon.svg
663
+ - lib/multiwoven/integrations/destination/odoo/client.rb
664
+ - lib/multiwoven/integrations/destination/odoo/config/meta.json
665
+ - lib/multiwoven/integrations/destination/odoo/config/spec.json
666
+ - lib/multiwoven/integrations/destination/odoo/icon.svg
663
667
  - lib/multiwoven/integrations/destination/oracle_db/client.rb
664
668
  - lib/multiwoven/integrations/destination/oracle_db/config/meta.json
665
669
  - lib/multiwoven/integrations/destination/oracle_db/config/spec.json