How to Contribute
There are thousands of data sources and their APIs constantly evolve, so no single team can build and maintain every connector. Within the OWOX Data Marts project we collaborate as a community to share this responsibility, and this guide explains how you can work with us to adjust existing integrations and create new ones.
When contributing, please keep the wider community in mind. We review pull requests for technical quality and for how broadly the connector can be applied beyond the contributor’s specific scenario. If you plan work that cannot be contributed back, be ready to maintain your own fork. We highly recommend designing every connector so that it can be upstreamed when requirements allow.
Prerequisites
Section titled “Prerequisites”Set up the following tools locally before you start developing:
- Node.js 22.22.0 or later (see
engines.noderequirement in rootpackage.json) - npm 10 or later (ships with the Node 22 installer)
- Git (any recent 2.x release) for working with the repository
After installing the tools, run npm install from the repository root to install all workspace dependencies, including those needed for this package.
Architecture Overview
Section titled “Architecture Overview”The @owox/connectors package is a Node.js library that bundles data source connectors and storage implementations. The package automatically discovers all connectors in the src/Sources/ directory.
Key Components
Section titled “Key Components”- Core (
src/Core/) — Abstract base classes and utilities shared across all connectors (TypeScript/JavaScript) - Sources (
src/Sources/[SOURCE_NAME]/) — Data source-specific implementations - Storages (
src/Storages/) — Storage implementations for persisting data - Constants (
src/Constants/) — Shared constants and enumerations - Configs (
src/Configs/) — Configuration utilities
Build System
Section titled “Build System”The build system automatically:
- Discovers all connectors in
src/Sources/*/ - Bundles each connector with its dependencies into isolated modules
- Generates a single distributable package with all connectors
- Creates manifests with metadata for each connector
No manual registration is required — just create your connector files in the correct location.
Creating a New Source
Section titled “Creating a New Source”For detailed step-by-step instructions on creating a new source, see Creating a New Source.
Architecture Concepts
Section titled “Architecture Concepts”Every connector runs through one engine. For each run the backend starts the connector
runner, which builds a context from the run’s configuration and hands it, with the
connector’s source and the destination storage, to AbstractConnector. A connector
contributes only its Source: there is no per-connector connector or config class.
Engine
Section titled “Engine”AbstractConnector (in src/Core/AbstractConnector.js) drives the whole import:
- Validates the configuration against the parameters the source declares
- Works out the dates for an incremental run or a manual backfill
- Imports catalog nodes first, then time series nodes one date at a time, each date for every account and every node
- Writes each node’s rows to the storage and records every date once it is complete, so an interrupted run, a manual backfill included, resumes from the day after the last one
- Attempts every account: one the API refuses with a 401 or 403 is skipped with a warning, and any other failure fails the run once the remaining accounts have had their turn
The per-date record is the engine’s job. A source does not save progress itself.
Context
Section titled “Context”AbstractContext (in src/Core/AbstractContext.js) carries one run: its parameters
(getParameter(name)), the run type, and the channel to the backend (log(level, message)
and emit(event)). A source reaches it as this.context.
Source
Section titled “Source”The Source class is responsible for fetching data from the external API. It declares its
parameters in the constructor and implements:
fetchData({ nodeName, fields, accountId, startDate, endDate })— the rows of one node for one account and one date windowisValidToRetry(error)— determine if an error is transient (optional; the default retries nothing)getAccounts(context)andgetDateStrategy(nodeName)— the accounts to import and how a time series node’s dates are requested (optional)
Helper methods available:
urlFetchWithRetry(url, options)— HTTP fetch with automatic retry, returning a nativeResponsecalculateBackoff(attemptNumber)— exponential backoff calculation
All sources must extend AbstractSource (in src/Core/AbstractSource.js). See
Creating a New Source for the full contract.
Storage
Section titled “Storage”The Storage class handles data persistence. It must implement:
init()— create the destination table, or add the columns it lackssaveData(data)— merge rows into the table by the node’s unique keysreplaceData(data)— replace the table’s contents, for full-refresh nodes (optional)
All storages must extend AbstractStorage (in src/Core/AbstractStorage.js).
The @owox/connectors package is distributed under the MIT License. By submitting a contribution to this package, you affirm that you have the right to do so and that your work will be released under the same MIT License.
To clarify the intellectual property rights granted with each contribution, we also require a signed Contributor License Agreement (“CLA”) from every contributor. This protects you as the author, the OWOX team that stewards the project, and the community that depends on these connectors, ensuring everyone can rely on consistent MIT terms within this package.
For more details, review the full OWOX CLA.
Pull request authors must sign the OWOX CLA. The signing link appears automatically once you open a PR. If you cannot sign the CLA (for example, due to employment restrictions), do not submit a PR. Instead, please open an issue so that someone else can help.
Questions
Section titled “Questions”Got a question? Feel free to ask the community:
- Check Issues
- Join Discussions