Skip to content

Fixes #26102: Add Odoo ERP Database Connector and Ingestion Topology#27645

Open
mohitjeswani01 wants to merge 6 commits intoopen-metadata:mainfrom
mohitjeswani01:feat/26102-odoo-connector
Open

Fixes #26102: Add Odoo ERP Database Connector and Ingestion Topology#27645
mohitjeswani01 wants to merge 6 commits intoopen-metadata:mainfrom
mohitjeswani01:feat/26102-odoo-connector

Conversation

@mohitjeswani01
Copy link
Copy Markdown

@mohitjeswani01 mohitjeswani01 commented Apr 22, 2026

Description:

Fixes #26102

WeMakeDevs x OpenMetadata Hackathon

Overview
This PR introduces a net-new, enterprise-grade database ingestion connector for Odoo ERP. Odoo does not expose its rich metadata (like human-readable model descriptions or relational field boundaries) through standard SQL INFORMATION_SCHEMA. To bring actual ERP business context into OpenMetadata, this connector acts as an external client, querying Odoo's internal application registries (ir.model and ir.model.fields) via XML-RPC and mapping them to OpenMetadata entities.

What changes did you make?

  1. JSON Schemas & UI Frontend: - Created odooConnection.json. To prevent UI rendering bugs, all schema types are fully inlined (flattened) rather than using nested $refs.
    • Registered the Odoo type in databaseService.json and wired the dynamic React forms in DatabaseServiceUtils.tsx alongside a new odoo.svg asset.
  2. Zero-Dependency Python Client (odoo/client.py): - Built a lightweight client utilizing Python's native xmlrpc.client. It authenticates against the Odoo /common endpoint to retrieve a session uid, then proxies queries to the /object endpoint. Zero external Odoo libraries were introduced, keeping the dependency tree clean.
  3. Pydantic Data Models & Edge Case Handling (models.py): - Built OdooModel and OdooField validators.
    • Technical Note: Handled a notorious Odoo XML-RPC edge case where empty fields return the boolean False instead of None or an empty string. The models utilize Optional[Union[str, bool]] with downstream sanitization to prevent pipeline crashes during live ingestion.
  4. Ingestion Topology (metadata.py & constants.py): - Implemented the metadata extraction layer extending CommonDbSourceService (mirroring the SAP ERP architectural pattern).
    • Overrode get_tables_name_and_type to map Odoo models to OM Tables, caching human-readable descriptions in self.context.
    • Overrode yield_table to map Odoo fields to OM Columns using a strict Odoo-to-OpenMetadata DataType mapping dictionary.
    • All extraction steps are wrapped in defensive try/except blocks yielding Either(left=StackTraceError) to ensure single-table failures do not halt the entire pipeline.
  5. Offline Test Suite & Documentation: - Wrote a localized README.md within the connector package detailing the architecture.
    • Built a comprehensive pytest suite mocking the XML-RPC ServerProxy to test the client, connection tester, and topological mapping completely offline.

**Why did you make them? **
This unlocks deep ERP metadata for data governance. By extracting the human-readable field_description and module names from Odoo, and translating complex relational fields (many2one, one2many), we immediately surface business context to the end-user. This paves the way for automated lineage mapping between Odoo ERP and downstream data warehouses directly within OpenMetadata.

How did you test your changes?

  • Schema Compilation: Ran make generate to ensure Pydantic and Java models compiled cleanly from the new odooConnection.json spec.
  • Unit Testing: Executed pytest ingestion/tests/unit/source/database/odoo/ with a 100% pass rate. Tests utilize unittest.mock.patch to simulate Odoo API responses and connection drops.
  • Formatting: Ran make py_format and make py_lint for strict CI/CD compliance.

Test Coverage Evidence:
image

Type of change:

  • New feature

Checklist:

  • I have read the CONTRIBUTING document.

  • My PR title is Fixes #26102: Add Odoo ERP Database Connector and Ingestion Topology

  • I have commented on my code, particularly in hard-to-understand areas.

  • For JSON Schema changes: I updated the migration scripts or explained why it is not needed. (Note: Migration scripts are not required as this is a net-new schema addition, not a modification of an existing schema).

  • The issue properly describes why the new feature is needed, what's the goal, and how we are building it. Any discussion or decision-making process is reflected in the issue.

  • I have updated the documentation.

  • I have added tests around the new logic.


Summary by Gitar

  • Refined ingestion logic:
    • Replaced the context based storage for table descriptions with a private _model_descriptions dictionary in OdooSource.
    • Integrated filter_by_table to support table pattern filtering during the metadata extraction process.
  • Updated schema and API methods:
    • Renamed get_database_schema_names to get_raw_database_schema_names to improve internal consistency.
    • Added the relation field to the get_model_fields client method to capture relational metadata.

This will update automatically on new commits.

Copilot AI review requested due to automatic review settings April 22, 2026 23:16
@mohitjeswani01 mohitjeswani01 requested review from a team as code owners April 22, 2026 23:16
@github-actions
Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

Comment thread ingestion/src/metadata/ingestion/source/database/odoo/client.py
Comment thread ingestion/src/metadata/ingestion/source/database/odoo/metadata.py Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Odoo ERP database connector to OpenMetadata, spanning spec/UI wiring and a Python ingestion implementation that extracts Odoo model + field metadata via XML-RPC.

Changes:

  • Register Odoo as a DatabaseServiceType and add the odooConnection schema for UI-driven configuration.
  • Implement an Odoo ingestion source (OdooSource) plus XML-RPC client (OdooClient) and type mapping for tables/columns.
  • Add offline unit tests for client/connection testing/topology mapping.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
