Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
53dd3e0
chore(branding): update MetaForge logo and application icon
qianmoQ Oct 27, 2026
de0c901
refactor(plugin): unify redis jdbc connector
qianmoQ Oct 27, 2026
94bc465
feat(server): migrate legacy redis source types
qianmoQ Oct 27, 2026
4eac7ee
feat(query): redesign query console with IDE-style three-pane layout
qianmoQ Oct 27, 2026
8b29ada
feat(query): add editor settings dropdown and cmd+enter to run
qianmoQ Oct 27, 2026
5c5992a
fix(web): build metadata api client per call to keep auth header
qianmoQ Oct 27, 2026
a6e267a
feat(query): add query plan execution for supported engines
qianmoQ Oct 27, 2026
2e608ce
feat(query): resizable console panes with persisted layout
qianmoQ Oct 27, 2026
2d9c684
feat(query): run the selected SQL from the execute menu
qianmoQ Oct 27, 2026
f068659
feat(query): stretch sparse result columns to fill the grid
qianmoQ Oct 27, 2026
e372ebc
feat(query): surface the snippet library as the template tab
qianmoQ Oct 27, 2026
869db6e
feat(ui): add branded assembly loading animation
qianmoQ Oct 27, 2026
73ec007
fix(query): keep the source picker aligned with the panel width
qianmoQ Oct 27, 2026
195d069
fix(query): render tree icons, drop guide lines and unify loading
qianmoQ Oct 27, 2026
ba29738
fix(query): keep tab labels on one line with hover tooltips
qianmoQ Oct 27, 2026
5fcd966
feat(ui): add branded logo empty states
qianmoQ Oct 27, 2026
240aa31
fix(ui): space tree rows four pixels apart
qianmoQ Oct 27, 2026
450abd0
fix(query): keep metadata databases collapsed after selecting a source
qianmoQ Oct 27, 2026
380466e
fix(web): use the shared loading icon for username availability check
qianmoQ Oct 27, 2026
86e96ef
fix(query): load the structure tab with the shared spinner
qianmoQ Oct 27, 2026
20d493d
refactor(ui): reuse branded empty states for initial prompts
qianmoQ Oct 27, 2026
ee57ff6
fix(ui): gap the parent row and its first tree child
qianmoQ Oct 27, 2026
cf02ade
fix(query): scroll the side panel body when content overflows
qianmoQ Oct 27, 2026
f973238
fix(ui): replace the button icon with the loading glyph
qianmoQ Oct 27, 2026
d76f0a7
fix(query): show the source plugin icon on the picker trigger
qianmoQ Oct 27, 2026
af1f79a
fix(web): render ace editors inside flex and overlay hosts
qianmoQ Oct 27, 2026
322ffeb
feat(dataset): rework the list page into the shared filter-table layout
qianmoQ Oct 28, 2026
6fdde38
feat(dataset): split list columns and cap the name width
qianmoQ Oct 28, 2026
4b8b46e
feat(dataset): drop the create-another-dataset promo under the list
qianmoQ Oct 28, 2026
f5d5cb1
fix(dataset): keep preview cells on one line with tooltips
qianmoQ Oct 28, 2026
470a7bb
feat(dataset): land the in-flight dataset console rework
qianmoQ Oct 28, 2026
be7969d
fix(dataset): truncate the source summary name with a tooltip
qianmoQ Oct 28, 2026
1fe8f1b
fix(ui): flip the select popup when the viewport has no room below
qianmoQ Oct 28, 2026
07a5a8e
feat(dataset): configure column type and mode inline
qianmoQ Oct 28, 2026
3f8ffa2
feat(dataset): default new column names to the source field name
qianmoQ Oct 28, 2026
a21dbef
fix(dataset): keep the insert selectors showing the picked value
qianmoQ Oct 28, 2026
11de13d
fix(dataset): clear the stale column error after a successful save
qianmoQ Oct 28, 2026
6b05fb7
fix(dataset): apply the preview fixes to the save step
qianmoQ Oct 28, 2026
39dd4c9
fix(dataset): stop serializing the lazy user sources collection
qianmoQ Oct 28, 2026
be086c5
feat(dataset): label the snippet drawer as a template in the console
qianmoQ Oct 28, 2026
f47fdd7
fix(dataset): balance the list columns and fall back empty row counts
qianmoQ Oct 28, 2026
0996bb1
fix(web): tighten table action spacing and balance dataset columns
qianmoQ Oct 28, 2026
4478398
fix(ui): teleport the select popup out of clipping ancestors
qianmoQ Oct 28, 2026
893f83d
fix(dataset): match the user list header with a bare list title
qianmoQ Oct 28, 2026
45f0882
fix(ui): close dropdown menus when an item is clicked
qianmoQ Oct 28, 2026
c0e7304
fix(ui): drop the scale on the select popup transition
qianmoQ Oct 28, 2026
42232b8
fix(ui): keep click-focused tooltips hidden after overlays close
qianmoQ Oct 28, 2026
775abeb
feat(query): create datasets from query results
qianmoQ Oct 28, 2026
b120d62
fix(dataset): validate the wizard step form inline under the inputs
qianmoQ Oct 28, 2026
2035019
fix(ui): cap the teleported select popup height
qianmoQ Oct 28, 2026
c807e2d
ci: add a database matrix workflow for the service test suite
qianmoQ Oct 28, 2026
f86fd28
feat(server): add a versioned multi-database schema changelog
qianmoQ Oct 29, 2026
d428934
refactor(service)!: replace Spring Data JPA with Spring Data JDBC
qianmoQ Oct 29, 2026
592722b
feat(setup): install into PostgreSQL, SQL Server or Oracle
qianmoQ Oct 29, 2026
5d71418
ci: run the persistence suite on five databases
qianmoQ Oct 29, 2026
f9073d2
chore(schema): retire the MySQL schema snapshot
qianmoQ Oct 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/database-matrix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: database matrix

