Thank you for considering contributing to Integration Control Plane! This guide will help you get started.
This project follows the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code.
Before creating a bug report, check existing issues to avoid duplicates. Use the bug report template and include:
- Clear, descriptive title
- Steps to reproduce
- Expected vs actual behavior
- Environment details (Node version, MongoDB version, Docker/bare metal)
- Relevant logs or screenshots
Use the feature request template and explain:
- The problem you're trying to solve
- Your proposed solution
- Who benefits from this feature
-
Fork the repo and create your branch from
main:git checkout -b feature/my-new-feature
-
Make your changes following the code style guidelines below
-
Add tests for any new code (see Testing below)
-
Ensure CI passes locally:
# Backend cd backend && npm run check && npm test # Frontend cd frontend && npm run check && npm test
-
Write clear commit messages using Conventional Commits:
type(scope): subjectTypes:
feat,fix,docs,style,refactor,perf,test,choreExamples:
feat(integrations): add GraphQL webhook support fix(auth): resolve JWT token expiration issue test(dlq): add bulk retry route tests -
Push to your fork and open a pull request to
main
- Node.js 18+
- MongoDB 6.0+
- Docker 20.10+ (optional)
- Git
# Clone your fork
git clone https://github.com/YOUR_USERNAME/integration-control-plane.git
cd integration-control-plane
# Backend
cd backend
npm install
cp config.example.json config.json # Edit with your local settings
# Frontend
cd ../frontend
npm install
cp .env.example .env
# Start development servers
cd ../backend && npm run dev # Terminal 1
cd ../frontend && npm run dev # Terminal 2This project uses Husky with lint-staged. After npm install in backend/, Biome will auto-format staged files on every commit. No setup required — it runs automatically.
We use Biome for formatting and linting (not ESLint/Prettier):
# Check for issues
npm run check
# Auto-fix
npm run check:fixAll code must pass npm run check before merging. The pre-commit hook handles this automatically for staged files.
cd backend
npm test # Run all tests
npm run test:ci # CI mode with coverage
# Run specific test files
cd test
npx jest --testPathPattern=routes-dlq
npx jest --testPathPattern=routes-usersCoverage thresholds are enforced in CI — new code in PRs should aim for 50%+ coverage.
All route tests follow a consistent pattern. Here's a minimal example:
'use strict';
const express = require('express');
const request = require('supertest');
// Mock dependencies before requiring route
jest.mock('../../src/mongodb', () => ({
getDb: jest.fn(),
getDbSafe: jest.fn(),
isConnected: jest.fn(() => true)
}));
jest.mock('../../src/db', () => ({
isConfigured: jest.fn(() => false),
ping: jest.fn(async () => false)
}));
jest.mock('../../src/data/store', () => ({
initStore: jest.fn(async () => {}),
getTenant: jest.fn(() => null),
findTenantByChildRid: jest.fn(() => null)
}));
jest.mock('../../src/logger', () => ({
log: jest.fn(),
logError: jest.fn(),
requestLogger: (_req, _res, next) => next(),
setDb: jest.fn(),
closeLogStreams: jest.fn()
}));
jest.mock('../../src/config', () => ({
api: { basePrefix: '/api/v1' },
security: { jwtSecret: 'test-secret' },
worker: {}
}));
jest.mock('../../src/middleware/rate-limit', () => (_req, _res, next) => next());
jest.mock('../../src/middleware/request-id', () => (req, _res, next) => {
req.id = 'req-test-id';
next();
});
// Mock your data layer
const mockData = {
listItems: jest.fn(async () => []),
};
jest.mock('../../src/data', () => mockData);
function buildApp() {
const app = express();
app.use(express.json());
// Inject auth context
app.use((req, _res, next) => {
req.orgId = 1;
req.user = { id: 'user-123' };
next();
});
const router = require('../../src/routes/your-route');
const errorHandler = require('../../src/middleware/error-handler');
app.use('/api/v1/your-route', router);
app.use(errorHandler);
return app;
}
describe('Your Route', () => {
let app;
beforeEach(() => {
jest.clearAllMocks();
app = buildApp();
});
it('returns 200 with items', async () => {
const res = await request(app).get('/api/v1/your-route');
expect(res.status).toBe(200);
expect(res.body).toHaveProperty('items');
});
});Key patterns:
- Always mock
mongodb,db,data/store,logger,config,rate-limit,request-id - Use
buildApp()inbeforeEachso each test gets a fresh Express app - Call
jest.clearAllMocks()inbeforeEachto reset all mock call counts - Mock the data layer, not MongoDB collections directly (tests should exercise route logic, not DB queries)
- Use exact assertions —
toBe(200), nottoBeOneOf([200, 201]) - Test both success and error paths — 200, 400, 404, etc.
- Routes using
ObjectIdfrom mongodb require valid 24-character hex strings as test IDs (e.g.,'507f1f77bcf86cd799439011')
cd frontend
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:coverage # With coverage reportFrontend tests use Vitest with React Testing Library. Place test files in src/tests/ or colocate them with components as *.test.tsx.
All data is scoped by orgId. The middleware extracts this from JWT/API key context and injects it into every query. Always include orgId when writing new data layer queries — never query across organizations.
integration-control-plane/
├── backend/
│ ├── src/
│ │ ├── routes/ # Express routers (30 files)
│ │ ├── data/ # MongoDB data access (domain modules)
│ │ ├── services/ # Business logic + AI providers
│ │ ├── middleware/ # Auth, RBAC, rate-limit, audit, error handling
│ │ ├── processor/ # Background worker processes
│ │ ├── adapters/ # Event source adapters (MySQL, Kafka, HTTP Push)
│ │ └── rbac/ # Role/permission definitions
│ └── test/ # Jest test suite
├── frontend/
│ ├── src/
│ │ ├── features/ # Domain feature modules
│ │ ├── components/ # Shared UI components
│ │ ├── services/ # API client (Axios)
│ │ └── tests/ # Vitest test suite
├── .github/
│ ├── workflows/ # CI + Docker build pipelines
│ └── ISSUE_TEMPLATE/ # Bug report + feature request forms
└── docker-compose.yml
main— Production-ready codefeature/*— New featuresfix/*— Bug fixesdocs/*— Documentation updatesrefactor/*— Code refactoring
- Fill out the PR template completely
- Ensure all CI checks pass (tests, linting, build)
- Maintainers will review within 48-72 hours on weekdays
- Address any requested changes and push updates
- Maintainers will merge once approved
Releases follow Semantic Versioning:
- MAJOR: Breaking changes
- MINOR: New features (backwards compatible)
- PATCH: Bug fixes (backwards compatible)
By contributing, you agree that your contributions will be licensed under the GNU Affero General Public License v3.0.