openmetadata-ui/src/main/resources/ui/src/utils/DatabaseServiceUtils.tsx Adds Odoo connection schema selection in DB service config form logic.
openmetadata-ui/src/main/resources/ui/src/assets/svg/odoo.svg Adds an Odoo icon asset for UI usage.
openmetadata-spec/src/main/resources/json/schema/entity/services/databaseService.json Registers Odoo in database service enums and connection anyOf.
openmetadata-spec/src/main/resources/json/schema/entity/services/connections/database/odooConnection.json New JSON schema for configuring Odoo XML-RPC connectivity.
ingestion/src/metadata/utils/constants.py Adds Odoo to the non-SQLAlchemy database connection allowlist.
ingestion/src/metadata/ingestion/source/database/odoo/service_spec.py Registers the metadata source class via ServiceSpec.
ingestion/src/metadata/ingestion/source/database/odoo/models.py Adds Pydantic models for Odoo ir.model / ir.model.fields payloads.
ingestion/src/metadata/ingestion/source/database/odoo/constants.py Defines Odoo-to-OpenMetadata DataType mapping.
ingestion/src/metadata/ingestion/source/database/odoo/client.py Implements the XML-RPC client (auth + model/field queries + test calls).
ingestion/src/metadata/ingestion/source/database/odoo/connection.py Implements connector get_connection and test_connection steps.
ingestion/src/metadata/ingestion/source/database/odoo/metadata.py Implements ingestion topology overrides to emit OM Database/Schema/Table/Column entities.
ingestion/src/metadata/ingestion/source/database/odoo/README.md Adds connector-local documentation and testing notes.
ingestion/src/metadata/ingestion/source/database/odoo/init.py Package init for the new connector module.
ingestion/tests/unit/source/database/odoo/init.py Test package init.
ingestion/tests/unit/source/database/odoo/test_client.py Unit tests for XML-RPC client auth + basic calls.
ingestion/tests/unit/source/database/odoo/test_connection.py Unit tests for connection factory + test-connection wiring.
ingestion/tests/unit/source/database/odoo/test_metadata.py Unit tests for topology mapping (models → tables, fields → columns).

Comment thread ingestion/tests/unit/source/database/odoo/test_metadata.py
Comment thread ingestion/src/metadata/ingestion/source/database/odoo/README.md Outdated
Comment thread ingestion/src/metadata/ingestion/source/database/odoo/README.md
Comment thread ingestion/src/metadata/ingestion/source/database/odoo/client.py
Comment thread ingestion/src/metadata/ingestion/source/database/odoo/metadata.py
Comment thread ingestion/src/metadata/ingestion/source/database/odoo/metadata.py
Comment thread ingestion/src/metadata/ingestion/source/database/odoo/metadata.py Outdated
Comment thread ingestion/src/metadata/ingestion/source/database/odoo/metadata.py Outdated
@github-actions
Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented Apr 22, 2026

Code Review ✅ Approved 2 resolved / 2 findings

Integrates the Odoo ERP database connector and ingestion topology, resolving the missing relation field mapping and the fragile context upsert implementation. No issues found.

✅ 2 resolved
Bug: relation field never fetched from Odoo API, always None

📄 ingestion/src/metadata/ingestion/source/database/odoo/client.py:200-205 📄 ingestion/src/metadata/ingestion/source/database/odoo/metadata.py:172-173 📄 ingestion/tests/unit/source/database/odoo/test_metadata.py:89
In client.py:200-205, get_model_fields requests only ["name", "field_description", "ttype", "required"] from Odoo's ir.model.fields. The relation field is not included in the request.

However, metadata.py:172-173 checks field.relation to build the data_type_display string (e.g., "many2one (res.company)"). Since relation is never fetched, the Pydantic model defaults it to None, so relational context is silently lost for every many2one/one2many/many2many field.

The unit test masks this by injecting "relation": "res.company" directly into the mock return value (test_metadata.py:89), simulating data the real API call would never return.

Quality: Storing table description via context upsert is fragile

📄 ingestion/src/metadata/ingestion/source/database/odoo/metadata.py:144 📄 ingestion/src/metadata/ingestion/source/database/odoo/metadata.py:191
In metadata.py:144, odoo_model_description is stored via self.context.get().upsert() in the get_tables_name_and_type loop and read back in yield_table (line 191). This works because the topology runner currently processes each yielded table synchronously before advancing the iterator, but it creates an implicit coupling to the runner's execution order.

A safer pattern (used by some other connectors) is to store a dict mapping table names to descriptions and look up the description by name in yield_table. This would be resilient to any future changes in topology processing order.

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@mohitjeswani01
Copy link
Copy Markdown
Author

Hello @PubChimps , @harshach — I have addressed all 10 feedback items from the Copilot and Gitar-bot reviews.

Key Fixes Applied:
Relational Metadata: Fixed the relation field exclusion in the XML-RPC payload; many2one and one2many targets are now correctly fetched and mapped.
State Management: Replaced the shared topology context with a localized dictionary cache to prevent model description leakage.
Standardized Filtering: Refactored get_raw_database_schema_names and implemented filter_by_table to respect user-defined exclusion patterns.
Testing: Enhanced the unit test suite to mock multi-model scenarios, proving the fix for context overwrite issues.

Could you please take a look and provide the safe to test label so the CI workflows can verify the ingestion?

Thank you!🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Odoo Connector

2 participants