# Runs the metadata persistence suite (changelog, entity mapping, filters, setup install)
# against every database the setup wizard offers. Databases start in Testcontainers.
on:
push:
branches: [ main, master, 2026.0.0 ]
paths:
- 'pom.xml'
- 'core/metaforge-service/**'
- 'core/metaforge-server/pom.xml'
- 'core/metaforge-server/src/**'
- '.github/workflows/database-matrix.yml'
pull_request:
branches: [ main, master, 2026.0.0 ]
paths:
- 'pom.xml'
- 'core/metaforge-service/**'
- 'core/metaforge-server/pom.xml'
- 'core/metaforge-server/src/**'
- '.github/workflows/database-matrix.yml'

concurrency:
group: database-matrix-${{ github.ref }}
cancel-in-progress: true

jobs:
tests:
name: ${{ matrix.database }}
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
database: [ 'h2', 'mysql:8.0', 'postgres:16', 'sqlserver:2022', 'oracle:23' ]

steps:
- uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
cache: maven

- name: Build the server and its modules
run: >
./mvnw -B -q install -pl core/metaforge-server -am
-DskipTests -Dcheckstyle.skip -Dspotbugs.skip -Dmaven.javadoc.skip=true -Dgpg.skip
-Dskip.pnpm -Dskip.npm -Dskip.installnodenpm

- name: Run the persistence suite on ${{ matrix.database }}
run: >
./mvnw -B -pl core/metaforge-server
org.apache.maven.plugins:maven-surefire-plugin:3.2.2:test
-Dtest='DatabaseMigratorTest,EntityMappingTest,TableFilterTest,SetupInstallTest'
-Dsurefire.failIfNoSpecifiedTests=true
-Dmetaforge.it.database=${{ matrix.database }}

- name: Upload test reports
if: failure()
uses: actions/upload-artifact@v4
with:
name: surefire-${{ strategy.job-index }}
path: core/metaforge-server/target/surefire-reports
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div align="center">

<img width=68 height=65 src="core/metaforge-web/public/static/images/logo.png" />
<img width="68" height="68" alt="MetaForge" src="core/metaforge-web/public/static/images/logo.png" />

