Skip to content

CSV import: map CSV data onto an existing metamodel#23

Open
amirrza777 wants to merge 33 commits into
mde-optimiser:mainfrom
amirrza777:amir/csv-import
Open

CSV import: map CSV data onto an existing metamodel#23
amirrza777 wants to merge 33 commits into
mde-optimiser:mainfrom
amirrza777:amir/csv-import

Conversation

@amirrza777

Copy link
Copy Markdown
Contributor

Relates to #12

Approach

Metamodels stay manually authored. A .model file can import instance data from a CSV file for an existing class, matching CSV columns to metamodel properties by name:

import CSV {
    Person from "employees.csv"
}

What's included

  • .csv registered as a first-class file type in the workbench
  • import CSV { ClassName from "file.csv" } syntax in .model files, validated against the metamodel
  • Live diagram rendering straight from the import block, no separate materialize step
  • Node positions persist across re-renders
  • Optimizer execution pipeline resolves CSV-backed models correctly
  • Auto-registration of the CSV plugin on startup
  • Removed the earlier CSV-to-model materialize endpoint (POST /api/projects/{id}/csv-import) and the metamodel-inference engine behind it, superseded by the above

Known gaps, tracked separately

Still a draft while the contribution-plugin refactor is in progress.

amirrza777 added 30 commits July 3, 2026 07:40
The POST /api/projects/{id}/csv-import endpoint materialized a CSV into
a standalone .metamodel/.model file pair using metamodel inference.
That workflow is superseded by the import CSV {} syntax, which reads
CSV data directly at render/execution time against an existing,
manually authored metamodel. Removes CsvImportService, CsvImportRoutes,
the now-unused CsvModelInference engine and its tests, the backend's
:metamodel dependency (no longer needed), and the unused
CSV_IMPORT_FAILED error code.
The import CSV {} syntax was hardcoded directly into the base model
grammar. Model now exposes a generic extension point (imports:
[BaseModelImport], mirroring Config's sections: [BaseConfigSection]),
and CSV is implemented as a real contribution plugin using the same
SerializedGrammar-based merging approach as config/config-mdeo:

- ModelContributionPlugin now carries a serialized grammar and import
  metadata instead of raw parser rule objects, so it can cross service
  boundaries as plain JSON
- resolveModelPlugins deserializes and wraps each contributed import in
  a BaseModelImport-extending rule, and createModelRule builds the root
  Model rule from whatever imports are actually resolved
- language-model-csv now builds a real contribution plugin (external
  references for Class/ID/STRING/NEWLINE so its serialized grammar
  doesn't duplicate definitions already provided by the host)
- New service-model-csv microservice mirrors service-config-mdeo:
  registers the CSV contribution with the model language and hosts its
  standalone generated language service
- Deleted the dead mutable-registry scaffolding (registerModelContributionPlugin)
  that caused the original ESM dual-instance bug, and the orphaned files
  left over from the previous abandoned attempt
- modelScopeProvider's class-reference resolution is now plugin-agnostic
  (dispatches on the "class" property generically instead of hardcoding
  CsvClassImport by type)
- Fixed a pre-existing bug in modelDataHandler where the CSV file path
  was passed as a full URI to an API expecting a plain relative path

Verified end to end: grammar parses with no errors, live diagram
rendering from CSV still works, and the model-data execution pipeline
correctly resolves CSV-backed instances.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a plugin-contribution mechanism for the Model language to support import CSV { ClassName from "file.csv" } blocks, and wires up new CSV-related language/services so CSV files become first-class in the workbench and can drive live diagram rendering.

Changes:

  • Adds a generic “model contribution plugin” framework that lets external plugins contribute import <KEYWORD> { ... } blocks into the Model language grammar.
  • Adds CSV language + services (language-csv, service-csv) and the Model CSV contribution (language-model-csv, service-model-csv) plus docker/nginx/compose wiring.
  • Updates model file-data computation and diagram rendering to recognize CSV imports and synthesize instances/nodes from CSV-backed data.

Reviewed changes

Copilot reviewed 43 out of 49 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
platform/common/src/main/kotlin/com/mdeo/common/model/ApiResult.kt Whitespace-only change in error code list area.
platform/backend/build.gradle.kts Whitespace-only change in dependencies block.
infra/docker/workbench/nginx.conf Adds reverse-proxy routes for /plugin/csv/ and /plugin/model-csv/.
infra/docker/workbench/Dockerfile Increases Node build memory for workbench build step.
infra/docker/service-model-csv/Dockerfile New container build for service-model-csv.
infra/docker/service-csv/Dockerfile New container build for service-csv.
infra/docker-compose-dev.yaml Registers CSV plugins in defaults and adds new CSV services/env vars.
app/tsconfig.build.json Includes new CSV-related packages in the build references.
app/packages/service-model/tsconfig.json Adds reference to language-model-csv.
app/packages/service-model/src/index.ts Updates Model service plugin registration (now only .m).
app/packages/service-model/src/handler/modelDataHandler.ts Adds CSV-import-aware ModelData computation path.
app/packages/service-model/package.json Adds dependency on @mdeo/language-model-csv.
app/packages/service-model-csv/vite.config.ts New Vite build for serving the model-csv language module.
app/packages/service-model-csv/tsconfig.json New TS config for service-model-csv.
app/packages/service-model-csv/src/served/language.ts Re-exports model-csv language plugin provider for server import.
app/packages/service-model-csv/src/index.ts New service registering the model-csv contribution for the model language.
app/packages/service-model-csv/package.json New package definition for @mdeo/service-model-csv.
app/packages/service-csv/vite.config.ts New Vite build for serving the CSV language module.
app/packages/service-csv/tsconfig.json New TS config for service-csv.
app/packages/service-csv/src/served/language.ts Re-exports CSV language plugin provider for server import.
app/packages/service-csv/src/index.ts New service registering .csv as a first-class language plugin.
app/packages/service-csv/package.json New package definition for @mdeo/service-csv.
app/packages/language-model/src/plugin/resolvePlugins.ts New resolver that merges contributed import grammars into the Model language.
app/packages/language-model/src/plugin/modelContributionPlugin.ts New contribution plugin contract for model import formats.
app/packages/language-model/src/modelPlugin.ts Updates Model plugin creation to resolve and include contribution plugins.
app/packages/language-model/src/index.ts Exports contribution plugin types/resolution helpers.
app/packages/language-model/src/grammar/modelTypes.ts Adds BaseModelImport + Model.imports to host contributed imports.
app/packages/language-model/src/grammar/modelRules.ts Adds createModelRule() to include contributed import wrapper rules.
app/packages/language-model/src/features/modelScopeProvider.ts Broadens class-reference scoping to work inside contributed import blocks.
app/packages/language-model/src/features/diagram-server/modelMetadataManager.ts Persists metadata for synthetic CSV nodes across re-renders.
app/packages/language-model/src/features/diagram-server/modelGModelFactory.ts Creates synthetic diagram nodes based on CSV import blocks.
app/packages/language-model-csv/tsconfig.json New TS config for language-model-csv.
app/packages/language-model-csv/src/plugin/modelCsvContributionPlugin.ts Defines the CSV model-contribution plugin and its serialized grammar.
app/packages/language-model-csv/src/index.ts Exports CSV import grammar + contribution plugin + import feature helpers.
app/packages/language-model-csv/src/grammar/csvImportTypes.ts Defines AST types for import CSV { ... } content.
app/packages/language-model-csv/src/grammar/csvImportRules.ts Defines grammar rules for CSV import content block.
app/packages/language-model-csv/src/features/csvImport.ts Implements CSV-to-ModelData instance/link mapping.
app/packages/language-model-csv/src/csvPlugin.ts Standalone “model-csv” generated language plugin for parsing CSV import blocks.
app/packages/language-model-csv/package.json New package definition for @mdeo/language-model-csv.
app/packages/language-csv/tsconfig.json New TS config for language-csv.
app/packages/language-csv/src/index.ts Exports CSV language grammar/plugin.
app/packages/language-csv/src/grammar/csvTypes.ts Defines a minimal AST for a CSV file.
app/packages/language-csv/src/grammar/csvRules.ts Defines a permissive CSV grammar (captures full content).
app/packages/language-csv/src/csvPlugin.ts Defines the CSV language plugin provider.
app/packages/language-csv/package.json New package definition for @mdeo/language-csv.
app/package-lock.json Adds workspace links/deps for new packages.
.gitignore Ignores .DS_Store.
Files not reviewed (1)
  • app/package-lock.json: Generated file
Comments suppressed due to low confidence (1)

infra/docker-compose-dev.yaml:118

  • workbench proxies /plugin/csv/ to service-csv, but workbench.depends_on does not wait for service-csv to become healthy. This can cause startup-time 502s / missing plugin manifests while the CSV service is still starting.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +55 to +59
location ^~ /plugin/csv/ {
proxy_pass ${PLUGIN_CSV_SERVICE}/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
Comment thread app/packages/service-model/src/index.ts Outdated
Comment on lines +52 to +56
id: "model-service",
name: "Model",
description: "Language support for model definitions (.m files)",
icon: convertIcon(BookOpen),
languagePlugins: [modelLanguagePlugin],
Comment on lines +94 to +100
const result = importCsvEntries(csvEntries, metamodelClasses);

const modelData: ModelData = {
metamodelPath,
instances: result.instances,
links: result.links
};
Comment on lines +497 to +500
const uri = resolveRelativePath(doc, entry.file ?? "");
const csvContent = await this.modelState.languageServices.shared.workspace.FileSystemProvider.readFile(uri);
const rows = csvContent.split(/\r?\n/).filter((line: string) => line.trim() !== "").map((line: string) => line.split(","));
if (rows.length < 2) continue;
@amirrza777
amirrza777 marked this pull request as ready for review July 14, 2026 17:01
npm ci in CI failed because these two new packages, added as part of
the contribution-plugin refactor, were never registered in the
committed lock file.
workbench proxies /plugin/csv/ to service-csv but was missing it from
depends_on, unlike every other plugin service. Could cause startup-time
502s or a missing plugin manifest while service-csv was still starting.
@amirrza777

Copy link
Copy Markdown
Contributor Author

Two fixes pushed since this went up:

  • CI was failing on every service build. npm ci needs package.json and package-lock.json to be in sync, and the two new packages from the contribution plugin refactor, language-model-csv and service-model-csv, never got added to the committed lock file. Fixed and pushed.

  • Copilot's review caught a real issue: workbench proxies /plugin/csv/ to service-csv but was missing it from depends_on, unlike every other plugin service. Could cause startup-time 502s or a missing plugin manifest while service-csv was still starting up. Fixed.

Everything else in the PR description still stands. Ready for another look whenever you get a chance.

@szschaler

Copy link
Copy Markdown
Member

@amirrza777 what about the other Copilot review comments?

@szschaler

Copy link
Copy Markdown
Member

What would help me massively with this (and #26) is a short demo. Perhaps you could put together a quick show-and-tell video and share it either in this PR or via email (link rather than attachment of course)?

- nginx: /plugin/csv/ location was missing the proxy_http_version,
  forwarded headers, and cross-origin headers every other plugin
  location has, breaking ESM module loading and cross-origin isolation
  for the CSV plugin specifically.
- service-model: register the generated model language (.m_gen) that
  was dropped when this service was rewritten for the CSV contribution
  plugin refactor, even though the platform still produces .m_gen
  files (e.g. optimizer solutions) and language-model still has full
  support for it.
- modelDataHandler: a CSV import present in a .m file was replacing
  all hand-authored objects/links instead of merging with them, so
  mixing manually-authored data with CSV-imported data in the same
  file silently lost the manual entries.
- Moved the quote-aware CSV row/field parser out of
  language-model-csv (used by the backend import) into language-shared
  so language-model's live diagram rendering (modelGModelFactory) can
  use the same correct parser instead of a naive split(",")/split("\n")
  that miscounts rows when a field contains a quoted comma or newline.

Verified live: mixed manual + CSV objects now render together in the
diagram (confirmed with a hand-authored Company instance alongside
CSV-imported Person rows), .m_gen is now registered in the plugin
manifest, and the /plugin/csv/ headers match the other plugin
locations.
@amirrza777

amirrza777 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Addressed all four @szschaler :

  1. /plugin/csv/ in nginx.conf was missing the proxy_http_version, forwarded headers, and cross-origin headers every other plugin location has. Brought it in line with the others.

  2. service-model no longer registered the generated model language (.m_gen), which is a real regression from the rewrite: the platform still produces .m_gen files (optimizer solutions), and language-model still has full support for it, it just wasn't wired up in this service anymore. Registered it the same way the classic pipeline does, confirmed via the plugin API that .m_gen now shows up in the manifest (isGenerated: true) alongside .m.

  3. modelDataHandler was replacing hand-authored objects/links entirely whenever a CSV import was present, instead of merging with them, so a .m file mixing manual data and CSV-imported data would silently lose the manual entries. Now merges both. Verified live: added a hand-authored acme : Company instance alongside a CSV import of Person rows, both render together in the diagram now.

  4. The live diagram rendering path (modelGModelFactory, separate from the backend import) had its own naive split(",") / split("\n") for counting CSV rows, which miscounts if a field contains a quoted comma or embedded newline. The backend import path already had a proper quote-aware parser for this; moved it into language-shared so both paths use the same correct one instead of two different implementations of the same problem.

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.

3 participants