diff --git a/.github/workflows/build-app-pro-beta.yaml b/.github/workflows/build-app-pro-beta.yaml index 750be45bd..46704c546 100644 --- a/.github/workflows/build-app-pro-beta.yaml +++ b/.github/workflows/build-app-pro-beta.yaml @@ -43,7 +43,7 @@ jobs: repository: dbgate/dbgate-pro token: ${{ secrets.GH_TOKEN }} path: dbgate-pro - ref: 911941a53e91a5a777b8c7d455be0719234dde5f + ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a - name: Merge dbgate/dbgate-pro run: | mkdir ../dbgate-pro diff --git a/.github/workflows/build-app-pro.yaml b/.github/workflows/build-app-pro.yaml index b6203eed9..cb0c217dd 100644 --- a/.github/workflows/build-app-pro.yaml +++ b/.github/workflows/build-app-pro.yaml @@ -43,7 +43,7 @@ jobs: repository: dbgate/dbgate-pro token: ${{ secrets.GH_TOKEN }} path: dbgate-pro - ref: 911941a53e91a5a777b8c7d455be0719234dde5f + ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a - name: Merge dbgate/dbgate-pro run: | mkdir ../dbgate-pro diff --git a/.github/workflows/build-cloud-pro.yaml b/.github/workflows/build-cloud-pro.yaml index d0f844d70..396a7dbdb 100644 --- a/.github/workflows/build-cloud-pro.yaml +++ b/.github/workflows/build-cloud-pro.yaml @@ -39,7 +39,7 @@ jobs: repository: dbgate/dbgate-pro token: ${{ secrets.GH_TOKEN }} path: dbgate-pro - ref: 911941a53e91a5a777b8c7d455be0719234dde5f + ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a - name: Merge dbgate/dbgate-pro run: | mkdir ../dbgate-pro diff --git a/.github/workflows/build-docker-pro.yaml b/.github/workflows/build-docker-pro.yaml index 6944edcd6..0a8469b43 100644 --- a/.github/workflows/build-docker-pro.yaml +++ b/.github/workflows/build-docker-pro.yaml @@ -44,7 +44,7 @@ jobs: repository: dbgate/dbgate-pro token: ${{ secrets.GH_TOKEN }} path: dbgate-pro - ref: 911941a53e91a5a777b8c7d455be0719234dde5f + ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a - name: Merge dbgate/dbgate-pro run: | mkdir ../dbgate-pro diff --git a/.github/workflows/build-npm-pro.yaml b/.github/workflows/build-npm-pro.yaml index 3d4cebfad..eee812d47 100644 --- a/.github/workflows/build-npm-pro.yaml +++ b/.github/workflows/build-npm-pro.yaml @@ -35,7 +35,7 @@ jobs: repository: dbgate/dbgate-pro token: ${{ secrets.GH_TOKEN }} path: dbgate-pro - ref: 911941a53e91a5a777b8c7d455be0719234dde5f + ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a - name: Merge dbgate/dbgate-pro run: | mkdir ../dbgate-pro diff --git a/.github/workflows/e2e-pro.yaml b/.github/workflows/e2e-pro.yaml index b46572e96..d7ad2b1ef 100644 --- a/.github/workflows/e2e-pro.yaml +++ b/.github/workflows/e2e-pro.yaml @@ -26,7 +26,7 @@ jobs: repository: dbgate/dbgate-pro token: ${{ secrets.GH_TOKEN }} path: dbgate-pro - ref: 911941a53e91a5a777b8c7d455be0719234dde5f + ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a - name: Merge dbgate/dbgate-pro run: | mkdir ../dbgate-pro diff --git a/CHANGELOG.md b/CHANGELOG.md index 2785d3479..01c6f2bf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,19 @@ Builds: - linux - application for linux - win - application for Windows -## 6.7.2 - not released +## 6.7.3 +- FIXED: Fixed problem in analyser core - in PostgreSQL, after dropping table, dropped table still appeared in structure +- FIXED: PostgreSQL numeric columns do not align right #1254 +- ADDED: Custom thousands separator #1213 + +## 6.7.2 - CHANGED: Settings modal redesign - now is settings opened in tab instead of modal, similarily as in VSCode - FIXED: Fixed search in table shortcuts #1273 - CHANGED: Improved foreign key editor UX - FIXED: Fixed incremental DB structure refresh for PostgreSQL, optimalized slow loading primary keys in PostgreSQL - CHANGED: You could now choose, how to refresh structure, added ability to disconnect or reconnect - ADDED: Better processing of table backups, generate table restore script #1274 +- CHANGED: Improved storage of settings, especially for Team Premium edition ## 6.7.1 - ADDED: LANGUAGE environment variable for the web version. #1266 diff --git a/e2e-tests/cypress.config.js b/e2e-tests/cypress.config.js index ef2d17717..28fa48e60 100644 --- a/e2e-tests/cypress.config.js +++ b/e2e-tests/cypress.config.js @@ -10,6 +10,7 @@ module.exports = defineConfig({ // baseUrl: 'http://localhost:3000', // trashAssetsBeforeRuns: false, chromeWebSecurity: false, + reporter: process.env.CI ? 'mocha-reporter-gha' : 'spec', setupNodeEvents(on, config) { // implement node event listeners here diff --git a/e2e-tests/cypress/e2e/charts.cy.js b/e2e-tests/cypress/e2e/charts.cy.js index 7a5199c46..f679d9b31 100644 --- a/e2e-tests/cypress/e2e/charts.cy.js +++ b/e2e-tests/cypress/e2e/charts.cy.js @@ -110,7 +110,7 @@ describe('Charts', () => { cy.themeshot('new-object-window'); }); - it('Database chat - charts', () => { + it.skip('Database chat - charts', () => { cy.contains('MySql-connection').click(); cy.contains('MyChinook').click(); cy.testid('TabsPanel_buttonNewObject').click(); @@ -125,7 +125,7 @@ describe('Charts', () => { cy.themeshot('database-chat-chart'); }); - it('Database chat', () => { + it.skip('Database chat', () => { cy.contains('MySql-connection').click(); cy.contains('MyChinook').click(); cy.testid('TabsPanel_buttonNewObject').click(); @@ -146,7 +146,7 @@ describe('Charts', () => { // cy.themeshot('database-chat'); }); - it('Explain query error', () => { + it.skip('Explain query error', () => { cy.contains('MySql-connection').click(); cy.contains('MyChinook').click(); cy.testid('TabsPanel_buttonNewObject').click(); @@ -199,7 +199,7 @@ describe('Charts', () => { cy.testid('WidgetIconPanel_settings'); }); - it.only('Settings', () => { + it('Settings', () => { cy.testid('WidgetIconPanel_settings').click(); cy.themeshot('app-settings-general'); @@ -234,10 +234,10 @@ describe('Charts', () => { cy.contains('Light').click(); cy.get('body').find('.theme-light').should('exist'); - // Connection - cy.contains(/^Connection$/).click(); + // General + cy.contains(/^General$/).click(); cy.contains('charts_sample'); - cy.get('[data-testid=ConnectionSettings_lockedDatabaseMode]').check(); + cy.get('[data-testid=GeneralSettings_lockedDatabaseMode]').check(); cy.contains('Connections').click(); cy.contains('charts_sample').should('not.exist'); diff --git a/e2e-tests/cypress/e2e/multi-sql.cy.js b/e2e-tests/cypress/e2e/multi-sql.cy.js index 59a537208..496a40a3c 100644 --- a/e2e-tests/cypress/e2e/multi-sql.cy.js +++ b/e2e-tests/cypress/e2e/multi-sql.cy.js @@ -210,7 +210,8 @@ describe('Import CSV', () => { cy.testid('ImportExportConfigurator_tableMappingSection').contains('20 rows written').should('be.visible'); cy.testid('SqlObjectList_refreshButton').click(); - cy.contains('Refresh DB structure (incremental)').click(); + cy.testid('DatabasStatusMenu_refreshFull').click(); + // cy.contains('Refresh DB structure (incremental)').click(); cy.testid('SqlObjectList_container').contains('customers-20').click(); cy.contains('Rows: 20').should('be.visible'); diff --git a/e2e-tests/package.json b/e2e-tests/package.json index 7f6aeaaf7..5ad1e3008 100644 --- a/e2e-tests/package.json +++ b/e2e-tests/package.json @@ -10,11 +10,11 @@ "cypress-real-events": "^1.13.0", "env-cmd": "^10.1.0", "kill-port": "^2.0.1", + "mocha-reporter-gha": "^1.1.1", "start-server-and-test": "^2.0.8" }, "scripts": { "cy:open": "cypress open --config experimentalInteractiveRunEvents=true", - "cy:run:add-connection": "cypress run --spec cypress/e2e/add-connection.cy.js", "cy:run:portal": "cypress run --spec cypress/e2e/portal.cy.js", "cy:run:oauth": "cypress run --spec cypress/e2e/oauth.cy.js", @@ -23,7 +23,6 @@ "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", "start:oauth": "node clearTestingData && cd .. && env-cmd -f e2e-tests/env/oauth/.env node packer/build/bundle.js --listen-api --run-e2e-tests", @@ -32,7 +31,6 @@ "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", "test:oauth": "start-server-and-test start:oauth http://localhost:3000 cy:run:oauth", @@ -41,7 +39,6 @@ "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 && yarn test:charts", "test:ci": "yarn test" }, diff --git a/e2e-tests/yarn.lock b/e2e-tests/yarn.lock index 29aa0b706..05a4005e4 100644 --- a/e2e-tests/yarn.lock +++ b/e2e-tests/yarn.lock @@ -2,6 +2,34 @@ # yarn lockfile v1 +"@actions/core@^1.10.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.11.1.tgz#ae683aac5112438021588030efb53b1adb86f172" + integrity sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A== + dependencies: + "@actions/exec" "^1.1.1" + "@actions/http-client" "^2.0.1" + +"@actions/exec@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@actions/exec/-/exec-1.1.1.tgz#2e43f28c54022537172819a7cf886c844221a611" + integrity sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w== + dependencies: + "@actions/io" "^1.0.1" + +"@actions/http-client@^2.0.1": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.2.3.tgz#31fc0b25c0e665754ed39a9f19a8611fc6dab674" + integrity sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA== + dependencies: + tunnel "^0.0.6" + undici "^5.25.4" + +"@actions/io@^1.0.1": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@actions/io/-/io-1.1.3.tgz#4cdb6254da7962b07473ff5c335f3da485d94d71" + integrity sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q== + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -39,6 +67,11 @@ debug "^3.1.0" lodash.once "^4.1.1" +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + "@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": version "9.3.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" @@ -947,6 +980,13 @@ minimist@^1.2.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +mocha-reporter-gha@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/mocha-reporter-gha/-/mocha-reporter-gha-1.1.1.tgz#e1248abd0769f55b57b36ccd7db2b0b6573d5adf" + integrity sha512-CFbcgM56V4yWlbF91XuwrE6a5X/IqjVXTPefO7m8cY8Es8G1UhJ2KKOrk16AcSemRzVWXp2Fdy3bWJ7j45snWw== + dependencies: + "@actions/core" "^1.10.1" + ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -1292,6 +1332,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -1307,6 +1352,13 @@ undici-types@~6.20.0: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== +undici@^5.25.4: + version "5.29.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.29.0.tgz#419595449ae3f2cdcba3580a2e8903399bd1f5a3" + integrity sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg== + dependencies: + "@fastify/busboy" "^2.0.0" + universalify@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" diff --git a/integration-tests/__tests__/schema-tests.spec.js b/integration-tests/__tests__/schema-tests.spec.js index bf3f47f4a..53aa0bfb5 100644 --- a/integration-tests/__tests__/schema-tests.spec.js +++ b/integration-tests/__tests__/schema-tests.spec.js @@ -28,12 +28,12 @@ describe('Schema tests', () => { const count = schemas1.length; expect(structure1.tables.length).toEqual(2); await runCommandOnDriver(conn, driver, dmp => dmp.createSchema('myschema')); + const structure2 = await driver.analyseIncremental(conn, structure1); + const schemas2 = await driver.listSchemas(conn); + expect(schemas2.find(x => x.schemaName == 'myschema')).toBeTruthy(); + expect(schemas2.length).toEqual(count + 1); + expect(schemas2.find(x => x.isDefault).schemaName).toEqual(engine.defaultSchemaName); if (!engine.skipIncrementalAnalysis) { - const structure2 = await driver.analyseIncremental(conn, structure1); - const schemas2 = await driver.listSchemas(conn); - expect(schemas2.find(x => x.schemaName == 'myschema')).toBeTruthy(); - expect(schemas2.length).toEqual(count + 1); - expect(schemas2.find(x => x.isDefault).schemaName).toEqual(engine.defaultSchemaName); expect(structure2).toBeNull(); } }) @@ -50,10 +50,10 @@ describe('Schema tests', () => { expect(schemas1.find(x => x.schemaName == 'myschema')).toBeTruthy(); expect(structure1.tables.length).toEqual(2); await runCommandOnDriver(conn, driver, dmp => dmp.dropSchema('myschema')); + const structure2 = await driver.analyseIncremental(conn, structure1); + const schemas2 = await driver.listSchemas(conn); + expect(schemas2.find(x => x.schemaName == 'myschema')).toBeFalsy(); if (!engine.skipIncrementalAnalysis) { - const structure2 = await driver.analyseIncremental(conn, structure1); - const schemas2 = await driver.listSchemas(conn); - expect(schemas2.find(x => x.schemaName == 'myschema')).toBeFalsy(); expect(structure2).toBeNull(); } }) diff --git a/integration-tests/__tests__/table-analyse.spec.js b/integration-tests/__tests__/table-analyse.spec.js index 570a2e2f2..705adf077 100644 --- a/integration-tests/__tests__/table-analyse.spec.js +++ b/integration-tests/__tests__/table-analyse.spec.js @@ -94,7 +94,7 @@ describe('Table analyse', () => { }) ); - test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))( + test.each(engines.map(engine => [engine.label, engine]))( 'Table add - incremental analysis - %s', testWrapper(async (conn, driver, engine) => { await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine))); @@ -112,7 +112,7 @@ describe('Table analyse', () => { }) ); - test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))( + test.each(engines.map(engine => [engine.label, engine]))( 'Table remove - incremental analysis - %s', testWrapper(async (conn, driver, engine) => { await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql(engine))); @@ -130,7 +130,7 @@ describe('Table analyse', () => { }) ); - test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))( + test.each(engines.map(engine => [engine.label, engine]))( 'Table change - incremental analysis - %s', testWrapper(async (conn, driver, engine) => { await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql(engine))); diff --git a/package.json b/package.json index 4706df1b5..1e0781e0b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "6.7.2-premium-beta.5", + "version": "6.7.3", "name": "dbgate-all", "workspaces": [ "packages/*", diff --git a/packages/api/src/controllers/config.js b/packages/api/src/controllers/config.js index f47c5245a..a7425ab72 100644 --- a/packages/api/src/controllers/config.js +++ b/packages/api/src/controllers/config.js @@ -289,16 +289,11 @@ module.exports = { const res = await lock.acquire('settings', async () => { const currentValue = await this.loadSettings(); try { - let updated = currentValue; + let updated = { + ...currentValue, + ...values, + }; if (process.env.STORAGE_DATABASE) { - updated = { - ...currentValue, - ..._.mapValues(values, v => { - if (v === true) return 'true'; - if (v === false) return 'false'; - return v; - }), - }; await storage.writeConfig({ group: 'settings', config: updated, diff --git a/packages/api/src/storageModel.js b/packages/api/src/storageModel.js index dfeb773eb..208358e5d 100644 --- a/packages/api/src/storageModel.js +++ b/packages/api/src/storageModel.js @@ -360,6 +360,12 @@ module.exports = { "columnName": "value", "dataType": "varchar(1000)", "notNull": false + }, + { + "pureName": "config", + "columnName": "valueType", + "dataType": "varchar(50)", + "notNull": false } ], "foreignKeys": [], diff --git a/packages/tools/src/DatabaseAnalyser.ts b/packages/tools/src/DatabaseAnalyser.ts index e10e23ab8..f394cc939 100644 --- a/packages/tools/src/DatabaseAnalyser.ts +++ b/packages/tools/src/DatabaseAnalyser.ts @@ -164,6 +164,11 @@ export class DatabaseAnalyser { const res = {}; for (const field of STRUCTURE_FIELDS) { + const isAll = this.modifications.some(x => x.action == 'all' && x.objectTypeField == field); + if (isAll) { + res[field] = newlyAnalysed[field] || []; + continue; + } const removedIds = this.modifications .filter(x => x.action == 'remove' && x.objectTypeField == field) .map(x => x.objectId); diff --git a/packages/tools/src/stringTools.ts b/packages/tools/src/stringTools.ts index f44017b69..0c12ac9ea 100644 --- a/packages/tools/src/stringTools.ts +++ b/packages/tools/src/stringTools.ts @@ -45,14 +45,15 @@ export function hexStringToArray(inputString) { export function base64ToHex(base64String) { const binaryString = atob(base64String); - const hexString = Array.from(binaryString, c => - c.charCodeAt(0).toString(16).padStart(2, '0') - ).join(''); + const hexString = Array.from(binaryString, c => c.charCodeAt(0).toString(16).padStart(2, '0')).join(''); return '0x' + hexString.toUpperCase(); -}; +} export function hexToBase64(hexString) { - const binaryString = hexString.match(/.{1,2}/g).map(byte => String.fromCharCode(parseInt(byte, 16))).join(''); + const binaryString = hexString + .match(/.{1,2}/g) + .map(byte => String.fromCharCode(parseInt(byte, 16))) + .join(''); return btoa(binaryString); } @@ -68,9 +69,9 @@ export function parseCellValue(value, editorTypes?: DataEditorTypesBehaviour) { if (mHex) { return { $binary: { - base64: hexToBase64(value.substring(2)) - } - } + base64: hexToBase64(value.substring(2)), + }, + }; } } @@ -200,6 +201,26 @@ function stringifyJsonToGrid(value): ReturnType { return { value: '(JSON)', gridStyle: 'nullCellStyle' }; } +function formatNumberCustomSeparator(value, thousandsSeparator) { + const [intPart, decPart] = value.split('.'); + const intPartWithSeparator = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSeparator); + return decPart ? `${intPartWithSeparator}.${decPart}` : intPartWithSeparator; +} + +function formatCellNumber(value, gridFormattingOptions?: { thousandsSeparator?: string }) { + const separator = gridFormattingOptions?.thousandsSeparator; + if (_isNumber(value)) { + if (separator === 'none' || (value < 1000 && value > -1000)) return value.toString(); + if (separator === 'system') return value.toLocaleString(); + } + // fallback for system locale + if (separator === 'space' || separator === 'system') return formatNumberCustomSeparator(value.toString(), ' '); + if (separator === 'narrowspace') return formatNumberCustomSeparator(value.toString(), '\u202F'); + if (separator === 'comma') return formatNumberCustomSeparator(value.toString(), ','); + if (separator === 'dot') return formatNumberCustomSeparator(value.toString(), '.'); + return value.toString(); +} + export function stringifyCellValue( value, intent: @@ -210,7 +231,7 @@ export function stringifyCellValue( | 'exportIntent' | 'clipboardIntent', editorTypes?: DataEditorTypesBehaviour, - gridFormattingOptions?: { useThousandsSeparator?: boolean }, + gridFormattingOptions?: { thousandsSeparator?: string }, jsonParsedValue?: any ): { value: string; @@ -256,7 +277,7 @@ export function stringifyCellValue( // return { value: '0x' + arrayToHexString(value.data), gridStyle: 'valueCellStyle' }; // } } - + if (editorTypes?.parseObjectIdAsDollar) { if (value?.$oid) { switch (intent) { @@ -270,13 +291,13 @@ export function stringifyCellValue( } if (value?.$bigint) { return { - value: value.$bigint, + value: formatCellNumber(value.$bigint, gridFormattingOptions), gridStyle: 'valueCellStyle', }; } if (typeof value === 'bigint') { return { - value: value.toString(), + value: formatCellNumber(value.toString(), gridFormattingOptions), gridStyle: 'valueCellStyle', }; } @@ -351,13 +372,8 @@ export function stringifyCellValue( if (_isNumber(value)) { switch (intent) { case 'gridCellIntent': - return { - value: - gridFormattingOptions?.useThousandsSeparator && (value >= 10000 || value <= -10000) - ? value.toLocaleString() - : value.toString(), - gridStyle: 'valueCellStyle', - }; + const separator = gridFormattingOptions?.thousandsSeparator; + return { value: formatCellNumber(value, gridFormattingOptions), gridStyle: 'valueCellStyle' }; default: return { value: value.toString() }; } diff --git a/packages/web/src/commands/stdCommands.ts b/packages/web/src/commands/stdCommands.ts index 938d56bff..fdf6556b1 100644 --- a/packages/web/src/commands/stdCommands.ts +++ b/packages/web/src/commands/stdCommands.ts @@ -40,8 +40,6 @@ import { getSettings } from '../utility/metadataLoaders'; import { isMac, switchCurrentDatabase } from '../utility/common'; import { doLogout } from '../clientAuth'; import { disconnectServerConnection } from '../appobj/ConnectionAppObject.svelte'; -import UploadErrorModal from '../modals/UploadErrorModal.svelte'; -import ErrorMessageModal from '../modals/ErrorMessageModal.svelte'; import NewCollectionModal from '../modals/NewCollectionModal.svelte'; import ConfirmModal from '../modals/ConfirmModal.svelte'; import localforage from 'localforage'; @@ -73,7 +71,8 @@ registerCommand({ category: __t('command.theme', { defaultMessage: 'Theme' }), name: __t('command.theme.change', { defaultMessage: 'Change' }), toolbarName: __t('command.theme.changeToolbar', { defaultMessage: 'Change theme' }), - onClick: () => openNewTab({ + onClick: () => + openNewTab({ title: 'Settings', icon: 'icon settings', tabComponent: 'SettingsTab', @@ -1230,8 +1229,7 @@ registerCommand({ }, }); -if ( hasPermission('application-log')) -{ +if (hasPermission('application-log')) { registerCommand({ id: 'app.showLogs', category: __t('command.application', { defaultMessage: 'Application' }), @@ -1246,8 +1244,7 @@ if ( hasPermission('application-log')) }); } -if (hasPermission('widgets/plugins')) -{ +if (hasPermission('widgets/plugins')) { registerCommand({ id: 'app.managePlugins', category: __t('command.application', { defaultMessage: 'Application' }), diff --git a/packages/web/src/datagrid/CellValue.svelte b/packages/web/src/datagrid/CellValue.svelte index 8d006962e..24b5c5427 100644 --- a/packages/web/src/datagrid/CellValue.svelte +++ b/packages/web/src/datagrid/CellValue.svelte @@ -1,6 +1,6 @@
@@ -12,6 +15,9 @@ {label} {:else}
+ {#if labelIcon} + + {/if} {label}
diff --git a/packages/web/src/modals/DropDownMenu.svelte b/packages/web/src/modals/DropDownMenu.svelte index aeacb017b..2f45c1edf 100644 --- a/packages/web/src/modals/DropDownMenu.svelte +++ b/packages/web/src/modals/DropDownMenu.svelte @@ -164,7 +164,12 @@ changeActiveSubmenu(); }} > - handleClick(e, item)} class:disabled={item.disabled} class:bold={item.isBold}> + handleClick(e, item)} + class:disabled={item.disabled} + class:bold={item.isBold} + data-testid={item.testid} + > {#if item.switchValue && item.switchStoreGetter} {#key switchIndex} diff --git a/packages/web/src/settings/ConnectionSettings.svelte b/packages/web/src/settings/ConnectionSettings.svelte index 31903e5a4..0576be217 100644 --- a/packages/web/src/settings/ConnectionSettings.svelte +++ b/packages/web/src/settings/ConnectionSettings.svelte @@ -1,81 +1,64 @@
- +
{_t('settings.connection', { defaultMessage: 'Connection' })}
- { - $lockedDatabaseMode = !$lockedDatabaseMode; - }, - }} - > - ($lockedDatabaseMode = e.target.checked)} data-testid="ConnectionSettings_lockedDatabaseMode"/> - -
{_t('settings.session', { defaultMessage: 'Query sessions' })}
-
+
- \ No newline at end of file + diff --git a/packages/web/src/settings/DataGridSettings.svelte b/packages/web/src/settings/DataGridSettings.svelte index eb47f76ca..30d1c1ef4 100644 --- a/packages/web/src/settings/DataGridSettings.svelte +++ b/packages/web/src/settings/DataGridSettings.svelte @@ -1,94 +1,105 @@
-
{_t('settings.dataGrid.title', { defaultMessage: 'Data grid' })}
- -{#if isProApp()} - -{/if} - +
{_t('settings.dataGrid.title', { defaultMessage: 'Data grid' })}
+ + {#if isProApp()} + + {/if} + - + - + - + - + - + - +
\ No newline at end of file + diff --git a/packages/web/src/settings/GeneralSettings.svelte b/packages/web/src/settings/GeneralSettings.svelte index 364c6d939..4807b0777 100644 --- a/packages/web/src/settings/GeneralSettings.svelte +++ b/packages/web/src/settings/GeneralSettings.svelte @@ -10,6 +10,9 @@ import { isMac } from '../utility/common'; import getElectron from '../utility/getElectron'; import ConfirmModal from '../modals/ConfirmModal.svelte'; + import hasPermission from '../utility/hasPermission'; + import CheckboxField from '../forms/CheckboxField.svelte'; + import { lockedDatabaseMode } from '../stores'; const electron = getElectron(); let restartWarning = false; @@ -17,7 +20,11 @@
{_t('settings.application', { defaultMessage: 'Application' })}
- + {/if} + { + $lockedDatabaseMode = !$lockedDatabaseMode; + }, + }} + > + ($lockedDatabaseMode = e.target['checked'])} + data-testid="GeneralSettings_lockedDatabaseMode" + /> + +
{_t('settings.appearance', { defaultMessage: 'Appearance' })}
{#if electron} @@ -106,6 +131,7 @@ defaultMessage: 'Show server name alongside database name in title of the tab group', })} defaultValue={false} + disabled={!hasPermission('settings/change')} />
diff --git a/packages/web/src/settings/settingsTools.ts b/packages/web/src/settings/settingsTools.ts index 41028529d..8e2e2ec79 100644 --- a/packages/web/src/settings/settingsTools.ts +++ b/packages/web/src/settings/settingsTools.ts @@ -29,6 +29,13 @@ export function getStringSettingsValue(name, defaultValue) { return res; } +export function getObjectSettingsValue(name, defaultValue) { + const settings = getCurrentSettings(); + const res = settings[name]; + if (res == null) return defaultValue; + return res; +} + export function getConnectionClickActionSetting(): 'connect' | 'openDetails' | 'none' { return getStringSettingsValue('defaultAction.connectionClick', 'connect'); } diff --git a/packages/web/src/tabs/SettingsTab.svelte b/packages/web/src/tabs/SettingsTab.svelte index 290530b96..431a15ceb 100644 --- a/packages/web/src/tabs/SettingsTab.svelte +++ b/packages/web/src/tabs/SettingsTab.svelte @@ -3,111 +3,114 @@ - - \ No newline at end of file + diff --git a/packages/web/src/utility/common.ts b/packages/web/src/utility/common.ts index a08b8e370..d95c36b04 100644 --- a/packages/web/src/utility/common.ts +++ b/packages/web/src/utility/common.ts @@ -166,6 +166,7 @@ export function getDatabasStatusMenu(dbid, driver = null) { apiCall('database-connections/sync-model', dbid); callSchemalListChanged(); }, + testid: 'DatabasStatusMenu_refreshIncremental', }, { text: driver?.supportsIncrementalAnalysis @@ -175,6 +176,7 @@ export function getDatabasStatusMenu(dbid, driver = null) { apiCall('database-connections/sync-model', { ...dbid, isFullRefresh: true }); callSchemalListChanged(); }, + testid: 'DatabasStatusMenu_refreshFull', }, { text: _t('command.database.reopenConnection', { defaultMessage: 'Reopen connection' }), @@ -182,6 +184,7 @@ export function getDatabasStatusMenu(dbid, driver = null) { apiCall('database-connections/refresh', dbid); callSchemalListChanged(); }, + testid: 'DatabasStatusMenu_reopenConnection', }, { text: _t('command.database.disconnect', { defaultMessage: 'Disconnect' }), @@ -190,6 +193,7 @@ export function getDatabasStatusMenu(dbid, driver = null) { if (electron) apiCall('database-connections/disconnect', dbid); switchCurrentDatabase(null); }, + testid: 'DatabasStatusMenu_disconnect', }, ]); } diff --git a/translations/cs.json b/translations/cs.json index 674d2490a..ff0088bb8 100644 --- a/translations/cs.json +++ b/translations/cs.json @@ -171,6 +171,7 @@ "command.database.databaseSearch": "Vyhledávání v databázi", "command.database.disconnect": "Odpojit", "command.database.export": "Exportovat databázi", + "command.database.refresh": "Obnovit strukturu DB", "command.database.refreshFull": "Obnovit strukturu DB (úplně)", "command.database.refreshIncremental": "Obnovit strukturu DB (inkrementálně)", "command.database.reopenConnection": "Znovu otevřít připojení", @@ -1198,6 +1199,7 @@ "settings.sqlEditor.sqlCommandsCase": "Velikost písmen SQL příkazů", "settings.sqlEditor.title": "SQL editor", "settings.tabGroup.showServerName": "Zobrazit název serveru vedle názvu databáze v záhlaví skupiny karet", + "settings.tabPreviewMode": "Režim náhledu karet", "settings.theme": "Vzhled", "settings.title": "Nastavení", "settings.useNativeWindowTitle": "Použít nativní titulek okna", diff --git a/translations/de.json b/translations/de.json index cd33c3422..85ccc86c4 100644 --- a/translations/de.json +++ b/translations/de.json @@ -171,6 +171,7 @@ "command.database.databaseSearch": "Datenbanksuche", "command.database.disconnect": "Trennen", "command.database.export": "Datenbank exportieren", + "command.database.refresh": "DB-Struktur aktualisieren", "command.database.refreshFull": "DB-Struktur aktualisieren (vollständig)", "command.database.refreshIncremental": "DB-Struktur aktualisieren (inkrementell)", "command.database.reopenConnection": "Verbindung erneut öffnen", @@ -1198,6 +1199,7 @@ "settings.sqlEditor.sqlCommandsCase": "Groß-/Kleinschreibung der SQL-Befehle", "settings.sqlEditor.title": "SQL-Editor", "settings.tabGroup.showServerName": "Servername neben Datenbankname im Titel der Tab-Gruppe anzeigen", + "settings.tabPreviewMode": "Tab-Vorschaumodus", "settings.theme": "Designs", "settings.title": "Einstellungen", "settings.useNativeWindowTitle": "Nativen Fenstertitel verwenden", diff --git a/translations/en.json b/translations/en.json index ec5580165..93c3146fa 100644 --- a/translations/en.json +++ b/translations/en.json @@ -167,6 +167,7 @@ "command.database.databaseSearch": "Database search", "command.database.disconnect": "Disconnect", "command.database.export": "Export database", + "command.database.refresh": "Refresh DB structure", "command.database.refreshFull": "Refresh DB structure (full)", "command.database.refreshIncremental": "Refresh DB structure (incremental)", "command.database.reopenConnection": "Reopen connection", @@ -1190,6 +1191,7 @@ "settings.sqlEditor.sqlCommandsCase": "SQL commands case", "settings.sqlEditor.title": "SQL Editor", "settings.tabGroup.showServerName": "Show server name alongside database name in title of the tab group", + "settings.tabPreviewMode": "Tab Preview Mode", "settings.theme": "Themes", "settings.useNativeWindowTitle": "Use native window title", "settings.useSystemNativeMenu": "Use system native menu", diff --git a/translations/es.json b/translations/es.json index 36f7a9cc5..34070b779 100644 --- a/translations/es.json +++ b/translations/es.json @@ -171,6 +171,7 @@ "command.database.databaseSearch": "Búsqueda en base de datos", "command.database.disconnect": "Desconectar", "command.database.export": "Exportar base de datos", + "command.database.refresh": "Refrescar estructura de BD", "command.database.refreshFull": "Refrescar estructura de BD (completa)", "command.database.refreshIncremental": "Refrescar estructura de BD (incremental)", "command.database.reopenConnection": "Reabrir conexión", @@ -1198,6 +1199,7 @@ "settings.sqlEditor.sqlCommandsCase": "Mayúsculas/minúsculas de comandos SQL", "settings.sqlEditor.title": "Editor SQL", "settings.tabGroup.showServerName": "Mostrar nombre del servidor junto con nombre de base de datos en el título del grupo de pestañas", + "settings.tabPreviewMode": "Modo de vista previa de pestaña", "settings.theme": "Temas", "settings.title": "Configuración", "settings.useNativeWindowTitle": "Usar título de ventana nativo", diff --git a/translations/fr.json b/translations/fr.json index fef8f4eb2..dea337b8f 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -171,6 +171,7 @@ "command.database.databaseSearch": "Recherche dans la base de données", "command.database.disconnect": "Déconnecter", "command.database.export": "Exporter la base de données", + "command.database.refresh": "Rafraîchir la structure de BD", "command.database.refreshFull": "Rafraîchir la structure de BD (complète)", "command.database.refreshIncremental": "Rafraîchir la structure de BD (incrémentale)", "command.database.reopenConnection": "Rouvrir la connexion", @@ -1198,6 +1199,7 @@ "settings.sqlEditor.sqlCommandsCase": "Casse des commandes SQL", "settings.sqlEditor.title": "Éditeur SQL", "settings.tabGroup.showServerName": "Afficher le nom du serveur à côté du nom de la base de données dans le titre du groupe d'onglets", + "settings.tabPreviewMode": "Mode aperçu d'onglet", "settings.theme": "Thèmes", "settings.title": "Paramètres", "settings.useNativeWindowTitle": "Utiliser le titre de fenêtre natif", diff --git a/translations/it.json b/translations/it.json index 140ea8035..62970482f 100644 --- a/translations/it.json +++ b/translations/it.json @@ -171,6 +171,7 @@ "command.database.databaseSearch": "Ricerca database", "command.database.disconnect": "Disconnetti", "command.database.export": "Esporta database", + "command.database.refresh": "Aggiorna struttura DB", "command.database.refreshFull": "Aggiorna struttura DB (completo)", "command.database.refreshIncremental": "Aggiorna struttura DB (incrementale)", "command.database.reopenConnection": "Riapri connessione", @@ -1198,6 +1199,7 @@ "settings.sqlEditor.sqlCommandsCase": "Maiuscole/minuscole comandi SQL", "settings.sqlEditor.title": "Editor SQL", "settings.tabGroup.showServerName": "Mostra nome server accanto al nome database nel titolo del gruppo schede", + "settings.tabPreviewMode": "Modalità anteprima scheda", "settings.theme": "Temi", "settings.title": "Impostazioni", "settings.useNativeWindowTitle": "Usa titolo finestra nativo", diff --git a/translations/ja.json b/translations/ja.json index e202a1623..6c854934b 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -171,6 +171,7 @@ "command.database.databaseSearch": "データベース検索", "command.database.disconnect": "切断", "command.database.export": "データベースをエクスポート", + "command.database.refresh": "DB構造を更新", "command.database.refreshFull": "DB構造を更新(フル)", "command.database.refreshIncremental": "DB構造を更新(増分)", "command.database.reopenConnection": "接続を再度開く", @@ -1198,6 +1199,7 @@ "settings.sqlEditor.sqlCommandsCase": "SQLコマンドの大文字小文字", "settings.sqlEditor.title": "SQLエディター", "settings.tabGroup.showServerName": "タブグループのタイトルにデータベース名と並んでサーバー名を表示", + "settings.tabPreviewMode": "タブプレビューモード", "settings.theme": "テーマ", "settings.title": "設定", "settings.useNativeWindowTitle": "ネイティブウィンドウタイトルを使用", diff --git a/translations/pt.json b/translations/pt.json index 51823cabb..658ce1b99 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -171,6 +171,7 @@ "command.database.databaseSearch": "Pesquisar banco de dados", "command.database.disconnect": "Desconectar", "command.database.export": "Exportar banco de dados", + "command.database.refresh": "Atualizar estrutura do BD", "command.database.refreshFull": "Atualizar estrutura do BD (completa)", "command.database.refreshIncremental": "Atualizar estrutura do BD (incremental)", "command.database.reopenConnection": "Reabrir conexão", @@ -1198,6 +1199,7 @@ "settings.sqlEditor.sqlCommandsCase": "Maiúsculas/minúsculas de comandos SQL", "settings.sqlEditor.title": "Editor SQL", "settings.tabGroup.showServerName": "Mostrar nome do servidor junto ao nome do banco de dados no título do grupo de abas", + "settings.tabPreviewMode": "Modo de visualização de aba", "settings.theme": "Temas", "settings.title": "Configurações", "settings.useNativeWindowTitle": "Usar título de janela nativo", diff --git a/translations/sk.json b/translations/sk.json index 9f7d5723f..2ae3b52c2 100644 --- a/translations/sk.json +++ b/translations/sk.json @@ -171,6 +171,7 @@ "command.database.databaseSearch": "Hľadanie v databáze", "command.database.disconnect": "Odpojiť", "command.database.export": "Exportovať databázu", + "command.database.refresh": "Obnoviť štruktúru DB", "command.database.refreshFull": "Obnoviť štruktúru DB (úplne)", "command.database.refreshIncremental": "Obnoviť štruktúru DB (inkrementálne)", "command.database.reopenConnection": "Znovu otvoriť pripojenie", @@ -1198,6 +1199,7 @@ "settings.sqlEditor.sqlCommandsCase": "Veľkosť písmen", "settings.sqlEditor.title": "SQL editor", "settings.tabGroup.showServerName": "Zobraziť názov servera vedľa názvu databázy v názve skupiny kariet", + "settings.tabPreviewMode": "Režim náhľadu kariet", "settings.theme": "Vzhľad", "settings.title": "Nastavenia", "settings.useNativeWindowTitle": "Použiť natívne menu", diff --git a/translations/zh.json b/translations/zh.json index c720cd975..dd646c6d0 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -171,6 +171,7 @@ "command.database.databaseSearch": "数据库搜索", "command.database.disconnect": "断开连接", "command.database.export": "导出数据库", + "command.database.refresh": "刷新数据库结构", "command.database.refreshFull": "刷新数据库结构(完整)", "command.database.refreshIncremental": "刷新数据库结构(增量)", "command.database.reopenConnection": "重新打开连接", @@ -1198,6 +1199,7 @@ "settings.sqlEditor.sqlCommandsCase": "SQL命令大小写", "settings.sqlEditor.title": "SQL 编辑器", "settings.tabGroup.showServerName": "在标签页组标题中显示服务器名称和数据库名称", + "settings.tabPreviewMode": "标签页预览模式", "settings.theme": "主题", "settings.title": "设置", "settings.useNativeWindowTitle": "使用原生窗口标题", diff --git a/workflow-templates/includes.tpl.yaml b/workflow-templates/includes.tpl.yaml index 4011deeb2..89984c18f 100644 --- a/workflow-templates/includes.tpl.yaml +++ b/workflow-templates/includes.tpl.yaml @@ -7,7 +7,7 @@ checkout-and-merge-pro: repository: dbgate/dbgate-pro token: ${{ secrets.GH_TOKEN }} path: dbgate-pro - ref: 911941a53e91a5a777b8c7d455be0719234dde5f + ref: ae1fcf6e61c6f7dfbb21005daa259c68e899a80a - name: Merge dbgate/dbgate-pro run: | mkdir ../dbgate-pro