# MetaForge

Expand Down
2 changes: 1 addition & 1 deletion README.ru.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div align="center">

<img width=68 height=65 src="core/metaforge-web/public/static/images/logo.png" />
<img width="68" height="68" alt="MetaForge" src="core/metaforge-web/public/static/images/logo.png" />

# MetaForge

Expand Down
2 changes: 1 addition & 1 deletion README.zh_CN.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div align="center">

<img width=68 height=65 src="core/metaforge-web/public/static/images/logo.png" />
<img width="68" height="68" alt="MetaForge" src="core/metaforge-web/public/static/images/logo.png" />

# MetaForge

Expand Down
188 changes: 188 additions & 0 deletions configure/etc/conf/i18n/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1019,3 +1019,191 @@ navigation.dataDevelopment=Data development
navigation.dataAnalysis=Data analysis
navigation.systemManagement=Administration
navigation.accountSettings=Account settings

## Query console (2026-10-27 redesign)
query.tree.search=Search databases, tables, columns
query.tip.noMetadata=No metadata available
query.tip.noSource=No data sources found
query.tip.focus=Focus mode (hide side panels)
query.tip.exitFocus=Exit focus mode
query.tip.copySuccess=Copied to clipboard
query.message.success=Executed successfully
query.message.failed=Execution failed
query.message.elapsed=Query elapsed
query.message.returnedRows=Returned {0} rows
query.result.results=Results
query.result.messages=Messages
query.result.addToDataset=Add to dataset
query.result.searchResults=Search in results
query.result.empty=No data
query.result.emptyTip=The current query did not return any data
query.result.initial=Ready to query
query.result.initialTip=Choose a data source and run SQL to see results here.
query.result.perPage=Per page
query.result.rowsUnit=rows
query.side.addDataset=Add dataset
query.side.searchDataset=Search datasets
query.side.searchHistory=Search history
query.side.loadAndRun=Load and run
query.side.copySql=Copy SQL
query.side.openDetail=Open details
query.side.edit=Edit
query.side.empty=Nothing here yet
query.side.structureTip=Select a table on the left to view its columns
query.tip.editorSettings=Editor settings
query.common.plan=Query Plan
query.side.searchTemplate=Search templates

