Solution Builder assembles custom openIMIS deployments from reusable JSON building blocks. It reads a "shopping list" solution file (e.g. solution/solutions/coreMIS.json), recursively merges the modules/bundles/roles/menus it references, and emits a ready-to-deploy package: frontend and backend package manifests, a Docker Compose file, navigation config, role/right fixtures, and seed data.
This is the current, JavaScript-based implementation. For the full architecture reference (library internals, catalog layout, merge semantics, conventions for changes), see AGENTS.md — it is the source of truth for this repo and is kept in sync with the code. This README is a lighter, task-oriented quick start; when the two disagree, trust AGENTS.md.
- Node.js v18+ (needed for
Blob/arrayBuffer()support used during ZIP creation)
node -vnpm installInstalls jszip, js-yaml, simple-git, unzipper, uuid, axios, glob, mime-types.
node workbench.jsworkbench.js is the batch CLI launcher. It walks a hardcoded map of named solutions (currently coreMIS, SHI, claimai, SR, IBR — see the solutions object near the top of main() in workbench.js) and, for each one:
- Calls
processSolutions()(fromsolutionBuilder.js) to merge the solution's modules, bundles, roles, menus, packages, services, and fixtures. - Clones/updates
openimis/openimis-dist_dkrinto.cache/openimis-dist_dkrand copies the Docker Compose include files and.env.examples referenced by the generatedcompose.yml. - Writes
build/<name>.zipand the equivalent unpacked folderbuild/<name>/.
node workbench.js --packages # pip/npm registry specs instead of git URLs
node workbench.js --branch=develop --force-branch # git, every module on that branch
node workbench.js --docs # also generate aggregated Confluence markup under build/<name>/docs/
node workbench.js --publish # also copy the build into a local clone of openimis/solutions--packageswritesbe-openimis.json/fe-openimis.jsonwith registry pins (openimis-be-core~=v1.9.0,@openimis/fe-core@>=v1.9.0) taken fromsolution/sources/*-sources.json. Compatible-release operators (~=/>=) are intentional so patch/bugfix versions float. Mutually exclusive with--force-branch. Compose fragments come fromopenimis-dist_dkrrelease/26.04(falls back todevelop).--branch=NAMEsets the assembly git branch for non-package builds (and the dist-dkr checkout). Default is each package's ownbranchfield, falling back to the assembly definition.--force-branchignores per-packagebranchand points every git URL at--branch(or the assembly default). Git-only; do not combine with--packages.--docsshells out toscript/generate-confluence-aggregator.jsto build aggregated markup for the solution.--publishcloneshttps://github.com/openimis/solutions.gitinto.cache/temp_solutions_repo, checks out (or reuses) a dated branchsolution/<name>/<yyyy-mm-dd>, copiesbuild/<name>/into a subfolder of that clone, and commits. The actualgit pushis currently commented out insendToExternalSolutionRepo()(workbench.js), so nothing reaches the remote automatically yet — push the branch from.cache/temp_solutions_repoyourself, or re-enable the push line, until that's wired up.- A
--folder=NAMEflag is parsed but not currently used — the batch loop always runs the hardcoded solution names above.
To add a new solution to the batch run, add an entry to the solutions map in workbench.js and, if it seeds Django fixtures, matching fixtures/core/roles-*.json / rights-*.json files under solution/fixtures/core/.
For each solution, processSolutions() returns (and workbench.js writes under build/<name>/):
be-openimis.json # backend (pip) package manifest
fe-openimis.json # frontend (npm) package manifest
compose.yml # Docker Compose includes
module_permissions_map.json # permissions actually used by included modules
consolidated-solution.json # full dump of everything merged, for inspection/debugging
fixtures/module-configuration-core.json # core module-configuration fixture (menus, logo, theme)
fixtures/roles.json # core.role fixture
fixtures/roles-right.json # core.roleright fixture
fixtures/<...initData files> # seed data copied in from the solution's initData list
- Web UI: serve the repo root with any static server and open
index.html(needs a Chromium-based browser for the File System Access directory picker). SameprocessSolutionslibrary under the hood; lets you pick a source (local folder or GitHub), toggle modules, and download a ZIP. - Role authoring helper: open
role-permissions-form.htmlthe same way. Upload amodule_permissions_map.json(or any{ "module": ["perm.a", ...] }map), tick permissions, and it downloads a<code>-role.jsonfile matching the shape expected undersolution/roles/<solution>/.
A solution file is a shopping list; the same schema is used for top-level solutions and reusable bundles. Bundles are just solutions referenced through the solutions array. Example (solution/solutions/coreMIS.json):
{
"modules": {
"grievance": "../modules/grievance.json",
"opensearch-reports": "../modules/opensearch-reports.json"
},
"solutions": [
"./social-protection-bundle.json",
"./core-bundle.json",
"../sources/main-menus.json",
"../roles/coreMIS/local_admin-role.json"
],
"services": ["db"],
"moduleConfiguration": { "logo": "./sources/logo/coremis.png" },
"initData": [
"../fixtures/core/roles-coreMIS.json",
"../fixtures/core/rights-coreMIS.json"
]
}Key fields:
| Key | Meaning |
|---|---|
modules |
Map of { logicalName: "relative/path.json" } — one JSON file per feature. |
solutions |
Other JSON files to merge recursively — bundles, role files, menu/locale packs. Later files override earlier definitions for the same package/service key. |
roles |
Inline role objects (usually roles are included as files via solutions instead). |
menus |
Extra menu entries. |
fePackages / fePackageDefinitions |
Frontend packages to install and their npm/git/version info. |
bePackages / bePackageDefinitions |
Backend packages to install and their pip/git/version info. |
services / serviceDefinitions |
Docker Compose includes. |
initData |
Fixture files copied into the output as-is. |
moduleConfiguration.logo / .theme |
Branding injected into the core module-configuration fixture. |
A module file (solution/modules/*.json) describes one feature: menus, fePackages, bePackages, initData, services, and rights (the permission names it contributes). Role assembly only keeps permissions that appear in the union of included modules' rights — see transformRolesToFixture in solutionBuilder.js.
fePackageDefinitions / bePackageDefinitions (see solution/sources/fe-sources.json and be-sources.json) map a logical package name to its registry info:
"CoreModule": {
"package": "@openimis/fe-core",
"git": "https://github.com/openimis/openimis-fe-core_js",
"version": "v1.9.0",
"branch": "develop"
}--packages (library packageMode) emits registry specs from version (@openimis/fe-core@>=v1.9.0, openimis-be-core~=v1.9.0). Otherwise the spec points at git+branch (@openimis/fe-core@https://github.com/.../fe-core_js#develop). A package can override the git branch with its own branch field unless --force-branch is set. The name used in the output (CoreModule, core, …) is exactly the key you give it in the definitions map — there's no automatic PascalCasing. Version pins in the source JSON are generated by openimis-dev-tools (gh-make-release-openimis-json.py); do not treat hand-edits as the source of truth.
Role files (solution/roles/<solution>/<code>-role.json) look like:
{
"roles": [{
"code": "local_admin",
"name": "Local Admin",
"permissions": ["core.users", "core.roles", "..."]
}]
}Permissions across role files with the same code are merged (deduped by permission name). transformRolesToFixture then maps each permission through solution/permissions_map.json into core.role / core.roleright Django fixture entries; a permission missing from the included modules' rights or from permissions_map.json is dropped with a console warning, not a hard failure.
services (a list of names) + serviceDefinitions (name → { path, env_file }) get turned into compose.yml's include list. See solution/services/*.json for the base/cache/openSearch fragments used by the shipped solutions.
The fixtures/*.json files this tool produces are standard Django fixtures for the openIMIS backend, loaded from that project, not from here:
python manage.py loaddata fixtures/core/users.jsoncore.roleright fixtures reference core.role by a natural key (e.g. uuid) rather than a database id, so load them with the custom command that resolves the foreign key first:
python manage.py load_fixture_foreign_key fixtures/roles-right.json uuid| Name | Source file | Focus |
|---|---|---|
| coreMIS | solution/solutions/coreMIS.json |
Social protection / cash transfer |
| SHI | solution/solutions/HF.json |
Social health insurance (claims, enrollment, formal sector) |
| IBR | solution/solutions/IBR.json |
Insurance-based registry hybrid |
| SR | solution/solutions/SR.json |
Social registry |
| claim-ai | solution/solutions/claim-ai.json |
Health claims + AI quality |
| full | solution/solutions/full.json |
Maximal reference (not currently in the workbench.js loop) |
Business-facing write-ups for each live in docs/ (coreMIS.md, SHI.md, IBR.md, SR.md, claim-ai.md, …).
- Solution Building / Deployment Recipe Strategy
- Configuration of Main Menu and Submenus
- List of submenu entries available in the system
- Technical approach behind configurable menus
These links describe the conceptual menu/deployment model; for the exact current menu IDs and submenu entries, read solution/sources/main-menus.json and the menus arrays in solution/modules/*.json directly rather than relying on a hardcoded table here, since those move faster than this document.