Compare commits

..

246 Commits

Author SHA1 Message Date
SPRINX0\prochazka a156ec942b DBF reader - log errors 2025-01-07 18:28:19 +01:00
SPRINX0\prochazka 48eb8badec fixed import preview 2025-01-07 17:52:26 +01:00
SPRINX0\prochazka 2521f05526 DBF reader plugin 2025-01-07 17:24:56 +01:00
Jan Prochazka 79bf9016a3 Merge pull request #985 from dbgate/feature/mysql-event-scheduler
Feature/mysql event scheduler
2025-01-07 14:37:30 +01:00
Nybkox 202716e28c fix: show funcitons specific menu for functions 2025-01-07 14:35:40 +01:00
Nybkox 8c5e23f773 feat: show only 1 of disable/enable actions in events context menu 2025-01-07 14:29:17 +01:00
SPRINX0\prochazka bf9374bf6e fixed test config 2025-01-07 13:59:42 +01:00
SPRINX0\prochazka 2087574363 Merge branch 'master' of https://github.com/dbgate/dbgate 2025-01-07 13:39:24 +01:00
SPRINX0\prochazka 4765cbcfd4 Merge branch 'feature/portal-test' 2025-01-07 13:39:18 +01:00
SPRINX0\prochazka a48c23d169 ask user test 2025-01-07 13:38:47 +01:00
Jan Prochazka 03982d4ad2 Merge pull request #981 from dbgate/feature/triggers-sqlite
Feature/triggers sqlite
2025-01-07 13:23:36 +01:00
SPRINX0\prochazka e531a35f37 ask user portal conn 2025-01-07 13:19:44 +01:00
Nybkox a8a5864ff0 fix: escape mysql event name in createSql 2025-01-07 13:18:23 +01:00
SPRINX0\prochazka ee9ce66293 fix test env 2025-01-07 10:55:03 +01:00
SPRINX0\prochazka a11b87a923 test fix 2025-01-07 10:45:56 +01:00
SPRINX0\prochazka e3cd01aa2e portal test - import Chinook DB 2025-01-07 10:30:26 +01:00
SPRINX0\prochazka bf92943a6a portal tests WIP 2025-01-07 09:32:18 +01:00
SPRINX0\prochazka 8d2c2cd62d focus query editor 2025-01-07 09:32:01 +01:00
Nybkox 029f24b079 feat: update dbgate-query-splitter tp 4.11.3 2025-01-07 09:20:59 +01:00
SPRINX0\prochazka d9ca29b602 portal connection test 2025-01-06 14:56:16 +01:00
SPRINX0\prochazka ddcfc127b7 Merge branch 'master' of https://github.com/dbgate/dbgate 2025-01-06 11:58:29 +01:00
SPRINX0\prochazka 0659691a72 Merge branch 'feature/ssh-tunnel-test' 2025-01-06 11:57:59 +01:00
SPRINX0\prochazka 4e7c8d8b1e added missing services 2025-01-06 10:42:51 +01:00
SPRINX0\prochazka f477c03281 increase test timeout 2025-01-06 10:18:40 +01:00
SPRINX0\prochazka 362d4a5a0d test connect too SSH via keyfile 2025-01-06 10:16:58 +01:00
SPRINX0\prochazka 42ad78df58 keyfile - build containers 2025-01-06 09:46:57 +01:00
SPRINX0\prochazka fef3ffd996 workflow fix 2025-01-06 09:29:27 +01:00
SPRINX0\prochazka a5f824c778 changed location of mysql-ssh container 2025-01-06 09:00:56 +01:00
SPRINX0\prochazka 4f5bb563e8 JEST timeout 2025-01-06 08:35:15 +01:00
SPRINX0\prochazka 053ba471a6 SSH - handle SSH error 2025-01-06 08:24:14 +01:00
Jan Prochazka 066cd407a9 Merge pull request #986 from dbgate/feature/missing-factories
feat: add missing triggers and events factories
2025-01-05 08:05:02 +01:00
Jan Prochazka b418dd8d12 Merge pull request #984 from freitasm/master
Update runners.js to fix small typo
2025-01-05 08:02:47 +01:00
Nybkox 1be73d8a4c fix: build enable/disable sql on frontend 2025-01-03 22:57:33 +01:00
SPRINX0\prochazka aff282d31e ssh tunnel WIP 2025-01-03 16:44:57 +01:00
Nybkox 576a4fc2e5 feat: add missing triggers and events factories 2025-01-03 16:26:10 +01:00
Nybkox e73356c8da feat: add schedulerEvents to tools 2025-01-03 15:02:03 +01:00
Nybkox 0ffe2d8811 fix: disable rename tests for mysql 2025-01-03 14:59:57 +01:00
Nybkox c90011fc27 feat: scheduler events tests 2025-01-03 14:59:57 +01:00
Nybkox 891fb15290 feat: add scheduler events to ui 2025-01-03 14:59:57 +01:00
Nybkox 903297a1e9 feat: add scheduler events to analyser 2025-01-03 14:59:57 +01:00
SPRINX0\prochazka 59788faefd workflows 2025-01-03 08:14:49 +01:00
SPRINX0\prochazka f5a6ca18f0 run test containers - standalone workflow 2025-01-03 08:12:11 +01:00
Mauricio Freitas 30690304bb Update runners.js
Fixed typo "no data retured"
2025-01-03 10:01:29 +13:00
SPRINX0\prochazka 144777da58 SSH - force IPv4 (use 127.0.0.1 instead of localhost) 2025-01-02 17:03:49 +01:00
SPRINX0\prochazka 28a206c7ed upgrade ssh2 2025-01-02 17:03:20 +01:00
SPRINX0\prochazka 72d29a293c skip changed detection 2025-01-02 15:58:15 +01:00
SPRINX0\prochazka 561b43fa13 fix 2025-01-02 15:57:12 +01:00
SPRINX0\prochazka 1e9dc89da1 fixed detection changed files 2025-01-02 15:56:26 +01:00
SPRINX0\prochazka b4d1b9aa05 fix 2025-01-02 15:35:57 +01:00
SPRINX0\prochazka 77be448b41 fix 2025-01-02 14:13:24 +01:00
SPRINX0\prochazka 1ff2749c9b mysql ssh image 2025-01-02 14:11:07 +01:00
SPRINX0\prochazka e5fb21daf6 workflows 2025-01-02 14:02:38 +01:00
SPRINX0\prochazka c1ceea084b ssh connection test 2025-01-02 14:00:18 +01:00
Nybkox 9f4b55ce07 feat: sqlite triggers tests 2025-01-02 11:47:07 +01:00
Nybkox 01b7c42851 fix: prevent testing empty parameters 2025-01-02 11:41:00 +01:00
Nybkox f0221f6e43 feat: sqlite triggers 2025-01-02 11:26:40 +01:00
Nybkox 30bbdb0f1d refactor: move sqlite queries to sql directory 2025-01-02 11:26:40 +01:00
Nybkox a6f0bc78d4 fix: always show pin icon on the right 2025-01-02 10:59:22 +01:00
Jan Prochazka ceb6a88964 Merge branch 'feature/workflow-refactor' 2024-12-30 15:25:39 +01:00
Jan Prochazka a39fff8b3a ssh tunnel test disabled 2024-12-30 15:25:23 +01:00
Jan Prochazka 0cf3d5993c generated header 2024-12-30 08:28:25 +01:00
Jan Prochazka 2966484cda v6.1.3-beta.1 2024-12-30 08:23:36 +01:00
Jan Prochazka ac3bb58106 generated workflows 2024-12-30 08:23:18 +01:00
Jan Prochazka 36e2f1bdee workflow templates 2024-12-30 08:11:18 +01:00
Jan Prochazka 70083bd870 workflows - defs support 2024-12-30 07:05:25 +01:00
Jan Prochazka f7b39fca26 code cleanup 2024-12-29 09:57:33 +01:00
Jan Prochazka 70873e83bd docker pipeline refactor 2024-12-29 09:57:18 +01:00
Jan Prochazka 2858bba8b2 yaml replace changed 2024-12-28 21:16:52 +01:00
Jan Prochazka bd0404fbaf fixes 2024-12-28 15:36:05 +01:00
Jan Prochazka 317f6256f6 fix 2024-12-28 15:28:34 +01:00
Jan Prochazka 6c9b738717 fix 2024-12-28 15:10:45 +01:00
Jan Prochazka 2f8c584af5 workflows 2024-12-28 15:03:38 +01:00
Jan Prochazka d3b417679e workflow processor 2024-12-28 15:00:59 +01:00
Jan Prochazka 00dd8bfc72 fixed links to dbgate.org 2024-12-28 14:01:19 +01:00
Jan Prochazka e1eea4adf3 fixed links to dbgate.org 2024-12-28 11:30:18 +01:00
Jan Prochazka fd2d2e90d4 workflow refactor WIP 2024-12-27 08:34:17 +01:00
Jan Prochazka f97b70ce45 v6.1.2 2024-12-26 08:05:17 +01:00
Jan Prochazka 45d79478fb workflow fixes 2024-12-26 08:05:03 +01:00
Jan Prochazka a59f42bd62 v6.1.2-premium-beta.18 2024-12-26 07:47:14 +01:00
Jan Prochazka a189fa7b6d fix 2024-12-26 07:46:58 +01:00
Jan Prochazka 794bc97207 v6.1.2-beta.17 2024-12-26 07:45:27 +01:00
Jan Prochazka 24f7def7ab v6.1.2-premium-beta.16 2024-12-26 07:45:16 +01:00
Jan Prochazka 01071c236e notarization log 2024-12-26 07:44:53 +01:00
Jan Prochazka a6326ac9fa v6.1.2-premium-beta.15 2024-12-26 07:37:36 +01:00
Jan Prochazka 4c6d409be8 print notarization log 2024-12-26 07:37:19 +01:00
Jan Prochazka 44ad8e51a5 v6.1.2-premium-beta.14 2024-12-26 07:18:26 +01:00
Jan Prochazka e82578adfb v6.1.2-beta.14 2024-12-26 07:17:52 +01:00
Jan Prochazka 24f28b559e pro app build 2024-12-26 07:17:24 +01:00
Jan Prochazka b086a5d3d2 prevent double upload snap 2024-12-25 21:32:17 +01:00
Jan Prochazka bb0b87b770 v6.1.2-beta.13 2024-12-25 20:20:26 +01:00
Jan Prochazka aa4f82fa98 fix 2024-12-25 20:20:10 +01:00
Jan Prochazka 873ace4170 v6.1.2-beta.12 2024-12-25 19:51:24 +01:00
Jan Prochazka 5a9d8ba0a7 fix 2024-12-25 19:51:16 +01:00
Jan Prochazka 07f0fdcc1c v6.1.2-beta.11 2024-12-25 19:43:27 +01:00
Jan Prochazka ff7ee4fb98 fix 2024-12-25 19:43:16 +01:00
Jan Prochazka b1f5b62757 v6.1.2-beta.10 2024-12-25 19:39:37 +01:00
Jan Prochazka a67c857204 python 3.11 fix 2024-12-25 19:39:26 +01:00
Jan Prochazka 4032c53a30 v6.1.2-beta.9 2024-12-25 12:19:09 +01:00
Jan Prochazka 0a37ed74d9 electron reverted to version 30 2024-12-25 12:18:58 +01:00
Jan Prochazka f07efe440a v6.1.2-beta.8 2024-12-25 12:09:35 +01:00
Jan Prochazka 2d5e67802d removed mongodb-client-encryption from community build 2024-12-25 12:09:25 +01:00
Jan Prochazka 7b8e013084 v6.1.2-beta.7 2024-12-25 11:29:42 +01:00
Jan Prochazka b69fc04b5c try to upgrade electron 2024-12-25 11:29:23 +01:00
Jan Prochazka b6dfdd4741 v6.1.2-beta.6 2024-12-25 11:14:10 +01:00
Jan Prochazka ff3985a542 upgraded mongodb-client-encryption 2024-12-25 11:13:54 +01:00
Jan Prochazka 9c7e1dcaf1 v6.1.2-beta.5 2024-12-25 10:44:39 +01:00
Jan Prochazka 48eb09687c app build - node version 22 2024-12-25 10:44:02 +01:00
Jan Prochazka 86b339535d updated electron builder 2024-12-25 10:42:49 +01:00
Jan Prochazka 576b6dc774 v6.1.2-beta.4 2024-12-25 10:28:26 +01:00
Jan Prochazka 18ca971f67 try to use electron notarize 2024-12-25 10:28:10 +01:00
Jan Prochazka 3916a5e6e8 fixed ssh tunnel connection test #972 #973 2024-12-25 09:49:23 +01:00
Jan Prochazka 1446fdad5e v6.1.2-beta.3 2024-12-23 09:52:31 +01:00
Jan Prochazka 1478886e04 fix 2024-12-23 09:52:22 +01:00
Jan Prochazka 54d61790a9 v6.1.2-beta.2 2024-12-23 09:27:28 +01:00
Jan Prochazka bb638fd8db fix 2024-12-23 09:27:14 +01:00
Jan Prochazka e3a2589517 v6.1.2-beta.1 2024-12-23 09:21:42 +01:00
Jan Prochazka 6f557a6463 print notarization error 2024-12-23 09:21:31 +01:00
Jan Prochazka 323f27f6e4 changelog 2024-12-23 07:06:36 +01:00
Jan Prochazka 8d4a38dccb v6.1.1 2024-12-23 07:06:12 +01:00
SPRINX0\prochazka 54a3b30e83 Merge branch 'master' of https://github.com/dbgate/dbgate 2024-12-20 13:45:41 +01:00
SPRINX0\prochazka 6251bcd8fd changelog 2024-12-20 13:45:39 +01:00
SPRINX0\prochazka e42099c1a9 v6.1.1-beta.1 2024-12-20 12:57:25 +01:00
Jan Prochazka b07ee91cf9 fixed oracle export + clickhouse test 2024-12-20 12:23:29 +01:00
Jan Prochazka 62c028fc3c export test finished 2024-12-20 11:33:28 +01:00
SPRINX0\prochazka 07ea575b0f fixed postgres export + export tests #970 2024-12-20 11:17:43 +01:00
SPRINX0\prochazka ee78786974 #968 mysql table comment fix 2024-12-20 10:47:14 +01:00
SPRINX0\prochazka e1814663cd trigger fulltext search 2024-12-20 10:40:57 +01:00
SPRINX0\prochazka 6ae3e019ff ux 2024-12-20 10:38:29 +01:00
Jan Prochazka cadf539c3d fixed diff for triggers 2024-12-20 10:30:05 +01:00
Jan Prochazka 51929d7ef7 some tests fixed 2024-12-20 10:23:49 +01:00
Jan Prochazka 0edc7f077f test trigger createSql 2024-12-20 09:26:53 +01:00
SPRINX0\prochazka 2488cee7ea test definition refactor 2024-12-20 09:03:30 +01:00
SPRINX0\prochazka edf64db69a search settings improvement 2024-12-20 08:42:30 +01:00
SPRINX0\prochazka c65bf51dcd Merge branch 'feature/parameters-oracle' 2024-12-20 07:46:51 +01:00
Nybkox effe235a95 feat: oracle parameters 2024-12-19 16:06:56 +01:00
Nybkox 583d1494c2 feat: fallback to position if param does not have name 2024-12-19 12:50:50 +01:00
Nybkox 983daf0d1c feat: add options position to params typing 2024-12-19 12:09:25 +01:00
Jan Prochazka 756ddf1a8b Merge pull request #969 from dbgate/feature/triggers
Feature/triggers
2024-12-19 12:09:08 +01:00
Nybkox 9ccd647d97 fix: update triggers typing to cover all oracle timing options 2024-12-19 11:55:22 +01:00
Nybkox dc9bfdc553 fix: update pssql triggers query to match ts def 2024-12-19 10:50:39 +01:00
Nybkox af4e91faba fix: update oracle triggers test create queries 2024-12-19 10:46:40 +01:00
Nybkox 394020157b fix: update oracle triggers tests expected values 2024-12-19 10:26:48 +01:00
Nybkox f16aab12e1 fix: add trigger event type to oracle triggers query 2024-12-19 10:17:16 +01:00
Nybkox e08216d6dd feat: mssql triggers tests 2024-12-19 10:13:15 +01:00
Nybkox ebb4f8e73e fix: mssql triggers query naming 2024-12-19 10:13:04 +01:00
Nybkox b9939e5d5f fix: correctly map mysql triggers sql output 2024-12-19 09:58:54 +01:00
Nybkox 9eb3bca8d6 feat: add other create/drop for triggers tests 2024-12-19 09:54:17 +01:00
Nybkox 022f263bf9 fix: update pssql triggers test sql 2024-12-19 09:53:47 +01:00
Nybkox 56972652d5 fix: pssql triggers - map tgtype to timing / event type correctly 2024-12-19 09:53:11 +01:00
SPRINX0\prochazka d75b9e2688 ace-builds patch #954 2024-12-19 09:21:57 +01:00
Nybkox d2c5440e39 feat: pssql tirgger test 2024-12-19 08:33:26 +01:00
Jan Prochazka 8ff30e426e fix - expand limited when accessing bykeyboard nav 2024-12-18 14:13:01 +01:00
Jan Prochazka 7cdbef609e arm-64 builds 2024-12-18 10:59:21 +01:00
Jan Prochazka f6195a468d v6.1.0 2024-12-18 10:37:54 +01:00
Jan Prochazka c23bf72d55 changelog 2024-12-18 10:35:54 +01:00
Jan Prochazka c02441402b fixed search in list 2024-12-18 10:22:02 +01:00
Jan Prochazka b9a8764b55 fixed search 2024-12-18 10:01:52 +01:00
Jan Prochazka a2374c1981 v6.0.1-beta.6 2024-12-18 09:39:57 +01:00
Jan Prochazka 9cfd5af704 prevent jump to first item when focusing because of mouse 2024-12-18 09:37:40 +01:00
Jan Prochazka a6f473b8ed better connection UX 2024-12-18 09:08:42 +01:00
Jan Prochazka e0a74402cb dropdown for default database 2024-12-18 08:43:51 +01:00
Jan Prochazka c6e57b278e use default database 2024-12-18 08:18:09 +01:00
Jan Prochazka e63f1f8f09 clickAction refactor, settings - open detail after keyboard navigation 2024-12-18 08:08:45 +01:00
Nybkox e866c019f0 feat: mysql and oracle triggers tests 2024-12-18 06:09:30 +01:00
Nybkox 5f4bd6d3e3 fix: triggers typing and yaml conv 2024-12-18 06:05:49 +01:00
SPRINX0\prochazka 57da9c9885 changelog wip 2024-12-17 17:17:31 +01:00
SPRINX0\prochazka e6a3acf4c2 search GUI improved 2024-12-17 16:27:54 +01:00
SPRINX0\prochazka 537869e862 data grid - no rows info 2024-12-17 16:03:21 +01:00
SPRINX0\prochazka ae2ff7b3b1 fixed display CLOB an NCLOB columns in Oracle #944 2024-12-17 15:31:10 +01:00
SPRINX0\prochazka 1db01dbdb1 optimalization 2024-12-17 14:59:44 +01:00
SPRINX0\prochazka 7988438dc7 fix search connections 2024-12-17 14:43:05 +01:00
SPRINX0\prochazka 3b32823f94 v6.0.1-beta.5 2024-12-17 14:29:56 +01:00
SPRINX0\prochazka 3370c754f2 try to fix postgres plugin 2024-12-17 14:29:45 +01:00
SPRINX0\prochazka 2d84e5a611 dataGrid align numbers right #957 2024-12-17 13:43:21 +01:00
SPRINX0\prochazka 8d5f73849e upgraded ace-builds #954 2024-12-17 12:42:09 +01:00
SPRINX0\prochazka 7a5019164a configurable search in connections 2024-12-17 12:26:50 +01:00
SPRINX0\prochazka f5733ea2d7 handle camelCase in tokenizer 2024-12-17 10:16:19 +01:00
Nybkox 413287c691 fix: remove triggerName 2024-12-17 10:16:07 +01:00
Nybkox 9e941dfce2 feat: add triggers to ui 2024-12-17 10:16:07 +01:00
Nybkox d32af771dc fix: add purename to trigger analysers 2024-12-17 10:16:07 +01:00
Nybkox 1c84f40bcf feat: add triggers loading message 2024-12-17 10:16:06 +01:00
Nybkox 671eba22e0 feat: add oracle triggers to analyser 2024-12-17 10:16:06 +01:00
Nybkox c00cb3076c feat: add mysql triggers to analyser 2024-12-17 10:16:06 +01:00
Nybkox 62daa13e54 feat: add mssql triggers to analyser 2024-12-17 10:16:06 +01:00
Nybkox 4cc0a66a7d feat: add pssql triggers to analyser 2024-12-17 10:16:06 +01:00
SPRINX0\prochazka 92e13220d8 tokenized search in references 2024-12-17 10:12:45 +01:00
SPRINX0\prochazka 2a5fdd852a search tokenizer optimalization 2024-12-17 10:09:52 +01:00
SPRINX0\prochazka 7759fd862f ability to disable tab preview mode 2024-12-17 09:34:16 +01:00
SPRINX0\prochazka ca5dd0ac30 v6.0.1-beta.4 2024-12-16 17:01:39 +01:00
SPRINX0\prochazka 18be29fd88 Merge branch 'feature/search' 2024-12-16 17:01:26 +01:00
SPRINX0\prochazka 6ea54a5b0a tokenized column search 2024-12-16 17:01:05 +01:00
SPRINX0\prochazka 5d294f6236 camel search tokenizer 2024-12-16 16:58:10 +01:00
SPRINX0\prochazka 5544b6291b search tokenizer 2024-12-16 16:50:17 +01:00
SPRINX0\prochazka bf4841bca4 filter name optimalization 2024-12-16 16:23:55 +01:00
SPRINX0\prochazka 358a641449 refactor - not working 2024-12-16 15:53:52 +01:00
SPRINX0\prochazka 7e1ceb69ae UX fix 2024-12-16 15:36:26 +01:00
SPRINX0\prochazka 939f04ae62 optimalization 2024-12-16 15:19:54 +01:00
SPRINX0\prochazka 2fc2ac491c find in SQL text 2024-12-16 14:45:38 +01:00
SPRINX0\prochazka 20a5a50516 rename file 2024-12-16 14:10:18 +01:00
SPRINX0\prochazka 0932f4c537 search columns WIP 2024-12-16 14:08:19 +01:00
SPRINX0\prochazka c46c9a4e16 lisgt matcher refactor 2024-12-16 13:08:59 +01:00
SPRINX0\prochazka a20b4b3339 search settings 2024-12-16 11:47:53 +01:00
SPRINX0\prochazka dc302f89c7 Display comments into TABLES and COLUMNS lists #755 2024-12-16 09:46:07 +01:00
SPRINX0\prochazka f5a2d142e2 fixed [MSSQL] Foreign keys show up in a weird way #734 2024-12-16 08:26:25 +01:00
SPRINX0\prochazka 1020a5820b Merge branch 'master' of https://github.com/dbgate/dbgate 2024-12-13 16:27:17 +01:00
SPRINX0\prochazka deb13505b8 fixed loading constraints #734 2024-12-13 16:27:14 +01:00
Jan Prochazka 71c06e31f7 sql terminator in oracle routines 2024-12-13 15:06:12 +01:00
Jan Prochazka a203480a72 oracle function tests 2024-12-13 15:06:12 +01:00
Jan Prochazka c0fcd681be oracle procedure & function analyser 2024-12-13 15:06:12 +01:00
Jan Prochazka 7402bb6823 test fix 2024-12-13 15:06:11 +01:00
Jan Prochazka 35d791bee4 fixed some tests 2024-12-13 15:06:11 +01:00
Jan Prochazka ad4a599800 oracle view fix 2024-12-13 15:06:11 +01:00
Jan Prochazka 33db85f03b oracle tests 2024-12-13 15:06:11 +01:00
Jan Prochazka ec5d05fc26 oracle fixes 2024-12-13 15:06:11 +01:00
Jan Prochazka face7ecdb5 rename sql object oracle 2024-12-13 15:06:11 +01:00
Jan Prochazka 6035319035 uncommented oracle container for tests 2024-12-13 15:06:11 +01:00
Jan Prochazka e698da71fb oracle tests - run on CI 2024-12-13 15:06:11 +01:00
Jan Prochazka b466de781a oracle - rename table, deploy scripts passes 2024-12-13 15:06:11 +01:00
Jan Prochazka e5d583310d merged tests pipelines 2024-12-13 15:06:11 +01:00
Jan Prochazka f72dbf19c2 driver tests 2024-12-13 15:06:11 +01:00
Jan Prochazka 10538a04b4 oracle import pass 2024-12-13 15:06:11 +01:00
Jan Prochazka d71452a397 oracle - implemented scope identity 2024-12-13 15:06:11 +01:00
Jan Prochazka 3f45bfcdd0 select scope identity test 2024-12-13 15:06:11 +01:00
Jan Prochazka 545e9863b6 fix identity test 2024-12-13 15:06:11 +01:00
Jan Prochazka a37f2a5240 select scope identity test 2024-12-13 15:06:11 +01:00
Jan Prochazka d6b4c0a96b oracle alter table passes 2024-12-13 15:06:11 +01:00
Jan Prochazka d56e917b3f oracle query spec passed 2024-12-13 15:06:11 +01:00
Jan Prochazka 860c811504 oracle table-create test passes 2024-12-13 15:06:11 +01:00
Jan Prochazka c93e8c35ec table-analyse oracle tests pass 2024-12-13 15:06:11 +01:00
Jan Prochazka 5278e5da0c fixed oracle index analyser 2024-12-13 15:06:11 +01:00
Jan Prochazka e7e3c307fc table analyse refactor (works on oracle) 2024-12-13 15:06:11 +01:00
Jan Prochazka e9af85038e try to use smaller oracle image 2024-12-13 15:06:11 +01:00
Jan Prochazka bf04721e36 run oracle tests on CI 2024-12-13 15:06:11 +01:00
SPRINX0\prochazka a810dc4204 focused connection changes 2024-12-13 10:48:42 +01:00
Jan Prochazka faad82fc34 Merge pull request #961 from dbgate/feature/postgis-geo
feat: transform geography binary data to wkt
2024-12-12 16:47:29 +01:00
Nybkox b8ae53db7d feat: transform geography binary data to wkt 2024-12-12 16:37:54 +01:00
Jan Prochazka 1571295ab6 focus UX 2024-12-12 16:29:12 +01:00
Jan Prochazka d3cc3a92c1 v6.0.1-beta.3 2024-12-12 16:15:18 +01:00
Jan Prochazka 381dc6a535 Add apple silicon only build #949 2024-12-12 16:15:03 +01:00
Jan Prochazka 6b9df571af handler UX scroll problem 2024-12-12 16:06:36 +01:00
Jan Prochazka 897547371e configurable connection click, database click #959 2024-12-12 15:53:00 +01:00
Jan Prochazka bf85a922ca first oracle test works 2024-12-10 16:03:26 +01:00
Jan Prochazka 00525f6b81 oracle tests WIP 2024-12-10 15:40:15 +01:00
SPRINX0\prochazka 6dd27eb34f basic auth check config #934 2024-12-10 13:11:03 +01:00
SPRINX0\prochazka 0b30386fee e2e test config 2024-12-10 08:48:53 +01:00
SPRINX0\prochazka 1f7f0ea8a2 Merge branch 'feature/e2e-tests' 2024-12-10 08:33:52 +01:00
201 changed files with 37906 additions and 2687 deletions
+51 -43
View File
@@ -1,83 +1,86 @@
# --------------------------------------------------------------------------------------------
# This file is generated. Do not edit manually
# --------------------------------------------------------------------------------------------
name: Electron app BETA
on:
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+'
jobs:
build:
runs-on: ${{ matrix.os }}
runs-on: '${{ matrix.os }}'
strategy:
fail-fast: false
matrix:
os: [macos-14, windows-2022, ubuntu-22.04]
# os: [macOS-10.15]
os:
- macos-14
- windows-2022
- ubuntu-22.04
steps:
- name: Install python 3.11 (MacOS)
if: matrix.os == 'macos-14'
run: |
brew install python@3.11
echo "PYTHON=/opt/homebrew/bin/python3.11" >> $GITHUB_ENV
- name: Context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
GITHUB_CONTEXT: '${{ toJson(github) }}'
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Use Node.js 18.x
- name: Use Node.js 22.x
uses: actions/setup-node@v1
with:
node-version: 18.x
- name: yarn adjustPackageJson
node-version: 22.x
- name: adjustPackageJson
run: |
yarn adjustPackageJson
node adjustPackageJson --community
- name: setUpdaterChannel beta
run: |
node setUpdaterChannel beta
- name: yarn set timeout
run: |
yarn config set network-timeout 100000
- name: yarn install
run: |
yarn install
- name: setCurrentVersion
run: |
yarn setCurrentVersion
- name: printSecrets
run: |
yarn printSecrets
yarn printSecrets
env:
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
GIST_UPLOAD_SECRET: '${{secrets.GIST_UPLOAD_SECRET}}'
- name: fillPackagedPlugins
run: |
yarn fillPackagedPlugins
- name: Install Snapcraft
if: matrix.os == 'ubuntu-22.04'
uses: samuelmeuli/action-snapcraft@v1
- name: Publish
run: |
yarn run build:app
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
# WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
# WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
- name: publishSnap
if: matrix.os == 'ubuntu-22.04'
run: |
snapcraft upload --release=beta app/dist/*.snap
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
GH_TOKEN: '${{ secrets.GH_TOKEN }}'
WIN_CSC_LINK: '${{ secrets.WINCERT_2025 }}'
WIN_CSC_KEY_PASSWORD: '${{ secrets.WINCERT_2025_PASSWORD }}'
CSC_LINK: '${{ secrets.APPLECERT_CERTIFICATE }}'
CSC_KEY_PASSWORD: '${{ secrets.APPLECERT_PASSWORD }}'
APPLE_ID: '${{ secrets.APPLE_ID }}'
APPLE_TEAM_ID: '${{ secrets.APPLE_TEAM_ID }}'
APPLE_ID_PASSWORD: '${{ secrets.APPLE_ID_PASSWORD }}'
SNAPCRAFT_STORE_CREDENTIALS: '${{secrets.SNAPCRAFT_LOGIN}}'
APPLE_APP_SPECIFIC_PASSWORD: '${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}'
- name: Copy artifacts
run: |
mkdir artifacts
@@ -89,8 +92,10 @@ jobs:
cp app/dist/*win*.exe artifacts/dbgate-beta.exe || true
cp app/dist/*win_x64.zip artifacts/dbgate-windows-beta.zip || true
cp app/dist/*win_arm64.zip artifacts/dbgate-windows-beta-arm64.zip || true
cp app/dist/*-mac_x64.dmg artifacts/dbgate-beta.dmg || true
cp app/dist/*-mac_universal.dmg artifacts/dbgate-beta.dmg || true
cp app/dist/*-mac_x64.dmg artifacts/dbgate-beta-x64.dmg || true
cp app/dist/*-mac_arm64.dmg artifacts/dbgate-beta-arm64.dmg || true
mv app/dist/*.snap artifacts/dbgate-beta.snap || true
mv app/dist/*.exe artifacts/ || true
mv app/dist/*.zip artifacts/ || true
@@ -103,18 +108,21 @@ jobs:
mv app/dist/*.yml artifacts/ || true
rm artifacts/builder-debug.yml
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}
name: '${{ matrix.os }}'
path: artifacts
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
if: 'startsWith(github.ref, ''refs/tags/'')'
with:
files: 'artifacts/**'
files: artifacts/**
prerelease: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
- name: Print content of notarization-error.log
if: failure() && matrix.os == 'macos-14'
run: |
find . -type f -name "notarization-error.log" -exec echo "=== Start of {} ===" \; -exec cat {} \; -exec echo "=== End of {} ===" \;
+62 -50
View File
@@ -1,43 +1,44 @@
# --------------------------------------------------------------------------------------------
# This file is generated. Do not edit manually
# --------------------------------------------------------------------------------------------
name: Electron app PREMIUM BETA
on:
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+-premium-beta.[0-9]+'
jobs:
build:
runs-on: ${{ matrix.os }}
runs-on: '${{ matrix.os }}'
strategy:
fail-fast: false
matrix:
# os: [windows-2022]
# os: [ubuntu-22.04]
# os: [windows-2022, ubuntu-22.04]
os: [macos-14, windows-2022, ubuntu-22.04]
# os: [macOS-10.15]
os:
- macos-14
- windows-2022
- ubuntu-22.04
steps:
- name: Install python 3.11 (MacOS)
if: matrix.os == 'macos-14'
run: |
brew install python@3.11
echo "PYTHON=/opt/homebrew/bin/python3.11" >> $GITHUB_ENV
- name: Context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
GITHUB_CONTEXT: '${{ toJson(github) }}'
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Use Node.js 18.x
- name: Use Node.js 22.x
uses: actions/setup-node@v1
with:
node-version: 18.x
node-version: 22.x
- name: Checkout dbgate/dbgate-pro
uses: actions/checkout@v2
with:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
token: '${{ secrets.GH_TOKEN }}'
path: dbgate-pro
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -49,76 +50,82 @@ jobs:
yarn
node sync.js --nowatch
cd ..
- name: adjustPackageJson
run: |
cd ..
cd dbgate-merged
- name: yarn adjustPackageJson
run: |
cd ..
cd dbgate-merged
yarn adjustPackageJson
- name: adjustAppPackageJsonPremium
run: |
cd ..
cd dbgate-merged
node adjustAppPackageJsonPremium
node adjustPackageJson --premium
- name: setUpdaterChannel premium-beta
run: |
cd ..
cd dbgate-merged
node setUpdaterChannel premium-beta
- name: yarn set timeout
run: |
cd ..
cd dbgate-merged
yarn config set network-timeout 100000
- name: yarn install
run: |
cd ..
cd dbgate-merged
yarn install
- name: setCurrentVersion
run: |
cd ..
cd dbgate-merged
yarn setCurrentVersion
- name: printSecrets
run: |
cd ..
cd dbgate-merged
yarn printSecrets
yarn printSecrets
env:
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
GIST_UPLOAD_SECRET: '${{secrets.GIST_UPLOAD_SECRET}}'
- name: fillPackagedPlugins
run: |
cd ..
cd dbgate-merged
yarn fillPackagedPlugins
- name: Publish
run: |
cd ..
cd dbgate-merged
yarn run build:app
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
# WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
# WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
GH_TOKEN: '${{ secrets.GH_TOKEN }}'
WIN_CSC_LINK: '${{ secrets.WINCERT_2025 }}'
WIN_CSC_KEY_PASSWORD: '${{ secrets.WINCERT_2025_PASSWORD }}'
CSC_LINK: '${{ secrets.APPLECERT_CERTIFICATE }}'
CSC_KEY_PASSWORD: '${{ secrets.APPLECERT_PASSWORD }}'
APPLE_ID: '${{ secrets.APPLE_ID }}'
APPLE_TEAM_ID: '${{ secrets.APPLE_TEAM_ID }}'
APPLE_ID_PASSWORD: '${{ secrets.APPLE_ID_PASSWORD }}'
SNAPCRAFT_STORE_CREDENTIALS: '${{secrets.SNAPCRAFT_LOGIN}}'
APPLE_APP_SPECIFIC_PASSWORD: '${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}'
- name: Copy artifacts
run: |
mkdir artifacts
cp ../dbgate-merged/app/dist/*.deb artifacts/dbgate-premium-beta.deb || true
cp ../dbgate-merged/app/dist/*x86*.AppImage artifacts/dbgate-premium-beta.AppImage || true
cp ../dbgate-merged/app/dist/*arm64*.AppImage artifacts/dbgate-premium-beta-arm64.AppImage || true
cp ../dbgate-merged/app/dist/*armv7l*.AppImage artifacts/dbgate-premium-beta-armv7l.AppImage || true
cp ../dbgate-merged/app/dist/*win*.exe artifacts/dbgate-premium-beta.exe || true
cp ../dbgate-merged/app/dist/*-mac_x64.dmg artifacts/dbgate-premium-beta.dmg || true
cp ../dbgate-merged/app/dist/*win_x64.zip artifacts/dbgate-windows-premium-beta.zip || true
cp ../dbgate-merged/app/dist/*win_arm64.zip artifacts/dbgate-windows-premium-beta-arm64.zip || true
cp ../dbgate-merged/app/dist/*-mac_universal.dmg artifacts/dbgate-premium-beta.dmg || true
cp ../dbgate-merged/app/dist/*-mac_x64.dmg artifacts/dbgate-premium-beta-x64.dmg || true
cp ../dbgate-merged/app/dist/*-mac_arm64.dmg artifacts/dbgate-premium-beta-arm64.dmg || true
mv ../dbgate-merged/app/dist/*.snap artifacts/dbgate-premium-beta.snap || true
mv ../dbgate-merged/app/dist/*.exe artifacts/ || true
mv ../dbgate-merged/app/dist/*.zip artifacts/ || true
@@ -131,18 +138,23 @@ jobs:
mv ../dbgate-merged/app/dist/*.yml artifacts/ || true
rm artifacts/builder-debug.yml
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}
name: '${{ matrix.os }}'
path: artifacts
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
if: 'startsWith(github.ref, ''refs/tags/'')'
with:
files: 'artifacts/**'
files: artifacts/**
prerelease: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
- name: Print content of notarization-error.log
if: failure() && matrix.os == 'macos-14'
run: |
cd ..
cd dbgate-merged
find . -type f -name "notarization-error.log" -exec echo "=== Start of {} ===" \; -exec cat {} \; -exec echo "=== End of {} ===" \;
+62 -52
View File
@@ -1,44 +1,44 @@
# --------------------------------------------------------------------------------------------
# This file is generated. Do not edit manually
# --------------------------------------------------------------------------------------------
name: Electron app PREMIUM
on:
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
# - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
# branches:
# - production
jobs:
build:
runs-on: ${{ matrix.os }}
runs-on: '${{ matrix.os }}'
strategy:
fail-fast: false
matrix:
# os: [ubuntu-22.04, windows-2016]
os: [macos-14, windows-2022, ubuntu-22.04]
os:
- macos-14
- windows-2022
- ubuntu-22.04
steps:
- name: Install python 3.11 (MacOS)
if: matrix.os == 'macos-14'
run: |
brew install python@3.11
echo "PYTHON=/opt/homebrew/bin/python3.11" >> $GITHUB_ENV
- name: Context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
GITHUB_CONTEXT: '${{ toJson(github) }}'
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Use Node.js 18.x
- name: Use Node.js 22.x
uses: actions/setup-node@v1
with:
node-version: 18.x
node-version: 22.x
- name: Checkout dbgate/dbgate-pro
uses: actions/checkout@v2
with:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
token: '${{ secrets.GH_TOKEN }}'
path: dbgate-pro
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -50,101 +50,111 @@ jobs:
yarn
node sync.js --nowatch
cd ..
- name: adjustPackageJson
run: |
cd ..
cd dbgate-merged
- name: yarn adjustPackageJson
run: |
cd ..
cd dbgate-merged
yarn adjustPackageJson
- name: adjustAppPackageJsonPremium
run: |
cd ..
cd dbgate-merged
node adjustAppPackageJsonPremium
node adjustPackageJson --premium
- name: setUpdaterChannel premium
run: |
cd ..
cd dbgate-merged
node setUpdaterChannel premium
- name: yarn set timeout
run: |
cd ..
cd dbgate-merged
yarn config set network-timeout 100000
- name: yarn install
run: |
cd ..
cd dbgate-merged
yarn install
- name: setCurrentVersion
run: |
cd ..
cd dbgate-merged
yarn setCurrentVersion
- name: printSecrets
run: |
cd ..
cd dbgate-merged
yarn printSecrets
env:
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
GIST_UPLOAD_SECRET: '${{secrets.GIST_UPLOAD_SECRET}}'
- name: fillPackagedPlugins
run: |
cd ..
cd dbgate-merged
yarn fillPackagedPlugins
- name: Publish
run: |
cd ..
cd dbgate-merged
yarn run build:app
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
# WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
# WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
GH_TOKEN: '${{ secrets.GH_TOKEN }}'
WIN_CSC_LINK: '${{ secrets.WINCERT_2025 }}'
WIN_CSC_KEY_PASSWORD: '${{ secrets.WINCERT_2025_PASSWORD }}'
CSC_LINK: '${{ secrets.APPLECERT_CERTIFICATE }}'
CSC_KEY_PASSWORD: '${{ secrets.APPLECERT_PASSWORD }}'
APPLE_ID: '${{ secrets.APPLE_ID }}'
APPLE_TEAM_ID: '${{ secrets.APPLE_TEAM_ID }}'
APPLE_ID_PASSWORD: '${{ secrets.APPLE_ID_PASSWORD }}'
SNAPCRAFT_STORE_CREDENTIALS: '${{secrets.SNAPCRAFT_LOGIN}}'
APPLE_APP_SPECIFIC_PASSWORD: '${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}'
- name: Copy artifacts
run: |
mkdir artifacts
cp ../dbgate-merged/app/dist/*.deb artifacts/dbgate-premium-latest.deb || true
cp ../dbgate-merged/app/dist/*x86*.AppImage artifacts/dbgate-premium-latest.AppImage || true
cp ../dbgate-merged/app/dist/*.exe artifacts/dbgate-premium-latest.exe || true
cp ../dbgate-merged/app/dist/*win_x64.zip artifacts/dbgate-premium-windows-latest.zip || true
cp ../dbgate-merged/app/dist/*win_arm64.zip artifacts/dbgate-premium-windows-latest-arm64.zip || true
cp ../dbgate-merged/app/dist/*arm64*.AppImage artifacts/dbgate-premium-latest-arm64.AppImage || true
cp ../dbgate-merged/app/dist/*armv7l*.AppImage artifacts/dbgate-premium-latest-armv7l.AppImage || true
cp ../dbgate-merged/app/dist/*win*.exe artifacts/dbgate-premium-latest.exe || true
cp ../dbgate-merged/app/dist/*win_x64.zip artifacts/dbgate-windows-premium-latest.zip || true
cp ../dbgate-merged/app/dist/*win_arm64.zip artifacts/dbgate-windows-premium-latest-arm64.zip || true
cp ../dbgate-merged/app/dist/*-mac_universal.dmg artifacts/dbgate-premium-latest.dmg || true
cp ../dbgate-merged/app/dist/*-mac_x64.dmg artifacts/dbgate-premium-latest-x64.dmg || true
cp ../dbgate-merged/app/dist/*-mac_arm64.dmg artifacts/dbgate-premium-latest-arm64.dmg || true
mv ../dbgate-merged/app/dist/*.snap artifacts/dbgate-premium-latest.snap || true
mv ../dbgate-merged/app/dist/*.exe artifacts/ || true
mv ../dbgate-merged/app/dist/*.zip artifacts/ || true
mv ../dbgate-merged/app/dist/*.tar.gz artifacts/ || true
mv ../dbgate-merged/app/dist/*.AppImage artifacts/ || true
mv ../dbgate-merged/app/dist/*.deb artifacts/ || true
mv ../dbgate-merged/app/dist/*.snap artifacts/ || true
mv ../dbgate-merged/app/dist/*.dmg artifacts/ || true
mv ../dbgate-merged/app/dist/*.blockmap artifacts/ || true
mv ../dbgate-merged/app/dist/*.yml artifacts/ || true
rm artifacts/builder-debug.yml
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}
name: '${{ matrix.os }}'
path: artifacts
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
if: 'startsWith(github.ref, ''refs/tags/'')'
with:
files: 'artifacts/**'
files: artifacts/**
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
- name: Print content of notarization-error.log
if: failure() && matrix.os == 'macos-14'
run: |
cd ..
cd dbgate-merged
find . -type f -name "notarization-error.log" -exec echo "=== Start of {} ===" \; -exec cat {} \; -exec echo "=== End of {} ===" \;
+51 -86
View File
@@ -1,90 +1,85 @@
# --------------------------------------------------------------------------------------------
# This file is generated. Do not edit manually
# --------------------------------------------------------------------------------------------
name: Electron app
on:
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
# - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
# branches:
# - production
jobs:
build:
runs-on: ${{ matrix.os }}
runs-on: '${{ matrix.os }}'
strategy:
fail-fast: false
matrix:
# os: [ubuntu-22.04, windows-2016]
os: [macos-14, windows-2022, ubuntu-22.04]
os:
- macos-14
- windows-2022
- ubuntu-22.04
steps:
- name: Install python 3.11 (MacOS)
if: matrix.os == 'macos-14'
run: |
brew install python@3.11
echo "PYTHON=/opt/homebrew/bin/python3.11" >> $GITHUB_ENV
- name: Context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
GITHUB_CONTEXT: '${{ toJson(github) }}'
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Use Node.js 18.x
- name: Use Node.js 22.x
uses: actions/setup-node@v1
with:
node-version: 18.x
- name: yarn adjustPackageJson
node-version: 22.x
- name: adjustPackageJson
run: |
yarn adjustPackageJson
node adjustPackageJson --community
- name: yarn set timeout
run: |
yarn config set network-timeout 100000
- name: yarn install
run: |
# yarn --version
# yarn config set network-timeout 300000
yarn install
- name: setCurrentVersion
run: |
yarn setCurrentVersion
- name: printSecrets
run: |
yarn printSecrets
yarn printSecrets
env:
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
GIST_UPLOAD_SECRET: '${{secrets.GIST_UPLOAD_SECRET}}'
- name: fillPackagedPlugins
run: |
yarn fillPackagedPlugins
- name: Install Snapcraft
if: matrix.os == 'ubuntu-22.04'
uses: samuelmeuli/action-snapcraft@v1
- name: Publish
run: |
yarn run build:app
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
# WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
# WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
GH_TOKEN: '${{ secrets.GH_TOKEN }}'
WIN_CSC_LINK: '${{ secrets.WINCERT_2025 }}'
WIN_CSC_KEY_PASSWORD: '${{ secrets.WINCERT_2025_PASSWORD }}'
CSC_LINK: '${{ secrets.APPLECERT_CERTIFICATE }}'
CSC_KEY_PASSWORD: '${{ secrets.APPLECERT_PASSWORD }}'
APPLE_ID: '${{ secrets.APPLE_ID }}'
APPLE_TEAM_ID: '${{ secrets.APPLE_TEAM_ID }}'
APPLE_ID_PASSWORD: '${{ secrets.APPLE_ID_PASSWORD }}'
SNAPCRAFT_STORE_CREDENTIALS: '${{secrets.SNAPCRAFT_LOGIN}}'
APPLE_APP_SPECIFIC_PASSWORD: '${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}'
- name: generatePadFile
run: |
yarn generatePadFile
- name: publishSnap
if: matrix.os == 'ubuntu-22.04'
run: |
snapcraft upload --release=stable app/dist/*.snap
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{secrets.SNAPCRAFT_LOGIN}}
- name: Copy artifacts
run: |
mkdir artifacts
@@ -93,74 +88,44 @@ jobs:
cp app/dist/*x86*.AppImage artifacts/dbgate-latest.AppImage || true
cp app/dist/*arm64*.AppImage artifacts/dbgate-latest-arm64.AppImage || true
cp app/dist/*armv7l*.AppImage artifacts/dbgate-latest-armv7l.AppImage || true
cp app/dist/*.exe artifacts/dbgate-latest.exe || true
cp app/dist/*win*.exe artifacts/dbgate-latest.exe || true
cp app/dist/*win_x64.zip artifacts/dbgate-windows-latest.zip || true
cp app/dist/*win_arm64.zip artifacts/dbgate-windows-latest-arm64.zip || true
cp app/dist/*-mac_universal.dmg artifacts/dbgate-latest.dmg || true
cp app/dist/*-mac_x64.dmg artifacts/dbgate-latest-x64.dmg || true
cp app/dist/*-mac_arm64.dmg artifacts/dbgate-latest-arm64.dmg || true
mv app/dist/*.snap artifacts/dbgate-latest.snap || true
mv app/dist/*.exe artifacts/ || true
mv app/dist/*.zip artifacts/ || true
mv app/dist/*.tar.gz artifacts/ || true
mv app/dist/*.AppImage artifacts/ || true
mv app/dist/*.deb artifacts/ || true
mv app/dist/*.snap artifacts/ || true
mv app/dist/*.dmg artifacts/ || true
mv app/dist/*.snap artifacts/dbgate-latest.snap || true
mv app/dist/*.blockmap artifacts/ || true
mv app/dist/*.yml artifacts/ || true
rm artifacts/builder-debug.yml
# - name: Copy artifacts Linux, MacOs
# if: matrix.os != 'windows-2016'
# run: |
# mkdir artifacts
# cp app/dist/*.AppImage artifacts/ || true
# cp app/dist/*.dmg artifacts/ || true
# cp app/dist/*.deb artifacts/ || true
# mv app/dist/*.deb artifacts/dbgate-linux.deb || true
# mv app/dist/*.AppImage artifacts/dbgate-linux.AppImage || true
# mv app/dist/*.dmg artifacts/dbgate-mac.dmg || true
# - name: Copy artifacts Win
# if: matrix.os == 'windows-2016'
# run: |
# mkdir artifacts
# cp app/dist/*.exe artifacts/ || true
# mv app/dist/*.exe artifacts/dbgate-windows.exe
# mv app/dist/latest.yml artifacts/latest.yml || true
- name: Copy PAD file
if: matrix.os == 'windows-2022'
run: |
mv app/dist/dbgate-pad.xml artifacts/ || true
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}
name: '${{ matrix.os }}'
path: artifacts
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
if: 'startsWith(github.ref, ''refs/tags/'')'
with:
files: 'artifacts/**'
files: artifacts/**
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
- name: Print content of notarization-error.log
if: failure() && matrix.os == 'macos-14'
run: |
# - name: Create Release
# id: create_release
# uses: actions/create-release@v1
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# with:
# tag_name: ${{ github.ref }}
# release_name: Release ${{ github.ref }}
# draft: false
# prerelease: false
find . -type f -name "notarization-error.log" -exec echo "=== Start of {} ===" \; -exec cat {} \; -exec echo "=== End of {} ===" \;
+25 -50
View File
@@ -1,47 +1,41 @@
# --------------------------------------------------------------------------------------------
# This file is generated. Do not edit manually
# --------------------------------------------------------------------------------------------
name: AWS image PREMIUM
on:
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
# - 'v[0-9]+.[0-9]+.[0-9]+-premium-beta.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-packer-beta.[0-9]+'
jobs:
build:
runs-on: ${{ matrix.os }}
runs-on: '${{ matrix.os }}'
strategy:
matrix:
os: [ubuntu-22.04]
os:
- ubuntu-22.04
steps:
- name: Context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
GITHUB_CONTEXT: '${{ toJson(github) }}'
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Use Node.js 18.x
uses: actions/setup-node@v1
with:
node-version: 18.x
- name: Setup `packer`
uses: hashicorp/setup-packer@main
with:
version: latest
- name: Checkout dbgate/dbgate-pro
uses: actions/checkout@v2
with:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
token: '${{ secrets.GH_TOKEN }}'
path: dbgate-pro
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -53,33 +47,28 @@ jobs:
yarn
node sync.js --nowatch
cd ..
- name: yarn adjustPackageJson
- name: adjustPackageJson
run: |
cd ..
cd dbgate-merged
yarn adjustPackageJson
node adjustPackageJson --premium
- name: yarn install
run: |
cd ..
cd dbgate-merged
yarn install
- name: setCurrentVersion
run: |
cd ..
cd dbgate-merged
yarn setCurrentVersion
- name: printSecrets
run: |
cd ..
cd dbgate-merged
yarn printSecrets
env:
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
GIST_UPLOAD_SECRET: '${{secrets.GIST_UPLOAD_SECRET}}'
- name: Prepare packer build
run: |
cd ..
@@ -87,62 +76,48 @@ jobs:
yarn run prepare:packer
cd packer
zip -r cloud-build.zip build
- name: Copy artifacts
run: |
mkdir artifacts
cp ../dbgate-merged/packer/cloud-build.zip artifacts/cloud-build.zip || true
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}
name: '${{ matrix.os }}'
path: artifacts
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
if: 'startsWith(github.ref, ''refs/tags/'')'
with:
files: 'artifacts/**'
files: artifacts/**
prerelease: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
- name: Run `packer init`
run: |
cd ../dbgate-merged/packer
packer init ./aws-ubuntu.pkr.hcl
env:
AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}}
AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}}
AWS_DEFAULT_REGION: ${{secrets.AWS_DEFAULT_REGION}}
AWS_ACCESS_KEY_ID: '${{secrets.AWS_ACCESS_KEY_ID}}'
AWS_SECRET_ACCESS_KEY: '${{secrets.AWS_SECRET_ACCESS_KEY}}'
AWS_DEFAULT_REGION: '${{secrets.AWS_DEFAULT_REGION}}'
- name: Run `packer build`
run: |
cd ../dbgate-merged/packer
packer build ./aws-ubuntu.pkr.hcl
env:
AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}}
AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}}
AWS_DEFAULT_REGION: ${{secrets.AWS_DEFAULT_REGION}}
# - name: Install AWS CLI
# run: |
# curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
# unzip awscliv2.zip
# sudo ./aws/install
# sudo apt-get install jq -y
AWS_ACCESS_KEY_ID: '${{secrets.AWS_ACCESS_KEY_ID}}'
AWS_SECRET_ACCESS_KEY: '${{secrets.AWS_SECRET_ACCESS_KEY}}'
AWS_DEFAULT_REGION: '${{secrets.AWS_DEFAULT_REGION}}'
- name: Install jq
run: |
sudo apt-get install jq -y
- name: Delete old AMIs
run: |
cd ../dbgate-merged/packer
chmod +x delete-old-amis.sh
./delete-old-amis.sh
env:
AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}}
AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}}
AWS_DEFAULT_REGION: ${{secrets.AWS_DEFAULT_REGION}}
AWS_ACCESS_KEY_ID: '${{secrets.AWS_ACCESS_KEY_ID}}'
AWS_SECRET_ACCESS_KEY: '${{secrets.AWS_SECRET_ACCESS_KEY}}'
AWS_DEFAULT_REGION: '${{secrets.AWS_DEFAULT_REGION}}'
+24 -29
View File
@@ -1,35 +1,32 @@
# --------------------------------------------------------------------------------------------
# This file is generated. Do not edit manually
# --------------------------------------------------------------------------------------------
name: Docker image PREMIUM
on:
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-premium-beta.[0-9]+'
jobs:
build:
runs-on: ${{ matrix.os }}
runs-on: '${{ matrix.os }}'
strategy:
matrix:
os: [ubuntu-22.04]
os:
- ubuntu-22.04
steps:
- name: Context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
GITHUB_CONTEXT: '${{ toJson(github) }}'
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
dbgate/dbgate-premium
images: dbgate/dbgate-premium
flavor: |
latest=false
tags: |
@@ -37,19 +34,16 @@ jobs:
type=match,pattern=\d+.\d+.\d+,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }}
type=raw,value=latest,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }}
- name: Use Node.js 18.x
uses: actions/setup-node@v1
with:
node-version: 18.x
- name: Checkout dbgate/dbgate-pro
uses: actions/checkout@v2
with:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
token: '${{ secrets.GH_TOKEN }}'
path: dbgate-pro
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -61,50 +55,51 @@ jobs:
yarn
node sync.js --nowatch
cd ..
- name: yarn adjustPackageJson
- name: adjustPackageJson
run: |
cd ..
cd dbgate-merged
yarn adjustPackageJson
node adjustPackageJson --premium
- name: yarn install
run: |
cd ..
cd dbgate-merged
yarn install
# yarn --version
# yarn config set network-timeout 300000
yarn install
- name: setCurrentVersion
run: |
cd ..
cd dbgate-merged
yarn setCurrentVersion
- name: printSecrets
run: |
cd ..
cd dbgate-merged
yarn printSecrets
yarn printSecrets
env:
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
GIST_UPLOAD_SECRET: '${{secrets.GIST_UPLOAD_SECRET}}'
- name: Prepare docker image
run: |
cd ..
cd dbgate-merged
yarn run prepare:docker
yarn run prepare:docker
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
username: '${{ secrets.DOCKER_USERNAME }}'
password: '${{ secrets.DOCKER_PASSWORD }}'
- name: Build and push
uses: docker/build-push-action@v3
with:
push: true
context: ../dbgate-merged/docker
tags: ${{ steps.meta.outputs.tags }}
platforms: linux/amd64,linux/arm64
tags: '${{ steps.meta.outputs.tags }}'
platforms: 'linux/amd64,linux/arm64'
+24 -31
View File
@@ -1,35 +1,32 @@
name: Docker image
on:
# --------------------------------------------------------------------------------------------
# This file is generated. Do not edit manually
# --------------------------------------------------------------------------------------------
name: Docker image Community
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+'
jobs:
build:
runs-on: ${{ matrix.os }}
runs-on: '${{ matrix.os }}'
strategy:
matrix:
os: [ubuntu-22.04]
os:
- ubuntu-22.04
steps:
- name: Context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
GITHUB_CONTEXT: '${{ toJson(github) }}'
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
dbgate/dbgate
images: dbgate/dbgate
flavor: |
latest=false
tags: |
@@ -37,7 +34,6 @@ jobs:
type=match,pattern=\d+.\d+.\d+,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }}
type=raw,value=latest,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }}
- name: Docker alpine meta
id: alpmeta
uses: docker/metadata-action@v4
@@ -51,56 +47,53 @@ jobs:
type=match,pattern=\d+.\d+.\d+,suffix=-alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }}
type=raw,value=alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }}
- name: Use Node.js 18.x
uses: actions/setup-node@v1
with:
node-version: 18.x
- name: yarn adjustPackageJson
- name: adjustPackageJson
run: |
yarn adjustPackageJson
node adjustPackageJson --community
- name: yarn install
run: |
# yarn --version
# yarn config set network-timeout 300000
yarn install
- name: setCurrentVersion
run: |
yarn setCurrentVersion
- name: printSecrets
run: |
yarn printSecrets
yarn printSecrets
env:
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
GIST_UPLOAD_SECRET: '${{secrets.GIST_UPLOAD_SECRET}}'
- name: Prepare docker image
run: |
yarn run prepare:docker
yarn run prepare:docker
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
username: '${{ secrets.DOCKER_USERNAME }}'
password: '${{ secrets.DOCKER_PASSWORD }}'
- name: Build and push
uses: docker/build-push-action@v3
with:
push: true
context: ./docker
tags: ${{ steps.meta.outputs.tags }}
platforms: linux/amd64,linux/arm64,linux/arm/v7
tags: '${{ steps.meta.outputs.tags }}'
platforms: 'linux/amd64,linux/arm64,linux/arm/v7'
- name: Build and push alpine
uses: docker/build-push-action@v3
with:
push: true
context: ./docker
file: ./docker/Dockerfile-alpine
tags: ${{ steps.alpmeta.outputs.tags }}
platforms: linux/amd64,linux/arm64,linux/arm/v7
tags: '${{ steps.alpmeta.outputs.tags }}'
platforms: 'linux/amd64,linux/arm64,linux/arm/v7'
+11 -31
View File
@@ -1,31 +1,23 @@
# --------------------------------------------------------------------------------------------
# This file is generated. Do not edit manually
# --------------------------------------------------------------------------------------------
name: NPM packages PREMIUM
# on: [push]
on:
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+'
# on:
# push:
# branches:
# - production
jobs:
build:
runs-on: ${{ matrix.os }}
runs-on: '${{ matrix.os }}'
strategy:
matrix:
os: [ubuntu-22.04]
os:
- ubuntu-22.04
steps:
- name: Context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
GITHUB_CONTEXT: '${{ toJson(github) }}'
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v2
with:
@@ -34,14 +26,12 @@ jobs:
uses: actions/setup-node@v1
with:
node-version: 18.x
- name: Checkout dbgate/dbgate-pro
uses: actions/checkout@v2
with:
repository: dbgate/dbgate-pro
token: ${{ secrets.GH_TOKEN }}
token: '${{ secrets.GH_TOKEN }}'
path: dbgate-pro
- name: Merge dbgate/dbgate-pro
run: |
mkdir ../dbgate-pro
@@ -53,65 +43,55 @@ jobs:
yarn
node sync.js --nowatch
cd ..
- name: adjustNpmPackageJsonPremium
run: |
cd ..
cd dbgate-merged
node adjustNpmPackageJsonPremium
- name: Configure NPM token
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: '${{ secrets.NPM_TOKEN }}'
run: |
cd ..
cd dbgate-merged
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
- name: Remove dbmodel - should be not published
run: |
cd ..
cd dbgate-merged
rm -rf packages/dbmodel
- name: yarn install
run: |
cd ..
cd dbgate-merged
yarn install
- name: setCurrentVersion
run: |
cd ..
cd dbgate-merged
yarn setCurrentVersion
- name: printSecrets
run: |
cd ..
cd dbgate-merged
yarn printSecrets
env:
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
GIST_UPLOAD_SECRET: '${{secrets.GIST_UPLOAD_SECRET}}'
- name: Publish dbgate-api-premium
run: |
cd ..
cd dbgate-merged/packages/api
npm publish
- name: Publish dbgate-web-premium
run: |
cd ..
cd dbgate-merged/packages/web
npm publish
- name: Publish dbgate-serve-premium
run: |
cd ..
cd dbgate-merged/packages/serve
npm publish
- name: Publish dbgate-plugin-cosmosdb
run: |
cd ..
+11 -42
View File
@@ -1,31 +1,23 @@
# --------------------------------------------------------------------------------------------
# This file is generated. Do not edit manually
# --------------------------------------------------------------------------------------------
name: NPM packages
# on: [push]
on:
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-alpha.[0-9]+'
# on:
# push:
# branches:
# - production
jobs:
build:
runs-on: ${{ matrix.os }}
runs-on: '${{ matrix.os }}'
strategy:
matrix:
os: [ubuntu-22.04]
os:
- ubuntu-22.04
steps:
- name: Context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
GITHUB_CONTEXT: '${{ toJson(github) }}'
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v2
with:
@@ -34,37 +26,30 @@ jobs:
uses: actions/setup-node@v1
with:
node-version: 18.x
- name: Configure NPM token
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: '${{ secrets.NPM_TOKEN }}'
run: |
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
- name: yarn install
run: |
yarn install
- name: setCurrentVersion
run: |
yarn setCurrentVersion
- name: printSecrets
run: |
yarn printSecrets
yarn printSecrets
env:
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
GIST_UPLOAD_SECRET: '${{secrets.GIST_UPLOAD_SECRET}}'
- name: Publish types
working-directory: packages/types
run: |
npm publish
- name: Publish tools
working-directory: packages/tools
run: |
npm publish
- name: Publish sqltree
working-directory: packages/sqltree
run: |
@@ -74,82 +59,66 @@ jobs:
working-directory: packages/api
run: |
npm publish
- name: Publish datalib
working-directory: packages/datalib
run: |
npm publish
- name: Publish filterparser
working-directory: packages/filterparser
run: |
npm publish
- name: Publish web
working-directory: packages/web
run: |
npm publish
- name: Publish dbgate-serve
working-directory: packages/serve
run: |
npm publish
- name: Publish dbmodel
working-directory: packages/dbmodel
run: |
npm publish
- name: Publish dbgate-plugin-csv
working-directory: plugins/dbgate-plugin-csv
run: |
npm publish
- name: Publish dbgate-plugin-xml
working-directory: plugins/dbgate-plugin-xml
run: |
npm publish
- name: Publish dbgate-plugin-excel
working-directory: plugins/dbgate-plugin-excel
run: |
npm publish
- name: Publish dbgate-plugin-mssql
working-directory: plugins/dbgate-plugin-mssql
run: |
npm publish
- name: Publish dbgate-plugin-mysql
working-directory: plugins/dbgate-plugin-mysql
run: |
npm publish
- name: Publish dbgate-plugin-mongo
working-directory: plugins/dbgate-plugin-mongo
run: |
npm publish
- name: Publish dbgate-plugin-postgres
working-directory: plugins/dbgate-plugin-postgres
run: |
npm publish
- name: Publish dbgate-plugin-sqlite
working-directory: plugins/dbgate-plugin-sqlite
run: |
npm publish
- name: Publish dbgate-plugin-redis
working-directory: plugins/dbgate-plugin-redis
run: |
npm publish
- name: Publish dbgate-plugin-oracle
working-directory: plugins/dbgate-plugin-oracle
run: |
npm publish
- name: Publish dbgate-plugin-clickhouse
working-directory: plugins/dbgate-plugin-clickhouse
run: |
@@ -0,0 +1,38 @@
# --------------------------------------------------------------------------------------------
# This file is generated. Do not edit manually
# --------------------------------------------------------------------------------------------
name: Build test containers
'on':
push:
branches:
- master
- develop
- feature/**
paths:
- e2e-tests/containers/**
jobs:
build-docker-ssh:
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v3
- name: Build mysql-ssh-login image
run: |
docker build -t dbgate/mysql-ssh-login:latest e2e-tests/containers/mysql-ssh-login
- name: Build mysql-ssh-keyfile image
run: |
docker build -t dbgate/mysql-ssh-keyfile:latest e2e-tests/containers/mysql-ssh-keyfile
- name: Log in to GHCR
uses: docker/login-action@v2
with:
registry: ghcr.io
username: '${{ github.actor }}'
password: '${{ secrets.GITHUB_TOKEN }}'
- name: Push mysql-ssh-login to GHCR
run: |
docker tag dbgate/mysql-ssh-login:latest ghcr.io/dbgate/mysql-ssh-login:latest
docker push ghcr.io/dbgate/mysql-ssh-login:latest
- name: Push mysql-ssh-keyfile to GHCR
run: |
docker tag dbgate/mysql-ssh-keyfile:latest ghcr.io/dbgate/mysql-ssh-keyfile:latest
docker push ghcr.io/dbgate/mysql-ssh-keyfile:latest
-41
View File
@@ -1,41 +0,0 @@
name: E2E tests
on:
push:
branches:
- master
- develop
- 'feature/**'
jobs:
e2e-tests:
runs-on: ubuntu-latest
container: node:18
steps:
- name: Install dependencies
run: |
apt-get update
apt-get install -y xvfb libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: yarn install
run: |
yarn install
- name: Build
run: |
yarn prepare:packer
- name: yarn install cypress
run: |
cd e2e-tests
yarn install
- name: Run Cypress tests
run: |
cd e2e-tests
yarn test:ci
services:
mysql:
image: mysql:8.0.18
env:
MYSQL_ROOT_PASSWORD: Pwd2020Db
+38 -30
View File
@@ -1,32 +1,43 @@
# --------------------------------------------------------------------------------------------
# This file is generated. Do not edit manually
# --------------------------------------------------------------------------------------------
name: Run tests
on:
'on':
push:
branches:
- master
- develop
- 'feature/**'
- feature/**
jobs:
test-runner:
test:
runs-on: ubuntu-latest
container: node:18
container: 'node:18'
steps:
- name: Context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v2
- name: Install dependencies for cypress
run: |
apt-get update
apt-get install -y xvfb libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6
- uses: actions/checkout@v3
with:
fetch-depth: 1
- name: yarn install
run: |
yarn install
- name: Build packer dist for cypress
run: |
yarn prepare:packer
- name: yarn install cypress
run: |
cd e2e-tests
yarn install
- name: Run Cypress tests
run: |
cd e2e-tests
yarn test:ci
- name: Integration tests
run: |
cd integration-tests
yarn test:ci
# yarn wait:ci
- name: Filter parser tests
if: always()
run: |
@@ -40,49 +51,46 @@ jobs:
- uses: tanmen/jest-reporter@v1
if: always()
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
github-token: '${{ secrets.GITHUB_TOKEN }}'
result-file: integration-tests/result.json
action-name: Integration tests
- uses: tanmen/jest-reporter@v1
if: always()
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
github-token: '${{ secrets.GITHUB_TOKEN }}'
result-file: packages/filterparser/result.json
action-name: Filter parser test results
- uses: tanmen/jest-reporter@v1
if: always()
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
github-token: '${{ secrets.GITHUB_TOKEN }}'
result-file: packages/datalib/result.json
action-name: Datalib (perspectives) test results
services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: Pwd2020Db
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
options: '--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5'
mysql:
image: mysql:8.0.18
image: 'mysql:8.0.18'
env:
MYSQL_ROOT_PASSWORD: Pwd2020Db
mssql:
image: mcr.microsoft.com/mssql/server
env:
ACCEPT_EULA: Y
ACCEPT_EULA: 'Y'
SA_PASSWORD: Pwd2020Db
MSSQL_PID: Express
clickhouse:
image: bitnami/clickhouse:24.8.4
image: 'bitnami/clickhouse:24.8.4'
env:
CLICKHOUSE_ADMIN_PASSWORD: Pwd2020Db
# cockroachdb:
# image: cockroachdb/cockroach
oracle:
image: 'gvenzl/oracle-xe:21-slim'
env:
ORACLE_PASSWORD: Pwd2020Db
mysql-ssh-login:
image: 'ghcr.io/dbgate/mysql-ssh-login:latest'
mysql-ssh-keyfile:
image: 'ghcr.io/dbgate/mysql-ssh-keyfile:latest'
+23
View File
@@ -8,6 +8,29 @@ Builds:
- linux - application for linux
- win - application for Windows
### 6.1.1
- ADDED: Trigger support (SQL Server, PostgreSQL, MySQL, Oracle)
- FIXED: PostgreSQL and Oracle export #970
- FIXED: Cursor Becomes Stuck When Escaping "Case" #954
- CHANGED: Defualt search criteria for tables are names only
- FIXED: Search in packed list
### 6.1.0
- ADDED: Fulltext search in DB model and connections, highlight searched names
- ADDED: Tab preview mode configuration #963
- CHANGED: Single-click to open server connection/database + ability to configure this #959
- ADDED: Option to align numbers to right in data grid #957
- FIXED: Cursor Becomes Stuck When Escaping "Case" #954
- ADDED: Postgres GEOGRAPHY types are shown on map, event when executing query #948
- FIXED: Error displaying CLOB and NCLOB in Oracle
- FIXED: Analysing of foreign keys in Postgres and MS SQL, when the same FKS are used across different schemas
- ADDED: Support of views, procedures, functions to Oracle. Added integration tests for Oracle
- ADDED: Display "No rows" message, quick add new row
- ADDED: Choose default database from list
- ADDED: Default database is automatically selected on connect
- ADDED: Apple-Silicon-only build for Mac #949
- ADDED: Display comment into tables and column list #755
### 6.0.0
- ADDED: Order or filter the indexes for huge tables #922
- ADDED: Empty string filters
+2 -2
View File
@@ -17,7 +17,7 @@ DbGate is licensed under GPL-3.0 license and is free to use for any purpose.
* Try it online - [demo.dbgate.org](https://demo.dbgate.org) - online demo application
* **Download** application for Windows, Linux or Mac from [dbgate.org](https://dbgate.org/download/)
* Run web version as [NPM package](https://www.npmjs.com/package/dbgate-serve) or as [docker image](https://hub.docker.com/r/dbgate/dbgate)
* Use nodeJs [scripting interface](https://dbgate.org/docs/scripting.html) ([API documentation](https://dbgate.org/docs/apidoc.html))
* Use nodeJs [scripting interface](https://dbgate.org/docs/scripting) ([API documentation](https://dbgate.org/docs/apidoc))
* [Recommend DbGate](https://testimonial.to/dbgate) | [Rate on G2](https://www.g2.com/products/dbgate/reviews)
## Supported databases
@@ -87,7 +87,7 @@ Any contributions are welcome. If you want to contribute without coding, conside
* Create issue, if you find problem in app, or you have idea to new feature. If issue already exists, you could leave comment on it, to prioritise most wanted issues
* Create some tutorial video on [youtube](https://www.youtube.com/playlist?list=PLCo7KjCVXhr0RfUSjM9wJMsp_ShL1q61A)
* Become a backer on [GitHub sponsors](https://github.com/sponsors/dbgate) or [Open collective](https://opencollective.com/dbgate)
* Where a small coding is acceptable for you, you could [create plugin](https://dbgate.org/docs/plugin-development.html). Plugins for new themes can be created actually without JS coding
* Where a small coding is acceptable for you, you could [create plugin](https://dbgate.org/docs/plugin-development). Plugins for new themes can be created actually without JS coding
Thank you!
+23
View File
@@ -41,6 +41,29 @@ function adjustFile(file, isApp = false) {
delete json.optionalDependencies.msnodesqlv8;
}
if (process.argv.includes('--community')) {
delete json.optionalDependencies['mongodb-client-encryption'];
}
if (isApp && process.argv.includes('--premium')) {
json.build.win.target = [
{
target: 'nsis',
arch: ['x64'],
},
];
json.build.linux.target = [
{
target: 'AppImage',
arch: ['x64'],
},
];
json.name = 'dbgate-premium';
json.build.artifactName = 'dbgate-premium-${version}-${os}_${arch}.${ext}';
json.build.appId = 'org.dbgate.premium';
json.build.productName = 'DbGate Premium';
}
fs.writeFileSync(file, JSON.stringify(json, null, 2), 'utf-8');
}
+5 -5
View File
@@ -19,7 +19,6 @@
"artifactName": "dbgate-${version}-${os}_${arch}.${ext}",
"appId": "org.dbgate",
"productName": "DbGate",
"afterSign": "electron-builder-notarize",
"asarUnpack": "**/*.node",
"mac": {
"category": "database",
@@ -38,9 +37,11 @@
"target": "default",
"arch": [
"universal",
"x64"
"x64",
"arm64"
]
}
},
"notarize": true
},
"linux": {
"target": [
@@ -129,7 +130,6 @@
"copyfiles": "^2.2.0",
"cross-env": "^6.0.3",
"electron": "30.0.2",
"electron-builder": "23.1.0",
"electron-builder-notarize": "^1.5.2"
"electron-builder": "25.1.8"
}
}
+828 -1022
View File
File diff suppressed because it is too large Load Diff
+174
View File
@@ -0,0 +1,174 @@
const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');
const _ = require('lodash');
const indir = path.resolve(path.join(__dirname, '..', 'workflow-templates'));
const outdir = path.resolve(path.join(__dirname, '..', '.github', 'workflows'));
const includes = {};
const HEADER = `# --------------------------------------------------------------------------------------------
# This file is generated. Do not edit manually
# --------------------------------------------------------------------------------------------
`;
function readIncludes() {
for (const file of fs.readdirSync(indir)) {
const text = fs.readFileSync(path.join(indir, file), { encoding: 'utf-8' });
const json = yaml.load(text);
if (json._module) {
for (const key in json) {
if (key === '_module') {
continue;
}
includes[key] = json[key];
}
}
}
}
let modified = false;
function conditionMatch(condition, args) {
if (_.isString(condition)) {
return args.defs.includes(condition);
}
return false;
}
function processJsonStep(json, args) {
return _.cloneDeepWith(json, value => {
if (_.isArray(value)) {
const res = [];
let arrayModified = false;
for (const item of value) {
if (item._if) {
modified = true;
arrayModified = true;
if (conditionMatch(item._if, args)) {
res.push(_.omit(item, ['_if']));
}
} else if (item._replace || item._include) {
const replaceWith = item._replace ? args.replace?.[item._replace] : includes[item._include];
if (replaceWith) {
modified = true;
arrayModified = true;
if (_.isArray(replaceWith)) {
res.push(...replaceWith);
} else {
res.push(replaceWith);
}
} else {
res.push(item);
}
} else {
res.push(item);
}
}
if (arrayModified) {
return res;
}
return undefined;
}
if (_.isPlainObject(value)) {
if (_.intersection(args.allDefs ?? [], Object.keys(value))?.length > 0) {
modified = true;
for (const key in value) {
if (args.defs.includes(key)) {
return value[key];
}
}
return undefined;
}
}
if (_.isString(value)) {
let stringModified = false;
for (const key of Object.keys(args.stringReplace ?? {})) {
if (value.includes(key)) {
modified = true;
stringModified = true;
value = value.replaceAll(key, args.stringReplace[key]);
}
}
if (stringModified) {
return value;
}
return undefined;
}
if (value?._include) {
modified = true;
return includes[value?._include];
}
if (value?._replace) {
modified = true;
return args?.replace[value?._replace];
}
});
}
function processJson(json, args = {}) {
const MAX_STEPS = 64;
for (let i = 0; i < MAX_STEPS; i++) {
modified = false;
json = processJsonStep(json, args);
if (!modified) {
break;
}
}
return json;
}
function processFiles() {
const dumpOptions = {
lineWidth: -1,
};
for (const file of fs.readdirSync(indir)) {
const text = fs.readFileSync(path.join(indir, file), { encoding: 'utf-8' });
const json = yaml.load(text);
if (json._module) {
continue;
}
if (json._templates) {
const allDefs = Object.keys(json._templates);
for (const key in json._templates) {
allDefs.push(...(json._templates[key].defs ?? []));
}
for (const key in json._templates) {
const args = {
defs: [key, ...(json._templates[key]?.defs ?? [])],
replace: json._templates[key]?.replace,
stringReplace: json._templates[key]?.['string-replace'],
allDefs,
};
const converted = processJson(_.omit(json, ['_templates']), args);
const out = path.join(outdir, json._templates[key].file);
fs.writeFileSync(out, HEADER + yaml.dump(converted, dumpOptions));
}
} else {
fs.writeFileSync(path.join(outdir, file), HEADER + yaml.dump(processJson(json), dumpOptions));
}
}
}
function deleteOldFiles() {
const files = fs.readdirSync(outdir);
for (const file of files) {
fs.unlinkSync(path.join(outdir, file));
}
}
function run() {
deleteOldFiles();
readIncludes();
processFiles();
}
run();
+1
View File
@@ -19,6 +19,7 @@ const volatilePackages = [
'activedirectory2',
'axios',
'ssh2',
'wkx',
];
module.exports = volatilePackages;
@@ -0,0 +1,27 @@
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y openssh-server mysql-server && \
apt-get clean && rm -rf /var/lib/apt/lists/*
RUN mkdir /var/run/sshd
RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && \
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin without-password/' /etc/ssh/sshd_config
RUN mkdir -p /root/.ssh && chmod 700 /root/.ssh
COPY mykey.pub /root/.ssh/authorized_keys
RUN chmod 600 /root/.ssh/authorized_keys
RUN service mysql start && \
mysql -uroot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root'; FLUSH PRIVILEGES;" && \
service mysql stop
EXPOSE 22
COPY start.sh /start.sh
RUN chmod +x /start.sh
CMD ["/start.sh"]
@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQClN/tu+X7PVYpMBEQDwxtyG0oNuKqcJdBud/vBUalyYSpliXO30BsBusjCyAh8ENVllmGTRaTcqAUPg/vwekaAMFZu9EXmQsdhPEWSxrkPkXXKsWrZVPtRV8l3wuSy/VLxbXRLujTpIEE5Au88e2CFN3zzuhg9rvsXE0cGjyYe9Cw3Ub+Yo6rbnESI7F+Jun29/wD8RF+kRL5cr7BrDoOBQf4yGexrTbUPMZ4b8QlGfXUwZhn6LlWVoWq7BMRelnfIAkuC+LwavvnkMqXMITmGa7sG0zOXByW7iGdP6TWVq9Ks+yCZoAA9ncAkgkpwoNrOSXR6dM5UjrsfJ2Na332QfxK8MmC+nBiyXIKUaQY2G8Wfm93GQqDc+349JFwH4ZjgYWJlTd/UfRCn4j30jxe1RKnjt5xGqb3L+j8JfuSPcojWRekg/K8GxBmaMCcEdqjIFD4MxeIUW3bGHd30wnuZkTPWlSmAGrO0fZXj0s1fmOBME05TjE8jsyKPmb2Kd3JdtCuTrjOgrYiHaMfcv/VqVhIv2kCKjnvhAxCqfGfKxxgcerItbrsfFAvYak1kwZelBquHOK6SkMH5WhDyhvGY+ZF0Wbg3CpynlpJVu4E3LSglYNbkIWqS3pAf+hqJtXl9SHCLgKFUcCIz0ZNQkT+PtUlR3klp6qoPXUWrcL3pdQ== test
@@ -0,0 +1,7 @@
#!/bin/bash
service ssh start
service mysql start
tail -f /dev/null
@@ -0,0 +1,23 @@
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y openssh-server mysql-server && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN mkdir /var/run/sshd
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN echo 'root:root' | chpasswd
RUN service mysql start && \
mysql -uroot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root'; FLUSH PRIVILEGES;" && \
service mysql stop
EXPOSE 22
COPY start.sh /start.sh
RUN chmod +x /start.sh
CMD ["/start.sh"]
@@ -0,0 +1,7 @@
#!/bin/bash
service ssh start
service mysql start
tail -f /dev/null
+13 -4
View File
@@ -1,6 +1,6 @@
const { defineConfig } = require('cypress');
const killPort = require('kill-port');
const { clearDataWithBackup } = require('./e2eTestTools');
const { clearTestingData } = require('./e2eTestTools');
const waitOn = require('wait-on');
const { exec } = require('child_process');
@@ -10,11 +10,20 @@ module.exports = defineConfig({
// implement node event listeners here
on('before:spec', async details => {
await clearDataWithBackup();
await clearTestingData();
// console.log('********************* DETAILS *********************', JSON.stringify(details));
if (config.isInteractive) {
await killPort(3000);
serverProcess = exec('yarn start');
switch (details.fileName) {
case 'add-connection':
serverProcess = exec('yarn start:add-connection');
break;
case 'portal':
serverProcess = exec('yarn start:portal:local');
break;
}
await waitOn({ resources: ['http://localhost:3000'] });
serverProcess.stdout.on('data', data => {
console.log(data.toString());
@@ -0,0 +1,98 @@
const path = require('path');
describe('Add connection', () => {
it('successfully loads', () => {
cy.visit('http://localhost:3000');
cy.contains('Database not selected');
});
it('adds connection', () => {
const runOnCI = Cypress.env('runOnCI');
cy.visit('http://localhost:3000');
// cy.get('[data-testid=ConnectionList_buttonNewConnection]').click();
cy.get('[data-testid=ConnectionDriverFields_connectionType]').select('MySQL');
cy.get('[data-testid=ConnectionDriverFields_user]').clear().type('root');
cy.get('[data-testid=ConnectionDriverFields_password]').clear().type('Pwd2020Db');
if (runOnCI) {
cy.get('[data-testid=ConnectionDriverFields_server]').clear().type('mysql');
} else {
cy.get('[data-testid=ConnectionDriverFields_port]').clear().type('16004');
}
cy.get('[data-testid=ConnectionDriverFields_displayName]').clear().type('test-mysql-1');
cy.get('[data-testid=ConnectionTab_buttonSave]').click();
cy.get('[data-testid=ConnectionTab_buttonConnect]').click();
cy.contains('performance_schema');
});
it('SSH connection - user + password', () => {
const runOnCI = Cypress.env('runOnCI');
cy.visit('http://localhost:3000');
cy.contains('Connections');
// cy.realPress('F1');
// cy.realType('Close all');
// cy.realPress('Enter');
cy.get('[data-testid=ConnectionList_buttonNewConnection]').click();
cy.get('[data-testid=ConnectionDriverFields_connectionType]').select('MySQL');
cy.get('[data-testid=ConnectionDriverFields_user]').clear().type('root');
cy.get('[data-testid=ConnectionDriverFields_password]').clear().type('root');
cy.get('[data-testid=ConnectionDriverFields_displayName]').clear().type('test-mysql-ssh-1');
cy.get('[data-testid=ConnectionTab_tabSshTunnel]').click();
cy.get('[data-testid=ConnectionSshTunnelFields_useSshTunnel]').check();
cy.get('[data-testid=ConnectionSshTunnelFields_sshLogin]').clear().type('root');
cy.get('[data-testid=ConnectionSshTunnelFields_sshPassword]').clear().type('root');
if (runOnCI) {
cy.get('[data-testid=ConnectionSshTunnelFields_sshHost]').clear().type('mysql-ssh-login');
} else {
cy.get('[data-testid=ConnectionSshTunnelFields_sshPort]').clear().type('16006');
}
cy.get('[data-testid=ConnectionTab_buttonSave]').click();
cy.get('[data-testid=ConnectionTab_buttonConnect]').click();
cy.contains('performance_schema');
});
it('SSH connection - keyfile', () => {
const runOnCI = Cypress.env('runOnCI');
cy.visit('http://localhost:3000');
cy.contains('Connections');
// cy.realPress('F1');
// cy.realType('Close all');
// cy.realPress('Enter');
cy.get('[data-testid=ConnectionList_buttonNewConnection]').click();
cy.get('[data-testid=ConnectionDriverFields_connectionType]').select('MySQL');
cy.get('[data-testid=ConnectionDriverFields_user]').clear().type('root');
cy.get('[data-testid=ConnectionDriverFields_password]').clear().type('root');
cy.get('[data-testid=ConnectionDriverFields_displayName]').clear().type('test-mysql-ssh-2');
cy.get('[data-testid=ConnectionTab_tabSshTunnel]').click();
cy.get('[data-testid=ConnectionSshTunnelFields_useSshTunnel]').check();
cy.get('[data-testid=ConnectionSshTunnelFields_sshMode]').select('Key file');
cy.get('[data-testid=ConnectionSshTunnelFields_sshLogin]').clear()
cy.get('[data-testid=ConnectionSshTunnelFields_sshLogin]').type('root');
cy.get('[data-testid=ConnectionSshTunnelFields_sshKeyfile]')
.clear()
.type(path.join(Cypress.config('fileServerFolder'), 'cypress', 'e2e', 'mykey'));
if (runOnCI) {
cy.get('[data-testid=ConnectionSshTunnelFields_sshHost]').clear().type('mysql-ssh-keyfile');
} else {
cy.get('[data-testid=ConnectionSshTunnelFields_sshPort]').clear().type('16008');
}
cy.get('[data-testid=ConnectionTab_buttonSave]').click();
cy.get('[data-testid=ConnectionTab_buttonConnect]').click();
cy.contains('performance_schema');
});
// it('import chinook DB', () => {
// cy.visit('http://localhost:3000');
// cy.get('[data-testid=ConnectionTab_buttonConnect]').click();
// });
});
-31
View File
@@ -1,31 +0,0 @@
describe('Initialization', () => {
it('successfully loads', () => {
cy.visit('http://localhost:3000');
cy.contains('Database not selected');
});
it('adds connection', () => {
const runOnCI = Cypress.env('runOnCI');
cy.visit('http://localhost:3000');
// cy.get('[data-testid=ConnectionList_buttonNewConnection]').click();
cy.get('[data-testid=ConnectionDriverFields_connectionType]').select('MySQL');
cy.get('[data-testid=ConnectionDriverFields_user]').clear().type('root');
cy.get('[data-testid=ConnectionDriverFields_password]').clear().type('Pwd2020Db');
if (runOnCI) {
cy.get('[data-testid=ConnectionDriverFields_server]').clear().type('mysql');
} else {
cy.get('[data-testid=ConnectionDriverFields_port]').clear().type('16004');
}
cy.get('[data-testid=ConnectionDriverFields_displayName]').clear().type('test-mysql-1');
cy.get('[data-testid=ConnectionTab_buttonSave]').click();
cy.get('[data-testid=ConnectionTab_buttonConnect]').click();
cy.contains('performance_schema');
});
// it('import chinook DB', () => {
// cy.visit('http://localhost:3000');
// cy.get('[data-testid=ConnectionTab_buttonConnect]').click();
// });
});
+49
View File
@@ -0,0 +1,49 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAgEApTf7bvl+z1WKTAREA8MbchtKDbiqnCXQbnf7wVGpcmEqZYlzt9Ab
AbrIwsgIfBDVZZZhk0Wk3KgFD4P78HpGgDBWbvRF5kLHYTxFksa5D5F1yrFq2VT7UVfJd8
Lksv1S8W10S7o06SBBOQLvPHtghTd887oYPa77FxNHBo8mHvQsN1G/mKOq25xEiOxfibp9
vf8A/ERfpES+XK+waw6DgUH+Mhnsa021DzGeG/EJRn11MGYZ+i5VlaFquwTEXpZ3yAJLgv
i8Gr755DKlzCE5hmu7BtMzlwclu4hnT+k1lavSrPsgmaAAPZ3AJIJKcKDazkl0enTOVI67
HydjWt99kH8SvDJgvpwYslyClGkGNhvFn5vdxkKg3Pt+PSRcB+GY4GFiZU3f1H0Qp+I99I
8XtUSp47ecRqm9y/o/CX7kj3KI1kXpIPyvBsQZmjAnBHaoyBQ+DMXiFFt2xh3d9MJ7mZEz
1pUpgBqztH2V49LNX5jgTBNOU4xPI7Mij5m9indyXbQrk64zoK2Ih2jH3L/1alYSL9pAio
574QMQqnxnyscYHHqyLW67HxQL2GpNZMGXpQarhziukpDB+VoQ8obxmPmRdFm4Nwqcp5aS
VbuBNy0oJWDW5CFqkt6QH/oaibV5fUhwi4ChVHAiM9GTUJE/j7VJUd5JaeqqD11Fq3C96X
UAAAdAKwO+PisDvj4AAAAHc3NoLXJzYQAAAgEApTf7bvl+z1WKTAREA8MbchtKDbiqnCXQ
bnf7wVGpcmEqZYlzt9AbAbrIwsgIfBDVZZZhk0Wk3KgFD4P78HpGgDBWbvRF5kLHYTxFks
a5D5F1yrFq2VT7UVfJd8Lksv1S8W10S7o06SBBOQLvPHtghTd887oYPa77FxNHBo8mHvQs
N1G/mKOq25xEiOxfibp9vf8A/ERfpES+XK+waw6DgUH+Mhnsa021DzGeG/EJRn11MGYZ+i
5VlaFquwTEXpZ3yAJLgvi8Gr755DKlzCE5hmu7BtMzlwclu4hnT+k1lavSrPsgmaAAPZ3A
JIJKcKDazkl0enTOVI67HydjWt99kH8SvDJgvpwYslyClGkGNhvFn5vdxkKg3Pt+PSRcB+
GY4GFiZU3f1H0Qp+I99I8XtUSp47ecRqm9y/o/CX7kj3KI1kXpIPyvBsQZmjAnBHaoyBQ+
DMXiFFt2xh3d9MJ7mZEz1pUpgBqztH2V49LNX5jgTBNOU4xPI7Mij5m9indyXbQrk64zoK
2Ih2jH3L/1alYSL9pAio574QMQqnxnyscYHHqyLW67HxQL2GpNZMGXpQarhziukpDB+VoQ
8obxmPmRdFm4Nwqcp5aSVbuBNy0oJWDW5CFqkt6QH/oaibV5fUhwi4ChVHAiM9GTUJE/j7
VJUd5JaeqqD11Fq3C96XUAAAADAQABAAACAAOZKogKNbF8+BwbuQIpTP8Oim2uE0PUrFYb
lYdqUznDKqc3d9ENs+jfu+DyIQKCuK2+ZAr5vf4dnVlEffIzgjbnK/ASsIsJR/UILlkPVu
x4izY06FIqFackivtpao2zaAu2CeMJqn6r+H+uCVkp3rpIBm1QekKtqEOiZIOcuS8nZjx2
MqliYdA9S7N9DRT0037rKjGpqOOXqQW1FdnfC7d+9nL6OY9EtqgeuTDRQ8BzRGAfeSVdE6
QZ9eHI2CCvP5XtkYjICLYUaWDc8YYhRcggWdxrhnjIQRF1MRApJhpHubjmvJkGm7q70CQm
FPT1q+OeRJko022SsOScI6p7qkJjEsHS4RLbSGkDAn84U9l1MnHbzOqOQbI/Mnn7vNyZt4
e7n/ExUg9HglpdLgETl0oLIaiVgYGrJSLflfYtrgfEISGtoroQDj/X96HZsF+Aku+I3U4z
OD7UasFDl3LsTO3bWz8/vqkCfi/ncejZ4keWUA6VahbtJUg8E/eEIWMz8gFoO/vWf1l0H0
V51ISzInt1yVnqhdbRPH+Ce4NlNjq9JlhiZVJSsc6ke2580Okttc3JETzdHWNac7k+/0u4
lPZK2sII1cq/d+9YAe3JVpUuiQyX6R4Crga2GIyevoqm3EyXUNN/vjDCx4qyToOtJUzHao
tXI4tZZkBmtVrhqf1pAAABAQC3lG50iMbJXL8vVsB7P8UWLgmI13PqwHB8ldzsLrNtkU7q
N3KYdz/ycGGPAKxfMJiGs00qT8/d107MZYmj7hHE84VuBiHVGSXbzmL5zeth0aN4oINee0
jlvCPKjqhoImPtrU1fLglqOeTNJcZK7XzwiZry1ZHp0hXd4XPrWHZBZo37SYv9/t652zR4
qmXlxwwwdtzuNFiAexTu0K4zY12hjmoGlvlCS2YJ+B2wJNd3E1RnL505m7dIA/dLnCLeEj
zf8j1XiZ9i/m7mrU/g/e9SFP+HVwi5AV5eyHbNSPD6GZ7quWL4SlP+GGkNtKden+LIaB+G
iRwCLTgCVThdIC6oAAABAQDA6Gm7luZuzOoWBQzVKc74AOf64Op+eZwQBCC3V+eHjmyIQW
oBozvlvaNxapNxJZQjtnlckCkW59RheQxm155j/MV6aVvaD9WWNFB9k+0fujmPd4fPoZF1
9y5J3L3l9hmv+51vZvKvky5mXxOGdONTQZkpGqGvmlZpVO9jWe4WWACokqRIrS9zwtfLgf
hH+hUMh+9R1iIKACleNdfHbiqia+XIXxivbVdCtTgdPM6mW8i3gP1/oSTpYZI5KIRH4PTO
6OgG8EvRCIPc/0mddXZqiabhpPM/r5qe0jKxcQHFf1siwq7EyZ/C8ctBxpEK4dVCNnCTX4
k7+/JizOJzhzljAAABAQDbQT0BsBCM2Dl+LbwpAy6iaS0fWqhCnQ9B6ojWcfNouXuIW4tF
6fwmoqyFU2SoORmj0G6ww1NiBr+gwSDo5wSEpCI78S1CnFcp7J1HhONGthmdYDclpDlci5
t5AOIC7hhmdkqL46happxG9MH8oOua7/cqK7300nJo+ZS3XOo4O3siTjDVtVKXRAY5jWuG
mLNXQ8JKCzqmFfLlAAgGWaY7rMaGu+9Rom+F4FEZ4IUitcsN77jFNBXMXqnWy/ayr8CJpe
CxgwBm5JWQ7m+cmskMl3nmjkNr8nCiZoMcz4sIrVkZWS4PHoVFtN3jKYmo3c02kq8/378I
jIy5WYf/zrVHAAAABHRlc3QBAgMEBQY=
-----END OPENSSH PRIVATE KEY-----
+93
View File
@@ -0,0 +1,93 @@
describe('Run as portal', () => {
it('successfully loads', () => {
cy.visit('http://localhost:3000');
cy.contains('MySql-connection');
cy.contains('Postgres-connection');
});
// it('Delete chinook', () => {
// cy.visit('http://localhost:3000');
// cy.contains('MySql-connection').rightclick();
// cy.contains('New Query (server)').click();
// cy.realType('drop database if exists Chinook');
// cy.realPress('F5');
// cy.contains('Query execution finished');
// cy.contains('Postgres-connection').rightclick();
// cy.contains('New Query (server)').click();
// cy.realType('drop database if exists "Chinook"');
// cy.realPress('F5');
// cy.contains('Query execution finished');
// // cy.realPress('F1');
// // cy.realType('Close all');
// // cy.realPress('Enter');
// });
it('Create Chinook MySQL', () => {
cy.visit('http://localhost:3000');
cy.contains('MySql-connection').click();
cy.contains('MySql-connection').rightclick();
cy.contains('Create database').click();
cy.get('[data-testid=InputTextModal_value]').clear().type('Chinook');
cy.get('[data-testid=InputTextModal_ok]').click();
});
it('Create Chinook Postgres', () => {
cy.visit('http://localhost:3000');
cy.contains('Postgres-connection').click();
cy.contains('Postgres-connection').rightclick();
cy.contains('Create database').click();
cy.get('[data-testid=InputTextModal_value]').clear().type('Chinook');
cy.get('[data-testid=InputTextModal_ok]').click();
});
it('Import Chinook MySQL', () => {
cy.visit('http://localhost:3000');
cy.contains('MySql-connection').click();
cy.get('[data-testid=DatabaseAppObject_Chinook]').rightclick();
cy.contains('Chinook').rightclick();
cy.contains('Restore/import SQL dump').click();
cy.get('#uploadFileButton').selectFile('data/chinook-mysql.sql', { force: true });
cy.wait(500);
cy.get('[data-testid=ImportDatabaseDumpModal_runImport]').click();
cy.contains('Importing database');
cy.contains('Finished job script');
cy.get('[data-testid=RunScriptModal_close]').click();
cy.contains('Chinook').click();
cy.contains('Album');
});
it('Import Chinook Postgres', () => {
cy.visit('http://localhost:3000');
cy.contains('Postgres-connection').click();
cy.get('[data-testid=DatabaseAppObject_Chinook]').rightclick();
cy.contains('Restore/import SQL dump').click();
cy.get('#uploadFileButton').selectFile('data/chinook-postgres.sql', { force: true });
cy.wait(500);
cy.get('[data-testid=ImportDatabaseDumpModal_runImport]').click();
cy.contains('Importing database');
cy.contains('Finished job script');
cy.get('[data-testid=RunScriptModal_close]').click();
cy.contains('Chinook').click();
cy.contains('album');
});
it('Open ask pwd connection', () => {
cy.visit('http://localhost:3000');
cy.contains('Postgres-ask-connection').click();
cy.testid('DatabaseLoginModal_username').clear().type('postgres');
cy.testid('DatabaseLoginModal_password').clear().type('Pwd2020Db');
cy.testid('DatabaseLoginModal_connect').click();
cy.contains('Chinook').click();
cy.contains('album');
});
// it('import chinook DB', () => {
// cy.visit('http://localhost:3000');
// cy.get('[data-testid=ConnectionTab_buttonConnect]').click();
// });
});
+5 -1
View File
@@ -22,4 +22,8 @@
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
Cypress.Commands.add('testid', (testId, options = {}) => {
return cy.get(`[data-testid="${testId}"]`, options);
});
+26 -2
View File
@@ -14,7 +14,31 @@
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
import './commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')
// require('./commands')
import 'cypress-real-events';
beforeEach(() => {
// Replace 'my-database-name' with the actual IndexedDB name
cy.window().then(win => {
return new Promise((resolve, reject) => {
const request = win.indexedDB.deleteDatabase('localforage');
request.onsuccess = () => {
// Database successfully deleted
resolve();
};
request.onerror = () => {
// Some error occurred
reject(request.error);
};
request.onblocked = () => {
// Might happen if there are open connections
console.warn('IndexedDB deletion blocked');
resolve();
};
});
});
});
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+14
View File
@@ -16,3 +16,17 @@ services:
- 16004:3306
environment:
- MYSQL_ROOT_PASSWORD=Pwd2020Db
mysql-ssh-login:
build: containers/mysql-ssh-login
restart: always
ports:
- 16005:3306
- 16006:22
mysql-ssh-keyfile:
build: containers/mysql-ssh-keyfile
restart: always
ports:
- 16007:3306
- 16008:22
+16 -19
View File
@@ -4,29 +4,26 @@ const fs = require('fs');
const baseDir = path.join(os.homedir(), '.dbgate');
function createTimeStamp() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0'); // měsíc je 0-indexovaný
const day = String(now.getDate()).padStart(2, '0');
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
// function createTimeStamp() {
// const now = new Date();
// const year = now.getFullYear();
// const month = String(now.getMonth() + 1).padStart(2, '0'); // měsíc je 0-indexovaný
// const day = String(now.getDate()).padStart(2, '0');
// const hours = String(now.getHours()).padStart(2, '0');
// const minutes = String(now.getMinutes()).padStart(2, '0');
// const seconds = String(now.getSeconds()).padStart(2, '0');
// Poskládáme datum a čas do názvu souboru
const ts = `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`;
return ts;
}
// // Poskládáme datum a čas do názvu souboru
// const ts = `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`;
// return ts;
// }
function clearDataWithBackup() {
if (fs.existsSync(path.join(baseDir, 'connections.jsonl'))) {
fs.renameSync(
path.join(baseDir, 'connections.jsonl'),
path.join(baseDir, `connections-${createTimeStamp()}.jsonl.bak`)
);
function clearTestingData() {
if (fs.existsSync(path.join(baseDir, 'connections-e2etests.jsonl'))) {
fs.unlinkSync(path.join(baseDir, 'connections-e2etests.jsonl'));
}
}
module.exports = {
clearDataWithBackup,
clearTestingData,
};
+22
View File
@@ -0,0 +1,22 @@
CONNECTIONS=mysql,postgres,pgask
LABEL_mysql=MySql-connection
SERVER_mysql=mysql
USER_mysql=root
PASSWORD_mysql=Pwd2020Db
PORT_mysql=3306
ENGINE_mysql=mysql@dbgate-plugin-mysql
LABEL_postgres=Postgres-connection
SERVER_postgres=postgres
USER_postgres=postgres
PASSWORD_postgres=Pwd2020Db
PORT_postgres=5432
ENGINE_postgres=postgres@dbgate-plugin-postgres
LABEL_pgask=Postgres-ask-connection
SERVER_pgask=postgres
USER_pgask=postgres
PORT_pgask=5432
ENGINE_pgask=postgres@dbgate-plugin-postgres
PASSWORD_MODE_pgask=askUser
+22
View File
@@ -0,0 +1,22 @@
CONNECTIONS=mysql,postgres,pgask
LABEL_mysql=MySql-connection
SERVER_mysql=localhost
USER_mysql=root
PASSWORD_mysql=Pwd2020Db
PORT_mysql=16004
ENGINE_mysql=mysql@dbgate-plugin-mysql
LABEL_postgres=Postgres-connection
SERVER_postgres=localhost
USER_postgres=postgres
PASSWORD_postgres=Pwd2020Db
PORT_postgres=16000
ENGINE_postgres=postgres@dbgate-plugin-postgres
LABEL_pgask=Postgres-ask-connection
SERVER_pgask=localhost
USER_pgask=postgres
PORT_pgask=16000
ENGINE_pgask=postgres@dbgate-plugin-postgres
PASSWORD_MODE_pgask=askUser
+32
View File
@@ -0,0 +1,32 @@
const dbgateApi = require('dbgate-api');
dbgateApi.initializeApiEnvironment();
const dbgatePluginMysql = require('dbgate-plugin-mysql');
dbgateApi.registerPlugins(dbgatePluginMysql);
const dbgatePluginPostgres = require('dbgate-plugin-postgres');
dbgateApi.registerPlugins(dbgatePluginPostgres);
async function run() {
await dbgateApi.executeQuery({
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',
},
sql: 'drop database if exists Chinook',
});
await dbgateApi.executeQuery({
connection: {
server: process.env.SERVER_postgres,
user: process.env.USER_postgres,
password: process.env.PASSWORD_postgres,
port: process.env.PORT_postgres,
engine: 'postgres@dbgate-plugin-postgres',
},
sql: 'drop database if exists "Chinook"',
});
}
dbgateApi.runScript(run);
+16 -5
View File
@@ -7,15 +7,26 @@
"axios": "^1.7.9",
"cross-env": "^7.0.3",
"cypress": "^13.16.1",
"cypress-real-events": "^1.13.0",
"env-cmd": "^10.1.0",
"kill-port": "^2.0.1",
"start-server-and-test": "^2.0.8"
},
"scripts": {
"cy:open": "cypress open --config experimentalInteractiveRunEvents=true",
"cy:run": "cypress run",
"cy:run:ci": "cypress run --env runOnCI=true",
"start": "cd .. && node packer/build/bundle.js --listen-api --run-e2e-tests",
"test:ci": "start-server-and-test start http://localhost:3000 cy:run:ci",
"test": "start-server-and-test start http://localhost:3000 cy:run"
"cy:run:add-connection:local": "cypress run --spec cypress/e2e/add-connection.cy.js",
"cy:run:add-connection:ci": "cypress run --env runOnCI=true --spec cypress/e2e/add-connection.cy.js",
"cy:run:portal:local": "cypress run --spec cypress/e2e/portal.cy.js",
"cy:run:portal:ci": "cypress run --env runOnCI=true --spec cypress/e2e/portal.cy.js",
"start:add-connection": "cd .. && node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:portal:local": "cd .. && env-cmd -f e2e-tests/env/portal-local/.env node e2e-tests/init/portal.js && env-cmd -f e2e-tests/env/portal-local/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"start:portal:ci": "cd .. && env-cmd -f e2e-tests/env/portal-ci/.env node e2e-tests/init/portal.js && env-cmd -f e2e-tests/env/portal-ci/.env node packer/build/bundle.js --listen-api --run-e2e-tests",
"test:ci": "start-server-and-test start:add-connection http://localhost:3000 cy:run:add-connection:ci && start-server-and-test start:portal:ci http://localhost:3000 cy:run:portal:ci",
"test:local": "start-server-and-test start:add-connection http://localhost:3000 cy:run:add-connection:local && start-server-and-test start:portal:local http://localhost:3000 cy:run:portal:local"
}
}
+18
View File
@@ -319,6 +319,11 @@ combined-stream@^1.0.8, combined-stream@~1.0.6:
dependencies:
delayed-stream "~1.0.0"
commander@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
commander@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
@@ -350,6 +355,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
cypress-real-events@^1.13.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/cypress-real-events/-/cypress-real-events-1.13.0.tgz#6b7cd32dcac172db1493608f97a2576c7d0bd5af"
integrity sha512-LoejtK+dyZ1jaT8wGT5oASTPfsNV8/ClRp99ruN60oPj8cBJYod80iJDyNwfPAu4GCxTXOhhAv9FO65Hpwt6Hg==
cypress@^13.16.1:
version "13.16.1"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.16.1.tgz#82e776f6ad2037ccce6b6feabed768615c476258"
@@ -479,6 +489,14 @@ enquirer@^2.3.6:
ansi-colors "^4.1.1"
strip-ansi "^6.0.1"
env-cmd@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/env-cmd/-/env-cmd-10.1.0.tgz#c7f5d3b550c9519f137fdac4dd8fb6866a8c8c4b"
integrity sha512-mMdWTT9XKN7yNth/6N6g2GuKuJTsKMDHlQFUDacb/heQRRWOTIZ42t1rMHnQu4jYxU1ajdTeJM+9eEETlqToMA==
dependencies:
commander "^4.0.0"
cross-spawn "^7.0.0"
es-define-property@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
@@ -3,9 +3,15 @@ const _ = require('lodash');
const fp = require('lodash/fp');
const { testWrapper } = require('../tools');
const engines = require('../engines');
const { getAlterDatabaseScript, extendDatabaseInfo, generateDbPairingId } = require('dbgate-tools');
const {
getAlterDatabaseScript,
extendDatabaseInfo,
generateDbPairingId,
formatQueryWithoutParams,
runCommandOnDriver,
} = require('dbgate-tools');
const initSql = ['CREATE TABLE t1 (id int primary key)', 'CREATE TABLE t2 (id int primary key)'];
const initSql = ['CREATE TABLE ~t1 (~id int primary key)', 'CREATE TABLE ~t2 (~id int primary key)'];
function flatSource(engineCond = x => !x.skipReferences) {
return _.flatten(
@@ -16,13 +22,14 @@ function flatSource(engineCond = x => !x.skipReferences) {
}
async function testDatabaseDiff(conn, driver, mangle, createObject = null) {
await driver.query(conn, `create table t1 (id int not null primary key)`);
await runCommandOnDriver(conn, driver, `create table ~t1 (~id int not null primary key)`);
await driver.query(
await runCommandOnDriver(
conn,
`create table t2 (
id int not null primary key,
t1_id int null references t1(id)
driver,
`create table ~t2 (
~id int not null primary key,
~t1_id int null references ~t1(~id)
)`
);
@@ -63,29 +70,32 @@ describe('Alter database', () => {
db => {
_.remove(db[type], x => x.pureName == 'obj1');
},
object.create1
formatQueryWithoutParams(driver, object.create1)
);
expect(db[type].length).toEqual(0);
})
);
test.each(flatSource(x => x.supportRenameSqlObject))(
'Rename object - %s - %s',
testWrapper(async (conn, driver, type, object, engine) => {
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
const objectsSupportingRename = flatSource(x => x.supportRenameSqlObject);
if (objectsSupportingRename.length > 0) {
test.each(objectsSupportingRename)(
'Rename object - %s - %s',
testWrapper(async (conn, driver, type, object, engine) => {
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
await driver.query(conn, object.create1, { discardResult: true });
await runCommandOnDriver(conn, driver, object.create1);
const structure = extendDatabaseInfo(await driver.analyseFull(conn));
const structure = extendDatabaseInfo(await driver.analyseFull(conn));
const dmp = driver.createDumper();
dmp.renameSqlObject(structure[type][0], 'renamed1');
const dmp = driver.createDumper();
dmp.renameSqlObject(structure[type][0], 'renamed1');
await driver.query(conn, dmp.s);
await driver.query(conn, dmp.s);
const structure2 = await driver.analyseFull(conn);
expect(structure2[type].length).toEqual(1);
expect(structure2[type][0].pureName).toEqual('renamed1');
})
);
const structure2 = await driver.analyseFull(conn);
expect(structure2[type].length).toEqual(1);
expect(structure2[type][0].pureName).toEqual('renamed1');
})
);
}
});
+30 -14
View File
@@ -4,7 +4,12 @@ const fp = require('lodash/fp');
const { testWrapper } = require('../tools');
const engines = require('../engines');
const crypto = require('crypto');
const { getAlterTableScript, extendDatabaseInfo, generateDbPairingId } = require('dbgate-tools');
const {
getAlterTableScript,
extendDatabaseInfo,
generateDbPairingId,
formatQueryWithoutParams,
} = require('dbgate-tools');
function pickImportantTableInfo(engine, table) {
const props = ['columnName', 'defaultValue'];
@@ -15,7 +20,10 @@ function pickImportantTableInfo(engine, table) {
columns: table.columns
.filter(x => x.columnName != 'rowid')
.map(fp.pick(props))
.map(props => _.omitBy(props, x => x == null)),
.map(props => _.omitBy(props, x => x == null))
.map(props =>
_.omitBy(props, (v, k) => k == 'defaultValue' && v == 'NULL' && engine.setNullDefaultInsteadOfDrop)
),
};
}
@@ -25,27 +33,36 @@ function checkTableStructure(engine, t1, t2) {
}
async function testTableDiff(engine, conn, driver, mangle) {
await driver.query(conn, `create table t0 (id int not null primary key)`);
await driver.query(conn, formatQueryWithoutParams(driver, `create table ~t0 (~id int not null primary key)`));
await driver.query(
conn,
`create table t1 (
col_pk int not null primary key,
col_std int,
col_def int default 12,
${engine.skipReferences ? '' : 'col_fk int references t0(id),'}
col_idx int,
col_uq int ${engine.skipUnique ? '' : 'unique'} ,
col_ref int ${engine.skipUnique ? '' : 'unique'}
formatQueryWithoutParams(
driver,
`create table ~t1 (
~col_pk int not null primary key,
~col_std int,
~col_def int default 12,
${engine.skipReferences ? '' : '~col_fk int references ~t0(~id),'}
~col_idx int,
~col_uq int ${engine.skipUnique ? '' : 'unique'} ,
~col_ref int ${engine.skipUnique ? '' : 'unique'}
)`
)
);
if (!engine.skipIndexes) {
await driver.query(conn, `create index idx1 on t1(col_idx)`);
await driver.query(conn, formatQueryWithoutParams(driver, `create index ~idx1 on ~t1(~col_idx)`));
}
if (!engine.skipReferences) {
await driver.query(conn, `create table t2 (id int not null primary key, fkval int null references t1(col_ref))`);
await driver.query(
conn,
formatQueryWithoutParams(
driver,
`create table ~t2 (~id int not null primary key, ~fkval int null references ~t1(~col_ref))`
)
);
}
const tget = x => x.tables.find(y => y.pureName == 't1');
@@ -175,5 +192,4 @@ describe('Alter table', () => {
// });
// })
// );
});
@@ -2,7 +2,7 @@ const engines = require('../engines');
const stream = require('stream');
const { testWrapper } = require('../tools');
const dataDuplicator = require('dbgate-api/src/shell/dataDuplicator');
const { runCommandOnDriver } = require('dbgate-tools');
const { runCommandOnDriver, runQueryOnDriver } = require('dbgate-tools');
describe('Data duplicator', () => {
test.each(engines.filter(x => !x.skipDataDuplicator).map(engine => [engine.label, engine]))(
@@ -84,10 +84,10 @@ describe('Data duplicator', () => {
],
});
const res1 = await driver.query(conn, `select count(*) as cnt from t1`);
const res1 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t1`));
expect(res1.rows[0].cnt.toString()).toEqual('6');
const res2 = await driver.query(conn, `select count(*) as cnt from t2`);
const res2 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t2`));
expect(res2.rows[0].cnt.toString()).toEqual('6');
})
);
@@ -145,13 +145,15 @@ describe('Data duplicator', () => {
},
});
const res1 = await driver.query(conn, `select count(*) as cnt from t1`);
const res1 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t1`));
expect(res1.rows[0].cnt.toString()).toEqual('1');
const res2 = await driver.query(conn, `select count(*) as cnt from t2`);
const res2 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t2`));
expect(res2.rows[0].cnt.toString()).toEqual('2');
const res3 = await driver.query(conn, `select count(*) as cnt from t2 where valfk is not null`);
const res3 = await runQueryOnDriver(conn, driver, dmp =>
dmp.put(`select count(*) as ~cnt from ~t2 where ~valfk is not null`)
);
expect(res3.rows[0].cnt.toString()).toEqual('1');
})
);
@@ -2,9 +2,11 @@ const engines = require('../engines');
const stream = require('stream');
const { testWrapper } = require('../tools');
const tableWriter = require('dbgate-api/src/shell/tableWriter');
const tableReader = require('dbgate-api/src/shell/tableReader');
const copyStream = require('dbgate-api/src/shell/copyStream');
const importDatabase = require('dbgate-api/src/shell/importDatabase');
const fakeObjectReader = require('dbgate-api/src/shell/fakeObjectReader');
const { runQueryOnDriver, runCommandOnDriver } = require('dbgate-tools');
function createImportStream() {
const pass = new stream.PassThrough({
@@ -22,7 +24,17 @@ function createImportStream() {
return pass;
}
describe('DB Import', () => {
function createExportStream() {
const writable = new stream.Writable({ objectMode: true });
writable.resultArray = [];
writable._write = (chunk, encoding, callback) => {
writable.resultArray.push(chunk);
callback();
};
return writable;
}
describe('DB Import/export', () => {
test.each(engines.map(engine => [engine.label, engine]))(
'Import one table - %s',
testWrapper(async (conn, driver, engine) => {
@@ -37,7 +49,7 @@ describe('DB Import', () => {
});
await copyStream(reader, writer);
const res = await driver.query(conn, `select count(*) as cnt from t1`);
const res = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t1`));
expect(res.rows[0].cnt.toString()).toEqual('6');
})
);
@@ -65,10 +77,10 @@ describe('DB Import', () => {
});
await copyStream(reader2, writer2);
const res1 = await driver.query(conn, `select count(*) as cnt from t1`);
const res1 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t1`));
expect(res1.rows[0].cnt.toString()).toEqual('6');
const res2 = await driver.query(conn, `select count(*) as cnt from t2`);
const res2 = await runQueryOnDriver(conn, driver, dmp => dmp.put(`select count(*) as ~cnt from ~t2`));
expect(res2.rows[0].cnt.toString()).toEqual('6');
})
);
@@ -98,4 +110,35 @@ describe('DB Import', () => {
// expect(res2.rows[0].cnt.toString()).toEqual('6');
})
);
test.each(engines.map(engine => [engine.label, engine]))(
'Export one table - %s',
testWrapper(async (conn, driver, engine) => {
// const reader = await fakeObjectReader({ delay: 10 });
// const reader = await fakeObjectReader();
await runCommandOnDriver(conn, driver, 'create table ~t1 (~id int primary key, ~country varchar(100))');
const data = [
[1, 'Czechia'],
[2, 'Austria'],
[3, 'Germany'],
[4, 'Romania'],
[5, 'Great Britain'],
[6, 'Bosna, Hecegovina'],
];
for (const row of data) {
await runCommandOnDriver(conn, driver, dmp =>
dmp.put('insert into ~t1(~id, ~country) values (%v, %v)', ...row)
);
}
const reader = await tableReader({
systemConnection: conn,
driver,
pureName: 't1',
});
const writer = createExportStream();
await copyStream(reader, writer);
expect(writer.resultArray.filter(x => !x.__isStreamHeader).map(row => [row.id, row.country])).toEqual(data);
})
);
});
@@ -4,7 +4,7 @@ const { testWrapper, testWrapperPrepareOnly } = require('../tools');
const _ = require('lodash');
const engines = require('../engines');
const deployDb = require('dbgate-api/src/shell/deployDb');
const { databaseInfoFromYamlModel } = require('dbgate-tools');
const { databaseInfoFromYamlModel, runQueryOnDriver, formatQueryWithoutParams } = require('dbgate-tools');
const generateDeploySql = require('dbgate-api/src/shell/generateDeploySql');
const connectUtility = require('dbgate-api/src/utility/connectUtility');
@@ -69,6 +69,29 @@ function checkStructure(
}
}
// function convertObjectText(text, driver) {
// if (!text) return undefined;
// text = formatQueryWithoutParams(driver, text);
// if (driver.dialect.requireFromDual && text.startsWith('create view ') && !text.includes('from')) {
// text = text + ' from dual';
// }
// return text;
// }
// function convertModelToEngine(model, driver) {
// return model.map(x => ({
// ...x,
// text: convertObjectText(x.text, driver),
// }));
// }
function convertModelToEngine(model, driver) {
return model.map(x => ({
...x,
text: x.text ? formatQueryWithoutParams(driver, x.text) : undefined,
}));
}
async function testDatabaseDeploy(engine, conn, driver, dbModelsYaml, options) {
const { testEmptyLastScript, finalCheckAgainstModel, markDeleted, allowDropStatements } = options || {};
let index = 0;
@@ -83,13 +106,13 @@ async function testDatabaseDeploy(engine, conn, driver, dbModelsYaml, options) {
for (const loadedDbModel of dbModelsYaml) {
if (_.isString(loadedDbModel)) {
await driver.script(conn, loadedDbModel);
await driver.script(conn, formatQueryWithoutParams(driver, loadedDbModel));
} else {
const { sql, isEmpty } = await generateDeploySql({
systemConnection: conn.isPreparedOnly ? undefined : conn,
connection: conn.isPreparedOnly ? conn : undefined,
driver,
loadedDbModel,
loadedDbModel: convertModelToEngine(loadedDbModel, driver),
dbdiffOptionsExtra,
});
console.debug('Generated deploy script:', sql);
@@ -106,7 +129,7 @@ async function testDatabaseDeploy(engine, conn, driver, dbModelsYaml, options) {
systemConnection: conn.isPreparedOnly ? undefined : conn,
connection: conn.isPreparedOnly ? conn : undefined,
driver,
loadedDbModel,
loadedDbModel: convertModelToEngine(loadedDbModel, driver),
dbdiffOptionsExtra,
});
}
@@ -117,7 +140,12 @@ async function testDatabaseDeploy(engine, conn, driver, dbModelsYaml, options) {
const dbhan = conn.isPreparedOnly ? await connectUtility(driver, conn, 'read') : conn;
const structure = await driver.analyseFull(dbhan);
if (conn.isPreparedOnly) await driver.close(dbhan);
checkStructure(engine, structure, finalCheckAgainstModel ?? _.findLast(dbModelsYaml, x => _.isArray(x)), options);
checkStructure(
engine,
structure,
convertModelToEngine(finalCheckAgainstModel ?? _.findLast(dbModelsYaml, x => _.isArray(x)), driver),
options
);
}
describe('Deploy database', () => {
@@ -339,7 +367,7 @@ describe('Deploy database', () => {
],
]);
const res = await driver.query(conn, `select count(*) as cnt from t1`);
const res = await runQueryOnDriver(conn, driver, `select count(*) as ~cnt from ~t1`);
expect(res.rows[0].cnt.toString()).toEqual('3');
})
);
@@ -386,12 +414,12 @@ describe('Deploy database', () => {
],
]);
const res = await driver.query(conn, `select val from t1 where id = 2`);
const res = await runQueryOnDriver(conn, driver, `select ~val from ~t1 where ~id = 2`);
expect(res.rows[0].val.toString()).toEqual('5');
})
);
test.each(engines.enginesPostgre.map(engine => [engine.label, engine]))(
test.each([engines.postgreSqlEngine].map(engine => [engine.label, engine]))(
'Current timestamp default value - %s',
testWrapper(async (conn, driver, engine) => {
await testDatabaseDeploy(engine, conn, driver, [
@@ -414,8 +442,8 @@ describe('Deploy database', () => {
],
]);
await driver.query(conn, `insert into t1 (id) values (1)`);
const res = await driver.query(conn, ` select val from t1 where id = 1`);
await runQueryOnDriver(conn, driver, `insert into ~t1 (~id) values (1)`);
const res = await runQueryOnDriver(conn, driver, ` select ~val from ~t1 where ~id = 1`);
expect(res.rows[0].val.toString().substring(0, 2)).toEqual('20');
})
);
@@ -438,7 +466,7 @@ describe('Deploy database', () => {
},
},
],
'insert into t1 (id, val) values (1, 1); insert into t1 (id) values (2)',
'insert into ~t1 (~id, ~val) values (1, 1); insert into ~t1 (~id) values (2)',
[
{
name: 't1.table.yaml',
@@ -452,16 +480,16 @@ describe('Deploy database', () => {
},
},
],
'insert into t1 (id) values (3);',
'insert into ~t1 (~id) values (3);',
]);
const res1 = await driver.query(conn, `select val from t1 where id = 1`);
const res1 = await runQueryOnDriver(conn, driver, `select ~val from ~t1 where ~id = 1`);
expect(res1.rows[0].val).toEqual(1);
const res2 = await driver.query(conn, `select val from t1 where id = 2`);
const res2 = await runQueryOnDriver(conn, driver, `select ~val from ~t1 where ~id = 2`);
expect(res2.rows[0].val).toEqual(20);
const res3 = await driver.query(conn, `select val from t1 where id = 3`);
const res3 = await runQueryOnDriver(conn, driver, `select ~val from ~t1 where ~id = 3`);
expect(res2.rows[0].val).toEqual(20);
})
);
@@ -525,17 +553,17 @@ describe('Deploy database', () => {
const V1 = {
name: 'v1.view.sql',
text: 'create view v1 as select * from t1',
text: 'create view ~v1 as select * from ~t1',
};
const V1_VARIANT2 = {
name: 'v1.view.sql',
text: 'create view v1 as select 1 as c1',
text: 'create view ~v1 as select ~id + ~id ~idsum from ~t1',
};
const V1_DELETED = {
name: '_deleted_v1.view.sql',
text: 'create view _deleted_v1 as select * from t1',
text: 'create view ~_deleted_v1 as select * from ~t1',
};
test.each(engines.map(engine => [engine.label, engine]))(
@@ -682,15 +710,15 @@ describe('Deploy database', () => {
[
{
name: '1.predeploy.sql',
text: 'create table t1 (id int primary key); insert into t1 (id) values (1);',
text: 'create table ~t1 (~id int primary key); insert into ~t1 (~id) values (1);',
},
],
]);
const res1 = await driver.query(conn, 'SELECT COUNT(*) AS cnt FROM t1');
const res1 = await runQueryOnDriver(conn, driver, 'SELECT COUNT(*) AS ~cnt FROM ~t1');
expect(res1.rows[0].cnt == 1).toBeTruthy();
const res2 = await driver.query(conn, 'SELECT COUNT(*) AS cnt FROM dbgate_deploy_journal');
const res2 = await runQueryOnDriver(conn, driver, 'SELECT COUNT(*) AS ~cnt FROM ~dbgate_deploy_journal');
expect(res2.rows[0].cnt == 1).toBeTruthy();
})
);
@@ -702,48 +730,53 @@ describe('Deploy database', () => {
[
{
name: 't1.uninstall.sql',
text: 'drop table t1',
text: 'drop table ~t1',
},
{
name: 't1.install.sql',
text: 'create table t1 (id int primary key); insert into t1 (id) values (1)',
text: 'create table ~t1 (~id int primary key); insert into ~t1 (~id) values (1)',
},
{
name: 't2.once.sql',
text: 'create table t2 (id int primary key); insert into t2 (id) values (1)',
text: 'create table ~t2 (~id int primary key); insert into ~t2 (~id) values (1)',
},
],
[
{
name: 't1.uninstall.sql',
text: 'drop table t1',
text: 'drop table ~t1',
},
{
name: 't1.install.sql',
text: 'create table t1 (id int primary key, val int); insert into t1 (id, val) values (1, 11)',
text: 'create table ~t1 (~id int primary key, ~val int); insert into ~t1 (~id, ~val) values (1, 11)',
},
{
name: 't2.once.sql',
text: 'insert into t2 (id) values (2)',
text: 'insert into ~t2 (~id) values (2)',
},
],
]);
const res1 = await driver.query(conn, 'SELECT val from t1 where id = 1');
const res1 = await runQueryOnDriver(conn, driver, 'SELECT ~val from ~t1 where ~id = 1');
expect(res1.rows[0].val == 11).toBeTruthy();
const res2 = await driver.query(conn, 'SELECT COUNT(*) AS cnt FROM t2');
const res2 = await runQueryOnDriver(conn, driver, 'SELECT COUNT(*) AS ~cnt FROM ~t2');
expect(res2.rows[0].cnt == 1).toBeTruthy();
const res3 = await driver.query(conn, 'SELECT COUNT(*) AS cnt FROM dbgate_deploy_journal');
const res3 = await runQueryOnDriver(conn, driver, 'SELECT COUNT(*) AS ~cnt FROM ~dbgate_deploy_journal');
expect(res3.rows[0].cnt == 3).toBeTruthy();
const res4 = await driver.query(conn, "SELECT run_count from dbgate_deploy_journal where name = 't2.once.sql'");
const res4 = await runQueryOnDriver(
conn,
driver,
"SELECT ~run_count from ~dbgate_deploy_journal where ~name = 't2.once.sql'"
);
expect(res4.rows[0].run_count == 1).toBeTruthy();
const res5 = await driver.query(
const res5 = await runQueryOnDriver(
conn,
"SELECT run_count from dbgate_deploy_journal where name = 't1.install.sql'"
driver,
"SELECT ~run_count from ~dbgate_deploy_journal where ~name = 't1.install.sql'"
);
expect(res5.rows[0].run_count == 2).toBeTruthy();
})
@@ -1,8 +1,9 @@
const { testWrapper } = require('../tools');
const engines = require('../engines');
const _ = require('lodash');
const { formatQueryWithoutParams, runCommandOnDriver } = require('dbgate-tools');
const initSql = ['CREATE TABLE t1 (id int primary key)', 'CREATE TABLE t2 (id int primary key)'];
const initSql = ['CREATE TABLE ~t1 (~id int primary key)', 'CREATE TABLE ~t2 (~id int primary key)'];
function flatSource() {
return _.flatten(
@@ -18,6 +19,16 @@ function flatSourceParameters() {
);
}
function flatSourceTriggers() {
return _.flatten(engines.map(engine => (engine.triggers || []).map(trigger => [engine.label, trigger, engine])));
}
function flatSourceSchedulerEvents() {
return _.flatten(
engines.map(engine => (engine.schedulerEvents || []).map(schedulerEvent => [engine.label, schedulerEvent, engine]))
);
}
const obj1Match = expect.objectContaining({
pureName: 'obj1',
});
@@ -34,9 +45,9 @@ describe('Object analyse', () => {
test.each(flatSource())(
'Full analysis - %s - %s',
testWrapper(async (conn, driver, type, object, engine) => {
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
await driver.query(conn, object.create1, { discardResult: true });
await runCommandOnDriver(conn, driver, object.create1);
const structure = await driver.analyseFull(conn);
expect(structure[type].length).toEqual(1);
@@ -47,11 +58,11 @@ describe('Object analyse', () => {
test.each(flatSource())(
'Incremental analysis - add - %s - %s',
testWrapper(async (conn, driver, type, object, engine) => {
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
await driver.query(conn, object.create2, { discardResult: true });
await runCommandOnDriver(conn, driver, object.create2);
const structure1 = await driver.analyseFull(conn);
await driver.query(conn, object.create1, { discardResult: true });
await runCommandOnDriver(conn, driver, object.create1);
const structure2 = await driver.analyseIncremental(conn, structure1);
expect(structure2[type].length).toEqual(2);
@@ -62,12 +73,12 @@ describe('Object analyse', () => {
test.each(flatSource())(
'Incremental analysis - drop - %s - %s',
testWrapper(async (conn, driver, type, object, engine) => {
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
await driver.query(conn, object.create1, { discardResult: true });
await driver.query(conn, object.create2, { discardResult: true });
await runCommandOnDriver(conn, driver, object.create1);
await runCommandOnDriver(conn, driver, object.create2);
const structure1 = await driver.analyseFull(conn);
await driver.query(conn, object.drop2, { discardResult: true });
await runCommandOnDriver(conn, driver, object.drop2);
const structure2 = await driver.analyseIncremental(conn, structure1);
expect(structure2[type].length).toEqual(1);
@@ -78,11 +89,11 @@ describe('Object analyse', () => {
test.each(flatSource())(
'Create SQL - add - %s - %s',
testWrapper(async (conn, driver, type, object, engine) => {
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
await driver.query(conn, object.create1, { discardResult: true });
await runCommandOnDriver(conn, driver, object.create1);
const structure1 = await driver.analyseFull(conn);
await driver.query(conn, object.drop1, { discardResult: true });
await runCommandOnDriver(conn, driver, object.drop1);
const structure2 = await driver.analyseIncremental(conn, structure1);
expect(structure2[type].length).toEqual(0);
@@ -95,44 +106,94 @@ describe('Object analyse', () => {
})
);
test.each(flatSourceParameters())(
'Test parameters simple analyse - %s - %s',
testWrapper(async (conn, driver, testName, parameter, engine) => {
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
for (const sql of engine.parametersOtherSql) await driver.query(conn, sql, { discardResult: true });
const flatParameters = flatSourceParameters();
await driver.query(conn, parameter.create, { discardResult: true });
if (flatParameters.length > 0) {
test.each(flatParameters)(
'Test parameters simple analyse - %s - %s',
testWrapper(async (conn, driver, testName, parameter, engine) => {
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
for (const sql of engine.parametersOtherSql) await runCommandOnDriver(conn, driver, sql);
await runCommandOnDriver(conn, driver, parameter.create);
const structure = await driver.analyseFull(conn);
const parameters = structure[parameter.objectTypeField].find(x => x.pureName == 'obj1').parameters;
expect(parameters.length).toEqual(parameter.list.length);
for (let i = 0; i < parameters.length; i += 1) {
expect(parameters[i]).toEqual(expect.objectContaining(parameter.list[i]));
}
})
);
test.each(flatParameters)(
'Test parameters create SQL - %s - %s',
testWrapper(async (conn, driver, testName, parameter, engine) => {
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
for (const sql of engine.parametersOtherSql) await runCommandOnDriver(conn, driver, sql);
await runCommandOnDriver(conn, driver, parameter.create);
const structure1 = await driver.analyseFull(conn);
await runCommandOnDriver(conn, driver, parameter.drop);
const obj = structure1[parameter.objectTypeField].find(x => x.pureName == 'obj1');
await driver.script(conn, obj.createSql, { discardResult: true });
const structure2 = await driver.analyseFull(conn);
const parameters = structure2[parameter.objectTypeField].find(x => x.pureName == 'obj1').parameters;
expect(parameters.length).toEqual(parameter.list.length);
for (let i = 0; i < parameters.length; i += 1) {
expect(parameters[i]).toEqual(expect.objectContaining(parameter.list[i]));
}
})
);
}
test.each(flatSourceTriggers())(
'Test triggers - %s - %s',
testWrapper(async (conn, driver, trigger) => {
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
const { triggerOtherDropSql, triggerOtherCreateSql, create, drop, expected, objectTypeField } = trigger;
if (triggerOtherCreateSql) await runCommandOnDriver(conn, driver, triggerOtherCreateSql);
await runCommandOnDriver(conn, driver, create);
const structure = await driver.analyseFull(conn);
await runCommandOnDriver(conn, driver, drop);
const parameters = structure[parameter.objectTypeField].find(x => x.pureName == 'obj1').parameters;
if (triggerOtherDropSql) await runCommandOnDriver(conn, driver, triggerOtherDropSql);
expect(parameters.length).toEqual(parameter.list.length);
for (let i = 0; i < parameters.length; i += 1) {
expect(parameters[i]).toEqual(expect.objectContaining(parameter.list[i]));
}
})
);
const createdTrigger = structure[objectTypeField].find(x => x.pureName == expected.pureName);
expect(createdTrigger).toEqual(expect.objectContaining(expected));
test.each(flatSourceParameters())(
'Test parameters create SQL - %s - %s',
testWrapper(async (conn, driver, testName, parameter, engine) => {
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
for (const sql of engine.parametersOtherSql) await driver.query(conn, sql, { discardResult: true });
await driver.query(conn, parameter.create, { discardResult: true });
const structure1 = await driver.analyseFull(conn);
await driver.query(conn, parameter.drop, { discardResult: true });
const obj = structure1[parameter.objectTypeField].find(x => x.pureName == 'obj1');
await driver.script(conn, obj.createSql);
// test trigger createSql
if (triggerOtherCreateSql) await runCommandOnDriver(conn, driver, triggerOtherCreateSql);
await driver.script(conn, createdTrigger.createSql);
const structure2 = await driver.analyseFull(conn);
const parameters = structure2[parameter.objectTypeField].find(x => x.pureName == 'obj1').parameters;
expect(parameters.length).toEqual(parameter.list.length);
for (let i = 0; i < parameters.length; i += 1) {
expect(parameters[i]).toEqual(expect.objectContaining(parameter.list[i]));
}
const createdTrigger2 = structure2[objectTypeField].find(x => x.pureName == expected.pureName);
expect(createdTrigger2).toEqual(expect.objectContaining(expected));
})
);
const schedulerEvents = flatSourceSchedulerEvents();
if (schedulerEvents.length > 0) {
test.each(schedulerEvents)(
'Test scheduler events - %s - %s',
testWrapper(async (conn, driver, event) => {
for (const sql of initSql) await runCommandOnDriver(conn, driver, sql);
const { create, drop, objectTypeField, expected } = event;
await runCommandOnDriver(conn, driver, create);
const structure = await driver.analyseFull(conn);
await runCommandOnDriver(conn, driver, drop);
const createdEvent = structure[objectTypeField].find(x => x.pureName == expected.pureName);
expect(createdEvent).toEqual(expect.objectContaining(expected));
})
);
}
});
+65 -15
View File
@@ -1,11 +1,12 @@
const engines = require('../engines');
const { splitQuery } = require('dbgate-query-splitter');
const { testWrapper } = require('../tools');
const { runQueryOnDriver, runCommandOnDriver, formatQueryWithoutParams } = require('dbgate-tools');
const initSql = [
'CREATE TABLE t1 (id int primary key)',
'INSERT INTO t1 (id) VALUES (1)',
'INSERT INTO t1 (id) VALUES (2)',
'CREATE TABLE ~t1 (~id int primary key)',
'INSERT INTO ~t1 (~id) VALUES (1)',
'INSERT INTO ~t1 (~id) VALUES (2)',
];
expect.extend({
@@ -51,7 +52,7 @@ class StreamHandler {
function executeStreamItem(driver, conn, sql) {
return new Promise(resolve => {
const handler = new StreamHandler(resolve);
driver.stream(conn, sql, handler);
driver.stream(conn, formatQueryWithoutParams(driver, sql), handler);
});
}
@@ -68,9 +69,11 @@ describe('Query', () => {
test.each(engines.map(engine => [engine.label, engine]))(
'Simple query - %s',
testWrapper(async (conn, driver, engine) => {
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
for (const sql of initSql) {
await runCommandOnDriver(conn, driver, dmp => dmp.put(sql));
}
const res = await driver.query(conn, 'SELECT id FROM t1 ORDER BY id');
const res = await runQueryOnDriver(conn, driver, dmp => dmp.put('SELECT ~id FROM ~t1 ORDER BY ~id'));
expect(res.columns).toEqual([
expect.objectContaining({
columnName: 'id',
@@ -91,8 +94,11 @@ describe('Query', () => {
test.each(engines.map(engine => [engine.label, engine]))(
'Simple stream query - %s',
testWrapper(async (conn, driver, engine) => {
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
const results = await executeStream(driver, conn, 'SELECT id FROM t1 ORDER BY id');
for (const sql of initSql) {
await runCommandOnDriver(conn, driver, dmp => dmp.put(sql));
}
const results = await executeStream(driver, conn, 'SELECT ~id FROM ~t1 ORDER BY ~id');
expect(results.length).toEqual(1);
const res = results[0];
@@ -104,11 +110,14 @@ describe('Query', () => {
test.each(engines.map(engine => [engine.label, engine]))(
'More queries - %s',
testWrapper(async (conn, driver, engine) => {
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
for (const sql of initSql) {
await runCommandOnDriver(conn, driver, dmp => dmp.put(sql));
}
const results = await executeStream(
driver,
conn,
'SELECT id FROM t1 ORDER BY id; SELECT id FROM t1 ORDER BY id DESC'
'SELECT ~id FROM ~t1 ORDER BY ~id; SELECT ~id FROM ~t1 ORDER BY ~id DESC'
);
expect(results.length).toEqual(2);
@@ -128,7 +137,7 @@ describe('Query', () => {
const results = await executeStream(
driver,
conn,
'CREATE TABLE t1 (id int primary key); INSERT INTO t1 (id) VALUES (1); INSERT INTO t1 (id) VALUES (2); SELECT id FROM t1 ORDER BY id; '
'CREATE TABLE ~t1 (~id int primary key); INSERT INTO ~t1 (~id) VALUES (1); INSERT INTO ~t1 (~id) VALUES (2); SELECT ~id FROM ~t1 ORDER BY ~id; '
);
expect(results.length).toEqual(1);
@@ -144,7 +153,7 @@ describe('Query', () => {
const results = await executeStream(
driver,
conn,
'CREATE TABLE t1 (id int); INSERT INTO t1 (id) VALUES (1); INSERT INTO t1 (id) VALUES (2) '
'CREATE TABLE ~t1 (~id int); INSERT INTO ~t1 (~id) VALUES (1); INSERT INTO ~t1 (~id) VALUES (2) '
);
expect(results.length).toEqual(0);
})
@@ -153,16 +162,57 @@ describe('Query', () => {
test.each(engines.filter(x => !x.skipDataModifications).map(engine => [engine.label, engine]))(
'Save data query - %s',
testWrapper(async (conn, driver, engine) => {
for (const sql of initSql) await driver.query(conn, sql, { discardResult: true });
for (const sql of initSql) {
await runCommandOnDriver(conn, driver, dmp => dmp.put(sql));
}
await driver.script(
conn,
'INSERT INTO t1 (id) VALUES (3);INSERT INTO t1 (id) VALUES (4);UPDATE t1 SET id=10 WHERE id=1;DELETE FROM t1 WHERE id=2;',
formatQueryWithoutParams(
driver,
'INSERT INTO ~t1 (~id) VALUES (3);INSERT INTO ~t1 (~id) VALUES (4);UPDATE ~t1 SET ~id=10 WHERE ~id=1;DELETE FROM ~t1 WHERE ~id=2;'
),
{ discardResult: true }
);
const res = await driver.query(conn, 'SELECT COUNT(*) AS cnt FROM t1');
const res = await runQueryOnDriver(conn, driver, dmp => dmp.put('SELECT COUNT(*) AS ~cnt FROM ~t1'));
// console.log(res);
expect(res.rows[0].cnt == 3).toBeTruthy();
})
);
test.each(engines.filter(x => !x.skipDataDuplicator).map(engine => [engine.label, engine]))(
'Select scope identity - %s',
testWrapper(async (conn, driver, engine) => {
await runCommandOnDriver(conn, driver, dmp =>
dmp.createTable({
pureName: 't1',
columns: [
{ columnName: 'id', dataType: 'int', notNull: true, autoIncrement: true },
{ columnName: 'val', dataType: 'varchar(50)' },
],
primaryKey: {
columns: [{ columnName: 'id' }],
},
})
);
const structure = await driver.analyseFull(conn);
const table = structure.tables.find(x => x.pureName == 't1');
let res;
if (driver.dialect.requireStandaloneSelectForScopeIdentity) {
await runCommandOnDriver(conn, driver, dmp => dmp.put("INSERT INTO ~t1 (~val) VALUES ('aaa')"));
res = await runQueryOnDriver(conn, driver, dmp => dmp.selectScopeIdentity(table));
} else {
res = await runQueryOnDriver(conn, driver, dmp => {
dmp.putCmd("INSERT INTO ~t1 (~val) VALUES ('aaa')");
dmp.selectScopeIdentity(table);
});
}
const row = res.rows[0];
const keys = Object.keys(row);
expect(keys.length).toEqual(1);
expect(row[keys[0]] == 1).toBeTruthy();
})
);
});
@@ -76,7 +76,7 @@ describe('Schema tests', () => {
});
describe('Base analyser test', () => {
test.each(engines.map(engine => [engine.label, engine]))(
test.each(engines.filter(x => !x.skipIncrementalAnalysis).map(engine => [engine.label, engine]))(
'Structure without change - %s',
testWrapper(async (conn, driver, engine) => {
await baseStructure(conn, driver);
@@ -1,12 +1,13 @@
const { runCommandOnDriver } = require('dbgate-tools');
const engines = require('../engines');
const { testWrapper } = require('../tools');
const t1Sql = 'CREATE TABLE t1 (id int not null primary key, val1 varchar(50))';
const ix1Sql = 'CREATE index ix1 ON t1(val1, id)';
const t1Sql = 'CREATE TABLE ~t1 (~id int not null primary key, ~val1 varchar(50))';
const ix1Sql = 'CREATE index ~ix1 ON ~t1(~val1, ~id)';
const t2Sql = engine =>
`CREATE TABLE t2 (id int not null primary key, val2 varchar(50) ${engine.skipUnique ? '' : 'unique'})`;
const t3Sql = 'CREATE TABLE t3 (id int not null primary key, valfk int, foreign key (valfk) references t2(id))';
const t4Sql = 'CREATE TABLE t4 (id int not null primary key, valdef int not null default 12)';
`CREATE TABLE ~t2 (~id int not null primary key, ~val2 varchar(50) ${engine.skipUnique ? '' : 'unique'})`;
const t3Sql = 'CREATE TABLE ~t3 (~id int not null primary key, ~valfk int, foreign key (~valfk) references ~t2(~id))';
const t4Sql = 'CREATE TABLE ~t4 (~id int not null primary key, ~valdef int default 12 not null)';
// const fkSql = 'ALTER TABLE t3 ADD FOREIGN KEY (valfk) REFERENCES t2(id)'
const txMatch = (engine, tname, vcolname, nextcol, defaultValue) =>
@@ -15,7 +16,7 @@ const txMatch = (engine, tname, vcolname, nextcol, defaultValue) =>
columns: [
expect.objectContaining({
columnName: 'id',
dataType: expect.stringMatching(/int.*/i),
dataType: expect.stringMatching(/int.*|number/i),
...(engine.skipNullability ? {} : { notNull: true }),
}),
expect.objectContaining({
@@ -59,9 +60,10 @@ describe('Table analyse', () => {
test.each(engines.map(engine => [engine.label, engine]))(
'Table structure - full analysis - %s',
testWrapper(async (conn, driver, engine) => {
await driver.query(conn, t1Sql);
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
const structure = await driver.analyseFull(conn);
console.log(JSON.stringify(structure, null, 2));
expect(structure.tables.length).toEqual(1);
expect(structure.tables[0]).toEqual(t1Match(engine));
@@ -71,13 +73,13 @@ describe('Table analyse', () => {
test.each(engines.map(engine => [engine.label, engine]))(
'Table add - incremental analysis - %s',
testWrapper(async (conn, driver, engine) => {
await driver.query(conn, t2Sql(engine));
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
const structure1 = await driver.analyseFull(conn);
expect(structure1.tables.length).toEqual(1);
expect(structure1.tables[0]).toEqual(t2Match(engine));
await driver.query(conn, t1Sql);
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
const structure2 = await driver.analyseIncremental(conn, structure1);
expect(structure2.tables.length).toEqual(2);
@@ -89,14 +91,14 @@ describe('Table analyse', () => {
test.each(engines.map(engine => [engine.label, engine]))(
'Table remove - incremental analysis - %s',
testWrapper(async (conn, driver, engine) => {
await driver.query(conn, t1Sql);
await driver.query(conn, t2Sql(engine));
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
const structure1 = await driver.analyseFull(conn);
expect(structure1.tables.length).toEqual(2);
expect(structure1.tables.find(x => x.pureName == 't1')).toEqual(t1Match(engine));
expect(structure1.tables.find(x => x.pureName == 't2')).toEqual(t2Match(engine));
await driver.query(conn, 'DROP TABLE t2');
await runCommandOnDriver(conn, driver, dmp => dmp.put('DROP TABLE ~t2'));
const structure2 = await driver.analyseIncremental(conn, structure1);
expect(structure2.tables.length).toEqual(1);
@@ -107,15 +109,14 @@ describe('Table analyse', () => {
test.each(engines.map(engine => [engine.label, engine]))(
'Table change - incremental analysis - %s',
testWrapper(async (conn, driver, engine) => {
await driver.query(conn, t1Sql);
await driver.query(conn, t2Sql(engine));
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
const structure1 = await driver.analyseFull(conn);
if (engine.dbSnapshotBySeconds) await new Promise(resolve => setTimeout(resolve, 1100));
await driver.query(
conn,
`ALTER TABLE t2 ADD ${engine.alterTableAddColumnSyntax ? 'COLUMN' : ''} nextcol varchar(50)`
await runCommandOnDriver(conn, driver, dmp =>
dmp.put(`ALTER TABLE ~t2 ADD ${engine.alterTableAddColumnSyntax ? 'COLUMN' : ''} ~nextcol varchar(50)`)
);
const structure2 = await driver.analyseIncremental(conn, structure1);
@@ -130,8 +131,8 @@ describe('Table analyse', () => {
test.each(engines.filter(x => !x.skipIndexes).map(engine => [engine.label, engine]))(
'Index - full analysis - %s',
testWrapper(async (conn, driver, engine) => {
await driver.query(conn, t1Sql);
await driver.query(conn, ix1Sql);
await runCommandOnDriver(conn, driver, dmp => dmp.put(t1Sql));
await runCommandOnDriver(conn, driver, dmp => dmp.put(ix1Sql));
const structure = await driver.analyseFull(conn);
const t1 = structure.tables.find(x => x.pureName == 't1');
@@ -145,7 +146,7 @@ describe('Table analyse', () => {
test.each(engines.filter(x => !x.skipUnique).map(engine => [engine.label, engine]))(
'Unique - full analysis - %s',
testWrapper(async (conn, driver, engine) => {
await driver.query(conn, t2Sql(engine));
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
const structure = await driver.analyseFull(conn);
const t2 = structure.tables.find(x => x.pureName == 't2');
@@ -159,8 +160,8 @@ describe('Table analyse', () => {
test.each(engines.filter(x => !x.skipReferences).map(engine => [engine.label, engine]))(
'Foreign key - full analysis - %s',
testWrapper(async (conn, driver, engine) => {
await driver.query(conn, t2Sql(engine));
await driver.query(conn, t3Sql);
await runCommandOnDriver(conn, driver, dmp => dmp.put(t2Sql(engine)));
await runCommandOnDriver(conn, driver, dmp => dmp.put(t3Sql));
// await driver.query(conn, fkSql);
const structure = await driver.analyseFull(conn);
@@ -179,7 +180,7 @@ describe('Table analyse', () => {
test.each(engines.map(engine => [engine.label, engine]))(
'Table structure - default value - %s',
testWrapper(async (conn, driver, engine) => {
await driver.query(conn, t4Sql);
await runCommandOnDriver(conn, driver, dmp => dmp.put(t4Sql));
const structure = await driver.analyseFull(conn);
@@ -2,7 +2,7 @@ const _ = require('lodash');
const fp = require('lodash/fp');
const engines = require('../engines');
const { testWrapper } = require('../tools');
const { extendDatabaseInfo } = require('dbgate-tools');
const { extendDatabaseInfo, runCommandOnDriver } = require('dbgate-tools');
function createExpector(value) {
return _.cloneDeepWith(value, x => {
@@ -25,7 +25,7 @@ function checkTableStructure2(t1, t2) {
}
async function testTableCreate(conn, driver, table) {
await driver.query(conn, `create table t0 (id int not null primary key)`);
await runCommandOnDriver(conn, driver, dmp => dmp.put('create table ~t0 (~id int not null primary key)'));
const dmp = driver.createDumper();
const table1 = {
+2 -2
View File
@@ -71,8 +71,8 @@ services:
# restart: on-failure
oracle:
image: container-registry.oracle.com/database/express:21.3.0-xe
image: gvenzl/oracle-xe:21-slim
environment:
ORACLE_PWD: Pwd2020Db
ORACLE_PASSWORD: Pwd2020Db
ports:
- 15006:1521
File diff suppressed because it is too large Load Diff
+2 -5
View File
@@ -11,14 +11,11 @@
"scripts": {
"wait:local": "cross-env DEVMODE=1 LOCALTEST=1 node wait.js",
"wait:ci": "cross-env DEVMODE=1 CITEST=1 node wait.js",
"test:local": "cross-env DEVMODE=1 LOCALTEST=1 jest",
"test:local": "cross-env DEVMODE=1 LOCALTEST=1 jest --testTimeout=5000",
"test:local:path": "cross-env DEVMODE=1 LOCALTEST=1 jest --runTestsByPath __tests__/data-duplicator.spec.js",
"test:ci": "cross-env DEVMODE=1 CITEST=1 jest --runInBand --json --outputFile=result.json --testLocationInResults --detectOpenHandles --forceExit",
"test:ci": "cross-env DEVMODE=1 CITEST=1 jest --runInBand --json --outputFile=result.json --testLocationInResults --detectOpenHandles --forceExit --testTimeout=10000",
"run:local": "docker-compose down && docker-compose up -d && yarn wait:local && yarn test:local"
},
"jest": {
"testTimeout": 5000
},
"devDependencies": {
"cross-env": "^7.0.3",
"jest": "^27.0.1",
+6 -4
View File
@@ -1,10 +1,12 @@
const requireEngineDriver = require('dbgate-api/src/utility/requireEngineDriver');
const crypto = require('crypto');
function randomDbName() {
function randomDbName(dialect) {
const generatedKey = crypto.randomBytes(6);
const newKey = generatedKey.toString('hex');
return `db${newKey}`;
const res = `db${newKey}`;
if (dialect.upperCaseAllDbObjectNames) return res.toUpperCase();
return res;
}
function extractConnection(engine) {
@@ -75,7 +77,7 @@ const testWrapper =
async (label, ...other) => {
const engine = other[other.length - 1];
const driver = requireEngineDriver(engine.connection);
const conn = await connect(engine, randomDbName());
const conn = await connect(engine, randomDbName(driver.dialect));
try {
await body(conn, driver, ...other);
} finally {
@@ -88,7 +90,7 @@ const testWrapperPrepareOnly =
async (label, ...other) => {
const engine = other[other.length - 1];
const driver = requireEngineDriver(engine.connection);
const conn = await prepareConnection(engine, randomDbName());
const conn = await prepareConnection(engine, randomDbName(driver.dialect));
await body(conn, driver, ...other);
};
+3 -3
View File
@@ -1,6 +1,6 @@
{
"private": true,
"version": "6.0.1-packer-beta.2",
"version": "6.1.3-beta.1",
"name": "dbgate-all",
"workspaces": [
"packages/*",
@@ -47,7 +47,6 @@
"setCurrentVersion": "node setCurrentVersion",
"printSecrets": "node printSecrets",
"generatePadFile": "node generatePadFile",
"adjustPackageJson": "node adjustPackageJson",
"fillPackagedPlugins": "node fillPackagedPlugins",
"resetPackagedPlugins": "node resetPackagedPlugins",
"prettier": "prettier --write packages/api/src && prettier --write packages/datalib/src && prettier --write packages/filterparser/src && prettier --write packages/sqltree/src && prettier --write packages/tools/src && prettier --write packages/types && prettier --write packages/web/src && prettier --write app/src",
@@ -63,7 +62,8 @@
"ts:web": "yarn workspace dbgate-web ts",
"ts": "yarn ts:api && yarn ts:web",
"postinstall": "yarn resetPackagedPlugins && yarn build:lib && patch-package && yarn build:plugins:frontend",
"dbgate-serve": "node packages/dbgate/bin/dbgate-serve.js"
"dbgate-serve": "node packages/dbgate/bin/dbgate-serve.js",
"workflows": "node common/processWorkflows.js"
},
"dependencies": {
"concurrently": "^5.1.0",
+2 -2
View File
@@ -30,7 +30,7 @@
"cors": "^2.8.5",
"cross-env": "^6.0.3",
"dbgate-datalib": "^6.0.0-alpha.1",
"dbgate-query-splitter": "^4.11.2",
"dbgate-query-splitter": "^4.11.3",
"dbgate-sqltree": "^6.0.0-alpha.1",
"dbgate-tools": "^6.0.0-alpha.1",
"debug": "^4.3.4",
@@ -60,7 +60,7 @@
"rimraf": "^3.0.0",
"semver": "^7.6.3",
"simple-encryptor": "^4.0.0",
"ssh2": "^1.11.0",
"ssh2": "^1.16.0",
"stream-json": "^1.8.0",
"tar": "^6.0.5"
},
+10 -3
View File
@@ -230,7 +230,7 @@ class LoginsProvider extends AuthProviderBase {
),
};
}
return { error: 'Invalid credentials' };
}
@@ -271,11 +271,10 @@ function hasEnvLogins() {
return false;
}
function detectEnvAuthProvider() {
function detectEnvAuthProviderCore() {
if (process.env.AUTH_PROVIDER) {
return process.env.AUTH_PROVIDER;
}
if (process.env.STORAGE_DATABASE) {
return 'denyall';
}
@@ -291,6 +290,14 @@ function detectEnvAuthProvider() {
return 'none';
}
function detectEnvAuthProvider() {
const authProvider = detectEnvAuthProviderCore();
if (process.env.BASIC_AUTH && authProvider != 'logins' && authProvider != 'ad') {
throw new Error(`BASIC_AUTH is not supported with ${authProvider} auth provider`);
}
return authProvider;
}
function createEnvAuthProvider() {
const authProvider = detectEnvAuthProvider();
switch (authProvider) {
+7 -2
View File
@@ -9,6 +9,7 @@ const _ = require('lodash');
const AsyncLock = require('async-lock');
const jwt = require('jsonwebtoken');
const processArgs = require('../utility/processArgs');
const currentVersion = require('../currentVersion');
const platformInfo = require('../utility/platformInfo');
const connections = require('../controllers/connections');
@@ -83,7 +84,8 @@ module.exports = {
isElectron: platformInfo.isElectron,
isLicenseValid,
isLicenseExpired: checkedLicense?.isExpired,
trialDaysLeft: checkedLicense?.licenseTypeObj?.isTrial && !checkedLicense?.isExpired ? checkedLicense?.daysLeft : null,
trialDaysLeft:
checkedLicense?.licenseTypeObj?.isTrial && !checkedLicense?.isExpired ? checkedLicense?.daysLeft : null,
checkedLicense,
configurationError,
logoutUrl,
@@ -101,7 +103,10 @@ module.exports = {
adminPasswordState: adminConfig?.adminPasswordState,
storageDatabase: process.env.STORAGE_DATABASE,
logsFilePath: getLogsFilePath(),
connectionsFilePath: path.join(datadir(), 'connections.jsonl'),
connectionsFilePath: path.join(
datadir(),
processArgs.runE2eTests ? 'connections-e2etests.jsonl' : 'connections.jsonl'
),
...currentVersion,
};
+6 -4
View File
@@ -199,7 +199,9 @@ module.exports = {
const dir = datadir();
if (!portalConnections) {
// @ts-ignore
this.datastore = new JsonLinesDatabase(path.join(dir, 'connections.jsonl'));
this.datastore = new JsonLinesDatabase(
path.join(dir, processArgs.runE2eTests ? 'connections-e2etests.jsonl' : 'connections.jsonl')
);
}
await this.checkUnsavedConnectionsLimit();
},
@@ -220,7 +222,7 @@ module.exports = {
},
test_meta: true,
test(connection) {
test({ connection, requestDbList = false }) {
const subprocess = fork(
global['API_PACKAGE'] || process.argv[1],
[
@@ -235,7 +237,7 @@ module.exports = {
}
);
pipeForkLogs(subprocess);
subprocess.send(connection);
subprocess.send({ ...connection, requestDbList });
return new Promise(resolve => {
subprocess.on('message', resp => {
if (handleProcessCommunication(resp, subprocess)) return;
@@ -265,7 +267,7 @@ module.exports = {
}
if (test) {
const testRes = await this.test(res);
const testRes = await this.test({ connection: res });
if (testRes.msgtype == 'connected') {
volatileConnections[res._id] = res;
return {
+8 -3
View File
@@ -49,7 +49,8 @@ logger.info('Finished job script');
dbgateApi.runScript(run);
`;
const loaderScriptTemplate = (functionName, props, runid) => `
const loaderScriptTemplate = (prefix, functionName, props, runid) => `
${prefix}
const dbgateApi = require(process.env.DBGATE_API);
dbgateApi.initializeApiEnvironment();
${requirePluginsTemplate(extractShellApiPlugins(functionName, props))}
@@ -149,7 +150,7 @@ module.exports = {
byline(subprocess.stdout).on('data', pipeDispatcher('info'));
byline(subprocess.stderr).on('data', pipeDispatcher('error'));
subprocess.on('exit', code => {
this.rejectRequest(runid, { message: 'No data retured, maybe input data source is too big' });
this.rejectRequest(runid, { message: 'No data returned, maybe input data source is too big' });
logger.info({ code, pid: subprocess.pid }, 'Exited process');
socket.emit(`runner-done-${runid}`, code);
});
@@ -224,10 +225,14 @@ module.exports = {
loadReader_meta: true,
async loadReader({ functionName, props }) {
const prefix = extractShellApiPlugins(functionName)
.map(packageName => `// @require ${packageName}\n`)
.join('');
const promise = new Promise((resolve, reject) => {
const runid = crypto.randomUUID();
this.requests[runid] = [resolve, reject];
this.startCore(runid, loaderScriptTemplate(functionName, props, runid));
this.startCore(runid, loaderScriptTemplate(prefix, functionName, props, runid));
});
return promise;
},
+7 -1
View File
@@ -17,12 +17,18 @@ Platform: ${process.platform}
function start() {
childProcessChecker();
process.on('message', async connection => {
// @ts-ignore
const { requestDbList } = connection;
if (handleProcessCommunication(connection)) return;
try {
const driver = requireEngineDriver(connection);
const dbhan = await connectUtility(driver, connection, 'app');
const res = await driver.getVersion(dbhan);
process.send({ msgtype: 'connected', ...res });
let databases = undefined;
if (requestDbList) {
databases = await driver.listDatabases(dbhan);
}
process.send({ msgtype: 'connected', ...res, databases });
await driver.close(dbhan);
} catch (e) {
console.error(e);
@@ -166,7 +166,9 @@ function start() {
if (time - lastPing > 40 * 1000) {
logger.info('Server connection not alive, exiting');
const driver = requireEngineDriver(storedConnection);
await driver.close(dbhan);
if (dbhan) {
await driver.close(dbhan);
}
process.exit(0);
}
}, 10 * 1000);
+3 -2
View File
@@ -7,7 +7,7 @@ const { getLogger, extractErrorLogData, extractErrorMessage } = require('dbgate-
const logger = getLogger('sshProcess');
async function getSshConnection(connection) {
async function getSshConnection(connection, tunnelConfig) {
const sshConfig = {
endHost: connection.sshHost || '',
endPort: connection.sshPort || 22,
@@ -23,6 +23,7 @@ async function getSshConnection(connection) {
: undefined,
skipAutoPrivateKey: true,
noReadline: true,
bindHost: tunnelConfig.fromHost,
};
const sshConn = new SSHConnection(sshConfig);
@@ -31,7 +32,7 @@ async function getSshConnection(connection) {
async function handleStart({ connection, tunnelConfig }) {
try {
const sshConn = await getSshConnection(connection);
const sshConn = await getSshConnection(connection, tunnelConfig);
await sshConn.forward(tunnelConfig);
process.send({
+5 -2
View File
@@ -8,12 +8,15 @@ const logger = getLogger('tableReader');
* @param {object} options
* @param {connectionType} options.connection - connection object
* @param {object} options.systemConnection - system connection (result of driver.connect). If not provided, new connection will be created
* @param {object} options.driver - driver object. If not provided, it will be loaded from connection
* @param {string} options.pureName - table name
* @param {string} options.schemaName - schema name
* @returns {Promise<readerType>} - reader object
*/
async function tableReader({ connection, systemConnection, pureName, schemaName }) {
const driver = requireEngineDriver(connection);
async function tableReader({ connection, systemConnection, pureName, schemaName, driver }) {
if (!driver) {
driver = requireEngineDriver(connection);
}
const dbhan = systemConnection || (await connectUtility(driver, connection, 'read'));
logger.info(`Connected.`);
+4 -4
View File
@@ -130,7 +130,7 @@ class SSHConnection {
const connectionToBastion = await this.connect(bastionHost);
return new Promise((resolve, reject) => {
connectionToBastion.forwardOut(
'127.0.0.1',
this.options.bindHost,
22,
this.options.endHost,
this.options.endPort || 22,
@@ -228,9 +228,9 @@ class SSHConnection {
options.toPort
);
connection.forwardOut(
'localhost',
this.options.bindHost,
options.fromPort,
options.toHost || 'localhost',
options.toHost || this.options.bindHost,
options.toPort,
(error, stream) => {
if (error) {
@@ -241,7 +241,7 @@ class SSHConnection {
}
);
})
.listen(options.fromPort, 'localhost', () => {
.listen(options.fromPort, this.options.bindHost, () => {
return resolve();
});
});
+1 -1
View File
@@ -63,7 +63,7 @@ async function connectUtility(driver, storedConnection, connectionMode, addition
throw new Error(tunnel.message);
}
connection.server = 'localhost';
connection.server = tunnel.localHost;
connection.port = tunnel.localPort;
}
+2 -2
View File
@@ -97,7 +97,7 @@ function packagedPluginsDir() {
// }
}
if (processArgs.runE2eTests) {
return path.resolve('packer/build/plugins');
return path.resolve('packer/build/plugins');
}
return null;
}
@@ -113,7 +113,7 @@ function getPluginBackendPath(packageName) {
return path.join(packagedPluginsDir(), packageName, 'dist', 'backend.js');
}
const res = path.join(pluginsdir(), packageName, 'dist', 'backend.js')
const res = path.join(pluginsdir(), packageName, 'dist', 'backend.js');
if (fs.existsSync(res)) {
return res;
}
+7 -3
View File
@@ -1,3 +1,5 @@
const path = require('path');
function getNamedArg(name) {
const argIndex = process.argv.indexOf(name);
if (argIndex > 0) {
@@ -20,13 +22,15 @@ function getPassArgs() {
const res = [];
if (global['PLUGINS_DIR']) {
res.push('--plugins-dir', global['PLUGINS_DIR']);
} else if (runE2eTests) {
res.push('--plugins-dir', path.resolve('packer/build/plugins'));
}
if (listenApiChild) {
res.push('listen-api-child');
}
if (runE2eTests) {
res.push('--run-e2e-tests');
}
// if (runE2eTests) {
// res.push('--run-e2e-tests');
// }
return res;
}
+12
View File
@@ -43,33 +43,44 @@ function callForwardProcess(connection, tunnelConfig, tunnelCacheKey) {
logger.error(extractErrorLogData(err), 'Error connecting SSH');
}
return new Promise((resolve, reject) => {
let promiseHandled = false;
subprocess.on('message', resp => {
// @ts-ignore
const { msgtype, errorMessage } = resp;
if (msgtype == 'connected') {
resolve(subprocess);
promiseHandled = true;
}
if (msgtype == 'error') {
reject(new Error(errorMessage));
promiseHandled = true;
}
});
subprocess.on('exit', code => {
logger.info('SSH forward process exited');
delete sshTunnelCache[tunnelCacheKey];
if (!promiseHandled) {
reject(new Error('SSH forward process exited, try to change "Local host address for SSH connections" in Settings/Connections'));
}
});
});
}
async function getSshTunnel(connection) {
const config = require('../controllers/config');
const tunnelCacheKey = stableStringify(_.pick(connection, TUNNEL_FIELDS));
const globalSettings = await config.getSettings();
return await lock.acquire(tunnelCacheKey, async () => {
if (sshTunnelCache[tunnelCacheKey]) return sshTunnelCache[tunnelCacheKey];
const localPort = await portfinder.getPortPromise({ port: 10000, stopPort: 60000 });
const localHost = globalSettings?.['connection.sshBindHost'] || '127.0.0.1';
// workaround for `getPortPromise` not releasing the port quickly enough
await new Promise(resolve => setTimeout(resolve, 500));
const tunnelConfig = {
fromPort: localPort,
fromHost: localHost,
toPort: connection.port,
toHost: connection.server,
};
@@ -87,6 +98,7 @@ async function getSshTunnel(connection) {
sshTunnelCache[tunnelCacheKey] = {
state: 'ok',
localPort,
localHost,
subprocess,
};
return sshTunnelCache[tunnelCacheKey];
+5 -1
View File
@@ -66,7 +66,7 @@ class DuplicatorItemHolder {
this.autoColumn = this.table.columns.find(x => x.autoIncrement)?.columnName;
if (
this.table.primaryKey?.columns?.length != 1 ||
this.table.primaryKey?.columns?.[0].columnName != this.autoColumn
this.table.primaryKey?.columns?.[0]?.columnName != this.autoColumn
) {
this.autoColumn = null;
}
@@ -140,6 +140,9 @@ class DuplicatorItemHolder {
weakref.foreignKey.columns[0].columnName
);
});
if (this.duplicator.driver.dialect.requireFromDual) {
dmp.put(' ^from ^dual');
}
});
const qrow = qres.rows[0];
return this.weakReferences.filter(x => qrow[x.columnName] == 0).map(x => x.columnName);
@@ -194,6 +197,7 @@ class DuplicatorItemHolder {
res = await runQueryOnDriver(pool, driver, dmp => dmp.selectScopeIdentity(this.table));
}
// console.log('IDRES', JSON.stringify(res));
// console.log('*********** ENTRIES OF', res?.rows?.[0]);
const resId = Object.entries(res?.rows?.[0])?.[0]?.[1];
if (resId != null) {
this.idMap[chunk[this.autoColumn]] = resId;
@@ -1492,6 +1492,7 @@ export const chinookDbInfo: DatabaseInfo = {
collections: [],
matviews: [],
triggers: [],
schedulerEvents: [],
};
// const ARTIST_TABLE: TableInfo = {
+1 -1
View File
@@ -41,7 +41,7 @@ STORAGE_DATABASE=dbname
STORAGE_ENGINE=mysql@dbgate-plugin-mysql
```
You could find more about environment variable configuration on [DbGate docs](https://dbgate.org/docs/env-variables.html) page.
You could find more about environment variable configuration on [DbGate docs](https://dbgate.org/docs/env-variables/) page.
After installing, you can run dbgate with command:
```sh
+1 -1
View File
@@ -31,7 +31,7 @@
"typescript": "^4.4.3"
},
"dependencies": {
"dbgate-query-splitter": "^4.11.2",
"dbgate-query-splitter": "^4.11.3",
"dbgate-sqltree": "^6.0.0-alpha.1",
"debug": "^4.3.4",
"json-stable-stringify": "^1.0.1",
+16 -3
View File
@@ -10,7 +10,16 @@ import { extractErrorLogData } from './stringTools';
const logger = getLogger('dbAnalyser');
const STRUCTURE_FIELDS = ['tables', 'collections', 'views', 'matviews', 'functions', 'procedures', 'triggers'];
const STRUCTURE_FIELDS = [
'tables',
'collections',
'views',
'matviews',
'functions',
'procedures',
'triggers',
'schedulerEvents',
];
const fp_pick = arg => array => _pick(array, arg);
@@ -70,7 +79,9 @@ export class DatabaseAnalyser {
}
async fullAnalysis() {
logger.debug(`Performing full analysis, DB=${dbNameLogCategory(this.dbhan.database)}, engine=${this.driver.engine}`);
logger.debug(
`Performing full analysis, DB=${dbNameLogCategory(this.dbhan.database)}, engine=${this.driver.engine}`
);
const res = this.addEngineField(await this._runAnalysis());
// console.log('FULL ANALYSIS', res);
return res;
@@ -255,6 +266,7 @@ export class DatabaseAnalyser {
...this.getDeletedObjectsForField(snapshot, 'procedures'),
...this.getDeletedObjectsForField(snapshot, 'functions'),
...this.getDeletedObjectsForField(snapshot, 'triggers'),
...this.getDeletedObjectsForField(snapshot, 'schedulerEvents'),
];
}
@@ -355,11 +367,12 @@ export class DatabaseAnalyser {
functions: [],
procedures: [],
triggers: [],
schedulerEvents: [],
};
}
static byTableFilter(table) {
return x => x.pureName == table.pureName && x.schemaName == x.schemaName;
return x => x.pureName == table.pureName && x.schemaName == table.schemaName;
}
static extractPrimaryKeys(table, pkColumns) {
+3 -4
View File
@@ -708,6 +708,8 @@ export class SqlDumper implements AlterProcessor {
return 'TRIGGER';
case 'matviews':
return 'MATERIALIZED VIEW';
case 'schedulerEvents':
return 'EVENT';
}
}
@@ -785,10 +787,7 @@ export class SqlDumper implements AlterProcessor {
}
callableTemplate(func: CallableObjectInfo) {
this.put(
'^call %f(&>&n',
func,
);
this.put('^call %f(&>&n', func);
this.putCollection(',&n', func.parameters || [], param => {
this.putRaw(param.parameterMode == 'IN' ? ':' + param.parameterName : param.parameterName);
+10 -1
View File
@@ -155,6 +155,7 @@ export function generateDbPairingId(db: DatabaseInfo): DatabaseInfo {
procedures: db.procedures?.map(generateObjectPairingId),
functions: db.functions?.map(generateObjectPairingId),
triggers: db.triggers?.map(generateObjectPairingId),
schedulerEvents: db.schedulerEvents?.map(generateObjectPairingId),
matviews: db.matviews?.map(generateObjectPairingId),
};
}
@@ -715,7 +716,15 @@ export function createAlterDatabasePlan(
): AlterPlan {
const plan = new AlterPlan(wholeOldDb, wholeNewDb, driver.dialect, opts);
for (const objectTypeField of ['tables', 'views', 'procedures', 'matviews', 'functions']) {
for (const objectTypeField of [
'tables',
'views',
'procedures',
'matviews',
'functions',
'triggers',
'schedulerEvents',
]) {
for (const oldobj of oldDb[objectTypeField] || []) {
const newobj = (newDb[objectTypeField] || []).find(x => x.pairingId == oldobj.pairingId);
if (objectTypeField == 'tables') {
+22 -3
View File
@@ -1,4 +1,5 @@
import _compact from 'lodash/compact';
import _isString from 'lodash/isString';
import { SqlDumper } from './SqlDumper';
import { splitQuery } from 'dbgate-query-splitter';
import { dumpSqlSelect } from 'dbgate-sqltree';
@@ -26,9 +27,17 @@ const dialect = {
defaultSchemaName: null,
};
export async function runCommandOnDriver(pool, driver: EngineDriver, cmd: (dmp: SqlDumper) => void): Promise<void> {
export async function runCommandOnDriver(
pool,
driver: EngineDriver,
cmd: (dmp: SqlDumper) => void | string
): Promise<void> {
const dmp = driver.createDumper();
cmd(dmp as any);
if (_isString(cmd)) {
dmp.put(cmd);
} else {
cmd(dmp as any);
}
// console.log('CMD:', dmp.s);
await driver.query(pool, dmp.s, { discardResult: true });
}
@@ -39,11 +48,21 @@ export async function runQueryOnDriver(
cmd: (dmp: SqlDumper) => void
): Promise<QueryResult> {
const dmp = driver.createDumper();
cmd(dmp as any);
if (_isString(cmd)) {
dmp.put(cmd);
} else {
cmd(dmp as any);
}
// console.log('QUERY:', dmp.s);
return await driver.query(pool, dmp.s);
}
export function formatQueryWithoutParams(driver: EngineDriver, sql: string) {
const dmp = driver.createDumper();
dmp.put(sql);
return dmp.s;
}
export const driverBase = {
analyserClass: null,
dumperClass: SqlDumper,
+131 -18
View File
@@ -2,9 +2,9 @@ import _compact from 'lodash/compact';
import _isString from 'lodash/isString';
import _startCase from 'lodash/startCase';
export interface FilterNameDefinition {
childName: string;
}
// export interface FilterNameDefinition {
// childName: string;
// }
function camelMatch(filter: string, text: string): boolean {
if (!text) return false;
@@ -20,7 +20,7 @@ function camelMatch(filter: string, text: string): boolean {
}
}
export function filterName(filter: string, ...names: (string | FilterNameDefinition)[]) {
export function filterName(filter: string, ...names: string[]) {
if (!filter) return true;
// const camelVariants = [name.replace(/[^A-Z]/g, '')]
@@ -28,22 +28,135 @@ export function filterName(filter: string, ...names: (string | FilterNameDefinit
const namesCompacted = _compact(names);
// @ts-ignore
const namesOwn: string[] = namesCompacted.filter(x => _isString(x));
// @ts-ignore
const namesChild: string[] = namesCompacted.filter(x => x.childName).map(x => x.childName);
for (const token of tokens) {
// const tokenUpper = token.toUpperCase();
if (token.startsWith('#')) {
// const tokenUpperSub = tokenUpper.substring(1);
const found = namesChild.find(name => camelMatch(token.substring(1), name));
if (!found) return false;
} else {
const found = namesOwn.find(name => camelMatch(token, name));
if (!found) return false;
}
const found = namesCompacted.find(name => camelMatch(token, name));
if (!found) return false;
}
return true;
}
export function filterNameCompoud(
filter: string,
namesMain: string[],
namesChild: string[]
): 'main' | 'child' | 'both' | 'none' {
if (!filter) return 'both';
// const camelVariants = [name.replace(/[^A-Z]/g, '')]
const tokens = filter.split(' ').map(x => x.trim());
const namesCompactedMain = _compact(namesMain);
const namesCompactedChild = _compact(namesChild);
let isMainOnly = true;
let isChildOnly = true;
for (const token of tokens) {
const foundMain = namesCompactedMain.find(name => camelMatch(token, name));
const foundChild = namesCompactedChild.find(name => camelMatch(token, name));
if (!foundMain && !foundChild) return 'none';
if (!foundMain) isMainOnly = false;
if (!foundChild) isChildOnly = false;
}
if (isMainOnly && isChildOnly) return 'both';
if (isMainOnly) return 'main';
if (isChildOnly) return 'child';
return 'none';
}
export function tokenizeBySearchFilter(text: string, filter: string): { text: string; isMatch: boolean }[] {
const camelTokens = [];
const stdTokens = [];
for (const token of filter.split(' ').map(x => x.trim())) {
if (token.replace(/[A-Z]/g, '').length == 0) {
camelTokens.push(token);
} else {
stdTokens.push(token.toUpperCase());
}
}
let res = [
{
text,
isMatch: false,
},
];
for (const token of camelTokens) {
const nextres = [];
for (const item of res) {
const indexes = [];
for (const char of token) {
if (indexes.length == 0 && char == item.text[0]?.toUpperCase()) {
// handle first letter of camelcase
indexes.push(0);
} else {
const index = item.text.indexOf(char, indexes.length > 0 ? indexes[indexes.length - 1] + 1 : 0);
if (index < 0) {
indexes.push(-1);
} else {
indexes.push(index);
}
}
}
if (indexes.some(x => x < 0)) {
nextres.push(item);
} else {
let lastIndex = 0;
for (let i = 0; i < indexes.length; i++) {
if (indexes[i] > lastIndex) {
nextres.push({ text: item.text.substring(lastIndex, indexes[i]), isMatch: false });
}
nextres.push({ text: item.text.substring(indexes[i], indexes[i] + 1), isMatch: true });
lastIndex = indexes[i] + 1;
}
nextres.push({ text: item.text.substring(lastIndex), isMatch: false });
}
}
res = nextres;
}
for (const token of stdTokens) {
const nextres = [];
for (const item of res) {
const index = item.text?.toUpperCase().indexOf(token);
if (index < 0) {
nextres.push(item);
} else {
nextres.push({ text: item.text.substring(0, index), isMatch: false });
nextres.push({ text: item.text.substring(index, index + token.length), isMatch: true });
nextres.push({ text: item.text.substring(index + token.length), isMatch: false });
}
}
res = nextres;
}
res = res.filter(x => x.text.length > 0);
if (res.length == 1 && !res[0].isMatch) {
return null;
}
return res;
// const result = [];
// let lastMatch = 0;
// for (const token of tokens) {
// const index = text.indexOf(token, lastMatch);
// if (index < 0) {
// result.push({ token, isMatch: false });
// continue;
// }
// result.push({ token: text.substring(lastMatch, index), isMatch: false });
// result.push({ token: text.substring(index, index + token.length), isMatch: true });
// lastMatch = index + token.length;
// }
// result.push({ token: text.substring(lastMatch), isMatch: false });
// return result;
}
+1
View File
@@ -234,6 +234,7 @@ export function databaseInfoFromYamlModel(filesOrDbInfo: DatabaseModelFile[] | D
if (file.name.endsWith('.trigger.sql')) {
model.triggers.push({
objectId: `triggers:${file.name.slice(0, -'.trigger.sql'.length)}`,
pureName: file.name.slice(0, -'.trigger.sql'.length),
createSql: file.text,
});
+34 -2
View File
@@ -75,7 +75,7 @@ export interface DatabaseObjectInfo extends NamedObjectInfo {
modifyDate?: string;
hashCode?: string;
objectTypeField?: string;
obejctComment?: string;
objectComment?: string;
}
export interface SqlObjectInfo extends DatabaseObjectInfo {
@@ -124,6 +124,7 @@ export interface ParameterInfo extends NamedObjectInfo {
parameterName?: string;
dataType: string;
parameterMode?: ParameterMode;
position?: number;
}
export interface CallableObjectInfo extends SqlObjectInfo {
@@ -136,7 +137,37 @@ export interface FunctionInfo extends CallableObjectInfo {
returnType?: string;
}
export interface TriggerInfo extends SqlObjectInfo {}
export interface TriggerInfo extends SqlObjectInfo {
objectId: string;
functionName?: string;
tableName?: string;
triggerTiming?:
| 'BEFORE'
| 'AFTER'
| 'INSTEAD OF'
| 'BEFORE EACH ROW'
| 'INSTEAD OF'
| 'AFTER EACH ROW'
| 'AFTER STATEMENT'
| 'BEFORE STATEMENT'
| 'AFTER EVENT'
| 'BEFORE EVENT'
| null;
triggerLevel?: 'ROW' | 'STATEMENT';
eventType?: 'INSERT' | 'UPDATE' | 'DELETE' | 'TRUNCATE';
}
export interface SchedulerEventInfo extends SqlObjectInfo {
definer: string;
eventType: 'RECURRING' | 'ONE TIME';
onCompletion: 'PRESERVE' | 'NOT PRESERVE';
status: 'ENABLED' | 'DISABLED';
lastExecuted?: string;
intervalValue: number;
intervalField: string;
starts: string;
executeAt: string;
}
export interface SchemaInfo {
objectId?: string;
@@ -152,6 +183,7 @@ export interface DatabaseInfoObjects {
procedures: ProcedureInfo[];
functions: FunctionInfo[];
triggers: TriggerInfo[];
schedulerEvents: SchedulerEventInfo[];
}
export interface DatabaseInfo extends DatabaseInfoObjects {
+1
View File
@@ -52,6 +52,7 @@ export interface SqlDialect {
dropReferencesWhenDropTable?: boolean;
requireFromDual?: boolean;
userDatabaseNamePrefix?: string; // c## in Oracle
upperCaseAllDbObjectNames?: boolean;
predefinedDataTypes: string[];
+2 -2
View File
@@ -20,12 +20,12 @@
"@rollup/plugin-replace": "^3.0.0",
"@rollup/plugin-typescript": "^8.2.5",
"@tsconfig/svelte": "^1.0.0",
"ace-builds": "^1.4.8",
"ace-builds": "^1.36.5",
"chart.js": "^4.4.2",
"chartjs-adapter-moment": "^1.0.0",
"cross-env": "^7.0.3",
"dbgate-datalib": "^6.0.0-alpha.1",
"dbgate-query-splitter": "^4.11.2",
"dbgate-query-splitter": "^4.11.3",
"dbgate-sqltree": "^6.0.0-alpha.1",
"dbgate-tools": "^6.0.0-alpha.1",
"dbgate-types": "^6.0.0-alpha.1",
@@ -29,8 +29,8 @@
export const extractKey = data => data.fileName;
export const createMatcher =
({ fileName }) =>
filter =>
({ fileName }) =>
filterName(filter, fileName);
const APP_ICONS = {
'config.json': 'img json',
@@ -1,6 +1,6 @@
<script lang="ts" context="module">
export const extractKey = data => data.name;
export const createMatcher = data => filter => filterName(filter, data.name);
export const createMatcher = filter => data => filterName(filter, data.name);
</script>
<script lang="ts">
+26 -4
View File
@@ -5,6 +5,7 @@
import CheckboxField from '../forms/CheckboxField.svelte';
import { copyTextToClipboard } from '../utility/clipboard';
import { showSnackbarSuccess } from '../utility/snackbar';
import TokenizedFilteredText from '../widgets/TokenizedFilteredText.svelte';
const dispatch = createEventDispatcher();
@@ -30,6 +31,10 @@
export let onUnpin = null;
export let showPinnedInsteadOfUnpin = false;
export let indentLevel = 0;
export let disableBoldScroll = false;
export let filter = null;
export let disableHover = false;
export let divProps = {};
$: isChecked =
checkedObjectsStore && $checkedObjectsStore.find(x => module?.extractKey(data) == module?.extractKey(x));
@@ -57,6 +62,13 @@
}
}
function handleMouseDown(e) {
if (e.button == 1) {
e.preventDefault();
e.stopPropagation();
}
}
function setChecked(value) {
if (!value && isChecked) {
checkedObjectsStore.update(x => x.filter(y => module?.extractKey(data) != module?.extractKey(y)));
@@ -69,7 +81,7 @@
// $: console.log(title, indentLevel);
let domDiv;
$: if (isBold && domDiv) {
$: if (isBold && domDiv && !disableBoldScroll) {
domDiv.scrollIntoView({ block: 'nearest', inline: 'nearest' });
}
@@ -82,9 +94,12 @@
class="main"
class:isBold
class:isChoosed
class:disableHover
draggable={true}
on:click={handleClick}
on:mouseup={handleMouseUp}
on:mousedown={handleMouseDown}
on:mousedown
on:dblclick
use:contextMenu={disableContextMenu ? null : menu}
on:dragstart={e => {
@@ -95,6 +110,7 @@
on:dragend
on:drop
bind:this={domDiv}
{...divProps}
>
{#if checkedObjectsStore}
<CheckboxField
@@ -121,7 +137,7 @@
{#if colorMark}
<FontIcon style={`color:${colorMark}`} icon="icon square" />
{/if}
{title}
<TokenizedFilteredText text={title} {filter} />
{#if statusIconBefore}
<span class="status">
<FontIcon icon={statusIconBefore} />
@@ -143,7 +159,7 @@
{/if}
{#if extInfo}
<span class="ext-info">
{extInfo}
<TokenizedFilteredText text={extInfo} {filter} />
</span>
{/if}
{#if onPin}
@@ -180,13 +196,19 @@
<slot />
<style>
.pin,
.pin-active {
position: absolute;
right: 0;
z-index: 150;
}
.main {
padding: 5px;
cursor: pointer;
white-space: nowrap;
font-weight: normal;
}
.main:hover {
.main:hover:not(.disableHover) {
background-color: var(--theme-bg-hover);
}
.isBold {
@@ -19,6 +19,7 @@
export let onDropOnGroup = undefined;
export let groupContextMenu = null;
export let collapsedGroupNames;
export let filter = undefined;
$: isExpanded = !$collapsedGroupNames.includes(group);
@@ -86,6 +87,9 @@
on:objectClick
{disableContextMenu}
{passProps}
isExpandedBySearch={filter && item.isChildMatched}
{filter}
isMainMatched={item.isMainMatched}
/>
{/each}
</div>
+72 -32
View File
@@ -1,11 +1,13 @@
<script>
import _, { sortBy } from 'lodash';
import { asyncFilter } from '../utility/common';
import _ from 'lodash';
import AppObjectGroup from './AppObjectGroup.svelte';
import { plusExpandIcon } from '../icons/expandIcons';
import AppObjectListItem from './AppObjectListItem.svelte';
import { writable } from 'svelte/store';
import Link from '../elements/Link.svelte';
import { focusedConnectionOrDatabase } from '../stores';
import { tick } from 'svelte';
export let list;
export let module;
@@ -16,7 +18,7 @@
export let expandIconFunc = undefined;
export let checkedObjectsStore = null;
export let disableContextMenu = false;
export let passProps;
export let passProps = {};
export let getIsExpanded = null;
export let setIsExpanded = null;
export let sortGroups = false;
@@ -26,25 +28,47 @@
export let groupFunc = undefined;
export let onDropOnGroup = undefined;
export let emptyGroupNames = [];
export let isExpandedBySearch = false;
export let collapsedGroupNames = writable([]);
export let onChangeFilteredList;
export let onChangeFilteredList = undefined;
$: filtered = !groupFunc
? list.filter(data => {
const matcher = module.createMatcher && module.createMatcher(data);
if (matcher && !matcher(filter)) return false;
return true;
})
: null;
let expandLimited = false;
$: childrenMatched = !groupFunc
? list.filter(data => {
const matcher = module.createChildMatcher && module.createChildMatcher(data);
if (matcher && !matcher(filter)) return false;
return true;
})
: null;
$: matcher = module.createMatcher && module.createMatcher(filter, passProps?.searchSettings);
$: dataLabeled = _.compact(
(list || []).map(data => {
const matchResult = matcher ? matcher(data) : true;
let isMatched = true;
let isMainMatched = true;
let isChildMatched = true;
if (matchResult == false) {
isMatched = false;
isChildMatched = false;
isMainMatched = false;
} else if (matchResult == 'child') {
isMainMatched = false;
} else if (matchResult == 'main') {
isChildMatched = false;
} else if (matchResult == 'none') {
isMatched = false;
isChildMatched = false;
isMainMatched = false;
} else if (matchResult == 'both') {
isChildMatched = !module.disableShowChildrenWithParentMatch;
}
const group = groupFunc ? groupFunc(data) : undefined;
return { group, data, isMatched, isChildMatched, isMainMatched };
})
);
$: filtered = dataLabeled.filter(x => x.isMatched).map(x => x.data);
$: childrenMatched = dataLabeled.filter(x => x.isChildMatched).map(x => x.data);
$: mainMatched = dataLabeled.filter(x => x.isMainMatched).map(x => x.data);
// let filtered = [];
@@ -60,17 +84,6 @@
// }
// }
$: listGrouped = groupFunc
? _.compact(
(list || []).map(data => {
const matcher = module.createMatcher && module.createMatcher(data);
const isMatched = matcher && !matcher(filter) ? false : true;
const group = groupFunc(data);
return { group, data, isMatched };
})
)
: null;
function extendGroups(base, emptyList) {
const res = {
...base,
@@ -82,7 +95,24 @@
return res;
}
$: groups = groupFunc ? extendGroups(_.groupBy(listGrouped, 'group'), emptyGroupNames) : null;
function setExpandLimited() {
expandLimited = true;
}
$: groups = groupFunc ? extendGroups(_.groupBy(dataLabeled, 'group'), emptyGroupNames) : null;
$: listLimited = isExpandedBySearch && !expandLimited ? filtered.slice(0, filter.trim().length < 3 ? 1 : 3) : list;
$: isListLimited = isExpandedBySearch && listLimited.length < filtered.length;
$: listMissingItems = isListLimited ? filtered.slice(listLimited.length) : [];
$: if (
$focusedConnectionOrDatabase &&
listMissingItems.some(
x => $focusedConnectionOrDatabase.conid == x?.connection?._id && $focusedConnectionOrDatabase.database == x?.name
)
) {
tick().then(setExpandLimited);
}
</script>
{#if groupFunc}
@@ -108,7 +138,7 @@
/>
{/each}
{:else}
{#each list as data}
{#each listLimited as data}
<AppObjectListItem
isHidden={!filtered.includes(data)}
{module}
@@ -121,10 +151,20 @@
{checkedObjectsStore}
{disableContextMenu}
{filter}
isExpandedBySearch={childrenMatched.includes(data)}
isExpandedBySearch={filter && childrenMatched.includes(data)}
isMainMatched={filter && mainMatched.includes(data)}
{passProps}
{getIsExpanded}
{setIsExpanded}
/>
{/each}
{#if isListLimited}
<div class="ml-2">
<Link
onClick={() => {
expandLimited = true;
}}>Show next {filtered.length - listLimited.length}</Link
>
</div>
{/if}
{/if}
@@ -24,6 +24,7 @@
export let passProps;
export let getIsExpanded = null;
export let setIsExpanded = null;
export let isMainMatched = false;
let isExpandedCore = false;
@@ -57,16 +58,21 @@
{module}
{disableContextMenu}
{passProps}
{filter}
/>
{#if (isExpanded || isExpandedBySearch) && subItemsComponent}
<div class="subitems">
<svelte:component
this={subItemsComponent(data)}
this={subItemsComponent(data, {
isExpandedBySearch,
})}
{data}
{filter}
{passProps}
isExpandedOnlyBySearch={isExpandedBySearch && !isExpanded}
{isExpandedBySearch}
{isExpanded}
{isMainMatched}
/>
</div>
{/if}
@@ -42,8 +42,8 @@
export const extractKey = data => data.fileName;
export const createMatcher =
({ fileName }) =>
filter =>
({ fileName }) =>
filterName(filter, fileName);
const ARCHIVE_ICONS = {
'table.yaml': 'img table',
@@ -70,7 +70,7 @@
import { getExtensions } from '../stores';
import createQuickExportMenu from '../utility/createQuickExportMenu';
import { exportQuickExportFile, } from '../utility/exportFileTools';
import { exportQuickExportFile } from '../utility/exportFileTools';
import openNewTab from '../utility/openNewTab';
import AppObjectCore from './AppObjectCore.svelte';
import InputTextModal from '../modals/InputTextModal.svelte';
@@ -1,6 +1,6 @@
<script lang="ts" context="module">
export const extractKey = data => data.name;
export const createMatcher = data => filter => filterName(filter, data.name);
export const createMatcher = filter => data => filterName(filter, data.name);
</script>
<script lang="ts">
+30 -2
View File
@@ -1,5 +1,17 @@
<script lang="ts" context="module">
export const extractKey = ({ columnName }) => columnName;
export const createMatcher =
(filter, cfg = DEFAULT_OBJECT_SEARCH_SETTINGS) =>
data => {
const filterArgs = [];
if (cfg.columnName) filterArgs.push(data.columnName);
if (cfg.columnComment) filterArgs.push(data.columnComment);
if (cfg.columnDataType) filterArgs.push(data.dataType);
const res = filterName(filter, ...filterArgs);
return res;
};
</script>
<script lang="ts">
@@ -9,6 +21,8 @@
import { renameDatabaseObjectDialog, alterDatabaseDialog } from '../utility/alterDatabaseTools';
import AppObjectCore from './AppObjectCore.svelte';
import { DEFAULT_OBJECT_SEARCH_SETTINGS } from '../stores';
import { filterName } from 'dbgate-tools';
export let data;
@@ -35,7 +49,21 @@
];
}
$: extInfo = data.foreignKey ? `${data.dataType} -> ${data.foreignKey.refTableName}` : data.dataType;
function getExtInfo(data) {
const res = [];
if (data.foreignKey) {
res.push(`${data.dataType} -> ${data.foreignKey.refTableName}`);
} else {
res.push(data.dataType);
}
if (data.columnComment) {
res.push(data.columnComment);
}
if (res.length > 0) return res.join(', ');
return null;
}
$: extInfo = getExtInfo(data);
</script>
<AppObjectCore
@@ -45,5 +73,5 @@
{extInfo}
icon={getColumnIcon(data, true)}
menu={createMenu}
disableHover
\
/>
@@ -1,16 +1,24 @@
<script context="module">
export const extractKey = data => data._id;
export const createMatcher = props => filter => {
const { _id, displayName, server } = props;
const databases = getLocalStorage(`database_list_${_id}`) || [];
return filterName(filter, displayName, server, ...databases.map(x => x.name));
};
export const createChildMatcher = props => filter => {
if (!filter) return false;
const { _id } = props;
const databases = getLocalStorage(`database_list_${_id}`) || [];
return filterName(filter, ...databases.map(x => x.name));
};
export const createMatcher =
(filter, cfg = DEFAULT_CONNECTION_SEARCH_SETTINGS) =>
props => {
const { _id, displayName, server, user, engine } = props;
const databases = getLocalStorage(`database_list_${_id}`) || [];
const match = (engine || '').match(/^([^@]*)@/);
const engineDisplay = match ? match[1] : engine;
return filterNameCompoud(
filter,
[
cfg.displayName ? displayName : null,
cfg.server ? server : null,
cfg.user ? user : null,
cfg.engine ? engineDisplay : null,
],
cfg.database ? databases.map(x => x.name) : []
);
};
export function openConnection(connection, disableExpand = false) {
if (connection.singleDatabase) {
if (getOpenedSingleDatabaseConnections().includes(connection._id)) {
@@ -42,6 +50,10 @@
expandedConnections.update(x => _.uniq([...x, connection._id]));
}
if (connection.defaultDatabase) {
switchCurrentDatabase({ connection, name: connection.defaultDatabase });
}
// if (!config.runAsPortal && getCurrentSettings()['defaultAction.connectionClick'] != 'connect') {
// expandedConnections.update(x => _.uniq([...x, connection._id]));
// }
@@ -94,6 +106,7 @@
import AppObjectCore from './AppObjectCore.svelte';
import {
currentDatabase,
DEFAULT_CONNECTION_SEARCH_SETTINGS,
expandedConnections,
extensions,
focusedConnectionOrDatabase,
@@ -106,7 +119,7 @@
openedConnections,
openedSingleDatabaseConnections,
} from '../stores';
import { filterName } from 'dbgate-tools';
import { filterName, filterNameCompoud } from 'dbgate-tools';
import { showModal } from '../modals/modalTools';
import ConfirmModal from '../modals/ConfirmModal.svelte';
import InputTextModal from '../modals/InputTextModal.svelte';
@@ -123,6 +136,7 @@
import { getConnectionLabel } from 'dbgate-tools';
import hasPermission from '../utility/hasPermission';
import { switchCurrentDatabase } from '../utility/common';
import { getConnectionClickActionSetting } from '../settings/settingsTools';
export let data;
export let passProps;
@@ -177,24 +191,40 @@
};
const handleClick = async e => {
// focusedConnectionOrDatabase.set({
// conid: data?._id,
// connection: data,
// database: data.singleDatabase ? data.defaultDatabase : null,
// });
const config = getCurrentConfig();
const connectionClickAction = getConnectionClickActionSetting();
if (connectionClickAction == 'openDetails') {
if (config.runAsPortal == false && !config.storageDatabase) {
openNewTab({
title: getConnectionLabel(data),
icon: 'img connection',
tabComponent: 'ConnectionTab',
tabPreviewMode: true,
props: {
conid: data._id,
},
});
}
}
if (connectionClickAction == 'connect') {
await tick();
handleConnect();
}
};
const handleMouseDown = () => {
focusedConnectionOrDatabase.set({
conid: data?._id,
connection: data,
database: data.singleDatabase ? data.defaultDatabase : null,
});
const config = getCurrentConfig();
if (config.runAsPortal == false && !config.storageDatabase) {
openNewTab({
title: getConnectionLabel(data),
icon: 'img connection',
tabComponent: 'ConnectionTab',
tabPreviewMode: true,
props: {
conid: data._id,
},
});
}
};
const handleSqlRestore = () => {
@@ -254,6 +284,7 @@
icon: 'img sql-file',
tooltip,
tabComponent: 'QueryTab',
focused: true,
props: {
conid: data._id,
},
@@ -290,7 +321,7 @@
],
{ divider: true },
!data.singleDatabase && [
hasPermission(`dbops/query`) && { onClick: handleNewQuery, text: 'New query', isNewQuery: true },
hasPermission(`dbops/query`) && { onClick: handleNewQuery, text: 'New Query (server)', isNewQuery: true },
$openedConnections.includes(data._id) &&
data.status && {
text: 'Refresh',
@@ -373,6 +404,7 @@
colorMark={passProps?.connectionColorFactory && passProps?.connectionColorFactory({ conid: data._id })}
menu={getContextMenu}
on:click={handleClick}
on:mousedown={handleMouseDown}
on:dblclick
on:expand
on:dblclick={handleDoubleClick}
@@ -385,4 +417,5 @@
(data.singleDatabase
? $focusedConnectionOrDatabase?.database == data.defaultDatabase
: !$focusedConnectionOrDatabase?.database)}
disableBoldScroll={!!$focusedConnectionOrDatabase}
/>
@@ -3,6 +3,11 @@
export const extractKey = props => props.name;
export const createMatcher = filter => props => {
const { name, displayName, server } = props;
return filterName(filter, name, displayName, server);
};
export function disconnectDatabaseConnection(conid, database, showConfirmation = true) {
const closeCondition = x =>
x.props?.conid == conid &&
@@ -48,6 +53,7 @@
icon: 'img sql-file',
tooltip,
tabComponent: 'QueryTab',
focused: true,
props: {
conid: connection._id,
database: name,
@@ -235,6 +241,7 @@
title: 'Query #',
icon: 'img query-design',
tabComponent: 'QueryDesignTab',
focused: true,
props: {
conid: connection._id,
database: name,
@@ -351,11 +358,13 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
text: `New ${driver?.collectionSingularLabel ?? 'collection/container'}`,
},
hasPermission(`dbops/query`) &&
driver?.databaseEngineTypes?.includes('sql') && isProApp() && { onClick: handleQueryDesigner, text: 'Design query' },
driver?.databaseEngineTypes?.includes('sql') && isProApp() && {
onClick: handleNewPerspective,
text: 'Design perspective query',
},
driver?.databaseEngineTypes?.includes('sql') &&
isProApp() && { onClick: handleQueryDesigner, text: 'Design query' },
driver?.databaseEngineTypes?.includes('sql') &&
isProApp() && {
onClick: handleNewPerspective,
text: 'Design perspective query',
},
connection.useSeparateSchemas && { onClick: handleRefreshSchemas, text: 'Refresh schemas' },
{ divider: true },
@@ -463,7 +472,13 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
import openNewTab from '../utility/openNewTab';
import AppObjectCore from './AppObjectCore.svelte';
import { showSnackbarError, showSnackbarSuccess } from '../utility/snackbar';
import { extractDbNameFromComposite, extractPackageName, findEngineDriver, getConnectionLabel } from 'dbgate-tools';
import {
extractDbNameFromComposite,
extractPackageName,
filterName,
findEngineDriver,
getConnectionLabel,
} from 'dbgate-tools';
import InputTextModal from '../modals/InputTextModal.svelte';
import { getDatabaseInfo, useUsedApps } from '../utility/metadataLoaders';
import { openJsonDocument } from '../tabs/JsonTab.svelte';
@@ -486,6 +501,7 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
import ChooseArchiveFolderModal from '../modals/ChooseArchiveFolderModal.svelte';
import { extractShellConnection } from '../impexp/createImpExpScript';
import { getNumberIcon } from '../icons/FontIcon.svelte';
import { getDatabaseClickActionSetting } from '../settings/settingsTools';
export let data;
export let passProps;
@@ -522,6 +538,11 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
}}
on:click={() => {
// switchCurrentDatabase(data);
if (getDatabaseClickActionSetting() == 'switch') {
switchCurrentDatabase(data);
}
}}
on:mousedown={() => {
$focusedConnectionOrDatabase = { conid: data.connection?._id, database: data.name, connection: data.connection };
}}
on:dragstart
@@ -553,4 +574,8 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
: null}
isChoosed={data.connection?._id == $focusedConnectionOrDatabase?.conid &&
data.name == $focusedConnectionOrDatabase?.database}
disableBoldScroll={!!$focusedConnectionOrDatabase}
divProps={{
'data-testid': `DatabaseAppObject_${data.name}`,
}}
/>
@@ -3,14 +3,34 @@
export const extractKey = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName);
export const createMatcher =
({ schemaName, pureName, columns }) =>
filter =>
filterName(
filter,
pureName,
schemaName,
...(columns?.map(({ columnName }) => ({ childName: columnName })) || [])
);
(filter, cfg = DEFAULT_OBJECT_SEARCH_SETTINGS) =>
({ schemaName, pureName, objectComment, tableEngine, columns, objectTypeField, tableName, createSql }) => {
const mainArgs = [];
const childArgs = [];
if (cfg.schemaName) mainArgs.push(schemaName);
if (cfg.pureName) mainArgs.push(pureName);
if (objectTypeField == 'tables') {
if (cfg.tableComment) mainArgs.push(objectComment);
if (cfg.tableEngine) mainArgs.push(tableEngine);
for (const column of columns || []) {
if (cfg.columnName) childArgs.push(column.columnName);
if (cfg.columnComment) childArgs.push(column.columnComment);
if (cfg.columnDataType) childArgs.push(column.dataType);
}
} else {
if (cfg.sqlObjectText) childArgs.push(createSql);
}
if (objectTypeField == 'triggers' && cfg.pureName) {
mainArgs.push(tableName);
}
const res = filterNameCompoud(filter, mainArgs, childArgs);
return res;
};
export const disableShowChildrenWithParentMatch = true;
export const createTitle = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName);
export const databaseObjectIcons = {
@@ -21,6 +41,8 @@
procedures: 'img procedure',
functions: 'img function',
queries: 'img query-data',
triggers: 'icon trigger',
schedulerEvents: 'icon scheduler-event',
};
const defaultTabs = {
@@ -31,6 +53,7 @@
queries: 'QueryDataTab',
procedures: 'SqlObjectTab',
functions: 'SqlObjectTab',
triggers: 'SqlObjectTab',
};
function createScriptTemplatesSubmenu(objectTypeField) {
@@ -65,10 +88,12 @@
isDropCollection?: boolean;
isRenameCollection?: boolean;
isDuplicateCollection?: boolean;
isDisableEvent?: boolean;
isEnableEvent?: boolean;
submenu?: DbObjMenuItem[];
}
function createMenusCore(objectTypeField, driver): DbObjMenuItem[] {
function createMenusCore(objectTypeField, driver, data): DbObjMenuItem[] {
switch (objectTypeField) {
case 'tables':
return [
@@ -322,40 +347,9 @@
},
];
case 'functions':
return [
...defaultDatabaseObjectAppObjectActions['functions'],
{
divider: true,
},
hasPermission('dbops/model/edit') && {
label: 'Drop function',
isDrop: true,
requiresWriteAccess: true,
},
hasPermission('dbops/model/edit') && {
label: 'Rename function',
isRename: true,
requiresWriteAccess: true,
},
createScriptTemplatesSubmenu('functions'),
{
label: 'SQL generator',
submenu: [
{
label: 'CREATE FUNCTION',
sqlGeneratorProps: {
createFunctions: true,
},
},
{
label: 'DROP FUNCTION',
sqlGeneratorProps: {
dropFunctions: true,
},
},
],
},
];
return [...defaultDatabaseObjectAppObjectActions['functions']];
case 'triggers':
return [...defaultDatabaseObjectAppObjectActions['triggers']];
case 'collections':
return [
...defaultDatabaseObjectAppObjectActions['collections'],
@@ -393,6 +387,28 @@
},
...(driver?.getScriptTemplates?.('collections') || []),
];
case 'schedulerEvents':
const menu: DbObjMenuItem[] = [
...defaultDatabaseObjectAppObjectActions['schedulerEvents'],
{
divider: true,
},
,
];
if (data?.status === 'ENABLED') {
menu.push({
label: 'Disable',
isDisableEvent: true,
});
} else {
menu.push({
label: 'Enable',
isEnableEvent: true,
});
}
return menu;
}
}
@@ -431,6 +447,7 @@
title: 'Query #',
icon: 'img query-design',
tabComponent: 'QueryDesignTab',
focused: true,
props: {
conid: data.conid,
database: data.database,
@@ -489,6 +506,36 @@
x => x.schemaName == data.schemaName && x.pureName == data.pureName
);
});
} else if (menu.isDisableEvent) {
const { conid, database, pureName } = data;
const driver = await getDriver();
const dmp = driver.createDumper();
dmp.put('^alter ^event %i ^disable', pureName);
const sql = dmp.s;
showModal(ConfirmSqlModal, {
sql,
onConfirm: async () => {
saveScriptToDatabase({ conid, database }, sql);
},
engine: driver.engine,
});
} else if (menu.isEnableEvent) {
const { conid, database, pureName } = data;
const driver = await getDriver();
const dmp = driver.createDumper();
dmp.put('^alter ^event %i ^enable', pureName);
const sql = dmp.s;
showModal(ConfirmSqlModal, {
sql,
onConfirm: async () => {
saveScriptToDatabase({ conid, database }, sql);
},
engine: driver.engine,
});
} else if (menu.isTruncate) {
const { conid, database } = data;
const driver = await getDriver();
@@ -634,8 +681,8 @@
}
}
function createMenus(objectTypeField, driver): ReturnType<typeof createMenusCore> {
return createMenusCore(objectTypeField, driver).filter(x => {
function createMenus(objectTypeField, driver, data): ReturnType<typeof createMenusCore> {
return createMenusCore(objectTypeField, driver, data).filter(x => {
if (x.scriptTemplate) {
return hasPermission(`dbops/sql-template/${x.scriptTemplate}`);
}
@@ -676,6 +723,7 @@
{
// title: getObjectTitle(connection, schemaName, pureName),
title: tabComponent ? getObjectTitle(connection, schemaName, pureName) : 'Query #',
focused: tabComponent == null,
tooltip,
icon:
icon ||
@@ -733,7 +781,7 @@
return;
}
const availableDefaultActions = defaultDatabaseObjectAppObjectActions[objectTypeField];
const availableDefaultActions = defaultDatabaseObjectAppObjectActions[objectTypeField] ?? [];
const configuredActionId = getLastUsedDefaultActions()[objectTypeField];
const prefferedAction =
@@ -852,7 +900,7 @@
const driver = findEngineDriver(data, getExtensions());
const { objectTypeField } = data;
return createMenus(objectTypeField, driver)
return createMenus(objectTypeField, driver, data)
.filter(x => x)
.map(menu => menuItemMapper(menu, data, connection));
}
@@ -867,7 +915,35 @@
return createDatabaseObjectMenu(data);
}
export function handleObjectClick(data, { forceNewTab = false, tabPreviewMode = false, focusTab = false }) {
export function handleObjectClick(data, clickAction) {
// on:click={() => handleObjectClick(data, { tabPreviewMode: true })}
// on:middleclick={() => handleObjectClick(data, { forceNewTab: true })}
// on:dblclick={() => handleObjectClick(data, { tabPreviewMode: false, focusTab: true })}
const openDetailOnArrows = getOpenDetailOnArrowsSettings();
let forceNewTab = false;
let tabPreviewMode = false;
let focusTab = false;
switch (clickAction) {
case 'leftClick':
tabPreviewMode = true;
break;
case 'middleClick':
forceNewTab = true;
break;
case 'dblClick':
focusTab = true;
break;
case 'keyEnter':
focusTab = true;
break;
case 'keyArrow':
if (!openDetailOnArrows) return;
tabPreviewMode = true;
break;
}
return handleDatabaseObjectClick(data, { forceNewTab, tabPreviewMode, focusTab });
}
</script>
@@ -876,27 +952,18 @@
import _ from 'lodash';
import AppObjectCore from './AppObjectCore.svelte';
import {
currentDatabase,
extensions,
DEFAULT_OBJECT_SEARCH_SETTINGS,
getActiveTab,
getCurrentSettings,
getExtensions,
getLastUsedDefaultActions,
lastUsedDefaultActions,
openedConnections,
openedTabs,
pinnedTables,
selectedDatabaseObjectAppObject,
} from '../stores';
import openNewTab from '../utility/openNewTab';
import {
extractDbNameFromComposite,
filterName,
generateDbPairingId,
getAlterDatabaseScript,
getConnectionLabel,
} from 'dbgate-tools';
import { getConnectionInfo, getDatabaseInfo } from '../utility/metadataLoaders';
import { extractDbNameFromComposite, filterNameCompoud, getConnectionLabel } from 'dbgate-tools';
import { getConnectionInfo } from '../utility/metadataLoaders';
import fullDisplayName from '../utility/fullDisplayName';
import { showModal } from '../modals/modalTools';
import { findEngineDriver } from 'dbgate-tools';
@@ -907,7 +974,6 @@
import ConfirmSqlModal, { runOperationOnDatabase, saveScriptToDatabase } from '../modals/ConfirmSqlModal.svelte';
import { alterDatabaseDialog, renameDatabaseObjectDialog } from '../utility/alterDatabaseTools';
import ConfirmModal from '../modals/ConfirmModal.svelte';
import { apiCall } from '../utility/api';
import InputTextModal from '../modals/InputTextModal.svelte';
import { extractShellConnection } from '../impexp/createImpExpScript';
import { format as dateFormat } from 'date-fns';
@@ -916,23 +982,33 @@
import { openImportExportTab } from '../utility/importExportTools';
import { defaultDatabaseObjectAppObjectActions, matchDatabaseObjectAppObject } from './appObjectTools';
import { getSupportedScriptTemplates } from '../utility/applyScriptTemplate';
import { getBoolSettingsValue } from '../settings/settingsTools';
import { getBoolSettingsValue, getOpenDetailOnArrowsSettings } from '../settings/settingsTools';
import { isProApp } from '../utility/proTools';
export let data;
export let passProps;
function handleClick({ forceNewTab = false, tabPreviewMode = false, focusTab = false } = {}) {
$selectedDatabaseObjectAppObject = _.pick(data, ['conid', 'database', 'objectTypeField', 'pureName', 'schemaName']);
handleDatabaseObjectClick(data, { forceNewTab, tabPreviewMode, focusTab });
}
function createMenu() {
return createDatabaseObjectMenu(data, passProps?.connection);
}
function getExtInfo(data) {
const res = [];
if (data.objectTypeField === 'triggers') {
res.push(`${data.tableName}, ${data.triggerTiming?.toLowerCase() ?? ''} ${data.eventType?.toLowerCase() ?? ''}`);
}
if (data.objectTypeField == 'schedulerEvents') {
if (data.eventType == 'RECURRING') {
res.push(`${data.status}, ${data.eventType}, ${data.intervalValue} ${data.intervalField}`);
} else {
res.push(`${data.status}, ${data.eventType}, ${data.executeAt}`);
}
}
if (data.objectComment) {
res.push(data.objectComment);
}
if (data.tableRowCount != null) {
res.push(`${formatRowCount(data.tableRowCount)} rows`);
}
@@ -958,12 +1034,15 @@
onUnpin={isPinned ? () => pinnedTables.update(list => list.filter(x => !testEqual(x, data))) : null}
extInfo={getExtInfo(data)}
isChoosed={matchDatabaseObjectAppObject($selectedDatabaseObjectAppObject, data)}
on:click={() => handleClick({ tabPreviewMode: true })}
on:middleclick={() => handleClick({ forceNewTab: true })}
on:dblclick={() => handleClick({ tabPreviewMode: false, focusTab: true })}
on:click={() => handleObjectClick(data, 'leftClick')}
on:middleclick={() => handleObjectClick(data, 'middleClick')}
on:dblclick={() => handleObjectClick(data, 'dblClick')}
on:expand
on:dragstart
on:dragenter
on:dragend
on:drop
on:mousedown={() => {
$selectedDatabaseObjectAppObject = _.pick(data, ['conid', 'database', 'objectTypeField', 'pureName', 'schemaName']);
}}
/>
@@ -1,6 +1,9 @@
<script lang="ts" context="module">
export const extractKey = data => data.name;
export const createMatcher = ({ name, title }) => filter => filterName(filter, name, title);
export const createMatcher =
filter =>
({ name, title }) =>
filterName(filter, name, title);
</script>
<script lang="ts">
@@ -6,13 +6,26 @@
import AppObjectCore from './AppObjectCore.svelte';
export let data;
function createParameterTitle(parameter) {
if (!parameter.parameterName) return parameter.position ?? null;
if (parameter.parameterName.startsWith('@')) return parameter.parameterName.substring(1);
return data.parameterName;
}
function createParameterExtInfo(parameter) {
if (parameter.parameterMode && parameter.parameterMode !== 'IN') {
return `${parameter.dataType} ${parameter.parameterMode}`;
}
return parameter.dataType;
}
</script>
<AppObjectCore
{...$$restProps}
{data}
title={data.parameterName?.startsWith('@') ? data.parameterName.substring(1) : data.parameterName}
extInfo={data.parameterMode && data.parameterMode !== 'IN' ? `${data.dataType} ${data.parameterMode}` : data.dataType}
title={createParameterTitle(data)}
extInfo={createParameterExtInfo(data)}
icon={'icon parameter'}
disableHover
/>
@@ -0,0 +1,23 @@
<script lang="ts" context="module">
export const extractKey = ({ columnName }) => columnName;
export const createMatcher =
(filter, cfg = DEFAULT_OBJECT_SEARCH_SETTINGS) =>
data => {
const filterArgs = [];
if (cfg.sqlObjectText) filterArgs.push(data.lineData);
const res = filterName(filter, ...filterArgs);
return res;
};
</script>
<script lang="ts">
import AppObjectCore from './AppObjectCore.svelte';
import { filterName } from 'dbgate-tools';
import { DEFAULT_OBJECT_SEARCH_SETTINGS } from '../stores';
export let data;
</script>
<AppObjectCore {...$$restProps} {data} icon="icon text" title={data.lineData?.substring(0, 100)} disableHover />
@@ -104,8 +104,8 @@
export const extractKey = data => data.file;
export const createMatcher =
({ file }) =>
filter =>
({ file }) =>
filterName(filter, file);
</script>
+10 -5
View File
@@ -11,17 +11,22 @@
export let data;
export let passProps;
export let isExpandedOnlyBySearch;
export let isExpandedBySearch;
export let isExpanded;
export let isMainMatched;
$: isExpandedOnlyBySearch = isExpandedBySearch && !isExpanded;
$: databases = useDatabaseList({ conid: isExpandedOnlyBySearch ? null : data._id });
$: dbList = isExpandedOnlyBySearch ? getLocalStorage(`database_list_${data._id}`) || [] : $databases || [];
// .filter(x => filterName(filter, x.name, data.displayName, data.server))
</script>
<AppObjectList
list={_.sortBy(
dbList.filter(x => filterName(filter, x.name, data.displayName, data.server)),
x => x.sortOrder ?? x.name
).map(db => ({ ...db, connection: data }))}
list={_.sortBy(dbList, x => x.sortOrder ?? x.name).map(db => ({ ...db, connection: data }))}
module={databaseAppObject}
{passProps}
filter={isMainMatched ? '' : filter}
{isExpandedBySearch}
/>
@@ -0,0 +1,21 @@
<script lang="ts" context="module">
export const extractKey = ({ lineData }) => lineData;
</script>
<script lang="ts">
import AppObjectList from './AppObjectList.svelte';
import * as procedureLineAppObject from './ProcedureLineAppObject.svelte';
export let data;
export let filter;
export let isExpandedBySearch;
</script>
<AppObjectList
list={(data.createSql?.split('\n') || []).map(lineData => ({
lineData,
}))}
module={procedureLineAppObject}
{filter}
{isExpandedBySearch}
/>

Some files were not shown because too many files have changed in this diff Show More