# Dataset creation and editing
dataset.editor.intro=Configure a source and its fields, then preview and save.
dataset.editor.listHint=Manage datasets, synchronization and analysis. Create manually or from SQL results.
dataset.editor.basic=Basic information
dataset.editor.source=Data source
dataset.editor.columns=Columns
dataset.editor.preview=Preview and save
dataset.editor.steps=Dataset setup steps
dataset.editor.fromQuery=From SQL query
dataset.editor.basicHint=Provide a name and description, and choose an installed executor and sync mode.
dataset.editor.namePlaceholder=Enter a descriptive dataset name
dataset.editor.sourceHint=Choose a source, then select a table or write SQL to retrieve fields.
dataset.editor.inheritedHint=The source, executed SQL and result fields were inherited. Preview to confirm.
dataset.editor.selectTable=Select table
dataset.editor.customSql=Custom SQL
dataset.editor.generatedHint=SQL is generated by the source service without a preview row limit. Switch to custom SQL to edit it.
dataset.editor.runPreview=Run preview
dataset.editor.verified=Retrieved {0} fields
dataset.editor.sourceRequired=Choose a source and provide SQL or select a table.
dataset.editor.previewRequired=Preview the current source and SQL before continuing.
dataset.editor.columnSummary={0} columns · {1} metrics. Edit a column to configure its properties.
dataset.editor.searchColumns=Search names, aliases or source fields
dataset.editor.addColumn=Add virtual column
dataset.editor.editColumn=Edit column properties
dataset.editor.sourceField=Source field
dataset.editor.keys=Storage keys
dataset.editor.customColumn=Custom virtual column
dataset.editor.storageProperties=Storage properties
dataset.editor.virtualHint=Virtual columns are calculated by the storage engine in adhoc queries, without physical storage, storage keys or lifecycle settings.
dataset.editor.keyHint=Choose at least one physical order or primary key. Sampling keys must also be order keys.
dataset.editor.columnRequired=Column name and alias are required.
dataset.editor.duplicateName=Column names must be unique.
dataset.editor.duplicateAlias=Column aliases must be unique.
dataset.editor.noColumns=No fields were returned. Check the SQL or selected table.
dataset.editor.ambiguousHeaders=Result field names are duplicated. Assign unique aliases in SQL.
dataset.editor.executorRequired=Choose an installed executor.
dataset.editor.scheduleRequired=Timed sync requires an installed scheduler and an expression.
dataset.editor.lifeCycleRequired=Choose a valid date column, lifecycle unit and positive duration.
dataset.editor.previewHint=Review the source and fields. The table shows source query results; column properties are applied during dataset construction after saving.
dataset.editor.sourcePreview=Source data preview
dataset.editor.previewCount=Showing the first {0} rows · {1} rows returned
dataset.editor.emptyPreview=The query succeeded with no rows. The field configuration can still be saved.
dataset.editor.asyncHint=After saving, the server builds the dataset and synchronizes it as configured. Follow its status in the dataset list.
dataset.editor.submitted=Dataset configuration submitted. Follow the build status in the list.
dataset.editor.requestFailed=Request failed. Please retry.
dataset.editor.backToList=Back to datasets
dataset.editor.previous=Previous
dataset.editor.next=Next
dataset.editor.moveUp=Move column up
dataset.editor.moveDown=Move column down
dataset.editor.discardTitle=Discard unsaved changes?
dataset.editor.discardHint=Unsaved configuration will be lost when you leave.
dataset.editor.discard=Discard and leave
dataset.editor.draftFailed=Could not store the query draft. Check browser storage and retry.
dataset.editor.createFromResult=Create dataset from results
dataset.editor.loading=Loading dataset configuration…

dataset.editor.pluginUnavailable=not loaded
dataset.editor.chooseExecutor=Choose an executor
dataset.editor.chooseScheduler=Choose a scheduler
dataset.editor.noExecutors=No executors loaded
dataset.editor.noSchedulers=No schedulers loaded
dataset.editor.noExecutorsHint=The server has not loaded an executor plugin. Check plugin loading, then refresh the options before creating a dataset.
dataset.editor.virtualExpression=Functions and calculations
dataset.editor.expressionLabel=Calculation expression
dataset.editor.virtualExpressionRequired=A calculation expression is required for virtual columns.
dataset.editor.virtualEngine=Evaluated by dataset storage engine {0}, which may use a different SQL dialect from the source.
dataset.editor.insertField=Insert physical field
dataset.editor.insertFunction=Insert function
dataset.editor.noFunctions=No catalog functions available. You can type an expression directly.
dataset.editor.expressionPlaceholder=Example: round(`column_3` * 1.1, 2)
dataset.editor.expressionHint=Reference physical columns and combine scalar functions and arithmetic. Saving checks syntax and field references; engine execution determines function support and result types. Configure aggregation in adhoc metric settings.
dataset.editor.sourceOrFormula=Source field / formula
dataset.editor.deleteColumn=Delete column

