From ddf3c0810b09ea0516b8053ea246b45b02c230af Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Wed, 2 Jul 2025 15:49:10 +0200 Subject: [PATCH] SYNC: charts auto detect --- CHANGELOG.md | 1 + e2e-tests/cypress.config.js | 3 + e2e-tests/cypress/e2e/charts.cy.js | 26 ++++ .../data/charts-sample/departments.jsonl | 6 + .../data/charts-sample/departments.table.yaml | 12 ++ .../data/charts-sample/employee_project.jsonl | 39 +++++ .../charts-sample/employee_project.table.yaml | 18 +++ e2e-tests/data/charts-sample/employees.jsonl | 21 +++ .../data/charts-sample/employees.table.yaml | 28 ++++ .../data/charts-sample/finance_reports.jsonl | 141 ++++++++++++++++++ .../charts-sample/finance_reports.table.yaml | 15 ++ e2e-tests/data/charts-sample/projects.jsonl | 11 ++ .../data/charts-sample/projects.table.yaml | 18 +++ e2e-tests/data/files/sql/chart1.sql | 23 +++ e2e-tests/env/charts/.env | 8 + e2e-tests/init/charts.js | 58 +++++++ e2e-tests/package.json | 5 +- .../src/frontend/Dumper.js | 13 ++ .../src/frontend/drivers.js | 5 + 19 files changed, 450 insertions(+), 1 deletion(-) create mode 100644 e2e-tests/cypress/e2e/charts.cy.js create mode 100644 e2e-tests/data/charts-sample/departments.jsonl create mode 100644 e2e-tests/data/charts-sample/departments.table.yaml create mode 100644 e2e-tests/data/charts-sample/employee_project.jsonl create mode 100644 e2e-tests/data/charts-sample/employee_project.table.yaml create mode 100644 e2e-tests/data/charts-sample/employees.jsonl create mode 100644 e2e-tests/data/charts-sample/employees.table.yaml create mode 100644 e2e-tests/data/charts-sample/finance_reports.jsonl create mode 100644 e2e-tests/data/charts-sample/finance_reports.table.yaml create mode 100644 e2e-tests/data/charts-sample/projects.jsonl create mode 100644 e2e-tests/data/charts-sample/projects.table.yaml create mode 100644 e2e-tests/data/files/sql/chart1.sql create mode 100644 e2e-tests/env/charts/.env create mode 100644 e2e-tests/init/charts.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ae6228e7..b7b269931 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ Builds: - ADDED: Chart grouping (more measure determined from data) - CHANGED: Improved chart autodetection - string X axis (with bar type), COUNT as measure, split different measures - ADDED: Added chart data type detection +- FIXED: Fixed chart displaying problems - FIXED: Fixed exporting chart to HTML - CHANGED: Choose COUNT measure without selecting underlying ID field (use virtual __count) diff --git a/e2e-tests/cypress.config.js b/e2e-tests/cypress.config.js index d6554e27d..ef2d17717 100644 --- a/e2e-tests/cypress.config.js +++ b/e2e-tests/cypress.config.js @@ -45,6 +45,9 @@ module.exports = defineConfig({ case 'cloud': serverProcess = exec('yarn start:cloud'); break; + case 'charts': + serverProcess = exec('yarn start:charts'); + break; } await waitOn({ resources: ['http://localhost:3000'] }); diff --git a/e2e-tests/cypress/e2e/charts.cy.js b/e2e-tests/cypress/e2e/charts.cy.js new file mode 100644 index 000000000..b31aee0cb --- /dev/null +++ b/e2e-tests/cypress/e2e/charts.cy.js @@ -0,0 +1,26 @@ +Cypress.on('uncaught:exception', (err, runnable) => { + // if the error message matches the one about WorkerGlobalScope importScripts + if (err.message.includes("Failed to execute 'importScripts' on 'WorkerGlobalScope'")) { + // return false to let Cypress know we intentionally want to ignore this error + return false; + } + // otherwise let Cypress throw the error +}); + +beforeEach(() => { + cy.visit('http://localhost:3000'); + cy.viewport(1250, 900); +}); + +describe('Charts', () => { + it('Auto detect chart', () => { + cy.contains('MySql-connection').click(); + cy.contains('charts_sample').click(); + cy.testid('WidgetIconPanel_file').click(); + cy.contains('chart1').click(); + cy.contains('department_name'); + // cy.testid('QueryTab_executeButton').click(); + // cy.testid('QueryTab_openChartButton').click(); + cy.themeshot('choose-detected-chart'); + }); +}); diff --git a/e2e-tests/data/charts-sample/departments.jsonl b/e2e-tests/data/charts-sample/departments.jsonl new file mode 100644 index 000000000..f8e8b382a --- /dev/null +++ b/e2e-tests/data/charts-sample/departments.jsonl @@ -0,0 +1,6 @@ +{"__isStreamHeader":true,"pureName":"departments","schemaName":"dbo","objectId":1205579333,"createDate":"2025-06-12T10:30:34.083Z","modifyDate":"2025-06-12T10:30:34.120Z","contentHash":"2025-06-12T10:30:34.120Z","columns":[{"columnName":"id","dataType":"int","notNull":true,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false},{"columnName":"name","dataType":"varchar(100)","notNull":true,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false}],"primaryKey":{"constraintName":"PK__departme__3213E83FE8E7043D","schemaName":"dbo","pureName":"departments","constraintType":"primaryKey","columns":[{"columnName":"id"}]},"foreignKeys":[],"indexes":[],"uniques":[],"engine":"mssql@dbgate-plugin-mssql"} +{"id":1,"name":"IT"} +{"id":2,"name":"Marketing"} +{"id":3,"name":"Finance"} +{"id":4,"name":"Human Resources"} +{"id":5,"name":"Research and Development"} diff --git a/e2e-tests/data/charts-sample/departments.table.yaml b/e2e-tests/data/charts-sample/departments.table.yaml new file mode 100644 index 000000000..50dae138b --- /dev/null +++ b/e2e-tests/data/charts-sample/departments.table.yaml @@ -0,0 +1,12 @@ +name: departments +columns: + - name: id + type: int + default: null + notNull: true + - name: name + type: varchar(100) + default: null + notNull: true +primaryKey: + - id diff --git a/e2e-tests/data/charts-sample/employee_project.jsonl b/e2e-tests/data/charts-sample/employee_project.jsonl new file mode 100644 index 000000000..6278c8bd7 --- /dev/null +++ b/e2e-tests/data/charts-sample/employee_project.jsonl @@ -0,0 +1,39 @@ +{"__isStreamHeader":true,"pureName":"employee_project","schemaName":"dbo","objectId":1333579789,"createDate":"2025-06-12T10:30:34.133Z","modifyDate":"2025-06-12T10:30:34.133Z","contentHash":"2025-06-12T10:30:34.133Z","columns":[{"columnName":"employee_id","dataType":"int","notNull":true,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false},{"columnName":"project_id","dataType":"int","notNull":true,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false},{"columnName":"role","dataType":"varchar(50)","notNull":false,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false}],"primaryKey":{"constraintName":"PK__employee__2EE9924949ED9668","schemaName":"dbo","pureName":"employee_project","constraintType":"primaryKey","columns":[{"columnName":"employee_id"},{"columnName":"project_id"}]},"foreignKeys":[{"constraintName":"FK__employee___emplo__5165187F","constraintType":"foreignKey","schemaName":"dbo","pureName":"employee_project","refSchemaName":"dbo","refTableName":"employees","updateAction":"NO ACTION","deleteAction":"NO ACTION","columns":[{"columnName":"employee_id","refColumnName":"id"}]},{"constraintName":"FK__employee___proje__52593CB8","constraintType":"foreignKey","schemaName":"dbo","pureName":"employee_project","refSchemaName":"dbo","refTableName":"projects","updateAction":"NO ACTION","deleteAction":"NO ACTION","columns":[{"columnName":"project_id","refColumnName":"id"}]}],"indexes":[],"uniques":[],"engine":"mssql@dbgate-plugin-mssql"} +{"employee_id":1,"project_id":6,"role":"Manager"} +{"employee_id":1,"project_id":8,"role":"Developer"} +{"employee_id":2,"project_id":7,"role":"Tester"} +{"employee_id":2,"project_id":8,"role":"Developer"} +{"employee_id":3,"project_id":4,"role":"Analyst"} +{"employee_id":3,"project_id":6,"role":"Developer"} +{"employee_id":4,"project_id":2,"role":"Manager"} +{"employee_id":4,"project_id":4,"role":"Analyst"} +{"employee_id":4,"project_id":5,"role":"Analyst"} +{"employee_id":5,"project_id":5,"role":"Tester"} +{"employee_id":6,"project_id":1,"role":"Analyst"} +{"employee_id":6,"project_id":6,"role":"Tester"} +{"employee_id":6,"project_id":9,"role":"Manager"} +{"employee_id":7,"project_id":8,"role":"Manager"} +{"employee_id":8,"project_id":10,"role":"Analyst"} +{"employee_id":9,"project_id":2,"role":"Analyst"} +{"employee_id":9,"project_id":6,"role":"Analyst"} +{"employee_id":9,"project_id":7,"role":"Developer"} +{"employee_id":10,"project_id":2,"role":"Manager"} +{"employee_id":10,"project_id":6,"role":"Analyst"} +{"employee_id":11,"project_id":1,"role":"Tester"} +{"employee_id":12,"project_id":4,"role":"Tester"} +{"employee_id":13,"project_id":2,"role":"Developer"} +{"employee_id":13,"project_id":3,"role":"Analyst"} +{"employee_id":13,"project_id":7,"role":"Developer"} +{"employee_id":14,"project_id":3,"role":"Developer"} +{"employee_id":14,"project_id":9,"role":"Manager"} +{"employee_id":15,"project_id":1,"role":"Developer"} +{"employee_id":15,"project_id":5,"role":"Manager"} +{"employee_id":16,"project_id":3,"role":"Tester"} +{"employee_id":16,"project_id":5,"role":"Developer"} +{"employee_id":17,"project_id":6,"role":"Analyst"} +{"employee_id":18,"project_id":1,"role":"Tester"} +{"employee_id":18,"project_id":5,"role":"Tester"} +{"employee_id":18,"project_id":6,"role":"Manager"} +{"employee_id":19,"project_id":6,"role":"Analyst"} +{"employee_id":20,"project_id":2,"role":"Developer"} +{"employee_id":20,"project_id":4,"role":"Developer"} diff --git a/e2e-tests/data/charts-sample/employee_project.table.yaml b/e2e-tests/data/charts-sample/employee_project.table.yaml new file mode 100644 index 000000000..5a624c925 --- /dev/null +++ b/e2e-tests/data/charts-sample/employee_project.table.yaml @@ -0,0 +1,18 @@ +name: employee_project +columns: + - name: employee_id + type: int + default: null + notNull: true + references: employees + - name: project_id + type: int + default: null + notNull: true + references: projects + - name: role + type: varchar(50) + default: null +primaryKey: + - employee_id + - project_id diff --git a/e2e-tests/data/charts-sample/employees.jsonl b/e2e-tests/data/charts-sample/employees.jsonl new file mode 100644 index 000000000..a9e0ff1a6 --- /dev/null +++ b/e2e-tests/data/charts-sample/employees.jsonl @@ -0,0 +1,21 @@ +{"__isStreamHeader":true,"pureName":"employees","schemaName":"dbo","objectId":1237579447,"createDate":"2025-06-12T10:30:34.113Z","modifyDate":"2025-06-12T12:35:22.140Z","contentHash":"2025-06-12T12:35:22.140Z","columns":[{"columnName":"id","dataType":"int","notNull":true,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false},{"columnName":"name","dataType":"varchar(100)","notNull":true,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false},{"columnName":"email","dataType":"varchar(100)","notNull":true,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false},{"columnName":"hire_date","dataType":"date","notNull":true,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false},{"columnName":"department_id","dataType":"int","notNull":false,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false}],"primaryKey":{"constraintName":"PK__employee__3213E83FE576E55A","schemaName":"dbo","pureName":"employees","constraintType":"primaryKey","columns":[{"columnName":"id"}]},"foreignKeys":[{"constraintName":"FK__employees__depar__4CA06362","constraintType":"foreignKey","schemaName":"dbo","pureName":"employees","refSchemaName":"dbo","refTableName":"departments","updateAction":"NO ACTION","deleteAction":"NO ACTION","columns":[{"columnName":"department_id","refColumnName":"id"}]}],"indexes":[],"uniques":[{"constraintName":"UQ__employee__AB6E6164E18D883F","columns":[{"columnName":"email"}]}],"engine":"mssql@dbgate-plugin-mssql"} +{"id":1,"name":"John Smith","email":"john.smith@example.com","hire_date":"2018-07-09T00:00:00.000Z","department_id":2} +{"id":2,"name":"Jane Garcia","email":"jane.garcia@example.com","hire_date":"2019-10-13T00:00:00.000Z","department_id":5} +{"id":3,"name":"Grace Smith","email":"grace.smith@example.com","hire_date":"2019-03-16T00:00:00.000Z","department_id":1} +{"id":4,"name":"Charlie Williams","email":"charlie.williams@example.com","hire_date":"2020-10-18T00:00:00.000Z","department_id":2} +{"id":5,"name":"Eve Brown","email":"eve.brown@example.com","hire_date":"2018-04-12T00:00:00.000Z","department_id":4} +{"id":6,"name":"Alice Moore","email":"alice.moore@example.com","hire_date":"2019-04-20T00:00:00.000Z","department_id":2} +{"id":7,"name":"Eve Williams","email":"eve.williams@example.com","hire_date":"2020-04-26T00:00:00.000Z","department_id":4} +{"id":8,"name":"Eve Jones","email":"eve.jones@example.com","hire_date":"2022-10-04T00:00:00.000Z","department_id":3} +{"id":9,"name":"Diana Miller","email":"diana.miller@example.com","hire_date":"2021-03-28T00:00:00.000Z","department_id":1} +{"id":10,"name":"Diana Smith","email":"diana.smith@example.com","hire_date":"2018-04-12T00:00:00.000Z","department_id":2} +{"id":11,"name":"Hank Johnson","email":"hank.johnson@example.com","hire_date":"2020-09-16T00:00:00.000Z","department_id":2} +{"id":12,"name":"Frank Miller","email":"frank.miller@example.com","hire_date":"2023-01-12T00:00:00.000Z","department_id":4} +{"id":13,"name":"Jane Brown","email":"jane.brown@example.com","hire_date":"2023-05-07T00:00:00.000Z","department_id":3} +{"id":14,"name":"Grace Davis","email":"grace.davis@example.com","hire_date":"2019-08-22T00:00:00.000Z","department_id":3} +{"id":15,"name":"Jane Black","email":"jane.black@example.com","hire_date":"2019-04-28T00:00:00.000Z","department_id":2} +{"id":16,"name":"Charlie Smith","email":"charlie.smith@example.com","hire_date":"2019-06-12T00:00:00.000Z","department_id":5} +{"id":17,"name":"Eve Johnson","email":"eve.johnson@example.com","hire_date":"2020-11-07T00:00:00.000Z","department_id":5} +{"id":18,"name":"Jane Johnson","email":"jane.johnson@example.com","hire_date":"2020-04-06T00:00:00.000Z","department_id":2} +{"id":19,"name":"Hank Brown","email":"hank.brown@example.com","hire_date":"2023-05-10T00:00:00.000Z","department_id":2} +{"id":20,"name":"Frank Jones","email":"frank.jones@example.com","hire_date":"2020-10-26T00:00:00.000Z","department_id":1} diff --git a/e2e-tests/data/charts-sample/employees.table.yaml b/e2e-tests/data/charts-sample/employees.table.yaml new file mode 100644 index 000000000..359907a56 --- /dev/null +++ b/e2e-tests/data/charts-sample/employees.table.yaml @@ -0,0 +1,28 @@ +name: employees +columns: + - name: id + type: int + default: null + notNull: true + - name: name + type: varchar(100) + default: null + notNull: true + - name: email + type: varchar(100) + default: null + notNull: true + - name: hire_date + type: date + default: null + notNull: true + - name: department_id + type: int + default: null + references: departments +primaryKey: + - id +uniques: + - name: UQ__employee__AB6E6164E18D883F + columns: + - email diff --git a/e2e-tests/data/charts-sample/finance_reports.jsonl b/e2e-tests/data/charts-sample/finance_reports.jsonl new file mode 100644 index 000000000..784afc0eb --- /dev/null +++ b/e2e-tests/data/charts-sample/finance_reports.jsonl @@ -0,0 +1,141 @@ +{"__isStreamHeader":true,"pureName":"finance_reports","schemaName":"dbo","objectId":338100245,"createDate":"2025-06-23T12:15:08.727Z","modifyDate":"2025-06-23T12:15:08.750Z","contentHash":"2025-06-23T12:15:08.750Z","columns":[{"columnName":"id","dataType":"int","notNull":true,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false},{"columnName":"date","dataType":"date","notNull":true,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false},{"columnName":"profit","dataType":"money","notNull":true,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false}],"foreignKeys":[{"constraintName":"project_id","constraintType":"foreignKey","schemaName":"dbo","pureName":"finance_reports","refSchemaName":"dbo","refTableName":"projects","updateAction":"NO ACTION","deleteAction":"NO ACTION","columns":[{"columnName":"id","refColumnName":"id"}]}],"indexes":[],"uniques":[],"engine":"mssql@dbgate-plugin-mssql"} +{"id":1,"date":"2022-01-01T00:00:00.000Z","profit":73923.4} +{"id":1,"date":"2022-01-31T00:00:00.000Z","profit":21837.75} +{"id":1,"date":"2022-03-02T00:00:00.000Z","profit":67859.8} +{"id":1,"date":"2022-04-01T00:00:00.000Z","profit":77403.3} +{"id":1,"date":"2022-05-01T00:00:00.000Z","profit":84083.19} +{"id":1,"date":"2022-05-31T00:00:00.000Z","profit":30040.55} +{"id":1,"date":"2022-06-30T00:00:00.000Z","profit":50947.14} +{"id":1,"date":"2022-07-30T00:00:00.000Z","profit":63345.62} +{"id":1,"date":"2022-08-29T00:00:00.000Z","profit":23819.45} +{"id":1,"date":"2022-09-28T00:00:00.000Z","profit":-25919.19} +{"id":1,"date":"2022-10-28T00:00:00.000Z","profit":27967.6} +{"id":1,"date":"2022-11-27T00:00:00.000Z","profit":-37402.36} +{"id":1,"date":"2022-12-27T00:00:00.000Z","profit":94528.8} +{"id":1,"date":"2023-01-26T00:00:00.000Z","profit":29491.03} +{"id":1,"date":"2023-02-25T00:00:00.000Z","profit":81541.29} +{"id":2,"date":"2022-01-01T00:00:00.000Z","profit":18070.94} +{"id":2,"date":"2022-01-31T00:00:00.000Z","profit":-40609.87} +{"id":2,"date":"2022-03-02T00:00:00.000Z","profit":42435.51} +{"id":2,"date":"2022-04-01T00:00:00.000Z","profit":-11915.15} +{"id":2,"date":"2022-05-01T00:00:00.000Z","profit":-37417.4} +{"id":2,"date":"2022-05-31T00:00:00.000Z","profit":23028.66} +{"id":2,"date":"2022-06-30T00:00:00.000Z","profit":-6895.49} +{"id":2,"date":"2022-07-30T00:00:00.000Z","profit":63114.54} +{"id":2,"date":"2022-08-29T00:00:00.000Z","profit":94646.99} +{"id":2,"date":"2022-09-28T00:00:00.000Z","profit":99560.77} +{"id":2,"date":"2022-10-28T00:00:00.000Z","profit":62216.22} +{"id":2,"date":"2022-11-27T00:00:00.000Z","profit":85094.32} +{"id":2,"date":"2022-12-27T00:00:00.000Z","profit":-23378.37} +{"id":2,"date":"2023-01-26T00:00:00.000Z","profit":47635.86} +{"id":2,"date":"2023-02-25T00:00:00.000Z","profit":33727.72} +{"id":3,"date":"2022-01-01T00:00:00.000Z","profit":33088.03} +{"id":3,"date":"2022-01-31T00:00:00.000Z","profit":66668.91} +{"id":3,"date":"2022-03-02T00:00:00.000Z","profit":5344.27} +{"id":3,"date":"2022-04-01T00:00:00.000Z","profit":22122.99} +{"id":3,"date":"2022-05-01T00:00:00.000Z","profit":27342.01} +{"id":3,"date":"2022-05-31T00:00:00.000Z","profit":55479.42} +{"id":3,"date":"2022-06-30T00:00:00.000Z","profit":35956.11} +{"id":3,"date":"2022-07-30T00:00:00.000Z","profit":9667.12} +{"id":3,"date":"2022-08-29T00:00:00.000Z","profit":63430.18} +{"id":3,"date":"2022-09-28T00:00:00.000Z","profit":-4883.41} +{"id":3,"date":"2022-10-28T00:00:00.000Z","profit":38902.8} +{"id":3,"date":"2022-11-27T00:00:00.000Z","profit":-25500.13} +{"id":3,"date":"2022-12-27T00:00:00.000Z","profit":65074.21} +{"id":3,"date":"2023-01-26T00:00:00.000Z","profit":12570.27} +{"id":3,"date":"2023-02-25T00:00:00.000Z","profit":35418.36} +{"id":4,"date":"2022-01-01T00:00:00.000Z","profit":68282.98} +{"id":4,"date":"2022-01-31T00:00:00.000Z","profit":77778.99} +{"id":4,"date":"2022-03-02T00:00:00.000Z","profit":95490.49} +{"id":4,"date":"2022-04-01T00:00:00.000Z","profit":-44466.37} +{"id":4,"date":"2022-05-01T00:00:00.000Z","profit":40215.71} +{"id":4,"date":"2022-05-31T00:00:00.000Z","profit":-31228.87} +{"id":4,"date":"2022-06-30T00:00:00.000Z","profit":60667.69} +{"id":4,"date":"2022-07-30T00:00:00.000Z","profit":71439.16} +{"id":4,"date":"2022-08-29T00:00:00.000Z","profit":-25077.4} +{"id":4,"date":"2022-09-28T00:00:00.000Z","profit":-36128.2} +{"id":4,"date":"2022-10-28T00:00:00.000Z","profit":36727.68} +{"id":4,"date":"2022-11-27T00:00:00.000Z","profit":-24207.2} +{"id":4,"date":"2022-12-27T00:00:00.000Z","profit":63846.96} +{"id":5,"date":"2022-01-01T00:00:00.000Z","profit":21648.3} +{"id":5,"date":"2022-01-31T00:00:00.000Z","profit":59263.22} +{"id":5,"date":"2022-03-02T00:00:00.000Z","profit":49154.51} +{"id":5,"date":"2022-04-01T00:00:00.000Z","profit":34787.48} +{"id":5,"date":"2022-05-01T00:00:00.000Z","profit":-24120.19} +{"id":5,"date":"2022-05-31T00:00:00.000Z","profit":98437.86} +{"id":5,"date":"2022-06-30T00:00:00.000Z","profit":18614.77} +{"id":5,"date":"2022-07-30T00:00:00.000Z","profit":17680.34} +{"id":5,"date":"2022-08-29T00:00:00.000Z","profit":74406.86} +{"id":5,"date":"2022-09-28T00:00:00.000Z","profit":61845.3} +{"id":5,"date":"2022-10-28T00:00:00.000Z","profit":-37889.59} +{"id":5,"date":"2022-11-27T00:00:00.000Z","profit":76651.05} +{"id":5,"date":"2022-12-27T00:00:00.000Z","profit":58739.6} +{"id":5,"date":"2023-01-26T00:00:00.000Z","profit":82605.85} +{"id":6,"date":"2022-01-01T00:00:00.000Z","profit":-5206.8} +{"id":6,"date":"2022-01-31T00:00:00.000Z","profit":27498.27} +{"id":6,"date":"2022-03-02T00:00:00.000Z","profit":-2939.84} +{"id":6,"date":"2022-04-01T00:00:00.000Z","profit":-37261.08} +{"id":6,"date":"2022-05-01T00:00:00.000Z","profit":37069.04} +{"id":6,"date":"2022-05-31T00:00:00.000Z","profit":524.88} +{"id":6,"date":"2022-06-30T00:00:00.000Z","profit":-29620.85} +{"id":6,"date":"2022-07-30T00:00:00.000Z","profit":35540.81} +{"id":6,"date":"2022-08-29T00:00:00.000Z","profit":20608.94} +{"id":6,"date":"2022-09-28T00:00:00.000Z","profit":34809.33} +{"id":6,"date":"2022-10-28T00:00:00.000Z","profit":-44949.05} +{"id":6,"date":"2022-11-27T00:00:00.000Z","profit":-22524.26} +{"id":6,"date":"2022-12-27T00:00:00.000Z","profit":37841.58} +{"id":7,"date":"2022-01-01T00:00:00.000Z","profit":6903.17} +{"id":7,"date":"2022-01-31T00:00:00.000Z","profit":58480.84} +{"id":7,"date":"2022-03-02T00:00:00.000Z","profit":48217.34} +{"id":7,"date":"2022-04-01T00:00:00.000Z","profit":73592.44} +{"id":7,"date":"2022-05-01T00:00:00.000Z","profit":-21831.18} +{"id":7,"date":"2022-05-31T00:00:00.000Z","profit":-40926.16} +{"id":7,"date":"2022-06-30T00:00:00.000Z","profit":62299.5} +{"id":7,"date":"2022-07-30T00:00:00.000Z","profit":95376.53} +{"id":7,"date":"2022-08-29T00:00:00.000Z","profit":-13317.36} +{"id":7,"date":"2022-09-28T00:00:00.000Z","profit":81565.05} +{"id":7,"date":"2022-10-28T00:00:00.000Z","profit":77420.52} +{"id":7,"date":"2022-11-27T00:00:00.000Z","profit":-12052.47} +{"id":7,"date":"2022-12-27T00:00:00.000Z","profit":37742.07} +{"id":7,"date":"2023-01-26T00:00:00.000Z","profit":-8057.99} +{"id":8,"date":"2022-01-01T00:00:00.000Z","profit":27213.73} +{"id":8,"date":"2022-01-31T00:00:00.000Z","profit":34271.75} +{"id":8,"date":"2022-03-02T00:00:00.000Z","profit":-44549.47} +{"id":8,"date":"2022-04-01T00:00:00.000Z","profit":15236.34} +{"id":8,"date":"2022-05-01T00:00:00.000Z","profit":-27759.81} +{"id":8,"date":"2022-05-31T00:00:00.000Z","profit":7955.12} +{"id":8,"date":"2022-06-30T00:00:00.000Z","profit":-34484.38} +{"id":8,"date":"2022-07-30T00:00:00.000Z","profit":-49758.7} +{"id":8,"date":"2022-08-29T00:00:00.000Z","profit":-41990.86} +{"id":8,"date":"2022-09-28T00:00:00.000Z","profit":58123.01} +{"id":8,"date":"2022-10-28T00:00:00.000Z","profit":30128.78} +{"id":8,"date":"2022-11-27T00:00:00.000Z","profit":-10151.17} +{"id":8,"date":"2022-12-27T00:00:00.000Z","profit":54048.33} +{"id":8,"date":"2023-01-26T00:00:00.000Z","profit":-43123.17} +{"id":9,"date":"2022-01-01T00:00:00.000Z","profit":61031.83} +{"id":9,"date":"2022-01-31T00:00:00.000Z","profit":68577.58} +{"id":9,"date":"2022-03-02T00:00:00.000Z","profit":88698.97} +{"id":9,"date":"2022-04-01T00:00:00.000Z","profit":8906.03} +{"id":9,"date":"2022-05-01T00:00:00.000Z","profit":28824.73} +{"id":9,"date":"2022-05-31T00:00:00.000Z","profit":88280.34} +{"id":9,"date":"2022-06-30T00:00:00.000Z","profit":35266.09} +{"id":9,"date":"2022-07-30T00:00:00.000Z","profit":-38025.36} +{"id":9,"date":"2022-08-29T00:00:00.000Z","profit":-12118.53} +{"id":9,"date":"2022-09-28T00:00:00.000Z","profit":-27265.86} +{"id":9,"date":"2022-10-28T00:00:00.000Z","profit":56870.57} +{"id":9,"date":"2022-11-27T00:00:00.000Z","profit":88078.95} +{"id":9,"date":"2022-12-27T00:00:00.000Z","profit":-24059.67} +{"id":9,"date":"2023-01-26T00:00:00.000Z","profit":-13301.43} +{"id":10,"date":"2022-01-01T00:00:00.000Z","profit":-22479.23} +{"id":10,"date":"2022-01-31T00:00:00.000Z","profit":8106.27} +{"id":10,"date":"2022-03-02T00:00:00.000Z","profit":69372.19} +{"id":10,"date":"2022-04-01T00:00:00.000Z","profit":-11895.74} +{"id":10,"date":"2022-05-01T00:00:00.000Z","profit":-33206.5} +{"id":10,"date":"2022-05-31T00:00:00.000Z","profit":56073.34} +{"id":10,"date":"2022-06-30T00:00:00.000Z","profit":67488.3} +{"id":10,"date":"2022-07-30T00:00:00.000Z","profit":48529.23} +{"id":10,"date":"2022-08-29T00:00:00.000Z","profit":28680.2} +{"id":10,"date":"2022-09-28T00:00:00.000Z","profit":59311.16} +{"id":10,"date":"2022-10-28T00:00:00.000Z","profit":25315.78} +{"id":10,"date":"2022-11-27T00:00:00.000Z","profit":36116.38} +{"id":10,"date":"2022-12-27T00:00:00.000Z","profit":-42040.4} diff --git a/e2e-tests/data/charts-sample/finance_reports.table.yaml b/e2e-tests/data/charts-sample/finance_reports.table.yaml new file mode 100644 index 000000000..d19b81005 --- /dev/null +++ b/e2e-tests/data/charts-sample/finance_reports.table.yaml @@ -0,0 +1,15 @@ +name: finance_reports +columns: + - name: id + type: int + default: null + notNull: true + references: projects + - name: date + type: date + default: null + notNull: true + - name: profit + type: money + default: null + notNull: true diff --git a/e2e-tests/data/charts-sample/projects.jsonl b/e2e-tests/data/charts-sample/projects.jsonl new file mode 100644 index 000000000..2f7fdcbb9 --- /dev/null +++ b/e2e-tests/data/charts-sample/projects.jsonl @@ -0,0 +1,11 @@ +{"__isStreamHeader":true,"pureName":"projects","schemaName":"dbo","objectId":1301579675,"createDate":"2025-06-12T10:30:34.127Z","modifyDate":"2025-06-23T12:15:08.750Z","contentHash":"2025-06-23T12:15:08.750Z","columns":[{"columnName":"id","dataType":"int","notNull":true,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false},{"columnName":"name","dataType":"varchar(100)","notNull":true,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false},{"columnName":"start_date","dataType":"date","notNull":false,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false},{"columnName":"end_date","dataType":"date","notNull":false,"autoIncrement":false,"defaultValue":null,"defaultConstraint":null,"computedExpression":null,"hasAutoValue":false}],"primaryKey":{"constraintName":"PK__projects__3213E83F26A7ED11","schemaName":"dbo","pureName":"projects","constraintType":"primaryKey","columns":[{"columnName":"id"}]},"foreignKeys":[],"indexes":[],"uniques":[],"engine":"mssql@dbgate-plugin-mssql"} +{"id":1,"name":"Apollo Upgrade","start_date":"2020-04-27T00:00:00.000Z","end_date":"2020-10-19T00:00:00.000Z"} +{"id":2,"name":"Market Expansion","start_date":"2022-08-04T00:00:00.000Z","end_date":"2023-06-20T00:00:00.000Z"} +{"id":3,"name":"AI Integration","start_date":"2020-05-11T00:00:00.000Z","end_date":"2021-07-10T00:00:00.000Z"} +{"id":4,"name":"Cost Reduction","start_date":"2022-01-08T00:00:00.000Z","end_date":"2022-07-12T00:00:00.000Z"} +{"id":5,"name":"Cloud Migration","start_date":"2021-01-11T00:00:00.000Z","end_date":"2021-05-27T00:00:00.000Z"} +{"id":6,"name":"Customer Portal","start_date":"2021-07-13T00:00:00.000Z","end_date":"2022-09-22T00:00:00.000Z"} +{"id":7,"name":"Data Lake","start_date":"2021-02-25T00:00:00.000Z","end_date":"2021-08-21T00:00:00.000Z"} +{"id":8,"name":"UX Overhaul","start_date":"2021-05-20T00:00:00.000Z","end_date":"2022-09-10T00:00:00.000Z"} +{"id":9,"name":"Security Hardening","start_date":"2021-05-28T00:00:00.000Z","end_date":"2022-07-28T00:00:00.000Z"} +{"id":10,"name":"Mobile App Revamp","start_date":"2021-11-17T00:00:00.000Z","end_date":"2022-06-04T00:00:00.000Z"} diff --git a/e2e-tests/data/charts-sample/projects.table.yaml b/e2e-tests/data/charts-sample/projects.table.yaml new file mode 100644 index 000000000..5b314a8f3 --- /dev/null +++ b/e2e-tests/data/charts-sample/projects.table.yaml @@ -0,0 +1,18 @@ +name: projects +columns: + - name: id + type: int + default: null + notNull: true + - name: name + type: varchar(100) + default: null + notNull: true + - name: start_date + type: date + default: null + - name: end_date + type: date + default: null +primaryKey: + - id diff --git a/e2e-tests/data/files/sql/chart1.sql b/e2e-tests/data/files/sql/chart1.sql new file mode 100644 index 000000000..1ba64c7aa --- /dev/null +++ b/e2e-tests/data/files/sql/chart1.sql @@ -0,0 +1,23 @@ +-- >>> +-- autoExecute: true +-- splitterInitialValue: 20% +-- selected-chart: 1 +-- <<< + +SELECT + d.name AS department_name, + FORMAT(fr.date, 'yyyy-MM') AS month, + SUM(fr.profit) AS total_monthly_profit +FROM + departments d +JOIN + employees e ON d.id = e.department_id +JOIN + employee_project ep ON e.id = ep.employee_id +JOIN + finance_reports fr ON ep.project_id = fr.id +GROUP BY + d.name, FORMAT(fr.date, 'yyyy-MM') +ORDER BY + d.name, month; + diff --git a/e2e-tests/env/charts/.env b/e2e-tests/env/charts/.env new file mode 100644 index 000000000..3e302d325 --- /dev/null +++ b/e2e-tests/env/charts/.env @@ -0,0 +1,8 @@ +CONNECTIONS=mysql + +LABEL_mysql=MySql-connection +SERVER_mysql=localhost +USER_mysql=root +PASSWORD_mysql=Pwd2020Db +PORT_mysql=16004 +ENGINE_mysql=mysql@dbgate-plugin-mysql diff --git a/e2e-tests/init/charts.js b/e2e-tests/init/charts.js new file mode 100644 index 000000000..f5119f94d --- /dev/null +++ b/e2e-tests/init/charts.js @@ -0,0 +1,58 @@ +const path = require('path'); +const os = require('os'); +const fs = require('fs'); + +const baseDir = path.join(os.homedir(), '.dbgate'); + +const dbgateApi = require('dbgate-api'); +dbgateApi.initializeApiEnvironment(); +const dbgatePluginMysql = require('dbgate-plugin-mysql'); +dbgateApi.registerPlugins(dbgatePluginMysql); + +async function copyFolder(source, target) { + if (!fs.existsSync(target)) { + fs.mkdirSync(target, { recursive: true }); + } + for (const file of fs.readdirSync(source)) { + fs.copyFileSync(path.join(source, file), path.join(target, file)); + } +} + +async function run() { + const connection = { + server: process.env.SERVER_mysql, + user: process.env.USER_mysql, + password: process.env.PASSWORD_mysql, + port: process.env.PORT_mysql, + engine: 'mysql@dbgate-plugin-mysql', + }; + + try { + await dbgateApi.executeQuery({ + connection, + sql: 'drop database if exists charts_sample', + }); + } catch (err) { + console.error('Failed to drop database', err); + } + + await dbgateApi.executeQuery({ + connection, + sql: 'create database charts_sample', + }); + + await dbgateApi.importDbFromFolder({ + connection: { + ...connection, + database: 'charts_sample', + }, + folder: path.resolve(path.join(__dirname, '../data/charts-sample')), + }); + + await copyFolder( + path.resolve(path.join(__dirname, '../data/files/sql')), + path.join(baseDir, 'files-e2etests', 'sql') + ); +} + +dbgateApi.runScript(run); diff --git a/e2e-tests/package.json b/e2e-tests/package.json index a2b6fc434..7f6aeaaf7 100644 --- a/e2e-tests/package.json +++ b/e2e-tests/package.json @@ -22,6 +22,7 @@ "cy:run:team": "cypress run --spec cypress/e2e/team.cy.js", "cy:run:multi-sql": "cypress run --spec cypress/e2e/multi-sql.cy.js", "cy:run:cloud": "cypress run --spec cypress/e2e/cloud.cy.js", + "cy:run:charts": "cypress run --spec cypress/e2e/charts.cy.js", "start:add-connection": "node clearTestingData && cd .. && node packer/build/bundle.js --listen-api --run-e2e-tests", "start:portal": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/portal/.env node e2e-tests/init/portal.js && env-cmd -f e2e-tests/env/portal/.env node packer/build/bundle.js --listen-api --run-e2e-tests", @@ -30,6 +31,7 @@ "start:team": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/team/.env node e2e-tests/init/team.js && env-cmd -f e2e-tests/env/team/.env node packer/build/bundle.js --listen-api --run-e2e-tests", "start:multi-sql": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/multi-sql/.env node e2e-tests/init/multi-sql.js && env-cmd -f e2e-tests/env/multi-sql/.env node packer/build/bundle.js --listen-api --run-e2e-tests", "start:cloud": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/cloud/.env node packer/build/bundle.js --listen-api --run-e2e-tests", + "start:charts": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/charts/.env node e2e-tests/init/charts.js && env-cmd -f e2e-tests/env/charts/.env node packer/build/bundle.js --listen-api --run-e2e-tests", "test:add-connection": "start-server-and-test start:add-connection http://localhost:3000 cy:run:add-connection", "test:portal": "start-server-and-test start:portal http://localhost:3000 cy:run:portal", @@ -38,8 +40,9 @@ "test:team": "start-server-and-test start:team http://localhost:3000 cy:run:team", "test:multi-sql": "start-server-and-test start:multi-sql http://localhost:3000 cy:run:multi-sql", "test:cloud": "start-server-and-test start:cloud http://localhost:3000 cy:run:cloud", + "test:charts": "start-server-and-test start:charts http://localhost:3000 cy:run:charts", - "test": "yarn test:add-connection && yarn test:portal && yarn test:oauth && yarn test:browse-data && yarn test:team && yarn test:multi-sql && yarn test:cloud", + "test": "yarn test:add-connection && yarn test:portal && yarn test:oauth && yarn test:browse-data && yarn test:team && yarn test:multi-sql && yarn test:cloud && yarn test:charts", "test:ci": "yarn test" }, "dependencies": {} diff --git a/plugins/dbgate-plugin-mysql/src/frontend/Dumper.js b/plugins/dbgate-plugin-mysql/src/frontend/Dumper.js index 8853ed2ad..ec7eabe20 100644 --- a/plugins/dbgate-plugin-mysql/src/frontend/Dumper.js +++ b/plugins/dbgate-plugin-mysql/src/frontend/Dumper.js @@ -139,6 +139,19 @@ class Dumper extends SqlDumper { this.endCommand(); } } + + putValue(value, dataType) { + const dataLower = dataType?.toLowerCase(); + if (dataLower?.includes('date')) { + if (typeof value == 'string') { + this.putRaw("'"); + this.putRaw(this.escapeString(value.replace(/(?:Z|[+-]\d{2}:?\d{2})$/, ''))); + this.putRaw("'"); + return; + } + } + super.putValue(value, dataType); + } } module.exports = Dumper; diff --git a/plugins/dbgate-plugin-mysql/src/frontend/drivers.js b/plugins/dbgate-plugin-mysql/src/frontend/drivers.js index 3adfa6155..486942207 100644 --- a/plugins/dbgate-plugin-mysql/src/frontend/drivers.js +++ b/plugins/dbgate-plugin-mysql/src/frontend/drivers.js @@ -375,6 +375,11 @@ const mysqlDriverBase = { } return null; }, + + adaptDataType(dataType) { + if (dataType?.toLowerCase() == 'money') return 'decimal(15,2)'; + return dataType; + }, }; /** @type {import('dbgate-types').EngineDriver} */