# Dataset prototype alignment
dataset.prototype.stepDescription=Step {0} / 4 · {1}
dataset.prototype.fillBasic=Basic information
dataset.prototype.nameHelp=Use a descriptive name to identify this dataset in lists and reports.
dataset.prototype.sourceSafe=Use read-only SQL to query the source. Data is copied to separate dataset storage according to the sync mode; SQL and columns can be edited later.
dataset.prototype.creationMode=Creation mode
dataset.prototype.manualCreation=Manual creation
dataset.prototype.manualModeHint=Choose a source and a table, or write SQL.
dataset.prototype.queryModeHint=Reuse the query source, SQL and identified fields.
dataset.prototype.creationFlow=Creation flow
dataset.prototype.flow1=Name the dataset and configure synchronization
dataset.prototype.flow2=Choose a table or SQL
dataset.prototype.flow3=Configure fields and virtual formulas
dataset.prototype.flow4=Review data and save configuration
dataset.prototype.chooseSource=Choose a data source
dataset.prototype.inheritQuery=Reuse the query result
dataset.prototype.visualSelection=Browse tables
dataset.prototype.writeSql=Write a query
dataset.prototype.databaseType=Database type
dataset.prototype.generatedSql=Generated SQL
dataset.prototype.querySql=SQL query
dataset.prototype.identifyFields=Run and identify fields
dataset.prototype.sourceSummary=Source summary
dataset.prototype.sourceMethod=Source method
dataset.prototype.targetObject=Selected object
dataset.prototype.identifiedFields=Identified fields
dataset.prototype.sampleRows=First {0} rows
dataset.prototype.waitPreview=Waiting for preview
dataset.prototype.waitPreviewHint=Choose a source and run the query to inspect its results.
dataset.prototype.confirmInfo=Confirm configuration
dataset.prototype.previewValidation=Preview and validation
dataset.prototype.refreshPreview=Refresh preview
dataset.prototype.sqlDefinition=SQL definition
dataset.prototype.fromSql=From current SQL
dataset.prototype.resultCreation=Create from the query result
dataset.prototype.resultCreationHint=Carry over the executed source, SQL and result fields.
dataset.prototype.sqlCreation=Create from current SQL
dataset.prototype.sqlCreationHint=Use the editor SQL and identify its fields in the next step.
dataset.prototype.chooseCreation=Choose how to create a dataset
dataset.prototype.chooseCreationHint=Choose the starting point for this dataset.
dataset.prototype.resultSize=Query result: {0} rows · {1} columns
dataset.prototype.creationCarry=The selected source, SQL and fields are carried to the next step.
dataset.prototype.continueCreation=Continue
dataset.prototype.moreCreation=More dataset actions
dataset.prototype.applyExisting=Apply to an existing dataset
dataset.prototype.mappingHint=Apply this query definition and field mapping to an existing dataset.
dataset.prototype.uniqueMapping=Each target field can map to only one source field.
dataset.prototype.mappingRequired=Map every target physical field to a unique source field.
dataset.prototype.sourceField=Source field
dataset.prototype.targetField=Target dataset field
dataset.prototype.targetDataset=Target dataset
dataset.prototype.loadMore=Load more datasets
dataset.prototype.fieldUnit=columns
dataset.prototype.replaceDefinition=Replace query definition
dataset.prototype.replaceDefinitionHint=Update this dataset with the current SQL and mapping. Saving rebuilds and synchronizes dataset storage using the existing process; the original source stays unchanged.
dataset.prototype.fieldMapping=Field mapping
dataset.prototype.chooseTarget=Choose a target dataset
dataset.prototype.mapped=Mapped
dataset.prototype.ignored=Ignore source field
dataset.prototype.mappingFooter=Target field identities, types and virtual formulas are retained. Appending query versions is unavailable.
dataset.prototype.confirmReplacement=Replace definition
dataset.prototype.anotherDataset=Need another dataset?
dataset.prototype.startBlank=Start from scratch
dataset.prototype.fieldCount=Fields
dataset.prototype.configureColumnsHint=Identify fields and configure their display names, types and storage properties.
dataset.prototype.identifyAgain=Identify fields again
dataset.prototype.bulkSettings=Batch settings
dataset.prototype.fieldsCount={0} fields
dataset.prototype.selectedCount={0} fields selected
dataset.prototype.selectAllFields=Select all fields
dataset.prototype.keepExisting=Keep current values
dataset.prototype.enabled=Enable
dataset.prototype.disabled=Disable
dataset.prototype.datasetReady=Dataset construction is complete. It is ready for analysis.
dataset.prototype.storedPreviewHint=Preview the first 100 stored rows. Search and export apply to this preview.
dataset.prototype.buildingDataset=Dataset is being built
dataset.prototype.storageColumn=Storage column: {0}
template.tip.createSuccess=Template [ $VALUE ] saved successfully
Loading
Loading