From 9a09a8c9b320fdbd22d2ba0d74492dfe6e004598 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Oct 2021 15:33:24 +0000 Subject: [PATCH 001/100] Bump vm2 from 3.9.3 to 3.9.4 in /packages/server Bumps [vm2](https://github.com/patriksimek/vm2) from 3.9.3 to 3.9.4. - [Release notes](https://github.com/patriksimek/vm2/releases) - [Changelog](https://github.com/patriksimek/vm2/blob/master/CHANGELOG.md) - [Commits](https://github.com/patriksimek/vm2/compare/3.9.3...3.9.4) --- updated-dependencies: - dependency-name: vm2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- packages/server/yarn.lock | 808 +++++++++++++++++++++++++++++++++++++- 1 file changed, 789 insertions(+), 19 deletions(-) diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index bcca0c0ad1..db4824e8e0 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -943,6 +943,29 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@budibase/auth@^0.9.169-alpha.1": + version "0.9.169" + resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.169.tgz#fd2a8fc271782ba857259ace15118a4d53b3d161" + integrity sha512-Q087k/54Nzx6Oeg5uL7YD/9BB+qkBWIv7h4ct+cNQJFNK/aKKN8JLQft+z3mBN5omHTkdJYFmbgXWFxtX+rR3Q== + dependencies: + "@techpass/passport-openidconnect" "^0.3.0" + aws-sdk "^2.901.0" + bcryptjs "^2.4.3" + cls-hooked "^4.2.2" + ioredis "^4.27.1" + jsonwebtoken "^8.5.1" + koa-passport "^4.1.4" + lodash "^4.17.21" + node-fetch "^2.6.1" + passport-google-auth "^1.0.2" + passport-google-oauth "^2.0.0" + passport-jwt "^4.0.0" + passport-local "^1.0.0" + sanitize-s3-objectkey "^0.0.1" + tar-fs "^2.1.1" + uuid "^8.3.2" + zlib "^1.0.5" + "@budibase/bbui@^0.9.139": version "0.9.139" resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.139.tgz#e6cfc90e8f6c2aa3526fc6a7bef251bccdaf51bb" @@ -992,6 +1015,94 @@ svelte-flatpickr "^3.1.0" svelte-portal "^1.0.0" +"@budibase/bbui@^0.9.169": + version "0.9.169" + resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.169.tgz#e8dac59b9792a7edf03c4301a9069760e2ebd2f4" + integrity sha512-2hks6GEjcXbDUzC37WgJvgloiqTP5ZS7IuRjlHU9kStDr6dAnXuy8pO6JNJmKrTXt+rgtwhHHrVWzzcmNLIYxA== + dependencies: + "@adobe/spectrum-css-workflow-icons" "^1.2.1" + "@spectrum-css/actionbutton" "^1.0.1" + "@spectrum-css/actiongroup" "^1.0.1" + "@spectrum-css/avatar" "^3.0.2" + "@spectrum-css/button" "^3.0.1" + "@spectrum-css/buttongroup" "^3.0.2" + "@spectrum-css/checkbox" "^3.0.2" + "@spectrum-css/dialog" "^3.0.1" + "@spectrum-css/divider" "^1.0.3" + "@spectrum-css/dropzone" "^3.0.2" + "@spectrum-css/fieldgroup" "^3.0.2" + "@spectrum-css/fieldlabel" "^3.0.1" + "@spectrum-css/icon" "^3.0.1" + "@spectrum-css/illustratedmessage" "^3.0.2" + "@spectrum-css/inputgroup" "^3.0.2" + "@spectrum-css/label" "^2.0.10" + "@spectrum-css/link" "^3.1.1" + "@spectrum-css/menu" "^3.0.1" + "@spectrum-css/modal" "^3.0.1" + "@spectrum-css/pagination" "^3.0.3" + "@spectrum-css/picker" "^1.0.1" + "@spectrum-css/popover" "^3.0.1" + "@spectrum-css/progressbar" "^1.0.2" + "@spectrum-css/progresscircle" "^1.0.2" + "@spectrum-css/radio" "^3.0.2" + "@spectrum-css/search" "^3.0.2" + "@spectrum-css/sidenav" "^3.0.2" + "@spectrum-css/statuslight" "^3.0.2" + "@spectrum-css/stepper" "^3.0.3" + "@spectrum-css/switch" "^1.0.2" + "@spectrum-css/table" "^3.0.1" + "@spectrum-css/tabs" "^3.0.1" + "@spectrum-css/tags" "^3.0.2" + "@spectrum-css/textfield" "^3.0.1" + "@spectrum-css/toast" "^3.0.1" + "@spectrum-css/tooltip" "^3.0.3" + "@spectrum-css/treeview" "^3.0.2" + "@spectrum-css/typography" "^3.0.1" + "@spectrum-css/underlay" "^2.0.9" + "@spectrum-css/vars" "^3.0.1" + dayjs "^1.10.4" + svelte-flatpickr "^3.2.3" + svelte-portal "^1.0.0" + +"@budibase/client@^0.9.169-alpha.1": + version "0.9.169" + resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.169.tgz#bec370b8f069b42f62483b281d6b9e2c7c8625f3" + integrity sha512-/bDnwv2iRysZrcrBQJEKzuxdwkwoJ2FalmQFhsfj+V/MWBN/wpQSDbJZQwf/YcI5bQk8f7xIn95O+DMH/m5izg== + dependencies: + "@budibase/bbui" "^0.9.169" + "@budibase/standard-components" "^0.9.139" + "@budibase/string-templates" "^0.9.169" + regexparam "^1.3.0" + shortid "^2.2.15" + svelte-spa-router "^3.0.5" + +"@budibase/handlebars-helpers@^0.11.7": + version "0.11.7" + resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.11.7.tgz#8e5f9843d7dd10503e9f608555a96ccf4d836c46" + integrity sha512-PvGHAv22cWSFExs1kc0WglwsmCEUEOqWvSp6JCFZwtc3qAAr5yMfLK8WGVQ63ALvyzWZiyxF+yrlzeeaohCMJw== + dependencies: + array-sort "^1.0.0" + define-property "^2.0.2" + extend-shallow "^3.0.2" + for-in "^1.0.2" + get-object "^0.2.0" + get-value "^3.0.1" + handlebars "^4.7.7" + handlebars-utils "^1.0.6" + has-value "^2.0.2" + helper-date "^1.0.1" + helper-markdown "^1.0.0" + helper-md "^0.2.2" + html-tag "^2.0.0" + is-even "^1.0.0" + is-glob "^4.0.1" + kind-of "^6.0.3" + micromatch "^3.1.5" + relative "^3.0.2" + striptags "^3.1.1" + to-gfm-code-block "^0.1.1" + year "^0.2.1" + "@budibase/standard-components@^0.9.139": version "0.9.139" resolved "https://registry.yarnpkg.com/@budibase/standard-components/-/standard-components-0.9.139.tgz#cf8e2b759ae863e469e50272b3ca87f2827e66e3" @@ -1010,6 +1121,17 @@ svelte-apexcharts "^1.0.2" svelte-flatpickr "^3.1.0" +"@budibase/string-templates@^0.9.169", "@budibase/string-templates@^0.9.169-alpha.1": + version "0.9.169" + resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.169.tgz#3c0be97718f39a92ff6b2dbb8b470aaa7851005e" + integrity sha512-JUyg6XuUgFqnfdDSCAplo4cTtrqdSZ9NPrU3iGudZEQjO/Wk5sezWPznl3Yw/kFHKmPLjFHIveEa2+lODEAxIA== + dependencies: + "@budibase/handlebars-helpers" "^0.11.7" + dayjs "^1.10.4" + handlebars "^4.7.6" + handlebars-utils "^1.0.6" + lodash "^4.17.20" + "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -2012,6 +2134,17 @@ dependencies: defer-to-connect "^1.0.1" +"@techpass/passport-openidconnect@^0.3.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@techpass/passport-openidconnect/-/passport-openidconnect-0.3.2.tgz#f8fd5d97256286665dbf26dac92431f977ab1e63" + integrity sha512-fnCtEiexXSHA029B//hJcCJlLJrT3lhpNCyA0rnz58Qttz0BLGCVv6yMT8HmOnGThH6vcDOVwdgKM3kbCQtEhw== + dependencies: + base64url "^3.0.1" + oauth "^0.9.15" + passport-strategy "^1.0.0" + request "^2.88.0" + webfinger "^0.4.2" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -2658,7 +2791,7 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -argparse@^1.0.7: +argparse@^1.0.10, argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== @@ -2705,6 +2838,15 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= +array-sort@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" + integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== + dependencies: + default-compare "^1.0.0" + get-value "^2.0.6" + kind-of "^5.0.2" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -2747,6 +2889,13 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +async-hook-jl@^1.7.6: + version "1.7.6" + resolved "https://registry.yarnpkg.com/async-hook-jl/-/async-hook-jl-1.7.6.tgz#4fd25c2f864dbaf279c610d73bf97b1b28595e68" + integrity sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg== + dependencies: + stack-chain "^1.3.7" + async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" @@ -2764,6 +2913,13 @@ async@^2.6.3: dependencies: lodash "^4.17.14" +async@~2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" + integrity sha1-5YfGhYCZSsZ/xW/4bTrFa9voELw= + dependencies: + lodash "^4.14.0" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -2779,6 +2935,13 @@ atomic-sleep@^1.0.0: resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== +autolinker@~0.28.0: + version "0.28.1" + resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-0.28.1.tgz#0652b491881879f0775dace0cdca3233942a4e47" + integrity sha1-BlK0kYgYefB3XazgzcoyM5QqTkc= + dependencies: + gulp-header "^1.7.1" + aws-sdk@^2.767.0: version "2.981.0" resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.981.0.tgz#b2e6205db23d755e1c8fcbd0618e8309e69d3767" @@ -2794,6 +2957,21 @@ aws-sdk@^2.767.0: uuid "3.3.2" xml2js "0.4.19" +aws-sdk@^2.901.0: + version "2.1010.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1010.0.tgz#a0ff308149e2e358522b991f3b21d2f06c856f0c" + integrity sha512-ZiFMY0wJfgBSybAPKl4hEYi4zjs6c6A+kPmifF36C1bFja83Q4iNRUr2P13R41wJn/fdxMyWDgmx6r/5NNLUqg== + dependencies: + buffer "4.9.2" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.15.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + uuid "3.3.2" + xml2js "0.4.19" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -2968,6 +3146,11 @@ base64-js@^1.0.2, base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +base64url@3.x.x, base64url@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -2988,7 +3171,7 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -bcryptjs@2.4.3: +bcryptjs@2.4.3, bcryptjs@^2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms= @@ -3033,6 +3216,15 @@ bl@^3.0.0: dependencies: readable-stream "^3.0.1" +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + bluebird@^3.5.1: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -3402,6 +3594,11 @@ chokidar@^3.2.2: optionalDependencies: fsevents "~2.3.2" +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -3474,6 +3671,15 @@ clone-response@1.0.2, clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" +cls-hooked@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" + integrity sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw== + dependencies: + async-hook-jl "^1.7.6" + emitter-listener "^1.0.1" + semver "^5.4.1" + cluster-key-slot@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d" @@ -3595,6 +3801,13 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +concat-with-sourcemaps@*: + version "1.1.0" + resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" + integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== + dependencies: + source-map "^0.6.1" + configstore@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" @@ -3811,6 +4024,13 @@ date-utils@*: resolved "https://registry.yarnpkg.com/date-utils/-/date-utils-1.2.21.tgz#61fb16cdc1274b3c9acaaffe9fc69df8720a2b64" integrity sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q= +date.js@^0.3.1: + version "0.3.3" + resolved "https://registry.yarnpkg.com/date.js/-/date.js-0.3.3.tgz#ef1e92332f507a638795dbb985e951882e50bbda" + integrity sha512-HgigOS3h3k6HnW011nAb43c5xx5rBXk8P2v/WIT9Zv4koIaVXiH2BURguI78VVp+5Qc076T7OR378JViCnZtBw== + dependencies: + debug "~3.1.0" + dateformat@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -3954,6 +4174,13 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +default-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" + integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== + dependencies: + kind-of "^5.0.2" + default-shell@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/default-shell/-/default-shell-1.0.1.tgz#752304bddc6174f49eb29cb988feea0b8813c8bc" @@ -4193,6 +4420,13 @@ electron-to-chromium@^1.3.811: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.827.tgz#c725e8db8c5be18b472a919e5f57904512df0fc1" integrity sha512-ye+4uQOY/jbjRutMcE/EmOcNwUeo1qo9aKL2tPyb09cU3lmxNeyDF4RWiemmkknW+p29h7dyDqy02higTxc9/A== +emitter-listener@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8" + integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ== + dependencies: + shimmer "^1.2.0" + emittery@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" @@ -4223,7 +4457,7 @@ encoding-down@^6.3.0: level-codec "^9.0.0" level-errors "^2.0.0" -end-of-stream@^1.0.0, end-of-stream@^1.1.0: +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -4237,6 +4471,11 @@ end-stream@~0.1.0: dependencies: write-stream "~0.4.3" +ent@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" + integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= + errno@~0.1.1: version "0.1.8" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" @@ -5057,6 +5296,11 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-exists-sync@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" + integrity sha1-mC1ok6+RjnLQjeyehnP/K1qNat0= + fs-extra@8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -5113,6 +5357,14 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: has "^1.0.3" has-symbols "^1.0.1" +get-object@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/get-object/-/get-object-0.2.0.tgz#d92ff7d5190c64530cda0543dac63a3d47fe8c0c" + integrity sha1-2S/31RkMZFMM2gVD2sY6PUf+jAw= + dependencies: + is-number "^2.0.2" + isobject "^0.2.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -5160,6 +5412,13 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= +get-value@^3.0.0, get-value@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-3.0.1.tgz#5efd2a157f1d6a516d7524e124ac52d0a39ef5a8" + integrity sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA== + dependencies: + isobject "^3.0.1" + getopts@2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.2.5.tgz#67a0fe471cacb9c687d817cab6450b96dde8313b" @@ -5249,6 +5508,32 @@ globby@^11.0.3: merge2 "^1.3.0" slash "^3.0.0" +google-auth-library@~0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-0.10.0.tgz#6e15babee85fd1dd14d8d128a295b6838d52136e" + integrity sha1-bhW6vuhf0d0U2NEoopW2g41SE24= + dependencies: + gtoken "^1.2.1" + jws "^3.1.4" + lodash.noop "^3.0.1" + request "^2.74.0" + +google-p12-pem@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-0.1.2.tgz#33c46ab021aa734fa0332b3960a9a3ffcb2f3177" + integrity sha1-M8RqsCGqc0+gMys5YKmj/8svMXc= + dependencies: + node-forge "^0.7.1" + +googleapis@^16.0.0: + version "16.1.0" + resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-16.1.0.tgz#0f19f2d70572d918881a0f626e3b1a2fa8629576" + integrity sha1-Dxny1wVy2RiIGg9ibjsaL6hilXY= + dependencies: + async "~2.1.4" + google-auth-library "~0.10.0" + string-template "~1.0.0" + got@^8.3.1: version "8.3.2" resolved "https://registry.yarnpkg.com/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937" @@ -5294,6 +5579,45 @@ graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1. resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== +gtoken@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-1.2.3.tgz#5509571b8afd4322e124cf66cf68115284c476d8" + integrity sha512-wQAJflfoqSgMWrSBk9Fg86q+sd6s7y6uJhIvvIPz++RElGlMtEqsdAR2oWwZ/WTEtp7P9xFbJRrT976oRgzJ/w== + dependencies: + google-p12-pem "^0.1.0" + jws "^3.0.0" + mime "^1.4.1" + request "^2.72.0" + +gulp-header@^1.7.1: + version "1.8.12" + resolved "https://registry.yarnpkg.com/gulp-header/-/gulp-header-1.8.12.tgz#ad306be0066599127281c4f8786660e705080a84" + integrity sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ== + dependencies: + concat-with-sourcemaps "*" + lodash.template "^4.4.0" + through2 "^2.0.0" + +handlebars-utils@^1.0.2, handlebars-utils@^1.0.4, handlebars-utils@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/handlebars-utils/-/handlebars-utils-1.0.6.tgz#cb9db43362479054782d86ffe10f47abc76357f9" + integrity sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw== + dependencies: + kind-of "^6.0.0" + typeof-article "^0.1.1" + +handlebars@^4.7.6, handlebars@^4.7.7: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -5364,6 +5688,14 @@ has-value@^1.0.0: has-values "^1.0.0" isobject "^3.0.0" +has-value@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-2.0.2.tgz#d0f12e8780ba8e90e66ad1a21c707fdb67c25658" + integrity sha512-ybKOlcRsK2MqrM3Hmz/lQxXHZ6ejzSPzpNabKB45jb5qDgJvKPa3SdapTsTLwEb9WltgWpOmNax7i+DzNOk4TA== + dependencies: + get-value "^3.0.0" + has-values "^2.0.1" + has-values@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" @@ -5377,6 +5709,13 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" +has-values@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-2.0.1.tgz#3876200ff86d8a8546a9264a952c17d5fc17579d" + integrity sha512-+QdH3jOmq9P8GfdjFg0eJudqx1FqU62NQJ4P16rOEHeRdl7ckgwn6uqQjzYE0ZoHVV/e5E2esuJ5Gl5+HUW19w== + dependencies: + kind-of "^6.0.2" + has-yarn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" @@ -5389,6 +5728,39 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +helper-date@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/helper-date/-/helper-date-1.0.1.tgz#12fedea3ad8e44a7ca4c4efb0ff4104a5120cffb" + integrity sha512-wU3VOwwTJvGr/w5rZr3cprPHO+hIhlblTJHD6aFBrKLuNbf4lAmkawd2iK3c6NbJEvY7HAmDpqjOFSI5/+Ey2w== + dependencies: + date.js "^0.3.1" + handlebars-utils "^1.0.4" + moment "^2.18.1" + +helper-markdown@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/helper-markdown/-/helper-markdown-1.0.0.tgz#ee7e9fc554675007d37eb90f7853b13ce74f3e10" + integrity sha512-AnDqMS4ejkQK0MXze7pA9TM3pu01ZY+XXsES6gEE0RmCGk5/NIfvTn0NmItfyDOjRAzyo9z6X7YHbHX4PzIvOA== + dependencies: + handlebars-utils "^1.0.2" + highlight.js "^9.12.0" + remarkable "^1.7.1" + +helper-md@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/helper-md/-/helper-md-0.2.2.tgz#c1f59d7e55bbae23362fd8a0e971607aec69d41f" + integrity sha1-wfWdflW7riM2L9ig6XFgeuxp1B8= + dependencies: + ent "^2.2.0" + extend-shallow "^2.0.1" + fs-exists-sync "^0.1.0" + remarkable "^1.6.2" + +highlight.js@^9.12.0: + version "9.18.5" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.5.tgz#d18a359867f378c138d6819edfc2a8acd5f29825" + integrity sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA== + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -5418,6 +5790,14 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +html-tag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/html-tag/-/html-tag-2.0.0.tgz#36c3bc8d816fd30b570d5764a497a641640c2fed" + integrity sha512-XxzooSo6oBoxBEUazgjdXj7VwTn/iSTSZzTYKzYY6I916tkaYzypHxy+pbVU1h+0UQ9JlVf5XkNQyxOAiiQO1g== + dependencies: + is-self-closing "^1.0.1" + kind-of "^6.0.0" + http-assert@^1.3.0: version "1.5.0" resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f" @@ -5701,6 +6081,23 @@ ioredis@^4.27.0: redis-parser "^3.0.0" standard-as-callback "^2.1.0" +ioredis@^4.27.1: + version "4.28.0" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.28.0.tgz#5a2be3f37ff2075e2332f280eaeb02ab4d9ff0d3" + integrity sha512-I+zkeeWp3XFgPT2CtJKxvaF5FjGBGt4yGYljRjQecdQKteThuAsKqffeF1lgHVlYnuNeozRbPOCDNZ7tDWPeig== + dependencies: + cluster-key-slot "^1.1.0" + debug "^4.3.1" + denque "^1.1.0" + lodash.defaults "^4.2.0" + lodash.flatten "^4.4.0" + lodash.isarguments "^3.1.0" + p-map "^2.1.0" + redis-commands "1.7.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + standard-as-callback "^2.1.0" + ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -5832,6 +6229,13 @@ is-docker@^2.0.0: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-even@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-even/-/is-even-1.0.0.tgz#76b5055fbad8d294a86b6a949015e1c97b717c06" + integrity sha1-drUFX7rY0pSoa2qUkBXhyXtxfAY= + dependencies: + is-odd "^0.1.2" + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -5921,6 +6325,13 @@ is-number-object@^1.0.4: dependencies: has-tostringtag "^1.0.0" +is-number@^2.0.2: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= + dependencies: + kind-of "^3.0.2" + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -5943,6 +6354,13 @@ is-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== +is-odd@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-0.1.2.tgz#bc573b5ce371ef2aad6e6f49799b72bef13978a7" + integrity sha1-vFc7XONx7yqtbm9JeZtyvvE5eKc= + dependencies: + is-number "^3.0.0" + is-path-inside@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" @@ -5978,6 +6396,13 @@ is-retry-allowed@^1.1.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== +is-self-closing@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-self-closing/-/is-self-closing-1.0.1.tgz#5f406b527c7b12610176320338af0fa3896416e4" + integrity sha512-E+60FomW7Blv5GXTlYee2KDrnG6srxF7Xt1SjrhWUGUEsTFIqY/nq2y3DaftCsgUMdh89V07IVfhY9KIJhLezg== + dependencies: + self-closing-tags "^1.0.1" + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -6053,6 +6478,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +isobject@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-0.2.0.tgz#a3432192f39b910b5f02cc989487836ec70aa85e" + integrity sha1-o0MhkvObkQtfAsyYlIeDbscKqF4= + isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -7053,6 +7483,22 @@ jsonschema@1.4.0: resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.0.tgz#1afa34c4bc22190d8e42271ec17ac8b3404f87b2" integrity sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw== +jsonwebtoken@^8.2.0, jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -7092,7 +7538,7 @@ jwa@^1.4.1: ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" -jws@3.x.x: +jws@3.x.x, jws@^3.0.0, jws@^3.1.4, jws@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== @@ -7119,7 +7565,7 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.1.0, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= @@ -7133,12 +7579,12 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" -kind-of@^5.0.0: +kind-of@^5.0.0, kind-of@^5.0.2: version "5.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== -kind-of@^6.0.0, kind-of@^6.0.2: +kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -7217,6 +7663,13 @@ koa-is-json@^1.0.0: resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14" integrity sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ= +koa-passport@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/koa-passport/-/koa-passport-4.1.4.tgz#5f1665c1c2a37ace79af9f970b770885ca30ccfa" + integrity sha512-dJBCkl4X+zdYxbI2V2OtoGy0PUenpvp2ZLLWObc8UJhsId0iQpTFT8RVcuA0709AL2txGwRHnSPoT1bYNGa6Kg== + dependencies: + passport "^0.4.0" + koa-pino-logger@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/koa-pino-logger/-/koa-pino-logger-3.0.0.tgz#27600b4f3639e8767dfc6b66493109c5457f53ba" @@ -7498,6 +7951,11 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -7513,16 +7971,46 @@ lodash.flatten@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + lodash.isarguments@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + lodash.keys@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.2.0.tgz#a08602ac12e4fb83f91fc1fb7a360a4d9ba35205" @@ -7533,11 +8021,21 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.noop@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-3.0.1.tgz#38188f4d650a3a474258439b96ec45b32617133c" + integrity sha1-OBiPTWUKOkdCWEObluxFsyYXEzw= + lodash.omit@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60" integrity sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA= +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + lodash.pick@^4.0.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" @@ -7548,6 +8046,21 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= +lodash.template@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.without@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" @@ -7558,7 +8071,7 @@ lodash.xor@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.xor/-/lodash.xor-4.5.0.tgz#4d48ed7e98095b0632582ba714d3ff8ae8fb1db6" integrity sha1-TUjtfpgJWwYyWCunFNP/iuj7HbY= -lodash@4.17.21, lodash@4.x, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.7.0: +lodash@4.17.21, lodash@4.x, lodash@^4.14.0, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -7728,7 +8241,7 @@ methods@^1.0.1, methods@^1.1.1, methods@^1.1.2, methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromatch@^3.1.10, micromatch@^3.1.4: +micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.5: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -7817,6 +8330,11 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" @@ -7836,7 +8354,7 @@ moment-timezone@^0.5.31: dependencies: moment ">= 2.9.0" -"moment@>= 2.9.0": +"moment@>= 2.9.0", moment@^2.18.1: version "2.29.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== @@ -7930,6 +8448,11 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== +nanoid@^2.1.0: + version "2.1.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" + integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -7977,6 +8500,11 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +neo-async@^2.6.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -7997,6 +8525,11 @@ node-fetch@^2.6.0, node-fetch@^2.6.1: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== +node-forge@^0.7.1: + version "0.7.6" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" + integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== + node-gyp-build@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" @@ -8108,6 +8641,11 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== +oauth@0.9.x, oauth@^0.9.15: + version "0.9.15" + resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" + integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE= + object-assign@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" @@ -8405,6 +8943,84 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= +passport-google-auth@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/passport-google-auth/-/passport-google-auth-1.0.2.tgz#8b300b5aa442ef433de1d832ed3112877d0b2938" + integrity sha1-izALWqRC70M94dgy7TESh30LKTg= + dependencies: + googleapis "^16.0.0" + passport-strategy "1.x" + +passport-google-oauth1@1.x.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/passport-google-oauth1/-/passport-google-oauth1-1.0.0.tgz#af74a803df51ec646f66a44d82282be6f108e0cc" + integrity sha1-r3SoA99R7GRvZqRNgigr5vEI4Mw= + dependencies: + passport-oauth1 "1.x.x" + +passport-google-oauth20@2.x.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz#0d241b2d21ebd3dc7f2b60669ec4d587e3a674ef" + integrity sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ== + dependencies: + passport-oauth2 "1.x.x" + +passport-google-oauth@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/passport-google-oauth/-/passport-google-oauth-2.0.0.tgz#f6eb4bc96dd6c16ec0ecfdf4e05ec48ca54d4dae" + integrity sha512-JKxZpBx6wBQXX1/a1s7VmdBgwOugohH+IxCy84aPTZNq/iIPX6u7Mqov1zY7MKRz3niFPol0KJz8zPLBoHKtYA== + dependencies: + passport-google-oauth1 "1.x.x" + passport-google-oauth20 "2.x.x" + +passport-jwt@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/passport-jwt/-/passport-jwt-4.0.0.tgz#7f0be7ba942e28b9f5d22c2ebbb8ce96ef7cf065" + integrity sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg== + dependencies: + jsonwebtoken "^8.2.0" + passport-strategy "^1.0.0" + +passport-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee" + integrity sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4= + dependencies: + passport-strategy "1.x.x" + +passport-oauth1@1.x.x: + version "1.2.0" + resolved "https://registry.yarnpkg.com/passport-oauth1/-/passport-oauth1-1.2.0.tgz#5229d431781bf5b265bec86ce9a9cce58a756cf9" + integrity sha512-Sv2YWodC6jN12M/OXwmR4BIXeeIHjjbwYTQw4kS6tHK4zYzSEpxBgSJJnknBjICA5cj0ju3FSnG1XmHgIhYnLg== + dependencies: + oauth "0.9.x" + passport-strategy "1.x.x" + utils-merge "1.x.x" + +passport-oauth2@1.x.x: + version "1.6.1" + resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.6.1.tgz#c5aee8f849ce8bd436c7f81d904a3cd1666f181b" + integrity sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ== + dependencies: + base64url "3.x.x" + oauth "0.9.x" + passport-strategy "1.x.x" + uid2 "0.0.x" + utils-merge "1.x.x" + +passport-strategy@1.x, passport-strategy@1.x.x, passport-strategy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" + integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ= + +passport@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/passport/-/passport-0.4.1.tgz#941446a21cb92fc688d97a0861c38ce9f738f270" + integrity sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg== + dependencies: + passport-strategy "1.x.x" + pause "0.0.1" + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -8459,6 +9075,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pause@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d" + integrity sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10= + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -9171,7 +9792,7 @@ readable-stream@1.1.14, readable-stream@^1.0.27-1: isarray "0.0.1" string_decoder "~0.10.x" -"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.0.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.0.1, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -9305,6 +9926,16 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexparam@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-2.0.0.tgz#059476767d5f5f87f735fc7922d133fd1a118c8c" + integrity sha512-gJKwd2MVPWHAIFLsaYDZfyKzHNS4o7E/v8YmNf44vmeV2e4YfVoDToTOKTvE7ab68cRJ++kLuEXJBaEeJVt5ow== + +regexparam@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-1.3.0.tgz#2fe42c93e32a40eff6235d635e0ffa344b92965f" + integrity sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g== + regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" @@ -9348,6 +9979,21 @@ regjsparser@^0.6.4: dependencies: jsesc "~0.5.0" +relative@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/relative/-/relative-3.0.2.tgz#0dcd8ec54a5d35a3c15e104503d65375b5a5367f" + integrity sha1-Dc2OxUpdNaPBXhBFA9ZTdbWlNn8= + dependencies: + isobject "^2.0.0" + +remarkable@^1.6.2, remarkable@^1.7.1: + version "1.7.4" + resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-1.7.4.tgz#19073cb960398c87a7d6546eaa5e50d2022fcd00" + integrity sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg== + dependencies: + argparse "^1.0.10" + autolinker "~0.28.0" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -9384,7 +10030,7 @@ request-promise-native@^1.0.5: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.87.0: +request@^2.72.0, request@^2.74.0, request@^2.87.0, request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -9576,6 +10222,11 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" +sanitize-s3-objectkey@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/sanitize-s3-objectkey/-/sanitize-s3-objectkey-0.0.1.tgz#efa9887cd45275b40234fb4bb12fc5754fe64e7e" + integrity sha512-ZTk7aqLxy4sD40GWcYWoLfbe05XLmkKvh6vGKe13ADlei24xlezcvjgKy1qRArlaIbIMYaqK7PCalvZtulZlaQ== + saslprep@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226" @@ -9588,7 +10239,7 @@ sax@1.2.1: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o= -sax@>=0.6.0, sax@^1.2.4: +sax@>=0.1.1, sax@>=0.6.0, sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -9612,6 +10263,11 @@ seek-bzip@^1.0.5: dependencies: commander "^2.8.1" +self-closing-tags@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/self-closing-tags/-/self-closing-tags-1.0.1.tgz#6c5fa497994bb826b484216916371accee490a5d" + integrity sha512-7t6hNbYMxM+VHXTgJmxwgZgLGktuXtVVD5AivWzNTdJBM4DBjnDKDzkf2SrNjihaArpeJYNjxkELBu1evI4lQA== + semver-diff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" @@ -9619,7 +10275,7 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -9745,6 +10401,18 @@ shell-path@^2.1.0: dependencies: shell-env "^0.3.0" +shimmer@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" + integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== + +shortid@^2.2.15: + version "2.2.16" + resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.16.tgz#b742b8f0cb96406fd391c76bfc18a67a57fe5608" + integrity sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g== + dependencies: + nanoid "^2.1.0" + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -9989,6 +10657,11 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" +stack-chain@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" + integrity sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU= + stack-utils@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.5.tgz#a19b0b01947e0029c8e451d5d61a498f5bb1471b" @@ -10031,6 +10704,11 @@ stealthy-require@^1.1.1: resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= +step@0.0.x: + version "0.0.6" + resolved "https://registry.yarnpkg.com/step/-/step-0.0.6.tgz#143e7849a5d7d3f4a088fe29af94915216eeede2" + integrity sha1-FD54SaXX0/SgiP4pr5SRUhbu7eI= + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -10044,6 +10722,11 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-template@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96" + integrity sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y= + string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -10169,6 +10852,11 @@ strip-outer@^1.0.0: dependencies: escape-string-regexp "^1.0.2" +striptags@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/striptags/-/striptags-3.2.0.tgz#cc74a137db2de8b0b9a370006334161f7dd67052" + integrity sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw== + sublevel-pouchdb@7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/sublevel-pouchdb/-/sublevel-pouchdb-7.2.2.tgz#49e46cd37883bf7ff5006d7c5b9bcc7bcc1f422f" @@ -10253,11 +10941,25 @@ svelte-flatpickr@^3.1.0: dependencies: flatpickr "^4.5.2" +svelte-flatpickr@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/svelte-flatpickr/-/svelte-flatpickr-3.2.3.tgz#db5dd7ad832ef83262b45e09737955ad3d591fc8" + integrity sha512-PNkqK4Napx8nTvCwkaUXdnKo8dISThaxEOK+szTUXcY6H0dQM0TSyuoMaVWY2yX7pM+PN5cpCQCcVe8YvTRFSw== + dependencies: + flatpickr "^4.5.2" + svelte-portal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/svelte-portal/-/svelte-portal-1.0.0.tgz#36a47c5578b1a4d9b4dc60fa32a904640ec4cdd3" integrity sha512-nHf+DS/jZ6jjnZSleBMSaZua9JlG5rZv9lOGKgJuaZStfevtjIlUJrkLc3vbV8QdBvPPVmvcjTlazAzfKu0v3Q== +svelte-spa-router@^3.0.5: + version "3.2.0" + resolved "https://registry.yarnpkg.com/svelte-spa-router/-/svelte-spa-router-3.2.0.tgz#fae3311d292451236cb57131262406cf312b15ee" + integrity sha512-igemo5Vs82TGBBw+DjWt6qKameXYzNs6aDXcTxou5XbEvOjiRcAM6MLkdVRCatn6u8r42dE99bt/br7T4qe/AQ== + dependencies: + regexparam "2.0.0" + svelte@^3.38.2: version "3.42.4" resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.42.4.tgz#838ed98fa7b26fc5fffe4df0d7ba345f1c54cf4f" @@ -10333,6 +11035,16 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" +tar-fs@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + tar-stream@^1.5.2: version "1.6.2" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" @@ -10346,6 +11058,17 @@ tar-stream@^1.5.2: to-buffer "^1.1.1" xtend "^4.0.0" +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + tarn@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/tarn/-/tarn-1.1.5.tgz#7be88622e951738b9fa3fb77477309242cdddc2d" @@ -10517,6 +11240,11 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= +to-gfm-code-block@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/to-gfm-code-block/-/to-gfm-code-block-0.1.1.tgz#25d045a5fae553189e9637b590900da732d8aa82" + integrity sha1-JdBFpfrlUxielje1kJANpzLYqoI= + to-json-schema@0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/to-json-schema/-/to-json-schema-0.2.5.tgz#ef3c3f11ad64460dcfbdbafd0fd525d69d62a98f" @@ -10728,11 +11456,28 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +typeof-article@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/typeof-article/-/typeof-article-0.1.1.tgz#9f07e733c3fbb646ffa9e61c08debacd460e06af" + integrity sha1-nwfnM8P7tkb/qeYcCN66zUYOBq8= + dependencies: + kind-of "^3.1.0" + typescript@^4.3.5: version "4.3.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== +uglify-js@^3.1.4: + version "3.14.2" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.2.tgz#d7dd6a46ca57214f54a2d0a43cad0f35db82ac99" + integrity sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A== + +uid2@0.0.x: + version "0.0.4" + resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.4.tgz#033f3b1d5d32505f5ce5f888b9f3b667123c0a44" + integrity sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA== + unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" @@ -10924,7 +11669,7 @@ util.promisify@^1.0.0, util.promisify@^1.0.1: has-symbols "^1.0.1" object.getownpropertydescriptors "^2.1.1" -utils-merge@1.0.1: +utils-merge@1.0.1, utils-merge@1.x.x: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= @@ -10996,9 +11741,9 @@ verror@1.10.0: extsprintf "^1.2.0" vm2@^3.9.3: - version "3.9.3" - resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.3.tgz#29917f6cc081cc43a3f580c26c5b553fd3c91f40" - integrity sha512-smLS+18RjXYMl9joyJxMNI9l4w7biW8ilSDaVRvFBDwOH8P0BK1ognFQTpg0wyQ6wIKLTblHJvROW692L/E53Q== + version "3.9.4" + resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.4.tgz#2e118290fefe7bd8ea09ebe2f5faf53730dbddaa" + integrity sha512-sOdharrJ7KEePIpHekiWaY1DwgueuiBeX/ZBJUPgETsVlJsXuEx0K0/naATq2haFvJrvZnRiORQRubR0b7Ye6g== vuvuzela@1.0.3: version "1.0.3" @@ -11026,6 +11771,14 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" +webfinger@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/webfinger/-/webfinger-0.4.2.tgz#3477a6d97799461896039fcffc650b73468ee76d" + integrity sha1-NHem2XeZRhiWA5/P/GULc0aO520= + dependencies: + step "0.0.x" + xml2js "0.1.x" + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" @@ -11122,6 +11875,11 @@ word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -11220,6 +11978,13 @@ xml-parse-from-string@^1.0.0: resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28" integrity sha1-qQKekp09vN7RafPG4oI42VpdWig= +xml2js@0.1.x: + version "0.1.14" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.1.14.tgz#5274e67f5a64c5f92974cd85139e0332adc6b90c" + integrity sha1-UnTmf1pkxfkpdM2FE54DMq3GuQw= + dependencies: + sax ">=0.1.1" + xml2js@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" @@ -11358,6 +12123,11 @@ yauzl@^2.4.2: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" +year@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/year/-/year-0.2.1.tgz#4083ae520a318b23ec86037f3000cb892bdf9bb0" + integrity sha1-QIOuUgoxiyPshgN/MADLiSvfm7A= + ylru@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f" @@ -11368,7 +12138,7 @@ yn@3.1.1: resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== -zlib@1.0.5: +zlib@1.0.5, zlib@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/zlib/-/zlib-1.0.5.tgz#6e7c972fc371c645a6afb03ab14769def114fcc0" integrity sha1-bnyXL8NxxkWmr7A6sUdp3vEU/MA= From a23e1a87e11294e371685b2eedb1ec93aa314427 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 26 Oct 2021 15:11:14 +0200 Subject: [PATCH 002/100] allow opening a template directly from a URL --- .../pages/builder/portal/apps/index.svelte | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index a0b75a3a2e..8a3b6c41b1 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -18,7 +18,7 @@ import { onMount } from "svelte" import { apps, auth, admin } from "stores/portal" import download from "downloadjs" - import { goto } from "@roxi/routify" + import { goto, params } from "@roxi/routify" import ConfirmDialog from "components/common/ConfirmDialog.svelte" import AppCard from "components/start/AppCard.svelte" import AppRow from "components/start/AppRow.svelte" @@ -184,9 +184,27 @@ } } + function createAppFromTemplateUrl(templateKey) { + // validate the template key just to make sure + const templateParts = templateKey.split("/") + if (templateParts.length === 2 && templateParts[0] === "app") { + template = { + key: templateKey, + } + initiateAppCreation() + } else { + notifications.error("Your Template URL is invalid. Please try another.") + } + } + onMount(async () => { await apps.load() loaded = true + + // if the portal is loaded from an external URL + const templateKey = $params["?template"] + if (!templateKey) return + createAppFromTemplateUrl(templateKey) }) From 484a38c9f5c71e168007981eed288d2162c89afb Mon Sep 17 00:00:00 2001 From: Budibase Release Bot <> Date: Wed, 27 Oct 2021 15:13:34 +0000 Subject: [PATCH 003/100] v0.9.173 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 6 +++--- packages/server/package.json | 8 ++++---- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lerna.json b/lerna.json index aebd39acab..d879d3c65c 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.173-alpha.3", + "version": "0.9.173", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 5f6866e9ed..cecb16d00a 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.173-alpha.3", + "version": "0.9.173", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 80bacada00..8cd9b14520 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.173-alpha.3", + "version": "0.9.173", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index d262839f12..1b337e2baf 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.173-alpha.3", + "version": "0.9.173", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.173-alpha.3", - "@budibase/client": "^0.9.173-alpha.3", + "@budibase/bbui": "^0.9.173", + "@budibase/client": "^0.9.173", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.173-alpha.3", + "@budibase/string-templates": "^0.9.173", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index bdf01c50b4..0ed0c98266 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.173-alpha.3", + "version": "0.9.173", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 7cca15572a..9edca0f2a3 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.173-alpha.3", + "version": "0.9.173", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.173-alpha.3", + "@budibase/bbui": "^0.9.173", "@budibase/standard-components": "^0.9.139", - "@budibase/string-templates": "^0.9.173-alpha.3", + "@budibase/string-templates": "^0.9.173", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index 2945578e71..a1271be56b 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.173-alpha.3", + "version": "0.9.173", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -68,9 +68,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.173-alpha.3", - "@budibase/client": "^0.9.173-alpha.3", - "@budibase/string-templates": "^0.9.173-alpha.3", + "@budibase/auth": "^0.9.173", + "@budibase/client": "^0.9.173", + "@budibase/string-templates": "^0.9.173", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index a3b90b5951..1906bfde0d 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.173-alpha.3", + "version": "0.9.173", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index cf489d2d98..05a322c822 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.173-alpha.3", + "version": "0.9.173", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -29,8 +29,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.173-alpha.3", - "@budibase/string-templates": "^0.9.173-alpha.3", + "@budibase/auth": "^0.9.173", + "@budibase/string-templates": "^0.9.173", "@koa/router": "^8.0.0", "@sentry/node": "^6.0.0", "@techpass/passport-openidconnect": "^0.3.0", From 0ddd689b893ff05010ff2c4920fe035e4502fb79 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 27 Oct 2021 16:21:19 +0100 Subject: [PATCH 004/100] Postgres: Use another schema other than 'public' --- packages/server/src/integrations/postgres.ts | 21 ++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/server/src/integrations/postgres.ts b/packages/server/src/integrations/postgres.ts index abd1fa9286..3cf1bdb3e0 100644 --- a/packages/server/src/integrations/postgres.ts +++ b/packages/server/src/integrations/postgres.ts @@ -27,6 +27,7 @@ module PostgresModule { database: string user: string password: string + schema: string ssl?: boolean ca?: string rejectUnauthorized?: boolean @@ -64,6 +65,11 @@ module PostgresModule { default: "root", required: true, }, + schema: { + type: DatasourceFieldTypes.STRING, + default: "public", + required: true, + }, ssl: { type: DatasourceFieldTypes.BOOLEAN, default: false, @@ -137,8 +143,7 @@ module PostgresModule { private readonly client: any private readonly config: PostgresConfig - COLUMNS_SQL = - "select * from information_schema.columns where not table_schema = 'information_schema' and not table_schema = 'pg_catalog'" + COLUMNS_SQL!: string PRIMARY_KEYS_SQL = ` select tc.table_schema, tc.table_name, kc.column_name as primary_key @@ -168,6 +173,18 @@ module PostgresModule { } this.client = this.pool + this.setSchema() + + } + + setSchema() { + if (!this.config.schema) { + this.config.schema = 'public' + } + this.client.on('connect', (client: any) => { + client.query(`SET search_path TO ${this.config.schema}`); + }); + this.COLUMNS_SQL = `select * from information_schema.columns where table_schema = '${this.config.schema}'` } /** From 8b8c923541b48157f7f65ff2d7a14fdc6f9e5051 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 27 Oct 2021 17:21:29 +0100 Subject: [PATCH 005/100] Update tests --- packages/server/__mocks__/pg.ts | 3 +++ packages/server/src/integrations/tests/postgres.spec.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/packages/server/__mocks__/pg.ts b/packages/server/__mocks__/pg.ts index 0ec823ea39..ba7803f9a8 100644 --- a/packages/server/__mocks__/pg.ts +++ b/packages/server/__mocks__/pg.ts @@ -21,15 +21,18 @@ module PgMock { function Pool() { } + const on = jest.fn() Pool.prototype.query = query Pool.prototype.connect = jest.fn(() => { // @ts-ignore return new Client() }) + Pool.prototype.on = on pg.Client = Client pg.Pool = Pool pg.queryMock = query + pg.on = on module.exports = pg } diff --git a/packages/server/src/integrations/tests/postgres.spec.js b/packages/server/src/integrations/tests/postgres.spec.js index 4ce5f12e96..5c0d086ce0 100644 --- a/packages/server/src/integrations/tests/postgres.spec.js +++ b/packages/server/src/integrations/tests/postgres.spec.js @@ -15,6 +15,10 @@ describe("Postgres Integration", () => { config = new TestConfiguration() }) + it("calls the connection callback", async () => { + expect(pg.on).toHaveBeenCalledWith('connect', expect.anything()) + }) + it("calls the create method with the correct params", async () => { const sql = "insert into users (name, age) values ('Joe', 123);" await config.integration.create({ From c210802b4ce0154f98859bcaca42d0b38f67d658 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 27 Oct 2021 17:23:52 +0100 Subject: [PATCH 006/100] Remove whitespace --- packages/server/src/integrations/postgres.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/server/src/integrations/postgres.ts b/packages/server/src/integrations/postgres.ts index 3cf1bdb3e0..237ffc4c69 100644 --- a/packages/server/src/integrations/postgres.ts +++ b/packages/server/src/integrations/postgres.ts @@ -174,7 +174,6 @@ module PostgresModule { this.client = this.pool this.setSchema() - } setSchema() { From 09d83dea399cfea2d44c92f8989d6a2c84f63e6b Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Oct 2021 17:32:02 +0100 Subject: [PATCH 007/100] Adding docker compose for ms-sql with products, tasks table setup. --- .../integrations/mssql/data/Dockerfile | 9 +++++ .../integrations/mssql/data/entrypoint.sh | 24 +++++++++++++ .../scripts/integrations/mssql/data/setup.sql | 34 +++++++++++++++++++ .../integrations/mssql/docker-compose.yaml | 12 +++++++ .../scripts/integrations/mssql/reset.sh | 3 ++ 5 files changed, 82 insertions(+) create mode 100644 packages/server/scripts/integrations/mssql/data/Dockerfile create mode 100644 packages/server/scripts/integrations/mssql/data/entrypoint.sh create mode 100644 packages/server/scripts/integrations/mssql/data/setup.sql create mode 100644 packages/server/scripts/integrations/mssql/docker-compose.yaml create mode 100755 packages/server/scripts/integrations/mssql/reset.sh diff --git a/packages/server/scripts/integrations/mssql/data/Dockerfile b/packages/server/scripts/integrations/mssql/data/Dockerfile new file mode 100644 index 0000000000..8ac56409a0 --- /dev/null +++ b/packages/server/scripts/integrations/mssql/data/Dockerfile @@ -0,0 +1,9 @@ +FROM mcr.microsoft.com/mssql/server + +ENV ACCEPT_EULA=Y +ENV SA_PASSWORD=Passw0rd + +COPY ./data / + +ENTRYPOINT [ "/bin/bash", "entrypoint.sh" ] +CMD [ "/opt/mssql/bin/sqlservr" ] diff --git a/packages/server/scripts/integrations/mssql/data/entrypoint.sh b/packages/server/scripts/integrations/mssql/data/entrypoint.sh new file mode 100644 index 0000000000..04780d085e --- /dev/null +++ b/packages/server/scripts/integrations/mssql/data/entrypoint.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -e + +if [ "$1" = '/opt/mssql/bin/sqlservr' ]; then + # If this is the container's first run, initialize the application database + if [ ! -f /tmp/app-initialized ]; then + # Initialize the application database asynchronously in a background process. This allows a) the SQL Server process to be the main process in the container, which allows graceful shutdown and other goodies, and b) us to only start the SQL Server process once, as opposed to starting, stopping, then starting it again. + function initialize_app_database() { + # Wait a bit for SQL Server to start. SQL Server's process doesn't provide a clever way to check if it's up or not, and it needs to be up before we can import the application database + sleep 30s + + echo "RUNNING BUDIBASE SETUP" + + #run the setup script to create the DB and the schema in the DB + /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P Passw0rd -i setup.sql + + # Note that the container has been initialized so future starts won't wipe changes to the data + touch /tmp/app-initialized + } + initialize_app_database & + fi +fi + +exec "$@" diff --git a/packages/server/scripts/integrations/mssql/data/setup.sql b/packages/server/scripts/integrations/mssql/data/setup.sql new file mode 100644 index 0000000000..f6c94ee2c1 --- /dev/null +++ b/packages/server/scripts/integrations/mssql/data/setup.sql @@ -0,0 +1,34 @@ +USE master; + +IF OBJECT_ID ('dbo.products', 'U') IS NOT NULL + DROP TABLE products; +GO +CREATE TABLE products +( + id int IDENTITY(1,1), + name varchar (20), + description varchar(30) +); +IF OBJECT_ID ('dbo.tasks', 'U') IS NOT NULL + DROP TABLE tasks; +GO +CREATE TABLE tasks +( + taskid int IDENTITY(1,1), + taskname varchar (20) +); + +INSERT products + (name, description) +VALUES + ('Bananas', 'Fruit thing'); + +INSERT products + (name, description) +VALUES + ('Meat', 'Animal thing'); + +INSERT tasks + (taskname) +VALUES + ('Processing'); diff --git a/packages/server/scripts/integrations/mssql/docker-compose.yaml b/packages/server/scripts/integrations/mssql/docker-compose.yaml new file mode 100644 index 0000000000..89222eddaa --- /dev/null +++ b/packages/server/scripts/integrations/mssql/docker-compose.yaml @@ -0,0 +1,12 @@ +version: "3.8" +services: + # password: Passw0rd + # user: sa + # database: master + mssql: + image: bb/mssql + build: + context: . + dockerfile: data/Dockerfile + ports: + - "1433:1433" diff --git a/packages/server/scripts/integrations/mssql/reset.sh b/packages/server/scripts/integrations/mssql/reset.sh new file mode 100755 index 0000000000..32778bd11f --- /dev/null +++ b/packages/server/scripts/integrations/mssql/reset.sh @@ -0,0 +1,3 @@ +#!/bin/bash +docker-compose down +docker volume prune -f From 5e846ac64d644bcba0dc6a1a48610cce7105a909 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Oct 2021 18:36:27 +0100 Subject: [PATCH 008/100] First version of schema generation for ms-sql, able to retrieve basic tables and rows. --- .../src/integrations/microsoftSqlServer.ts | 103 +++++++++++++++++- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/packages/server/src/integrations/microsoftSqlServer.ts b/packages/server/src/integrations/microsoftSqlServer.ts index 2c0108acf4..d97c5f36b6 100644 --- a/packages/server/src/integrations/microsoftSqlServer.ts +++ b/packages/server/src/integrations/microsoftSqlServer.ts @@ -6,10 +6,18 @@ import { SqlQuery, } from "../definitions/datasource" import { getSqlQuery } from "./utils" +import { DatasourcePlus } from "./base/datasourcePlus" +import { Table, TableSchema } from "../definitions/common"; module MSSQLModule { const sqlServer = require("mssql") const Sql = require("./base/sql") + const { FieldTypes } = require("../constants") + const { + buildExternalTableId, + convertType, + finaliseExternalTables, + } = require("./utils") interface MSSQLConfig { user: string @@ -22,6 +30,7 @@ module MSSQLModule { const SCHEMA: Integration = { docs: "https://github.com/tediousjs/node-mssql", + plus: true, description: "Microsoft SQL Server is a relational database management system developed by Microsoft. ", friendlyName: "MS SQL Server", @@ -69,18 +78,66 @@ module MSSQLModule { }, } + // TODO: need to update this + const TYPE_MAP = { + text: FieldTypes.LONGFORM, + blob: FieldTypes.LONGFORM, + enum: FieldTypes.STRING, + varchar: FieldTypes.STRING, + float: FieldTypes.NUMBER, + int: FieldTypes.NUMBER, + numeric: FieldTypes.NUMBER, + bigint: FieldTypes.NUMBER, + mediumint: FieldTypes.NUMBER, + decimal: FieldTypes.NUMBER, + dec: FieldTypes.NUMBER, + double: FieldTypes.NUMBER, + real: FieldTypes.NUMBER, + fixed: FieldTypes.NUMBER, + smallint: FieldTypes.NUMBER, + timestamp: FieldTypes.DATETIME, + date: FieldTypes.DATETIME, + datetime: FieldTypes.DATETIME, + time: FieldTypes.DATETIME, + tinyint: FieldTypes.BOOLEAN, + json: DatasourceFieldTypes.JSON, + } + async function internalQuery(client: any, query: SqlQuery) { try { - return await client.query(query.sql, query.bindings || {}) + if (Array.isArray(query.bindings)) { + let count = 0 + for (let binding of query.bindings) { + client.input(`p${count++}`, binding) + } + } + return await client.query(query.sql) } catch (err) { // @ts-ignore throw new Error(err) } } - class SqlServerIntegration extends Sql { + class SqlServerIntegration extends Sql implements DatasourcePlus { private readonly config: MSSQLConfig static pool: any + public tables: Record = {} + public schemaErrors: Record = {} + + MASTER_TABLES = [ + "spt_fallback_db", + "spt_fallback_dev", + "spt_fallback_usg", + "spt_monitor", + "MSreplication_options" + ] + TABLES_SQL = "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'" + + getDefinitionSQL(tableName: string) { + return `select * + from INFORMATION_SCHEMA.COLUMNS + where TABLE_NAME='${tableName}'` + } constructor(config: MSSQLConfig) { super("mssql") @@ -89,6 +146,7 @@ module MSSQLModule { ...this.config, options: { encrypt: this.config.encrypt, + enableArithAbort: true, }, } delete clientCfg.encrypt @@ -107,6 +165,46 @@ module MSSQLModule { } } + /** + * Fetches the tables from the sql server database and assigns them to the datasource. + * @param {*} datasourceId - datasourceId to fetch + * @param entities - the tables that are to be built + */ + async buildSchema(datasourceId: string, entities: Record) { + + await this.connect() + let tableNames = await internalQuery(this.client, getSqlQuery(this.TABLES_SQL)) + if (tableNames == null || !Array.isArray(tableNames.recordset)) { + throw "Unable to get list of tables in database" + } + tableNames = tableNames.recordset.map((record: any) => record.TABLE_NAME).filter((name: string) => this.MASTER_TABLES.indexOf(name) === -1) + const tables: Record = {} + for (let tableName of tableNames) { + const definition = await internalQuery(this.client, getSqlQuery(this.getDefinitionSQL(tableName))) + let schema: TableSchema = {} + for (let def of definition.recordset) { + const name = def.COLUMN_NAME + if (typeof name !== "string") { + continue + } + const type: string = convertType(def.DATA_TYPE, TYPE_MAP) + const identity = false + schema[name] = { + autocolumn: identity, + name: name, + type, + } + } + tables[tableName] = { + _id: buildExternalTableId(datasourceId, tableName), + primary: ["id"], + name: tableName, + schema, + } + } + this.tables = tables + } + async read(query: SqlQuery | string) { await this.connect() const response = await internalQuery(this.client, getSqlQuery(query)) @@ -132,6 +230,7 @@ module MSSQLModule { } async query(json: QueryJson) { + await this.connect() const operation = this._operation(json).toLowerCase() const input = this._query(json) const response = await internalQuery(this.client, input) From 329156d6cc9c7b1a729f30391ad2ee518f31b462 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 28 Oct 2021 19:39:42 +0100 Subject: [PATCH 009/100] SQL table building. --- .../backend/DataTable/DataTable.svelte | 4 +- .../components/backend/DataTable/Table.svelte | 2 +- .../DataTable/modals/CreateEditColumn.svelte | 29 +++- .../popovers/EditTablePopover.svelte | 11 +- .../[selectedDatasource]/index.svelte | 21 +++ .../modals/CreateExternalTableModal.svelte | 44 +++++ packages/builder/src/stores/backend/tables.js | 3 + .../server/src/api/controllers/datasource.js | 4 +- .../api/controllers/row/ExternalRequest.ts | 22 +-- .../server/src/api/controllers/row/utils.js | 13 +- .../src/api/controllers/table/external.js | 116 +++++++++++++ .../server/src/api/controllers/table/index.js | 162 +++--------------- .../src/api/controllers/table/internal.js | 138 +++++++++++++++ packages/server/src/constants/index.js | 3 + packages/server/src/definitions/common.ts | 2 +- packages/server/src/definitions/datasource.ts | 5 + packages/server/src/integrations/base/sql.ts | 33 ++-- .../server/src/integrations/base/sqlTable.ts | 132 ++++++++++++++ .../server/src/integrations/base/utils.ts | 19 ++ packages/server/src/integrations/postgres.ts | 12 +- 20 files changed, 584 insertions(+), 191 deletions(-) create mode 100644 packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/modals/CreateExternalTableModal.svelte create mode 100644 packages/server/src/api/controllers/table/external.js create mode 100644 packages/server/src/api/controllers/table/internal.js create mode 100644 packages/server/src/integrations/base/sqlTable.ts create mode 100644 packages/server/src/integrations/base/utils.ts diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte index 336bb51670..1af703800f 100644 --- a/packages/builder/src/components/backend/DataTable/DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte @@ -98,9 +98,7 @@ on:updatecolumns={onUpdateColumns} on:updaterows={onUpdateRows} > - {#if isInternal} - - {/if} + {#if schema && Object.keys(schema).length > 0} {#if !isUsersTable} editColumn(e.detail)} on:editrow={e => editRow(e.detail)} diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index cd437bcad2..221d391cbf 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -56,7 +56,7 @@ let deletion $: tableOptions = $tables.list.filter( - table => table._id !== $tables.draft._id && table.type !== "external" + opt => opt._id !== $tables.draft._id && opt.type === table.type ) $: required = !!field?.constraints?.presence || primaryDisplay $: uneditable = @@ -83,6 +83,7 @@ $: canBeRequired = field.type !== LINK_TYPE && !uneditable && field.type !== AUTO_TYPE $: relationshipOptions = getRelationshipOptions(field) + $: external = table.type === "external" async function saveColumn() { if (field.type === AUTO_TYPE) { @@ -193,6 +194,27 @@ }, ] } + + function getAllowedTypes() { + if (!external) { + return [ + ...Object.values(fieldDefinitions), + { name: "Auto Column", type: AUTO_TYPE }, + ] + } else { + return [ + FIELDS.STRING, + FIELDS.LONGFORM, + FIELDS.OPTIONS, + FIELDS.DATETIME, + FIELDS.NUMBER, + FIELDS.BOOLEAN, + FIELDS.ARRAY, + FIELDS.FORMULA, + FIELDS.LINK, + ] + } + } field.name} getOptionValue={field => field.type} /> diff --git a/packages/builder/src/components/backend/TableNavigator/popovers/EditTablePopover.svelte b/packages/builder/src/components/backend/TableNavigator/popovers/EditTablePopover.svelte index 2513c6c7e5..04094b881a 100644 --- a/packages/builder/src/components/backend/TableNavigator/popovers/EditTablePopover.svelte +++ b/packages/builder/src/components/backend/TableNavigator/popovers/EditTablePopover.svelte @@ -1,7 +1,7 @@ @@ -130,6 +137,10 @@ + + + + {#if datasource && integration}
@@ -189,6 +200,11 @@ /> {/if}
+
+ + New table + +
{#each plusTables as table}
onClickTable(table)}>

{table.name}

@@ -325,4 +341,9 @@ .table-buttons div { grid-column-end: -1; } + + .add-table { + margin-right: 0; + margin-left: auto; + } diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/modals/CreateExternalTableModal.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/modals/CreateExternalTableModal.svelte new file mode 100644 index 0000000000..c7a040d89a --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/modals/CreateExternalTableModal.svelte @@ -0,0 +1,44 @@ + + + + Provide a name for your new table; you can add columns once it is created. + + diff --git a/packages/builder/src/stores/backend/tables.js b/packages/builder/src/stores/backend/tables.js index 161877f660..7f90a04a05 100644 --- a/packages/builder/src/stores/backend/tables.js +++ b/packages/builder/src/stores/backend/tables.js @@ -62,6 +62,9 @@ export function createTablesStore() { const response = await api.post(`/api/tables`, updatedTable) const savedTable = await response.json() await fetch() + if (table.type === "external") { + await datasources.fetch() + } await select(savedTable) return savedTable } diff --git a/packages/server/src/api/controllers/datasource.js b/packages/server/src/api/controllers/datasource.js index d75a8f8ced..604fee004e 100644 --- a/packages/server/src/api/controllers/datasource.js +++ b/packages/server/src/api/controllers/datasource.js @@ -9,7 +9,7 @@ const { } = require("../../db/utils") const { BuildSchemaErrors } = require("../../constants") const { integrations } = require("../../integrations") -const { makeExternalQuery } = require("./row/utils") +const { getDatasourceAndQuery } = require("./row/utils") exports.fetch = async function (ctx) { const database = new CouchDB(ctx.appId) @@ -138,7 +138,7 @@ exports.find = async function (ctx) { exports.query = async function (ctx) { const queryJson = ctx.request.body try { - ctx.body = await makeExternalQuery(ctx.appId, queryJson) + ctx.body = await getDatasourceAndQuery(ctx.appId, queryJson) } catch (err) { ctx.throw(400, err) } diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index c58364af07..f538e01f73 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -36,7 +36,7 @@ interface RunConfig { } module External { - const { makeExternalQuery } = require("./utils") + const { getDatasourceAndQuery } = require("./utils") const { DataSourceOperation, FieldTypes, @@ -46,6 +46,7 @@ module External { const { processObjectSync } = require("@budibase/string-templates") const { cloneDeep } = require("lodash/fp") const CouchDB = require("../../../db") + const { processFormulas } = require("../../../utilities/rowProcessor/utils") function buildFilters( id: string | undefined, @@ -225,7 +226,7 @@ module External { manyRelationships: ManyRelationship[] = [] for (let [key, field] of Object.entries(table.schema)) { // if set already, or not set just skip it - if ((!row[key] && row[key] !== "") || newRow[key] || field.autocolumn) { + if (row[key] == null || newRow[key] || field.autocolumn || field.type === FieldTypes.FORMULA) { continue } // if its an empty string then it means return the column to null (if possible) @@ -361,7 +362,7 @@ module External { relationships ) } - return Object.values(finalRows) + return processFormulas(table, Object.values(finalRows)) } /** @@ -428,7 +429,7 @@ module External { const tableId = isMany ? field.through : field.tableId const manyKey = field.throughFrom || primaryKey const fieldName = isMany ? manyKey : field.fieldName - const response = await makeExternalQuery(this.appId, { + const response = await getDatasourceAndQuery(this.appId, { endpoint: getEndpoint(tableId, DataSourceOperation.READ), filters: { equal: { @@ -479,7 +480,7 @@ module External { : DataSourceOperation.CREATE if (!found) { promises.push( - makeExternalQuery(appId, { + getDatasourceAndQuery(appId, { endpoint: getEndpoint(tableId, operation), // if we're doing many relationships then we're writing, only one response body, @@ -509,7 +510,7 @@ module External { : DataSourceOperation.UPDATE const body = isMany ? null : { [colName]: null } promises.push( - makeExternalQuery(this.appId, { + getDatasourceAndQuery(this.appId, { endpoint: getEndpoint(tableId, op), body, filters, @@ -532,16 +533,17 @@ module External { table: Table, includeRelations: IncludeRelationships = IncludeRelationships.INCLUDE ) { - function extractNonLinkFieldNames(table: Table, existing: string[] = []) { + function extractRealFields(table: Table, existing: string[] = []) { return Object.entries(table.schema) .filter( column => column[1].type !== FieldTypes.LINK && + column[1].type !== FieldTypes.FORMULA && !existing.find((field: string) => field === column[0]) ) .map(column => `${table.name}.${column[0]}`) } - let fields = extractNonLinkFieldNames(table) + let fields = extractRealFields(table) for (let field of Object.values(table.schema)) { if (field.type !== FieldTypes.LINK || !includeRelations) { continue @@ -549,7 +551,7 @@ module External { const { tableName: linkTableName } = breakExternalTableId(field.tableId) const linkTable = this.tables[linkTableName] if (linkTable) { - const linkedFields = extractNonLinkFieldNames(linkTable, fields) + const linkedFields = extractRealFields(linkTable, fields) fields = fields.concat(linkedFields) } } @@ -609,7 +611,7 @@ module External { }, } // can't really use response right now - const response = await makeExternalQuery(appId, json) + const response = await getDatasourceAndQuery(appId, json) // handle many to many relationships now if we know the ID (could be auto increment) if ( operation !== DataSourceOperation.READ && diff --git a/packages/server/src/api/controllers/row/utils.js b/packages/server/src/api/controllers/row/utils.js index ca6c782713..bd2df084a3 100644 --- a/packages/server/src/api/controllers/row/utils.js +++ b/packages/server/src/api/controllers/row/utils.js @@ -4,8 +4,8 @@ const CouchDB = require("../../../db") const { InternalTables } = require("../../../db/utils") const userController = require("../user") const { FieldTypes } = require("../../../constants") -const { integrations } = require("../../../integrations") const { processStringSync } = require("@budibase/string-templates") +const { makeExternalQuery } = require("../../../integrations/base/utils") validateJs.extend(validateJs.validators.datetime, { parse: function (value) { @@ -17,18 +17,11 @@ validateJs.extend(validateJs.validators.datetime, { }, }) -exports.makeExternalQuery = async (appId, json) => { +exports.getDatasourceAndQuery = async (appId, json) => { const datasourceId = json.endpoint.datasourceId const db = new CouchDB(appId) const datasource = await db.get(datasourceId) - const Integration = integrations[datasource.source] - // query is the opinionated function - if (Integration.prototype.query) { - const integration = new Integration(datasource.config) - return integration.query(json) - } else { - throw "Datasource does not support query." - } + return makeExternalQuery(datasource, json) } exports.findRow = async (ctx, db, tableId, rowId) => { diff --git a/packages/server/src/api/controllers/table/external.js b/packages/server/src/api/controllers/table/external.js new file mode 100644 index 0000000000..42454fd2e8 --- /dev/null +++ b/packages/server/src/api/controllers/table/external.js @@ -0,0 +1,116 @@ +const CouchDB = require("../../../db") +const { + buildExternalTableId, + breakExternalTableId, +} = require("../../../integrations/utils") +const { getTable } = require("./utils") +const { DataSourceOperation, FieldTypes } = require("../../../constants") +const { makeExternalQuery } = require("../../../integrations/base/utils") +const { cloneDeep } = require("lodash/fp") + +async function makeTableRequest( + datasource, + operation, + table, + tables, + oldTable = null +) { + const json = { + endpoint: { + datasourceId: datasource._id, + entityId: table._id, + operation, + }, + meta: { + tables, + }, + table, + } + if (oldTable) { + json.meta.table = oldTable + } + return makeExternalQuery(datasource, json) +} + +function getDatasourceId(table) { + if (!table) { + throw "No table supplied" + } + if (table.sourceId) { + return table.sourceId + } + return breakExternalTableId(table._id).datasourceId +} + +exports.save = async function (ctx) { + const appId = ctx.appId + const table = ctx.request.body + // can't do this + delete table.dataImport + const datasourceId = getDatasourceId(ctx.request.body) + let tableToSave = { + type: "table", + _id: buildExternalTableId(datasourceId, table.name), + ...table, + } + + let oldTable + if (ctx.request.body && ctx.request.body._id) { + oldTable = await getTable(appId, ctx.request.body._id) + } + + const db = new CouchDB(appId) + const datasource = await db.get(datasourceId) + const tables = datasource.entities + + // check if relations need setup + for (let [key, schema] of Object.entries(tableToSave.schema)) { + // TODO: this assumes all relationships are the same, need to handle cardinality and many to many + if (schema.type === FieldTypes.LINK) { + const relatedTable = Object.values(tables).find( + table => table._id === schema.tableId + ) + const relatedField = schema.fieldName + const foreignKey = `fk_${relatedTable.name}_${schema.fieldName}` + // create foreign key + tableToSave.schema[foreignKey] = { type: FieldTypes.NUMBER } + // setup the relation in other table and this one + schema.foreignKey = foreignKey + schema.fieldName = foreignKey + schema.main = true + const relatedSchema = cloneDeep(schema) + relatedSchema.fieldName = key + delete relatedSchema.main + relatedTable.schema[relatedField] = relatedSchema + } + } + + const operation = oldTable + ? DataSourceOperation.UPDATE_TABLE + : DataSourceOperation.CREATE_TABLE + await makeTableRequest(datasource, operation, tableToSave, tables, oldTable) + + // store it into couch now for budibase reference + datasource.entities[tableToSave.name] = tableToSave + await db.put(datasource) + + return tableToSave +} + +exports.destroy = async function (ctx) { + const appId = ctx.appId + const tableToDelete = await getTable(appId, ctx.params.tableId) + const datasourceId = getDatasourceId(tableToDelete) + + const db = new CouchDB(appId) + const datasource = await db.get(datasourceId) + const tables = datasource.entities + + const operation = DataSourceOperation.DELETE_TABLE + await makeTableRequest(datasource, operation, tableToDelete, tables) + + delete datasource.entities[tableToDelete.name] + await db.put(datasource) + + return tableToDelete +} diff --git a/packages/server/src/api/controllers/table/index.js b/packages/server/src/api/controllers/table/index.js index d4356c9c8b..97b48943b8 100644 --- a/packages/server/src/api/controllers/table/index.js +++ b/packages/server/src/api/controllers/table/index.js @@ -1,16 +1,28 @@ const CouchDB = require("../../../db") -const linkRows = require("../../../db/linkedRows") +const internal = require("./internal") +const external = require("./external") const csvParser = require("../../../utilities/csvParser") +const { isExternalTable } = require("../../../integrations/utils") const { - getRowParams, getTableParams, - generateTableID, getDatasourceParams, BudibaseInternalDB, } = require("../../../db/utils") -const { FieldTypes } = require("../../../constants") -const { TableSaveFunctions, getTable } = require("./utils") +const { getTable } = require("./utils") +function pickApi({ tableId, table }) { + if (table && !tableId) { + tableId = table._id + } + if (table && table.type === "external") { + return external + } else if (tableId && isExternalTable(tableId)) { + return external + } + return internal +} + +// covers both internal and external exports.fetch = async function (ctx) { const db = new CouchDB(ctx.appId) @@ -50,143 +62,23 @@ exports.find = async function (ctx) { exports.save = async function (ctx) { const appId = ctx.appId - const db = new CouchDB(appId) - const { dataImport, ...rest } = ctx.request.body - let tableToSave = { - type: "table", - _id: generateTableID(), - views: {}, - ...rest, - } - - // if the table obj had an _id then it will have been retrieved - let oldTable - if (ctx.request.body && ctx.request.body._id) { - oldTable = await db.get(ctx.request.body._id) - } - - // saving a table is a complex operation, involving many different steps, this - // has been broken out into a utility to make it more obvious/easier to manipulate - const tableSaveFunctions = new TableSaveFunctions({ - db, - ctx, - oldTable, - dataImport, - }) - tableToSave = await tableSaveFunctions.before(tableToSave) - - // make sure that types don't change of a column, have to remove - // the column if you want to change the type - if (oldTable && oldTable.schema) { - for (let propKey of Object.keys(tableToSave.schema)) { - let column = tableToSave.schema[propKey] - let oldColumn = oldTable.schema[propKey] - if (oldColumn && oldColumn.type === "internal") { - oldColumn.type = "auto" - } - if (oldColumn && oldColumn.type !== column.type) { - ctx.throw(400, "Cannot change the type of a column") - } - } - } - - // Don't rename if the name is the same - let { _rename } = tableToSave - /* istanbul ignore next */ - if (_rename && _rename.old === _rename.updated) { - _rename = null - delete tableToSave._rename - } - - // rename row fields when table column is renamed - /* istanbul ignore next */ - if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) { - ctx.throw(400, "Cannot rename a linked column.") - } - - tableToSave = await tableSaveFunctions.mid(tableToSave) - - // update schema of non-statistics views when new columns are added - for (let view in tableToSave.views) { - const tableView = tableToSave.views[view] - if (!tableView) continue - - if (tableView.schema.group || tableView.schema.field) continue - tableView.schema = tableToSave.schema - } - - // update linked rows - try { - const linkResp = await linkRows.updateLinks({ - appId, - eventType: oldTable - ? linkRows.EventType.TABLE_UPDATED - : linkRows.EventType.TABLE_SAVE, - table: tableToSave, - oldTable: oldTable, - }) - if (linkResp != null && linkResp._rev) { - tableToSave._rev = linkResp._rev - } - } catch (err) { - ctx.throw(400, err) - } - - // don't perform any updates until relationships have been - // checked by the updateLinks function - const updatedRows = tableSaveFunctions.getUpdatedRows() - if (updatedRows && updatedRows.length !== 0) { - await db.bulkDocs(updatedRows) - } - const result = await db.put(tableToSave) - tableToSave._rev = result.rev - - tableToSave = await tableSaveFunctions.after(tableToSave) - - ctx.eventEmitter && - ctx.eventEmitter.emitTable(`table:save`, appId, tableToSave) - + const table = ctx.request.body + const savedTable = await pickApi({ table }).save(ctx) ctx.status = 200 - ctx.message = `Table ${ctx.request.body.name} saved successfully.` - ctx.body = tableToSave + ctx.message = `Table ${table.name} saved successfully.` + ctx.eventEmitter && + ctx.eventEmitter.emitTable(`table:save`, appId, savedTable) + ctx.body = savedTable } exports.destroy = async function (ctx) { const appId = ctx.appId - const db = new CouchDB(appId) - const tableToDelete = await db.get(ctx.params.tableId) - - // Delete all rows for that table - const rows = await db.allDocs( - getRowParams(ctx.params.tableId, null, { - include_docs: true, - }) - ) - await db.bulkDocs(rows.rows.map(row => ({ ...row.doc, _deleted: true }))) - - // update linked rows - await linkRows.updateLinks({ - appId, - eventType: linkRows.EventType.TABLE_DELETE, - table: tableToDelete, - }) - - // don't remove the table itself until very end - await db.remove(tableToDelete) - - // remove table search index - const currentIndexes = await db.getIndexes() - const existingIndex = currentIndexes.indexes.find( - existing => existing.name === `search:${ctx.params.tableId}` - ) - if (existingIndex) { - await db.deleteIndex(existingIndex) - } - + const tableId = ctx.params.tableId + const deletedTable = await pickApi({ tableId }).destroy(ctx) ctx.eventEmitter && - ctx.eventEmitter.emitTable(`table:delete`, appId, tableToDelete) + ctx.eventEmitter.emitTable(`table:delete`, appId, deletedTable) ctx.status = 200 - ctx.body = { message: `Table ${ctx.params.tableId} deleted.` } + ctx.body = { message: `Table ${tableId} deleted.` } } exports.validateCSVSchema = async function (ctx) { diff --git a/packages/server/src/api/controllers/table/internal.js b/packages/server/src/api/controllers/table/internal.js new file mode 100644 index 0000000000..898cd0593b --- /dev/null +++ b/packages/server/src/api/controllers/table/internal.js @@ -0,0 +1,138 @@ +const CouchDB = require("../../../db") +const linkRows = require("../../../db/linkedRows") +const { getRowParams, generateTableID } = require("../../../db/utils") +const { FieldTypes } = require("../../../constants") +const { TableSaveFunctions } = require("./utils") + +exports.save = async function (ctx) { + const appId = ctx.appId + const db = new CouchDB(appId) + const { dataImport, ...rest } = ctx.request.body + let tableToSave = { + type: "table", + _id: generateTableID(), + views: {}, + ...rest, + } + + // if the table obj had an _id then it will have been retrieved + let oldTable + if (ctx.request.body && ctx.request.body._id) { + oldTable = await db.get(ctx.request.body._id) + } + + // saving a table is a complex operation, involving many different steps, this + // has been broken out into a utility to make it more obvious/easier to manipulate + const tableSaveFunctions = new TableSaveFunctions({ + db, + ctx, + oldTable, + dataImport, + }) + tableToSave = await tableSaveFunctions.before(tableToSave) + + // make sure that types don't change of a column, have to remove + // the column if you want to change the type + if (oldTable && oldTable.schema) { + for (let propKey of Object.keys(tableToSave.schema)) { + let column = tableToSave.schema[propKey] + let oldColumn = oldTable.schema[propKey] + if (oldColumn && oldColumn.type === "internal") { + oldColumn.type = "auto" + } + if (oldColumn && oldColumn.type !== column.type) { + ctx.throw(400, "Cannot change the type of a column") + } + } + } + + // Don't rename if the name is the same + let { _rename } = tableToSave + /* istanbul ignore next */ + if (_rename && _rename.old === _rename.updated) { + _rename = null + delete tableToSave._rename + } + + // rename row fields when table column is renamed + /* istanbul ignore next */ + if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) { + ctx.throw(400, "Cannot rename a linked column.") + } + + tableToSave = await tableSaveFunctions.mid(tableToSave) + + // update schema of non-statistics views when new columns are added + for (let view in tableToSave.views) { + const tableView = tableToSave.views[view] + if (!tableView) continue + + if (tableView.schema.group || tableView.schema.field) continue + tableView.schema = tableToSave.schema + } + + // update linked rows + try { + const linkResp = await linkRows.updateLinks({ + appId, + eventType: oldTable + ? linkRows.EventType.TABLE_UPDATED + : linkRows.EventType.TABLE_SAVE, + table: tableToSave, + oldTable: oldTable, + }) + if (linkResp != null && linkResp._rev) { + tableToSave._rev = linkResp._rev + } + } catch (err) { + ctx.throw(400, err) + } + + // don't perform any updates until relationships have been + // checked by the updateLinks function + const updatedRows = tableSaveFunctions.getUpdatedRows() + if (updatedRows && updatedRows.length !== 0) { + await db.bulkDocs(updatedRows) + } + const result = await db.put(tableToSave) + tableToSave._rev = result.rev + + tableToSave = await tableSaveFunctions.after(tableToSave) + + return tableToSave +} + +exports.destroy = async function (ctx) { + const appId = ctx.appId + const db = new CouchDB(appId) + const tableToDelete = await db.get(ctx.params.tableId) + + // Delete all rows for that table + const rows = await db.allDocs( + getRowParams(ctx.params.tableId, null, { + include_docs: true, + }) + ) + await db.bulkDocs(rows.rows.map(row => ({ ...row.doc, _deleted: true }))) + + // update linked rows + await linkRows.updateLinks({ + appId, + eventType: linkRows.EventType.TABLE_DELETE, + table: tableToDelete, + }) + + // don't remove the table itself until very end + await db.remove(tableToDelete) + + // remove table search index + const currentIndexes = await db.getIndexes() + const existingIndex = currentIndexes.indexes.find( + existing => existing.name === `search:${ctx.params.tableId}` + ) + if (existingIndex) { + await db.deleteIndex(existingIndex) + } + + return tableToDelete +} diff --git a/packages/server/src/constants/index.js b/packages/server/src/constants/index.js index 7a8958c36a..80c62cd02e 100644 --- a/packages/server/src/constants/index.js +++ b/packages/server/src/constants/index.js @@ -62,6 +62,9 @@ exports.DataSourceOperation = { READ: "READ", UPDATE: "UPDATE", DELETE: "DELETE", + CREATE_TABLE: "CREATE_TABLE", + UPDATE_TABLE: "UPDATE_TABLE", + DELETE_TABLE: "DELETE_TABLE", } exports.SortDirection = { diff --git a/packages/server/src/definitions/common.ts b/packages/server/src/definitions/common.ts index f439fc0d28..b2ab203bee 100644 --- a/packages/server/src/definitions/common.ts +++ b/packages/server/src/definitions/common.ts @@ -36,7 +36,7 @@ export interface TableSchema { export interface Table extends Base { type?: string views?: {} - name?: string + name: string primary?: string[] schema: TableSchema primaryDisplay?: string diff --git a/packages/server/src/definitions/datasource.ts b/packages/server/src/definitions/datasource.ts index c4b248fa17..a82e50b140 100644 --- a/packages/server/src/definitions/datasource.ts +++ b/packages/server/src/definitions/datasource.ts @@ -5,6 +5,9 @@ export enum Operation { READ = "READ", UPDATE = "UPDATE", DELETE = "DELETE", + CREATE_TABLE = "CREATE_TABLE", + UPDATE_TABLE = "UPDATE_TABLE", + DELETE_TABLE = "DELETE_TABLE", } export enum SortDirection { @@ -142,8 +145,10 @@ export interface QueryJson { sort?: SortJson paginate?: PaginationJson body?: object + table?: Table meta?: { table?: Table + tables?: Record } extra?: { idFilter?: SearchFilters diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 316e20e352..738b44afcc 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -1,19 +1,24 @@ import { Knex, knex } from "knex" -const BASE_LIMIT = 5000 import { - QueryJson, - SearchFilters, - QueryOptions, - SortDirection, Operation, + QueryJson, + QueryOptions, RelationshipsJson, + SearchFilters, + SortDirection, } from "../../definitions/datasource" import { isIsoDateString } from "../utils" +import SqlTableQueryBuilder from "./sqlTable" + +const BASE_LIMIT = 5000 type KnexQuery = Knex.QueryBuilder | Knex function parseBody(body: any) { for (let [key, value] of Object.entries(body)) { + if (Array.isArray(value)) { + body[key] = JSON.stringify(value) + } if (typeof value !== "string") { continue } @@ -243,23 +248,14 @@ function buildDelete( } } -class SqlQueryBuilder { - private readonly sqlClient: string +class SqlQueryBuilder extends SqlTableQueryBuilder { private readonly limit: number // pass through client to get flavour of SQL constructor(client: string, limit: number = BASE_LIMIT) { - this.sqlClient = client + super(client) this.limit = limit } - /** - * @param json the input JSON structure from which an SQL query will be built. - * @return {string} the operation that was found in the JSON. - */ - _operation(json: QueryJson): Operation { - return json.endpoint.operation - } - /** * @param json The JSON query DSL which is to be converted to SQL. * @param opts extra options which are to be passed into the query builder, e.g. disableReturning @@ -267,7 +263,8 @@ class SqlQueryBuilder { * @return {{ sql: string, bindings: object }} the query ready to be passed to the driver. */ _query(json: QueryJson, opts: QueryOptions = {}) { - const client = knex({ client: this.sqlClient }) + const sqlClient = this.getSqlClient() + const client = knex({ client: sqlClient }) let query switch (this._operation(json)) { case Operation.CREATE: @@ -282,6 +279,8 @@ class SqlQueryBuilder { case Operation.DELETE: query = buildDelete(client, json, opts) break + case Operation.CREATE_TABLE: case Operation.UPDATE_TABLE: case Operation.DELETE_TABLE: + return this._tableQuery(json) default: throw `Operation type is not supported by SQL query builder` } diff --git a/packages/server/src/integrations/base/sqlTable.ts b/packages/server/src/integrations/base/sqlTable.ts new file mode 100644 index 0000000000..f2e727ce62 --- /dev/null +++ b/packages/server/src/integrations/base/sqlTable.ts @@ -0,0 +1,132 @@ +import { Knex, knex } from "knex" +import { Table } from "../../definitions/common" +import { Operation, QueryJson } from "../../definitions/datasource" +import { breakExternalTableId } from "../utils" +import SchemaBuilder = Knex.SchemaBuilder +import CreateTableBuilder = Knex.CreateTableBuilder +const { FieldTypes } = require("../../constants") + +function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record, oldTable: null | Table = null) { + let primaryKey = table && table.primary ? table.primary[0] : null + // can't change primary once its set for now + if (primaryKey && !oldTable) { + schema.increments(primaryKey).primary() + } + const foreignKeys = Object.values(table.schema).map(col => col.foreignKey) + for (let [key, column] of Object.entries(table.schema)) { + // skip things that are already correct + const oldColumn = oldTable ? oldTable.schema[key] : null + if ((oldColumn && oldColumn.type === column.type) || primaryKey === key) { + continue + } + switch (column.type) { + case FieldTypes.STRING: case FieldTypes.OPTIONS: case FieldTypes.LONGFORM: + schema.string(key) + break + case FieldTypes.NUMBER: + if (foreignKeys.indexOf(key) === -1) { + schema.float(key) + } + break + case FieldTypes.BOOLEAN: + schema.boolean(key) + break + case FieldTypes.DATETIME: + schema.datetime(key) + break + case FieldTypes.ARRAY: + schema.json(key) + break + case FieldTypes.LINK: + if (!column.foreignKey || !column.tableId) { + throw "Invalid relationship schema" + } + const { tableName } = breakExternalTableId(column.tableId) + // @ts-ignore + const relatedTable = tables[tableName] + if (!relatedTable) { + throw "Referenced table doesn't exist" + } + schema.integer(column.foreignKey).unsigned() + schema.foreign(column.foreignKey).references(`${tableName}.${relatedTable.primary[0]}`) + } + } + return schema +} + +function buildCreateTable( + knex: Knex, + table: Table, + tables: Record, +): SchemaBuilder { + return knex.schema.createTable(table.name, schema => { + generateSchema(schema, table, tables) + }) +} + +function buildUpdateTable( + knex: Knex, + table: Table, + tables: Record, + oldTable: Table, +): SchemaBuilder { + return knex.schema.alterTable(table.name, schema => { + generateSchema(schema, table, tables, oldTable) + }) +} + +function buildDeleteTable( + knex: Knex, + table: Table, +): SchemaBuilder { + return knex.schema.dropTable(table.name) +} + +class SqlTableQueryBuilder { + private readonly sqlClient: string + + // pass through client to get flavour of SQL + constructor(client: string) { + this.sqlClient = client + } + + getSqlClient(): string { + return this.sqlClient + } + + /** + * @param json the input JSON structure from which an SQL query will be built. + * @return {string} the operation that was found in the JSON. + */ + _operation(json: QueryJson): Operation { + return json.endpoint.operation + } + + _tableQuery(json: QueryJson): any { + const client = knex({ client: this.sqlClient }) + let query + if (!json.table || !json.meta || !json.meta.tables) { + throw "Cannot execute without table being specified" + } + switch (this._operation(json)) { + case Operation.CREATE_TABLE: + query = buildCreateTable(client, json.table, json.meta.tables) + break + case Operation.UPDATE_TABLE: + if (!json.meta || !json.meta.table) { + throw "Must specify old table for update" + } + query = buildUpdateTable(client, json.table, json.meta.tables, json.meta.table) + break + case Operation.DELETE_TABLE: + query = buildDeleteTable(client, json.table) + break + default: + throw "Table operation is of unknown type" + } + return query.toSQL() + } +} + +export default SqlTableQueryBuilder +module.exports = SqlTableQueryBuilder \ No newline at end of file diff --git a/packages/server/src/integrations/base/utils.ts b/packages/server/src/integrations/base/utils.ts new file mode 100644 index 0000000000..5757232bc7 --- /dev/null +++ b/packages/server/src/integrations/base/utils.ts @@ -0,0 +1,19 @@ +import { QueryJson } from "../../definitions/datasource" +import { Datasource } from "../../definitions/common" + +module DatasourceUtils { + const { integrations } = require("../index") + + export async function makeExternalQuery(datasource: Datasource, json: QueryJson) { + const Integration = integrations[datasource.source] + // query is the opinionated function + if (Integration.prototype.query) { + const integration = new Integration(datasource.config) + return integration.query(json) + } else { + throw "Datasource does not support query." + } + } + + module.exports.makeExternalQuery = makeExternalQuery +} diff --git a/packages/server/src/integrations/postgres.ts b/packages/server/src/integrations/postgres.ts index dd7ecd5762..adae9b5fc1 100644 --- a/packages/server/src/integrations/postgres.ts +++ b/packages/server/src/integrations/postgres.ts @@ -263,8 +263,16 @@ module PostgresModule { async query(json: QueryJson) { const operation = this._operation(json).toLowerCase() const input = this._query(json) - const response = await internalQuery(this.client, input) - return response.rows.length ? response.rows : [{ [operation]: true }] + if (Array.isArray(input)) { + const responses = [] + for (let query of input) { + responses.push(await internalQuery(this.client, query)) + } + return responses + } else { + const response = await internalQuery(this.client, input) + return response.rows.length ? response.rows : [{ [operation]: true }] + } } } From 949c6b8653b61a0a18038e39da61b8730597dccf Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 28 Oct 2021 22:44:31 +0100 Subject: [PATCH 010/100] Adding SQL relationship building. --- .../DataTable/modals/CreateEditColumn.svelte | 6 ++- .../src/api/controllers/table/external.js | 41 ++++++++++++++----- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 221d391cbf..54bbb7fadf 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -89,6 +89,10 @@ if (field.type === AUTO_TYPE) { field = buildAutoColumn($tables.draft.name, field.name, field.subtype) } + // for now you can't create other options, just many to many for SQL + if (field.type === LINK_TYPE && external) { + field.relationshipType = RelationshipTypes.ONE_TO_MANY + } await tables.saveField({ originalName, field, @@ -324,7 +328,7 @@ getOptionLabel={table => table.name} getOptionValue={table => table._id} /> - {#if relationshipOptions && relationshipOptions.length > 0} + {#if relationshipOptions && relationshipOptions.length > 0 && !external} table._id === schema.tableId ) + // setup the schema in this table const relatedField = schema.fieldName + const relatedPrimary = relatedTable.primary[0] + // generate a foreign key const foreignKey = `fk_${relatedTable.name}_${schema.fieldName}` - // create foreign key - tableToSave.schema[foreignKey] = { type: FieldTypes.NUMBER } - // setup the relation in other table and this one + + schema.relationshipType = RelationshipTypes.ONE_TO_MANY schema.foreignKey = foreignKey - schema.fieldName = foreignKey + schema.fieldName = relatedPrimary schema.main = true - const relatedSchema = cloneDeep(schema) - relatedSchema.fieldName = key - delete relatedSchema.main - relatedTable.schema[relatedField] = relatedSchema + + relatedTable.schema[relatedField] = generateRelatedSchema(schema, table) + tableToSave.schema[foreignKey] = { + type: FieldTypes.NUMBER, + constraints: {}, + } } } From a94376ce430002c002686d5251e1898ed0681127 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 29 Oct 2021 13:34:10 +0100 Subject: [PATCH 011/100] Fixing a lot of issues around dropping columns, updating columns, relationships and bi-directionality, display columns now default to something for SQL tables as well. --- .../backend/DataTable/RowFieldControl.svelte | 4 +- .../DataTable/modals/CreateEditColumn.svelte | 41 ++++++++++++++---- .../[selectedDatasource]/index.svelte | 12 ++---- .../server/src/api/controllers/datasource.js | 10 +++++ .../src/api/controllers/table/external.js | 43 ++++++++++++++++++- .../server/src/integrations/base/sqlTable.ts | 19 +++++++- 6 files changed, 109 insertions(+), 20 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte index e82c55679a..25ad67b52e 100644 --- a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte +++ b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte @@ -16,8 +16,8 @@ export let value = defaultValue || (meta.type === "boolean" ? false : "") export let readonly - $: type = meta.type - $: label = capitalise(meta.name) + $: type = meta?.type + $: label = meta.name ? capitalise(meta.name) : "" {#if type === "options"} diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 54bbb7fadf..9af07fe28c 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -31,6 +31,9 @@ const AUTO_TYPE = "auto" const FORMULA_TYPE = FIELDS.FORMULA.type const LINK_TYPE = FIELDS.LINK.type + const STRING_TYPE = FIELDS.STRING.type + const NUMBER_TYPE = FIELDS.NUMBER.type + const dispatch = createEventDispatcher() const PROHIBITED_COLUMN_NAMES = ["type", "_id", "_rev", "tableId"] const { hide } = getContext(Context.Modal) @@ -55,6 +58,7 @@ let confirmDeleteDialog let deletion + $: checkConstraints(field) $: tableOptions = $tables.list.filter( opt => opt._id !== $tables.draft._id && opt.type === table.type ) @@ -180,23 +184,26 @@ } const thisName = truncate(table.name, { length: 14 }), linkName = truncate(linkTable.name, { length: 14 }) - return [ + const options = [ { name: `Many ${thisName} rows → many ${linkName} rows`, alt: `Many ${table.name} rows → many ${linkTable.name} rows`, value: RelationshipTypes.MANY_TO_MANY, }, - { - name: `One ${linkName} row → many ${thisName} rows`, - alt: `One ${linkTable.name} rows → many ${table.name} rows`, - value: RelationshipTypes.ONE_TO_MANY, - }, { name: `One ${thisName} row → many ${linkName} rows`, alt: `One ${table.name} rows → many ${linkTable.name} rows`, value: RelationshipTypes.MANY_TO_ONE, }, ] + if (!external) { + options.push({ + name: `One ${linkName} row → many ${thisName} rows`, + alt: `One ${linkTable.name} rows → many ${table.name} rows`, + value: RelationshipTypes.ONE_TO_MANY, + }) + } + return options } function getAllowedTypes() { @@ -219,6 +226,24 @@ ] } } + + function checkConstraints(fieldToCheck) { + // most types need this, just make sure its always present + if (fieldToCheck && !fieldToCheck.constraints) { + fieldToCheck.constraints = {} + } + // some string types may have been built by server, may not always have constraints + if (fieldToCheck.type === STRING_TYPE && !fieldToCheck.constraints.length) { + fieldToCheck.constraints.length = {} + } + // some number types made server-side will be missing constraints + if ( + fieldToCheck.type === NUMBER_TYPE && + !fieldToCheck.constraints.numericality + ) { + fieldToCheck.constraints.numericality = {} + } + } {/if} - {#if canBeSearched} + {#if canBeSearched && !external}
table.name} getOptionValue={table => table._id} /> - {#if relationshipOptions && relationshipOptions.length > 0 && !external} + {#if relationshipOptions && relationshipOptions.length > 0} {/if}
-
- - New table - -
{#each plusTables as table}
onClickTable(table)}>

{table.name}

@@ -212,6 +206,9 @@

→

{/each} +
+ +
{#if plusTables?.length !== 0} @@ -343,7 +340,6 @@ } .add-table { - margin-right: 0; - margin-left: auto; + margin-top: var(--spacing-m); } diff --git a/packages/server/src/api/controllers/datasource.js b/packages/server/src/api/controllers/datasource.js index 604fee004e..6f33faf3d4 100644 --- a/packages/server/src/api/controllers/datasource.js +++ b/packages/server/src/api/controllers/datasource.js @@ -152,6 +152,16 @@ const buildSchemaHelper = async datasource => { await connector.buildSchema(datasource._id, datasource.entities) datasource.entities = connector.tables + // make sure they all have a display name selected + for (let entity of Object.values(datasource.entities)) { + if (entity.primaryDisplay) { + continue + } + entity.primaryDisplay = Object.values(entity.schema).find( + schema => !schema.autocolumn + ).name + } + const errors = connector.schemaErrors let error = null if (errors && Object.keys(errors).length > 0) { diff --git a/packages/server/src/api/controllers/table/external.js b/packages/server/src/api/controllers/table/external.js index 5fe923cb96..d0ec01a1d8 100644 --- a/packages/server/src/api/controllers/table/external.js +++ b/packages/server/src/api/controllers/table/external.js @@ -36,6 +36,35 @@ async function makeTableRequest( return makeExternalQuery(datasource, json) } +function cleanupRelationships(table, tables, oldTable = null) { + const tableToIterate = oldTable ? oldTable : table + // clean up relationships in couch table schemas + for (let [key, schema] of Object.entries(tableToIterate.schema)) { + if ( + schema.type === FieldTypes.LINK && + (!oldTable || table.schema[key] == null) + ) { + const relatedTable = Object.values(tables).find( + table => table._id === schema.tableId + ) + const foreignKey = schema.foreignKey + if (!relatedTable || !foreignKey) { + continue + } + for (let [relatedKey, relatedSchema] of Object.entries( + relatedTable.schema + )) { + if ( + relatedSchema.type === FieldTypes.LINK && + relatedSchema.fieldName === foreignKey + ) { + delete relatedSchema[relatedKey] + } + } + } + } +} + function getDatasourceId(table) { if (!table) { throw "No table supplied" @@ -57,6 +86,14 @@ function generateRelatedSchema(linkColumn, table) { return relatedSchema } +function oneToManyRelationshipNeedsSetup(column) { + return ( + column.type === FieldTypes.LINK && + column.relationshipType === RelationshipTypes.ONE_TO_MANY && + !column.foreignKey + ) +} + exports.save = async function (ctx) { const appId = ctx.appId const table = ctx.request.body @@ -81,7 +118,7 @@ exports.save = async function (ctx) { // check if relations need setup for (let schema of Object.values(tableToSave.schema)) { // TODO: many to many handling - if (schema.type === FieldTypes.LINK) { + if (oneToManyRelationshipNeedsSetup(schema)) { const relatedTable = Object.values(tables).find( table => table._id === schema.tableId ) @@ -104,6 +141,8 @@ exports.save = async function (ctx) { } } + cleanupRelationships(tableToSave, tables, oldTable) + const operation = oldTable ? DataSourceOperation.UPDATE_TABLE : DataSourceOperation.CREATE_TABLE @@ -128,6 +167,8 @@ exports.destroy = async function (ctx) { const operation = DataSourceOperation.DELETE_TABLE await makeTableRequest(datasource, operation, tableToDelete, tables) + cleanupRelationships(tableToDelete, tables) + delete datasource.entities[tableToDelete.name] await db.put(datasource) diff --git a/packages/server/src/integrations/base/sqlTable.ts b/packages/server/src/integrations/base/sqlTable.ts index f2e727ce62..87d0f1e3b3 100644 --- a/packages/server/src/integrations/base/sqlTable.ts +++ b/packages/server/src/integrations/base/sqlTable.ts @@ -4,7 +4,7 @@ import { Operation, QueryJson } from "../../definitions/datasource" import { breakExternalTableId } from "../utils" import SchemaBuilder = Knex.SchemaBuilder import CreateTableBuilder = Knex.CreateTableBuilder -const { FieldTypes } = require("../../constants") +const { FieldTypes, RelationshipTypes } = require("../../constants") function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record, oldTable: null | Table = null) { let primaryKey = table && table.primary ? table.primary[0] : null @@ -12,6 +12,8 @@ function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record if (primaryKey && !oldTable) { schema.increments(primaryKey).primary() } + + // check if any columns need added const foreignKeys = Object.values(table.schema).map(col => col.foreignKey) for (let [key, column] of Object.entries(table.schema)) { // skip things that are already correct @@ -38,6 +40,10 @@ function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record schema.json(key) break case FieldTypes.LINK: + // this side of the relationship doesn't need any SQL work + if (column.relationshipType === RelationshipTypes.MANY_TO_ONE) { + break + } if (!column.foreignKey || !column.tableId) { throw "Invalid relationship schema" } @@ -51,6 +57,17 @@ function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record schema.foreign(column.foreignKey).references(`${tableName}.${relatedTable.primary[0]}`) } } + + // need to check if any columns have been deleted + if (oldTable) { + const deletedColumns = Object.entries(oldTable.schema) + .filter(([key, schema]) => schema.type !== FieldTypes.LINK && table.schema[key] == null) + .map(([key]) => key) + deletedColumns.forEach(key => { + schema.dropColumn(key) + }) + } + return schema } From f071cc5219d311a428638bb7bc5514c3c4f58019 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 29 Oct 2021 18:37:29 +0100 Subject: [PATCH 012/100] Adding many to many support, generating junction table and setting up constraints. --- .../DataTable/modals/CreateEditColumn.svelte | 19 +-- .../server/src/api/controllers/datasource.js | 7 +- .../src/api/controllers/table/external.js | 156 ++++++++++++++---- .../server/src/api/controllers/table/utils.js | 20 +++ packages/server/src/definitions/common.ts | 5 + .../server/src/integrations/base/sqlTable.ts | 46 ++++-- 6 files changed, 187 insertions(+), 66 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 9af07fe28c..ebfea9cee6 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -93,10 +93,6 @@ if (field.type === AUTO_TYPE) { field = buildAutoColumn($tables.draft.name, field.name, field.subtype) } - // for now you can't create other options, just many to many for SQL - if (field.type === LINK_TYPE && external) { - field.relationshipType = RelationshipTypes.ONE_TO_MANY - } await tables.saveField({ originalName, field, @@ -184,26 +180,23 @@ } const thisName = truncate(table.name, { length: 14 }), linkName = truncate(linkTable.name, { length: 14 }) - const options = [ + return [ { name: `Many ${thisName} rows → many ${linkName} rows`, alt: `Many ${table.name} rows → many ${linkTable.name} rows`, value: RelationshipTypes.MANY_TO_MANY, }, + { + name: `One ${linkName} row → many ${thisName} rows`, + alt: `One ${linkTable.name} rows → many ${table.name} rows`, + value: RelationshipTypes.ONE_TO_MANY, + }, { name: `One ${thisName} row → many ${linkName} rows`, alt: `One ${table.name} rows → many ${linkTable.name} rows`, value: RelationshipTypes.MANY_TO_ONE, }, ] - if (!external) { - options.push({ - name: `One ${linkName} row → many ${thisName} rows`, - alt: `One ${linkTable.name} rows → many ${table.name} rows`, - value: RelationshipTypes.ONE_TO_MANY, - }) - } - return options } function getAllowedTypes() { diff --git a/packages/server/src/api/controllers/datasource.js b/packages/server/src/api/controllers/datasource.js index 6f33faf3d4..643e822a36 100644 --- a/packages/server/src/api/controllers/datasource.js +++ b/packages/server/src/api/controllers/datasource.js @@ -157,9 +157,12 @@ const buildSchemaHelper = async datasource => { if (entity.primaryDisplay) { continue } - entity.primaryDisplay = Object.values(entity.schema).find( + const notAutoColumn = Object.values(entity.schema).find( schema => !schema.autocolumn - ).name + ) + if (notAutoColumn) { + entity.primaryDisplay = notAutoColumn.name + } } const errors = connector.schemaErrors diff --git a/packages/server/src/api/controllers/table/external.js b/packages/server/src/api/controllers/table/external.js index d0ec01a1d8..7d332519ba 100644 --- a/packages/server/src/api/controllers/table/external.js +++ b/packages/server/src/api/controllers/table/external.js @@ -3,7 +3,12 @@ const { buildExternalTableId, breakExternalTableId, } = require("../../../integrations/utils") -const { getTable } = require("./utils") +const { + getTable, + generateForeignKey, + generateJunctionTableName, + foreignKeyStructure, +} = require("./utils") const { DataSourceOperation, FieldTypes, @@ -58,7 +63,7 @@ function cleanupRelationships(table, tables, oldTable = null) { relatedSchema.type === FieldTypes.LINK && relatedSchema.fieldName === foreignKey ) { - delete relatedSchema[relatedKey] + delete relatedTable.schema[relatedKey] } } } @@ -75,23 +80,78 @@ function getDatasourceId(table) { return breakExternalTableId(table._id).datasourceId } -function generateRelatedSchema(linkColumn, table) { - // generate column for other table - const relatedSchema = cloneDeep(linkColumn) - relatedSchema.fieldName = linkColumn.foreignKey - relatedSchema.foreignKey = linkColumn.fieldName - relatedSchema.relationshipType = RelationshipTypes.MANY_TO_ONE - relatedSchema.tableId = table._id - delete relatedSchema.main - return relatedSchema +function otherRelationshipType(type) { + if (type === RelationshipTypes.MANY_TO_MANY) { + return RelationshipTypes.MANY_TO_MANY + } + return type === RelationshipTypes.ONE_TO_MANY + ? RelationshipTypes.MANY_TO_ONE + : RelationshipTypes.ONE_TO_MANY } -function oneToManyRelationshipNeedsSetup(column) { - return ( - column.type === FieldTypes.LINK && - column.relationshipType === RelationshipTypes.ONE_TO_MANY && - !column.foreignKey +function generateManyLinkSchema(datasource, column, table, relatedTable) { + const primary = table.name + table.primary[0] + const relatedPrimary = relatedTable.name + relatedTable.primary[0] + const jcTblName = generateJunctionTableName(column, table, relatedTable) + // first create the new table + const junctionTable = { + _id: buildExternalTableId(datasource._id, jcTblName), + name: jcTblName, + primary: [primary, relatedPrimary], + schema: { + [primary]: foreignKeyStructure(primary, { + toTable: table.name, + toKey: table.primary[0], + }), + [relatedPrimary]: foreignKeyStructure(relatedPrimary, { + toTable: relatedTable.name, + toKey: relatedTable.primary[0], + }), + }, + } + column.through = junctionTable._id + column.throughFrom = primary + column.throughTo = relatedPrimary + column.fieldName = relatedPrimary + return junctionTable +} + +function generateLinkSchema(column, table, relatedTable, type) { + const isOneSide = type === RelationshipTypes.ONE_TO_MANY + const primary = isOneSide ? relatedTable.primary[0] : table.primary[0] + // generate a foreign key + const foreignKey = generateForeignKey(column, relatedTable) + column.relationshipType = type + column.foreignKey = isOneSide ? foreignKey : primary + column.fieldName = isOneSide ? primary : foreignKey + return foreignKey +} + +function generateRelatedSchema(linkColumn, table, relatedTable, columnName) { + // generate column for other table + const relatedSchema = cloneDeep(linkColumn) + // swap them from the main link + if (linkColumn.foreignKey) { + relatedSchema.fieldName = linkColumn.foreignKey + relatedSchema.foreignKey = linkColumn.fieldName + } + // is many to many + else { + // don't need to copy through, already got it + relatedSchema.fieldName = linkColumn.throughFrom + relatedSchema.throughTo = linkColumn.throughFrom + relatedSchema.throughFrom = linkColumn.throughTo + } + relatedSchema.relationshipType = otherRelationshipType( + linkColumn.relationshipType ) + relatedSchema.tableId = relatedTable._id + relatedSchema.name = columnName + table.schema[columnName] = relatedSchema +} + +function isRelationshipSetup(column) { + return column.foreignKey || column.through } exports.save = async function (ctx) { @@ -113,32 +173,50 @@ exports.save = async function (ctx) { const db = new CouchDB(appId) const datasource = await db.get(datasourceId) + const oldTables = cloneDeep(datasource.entities) const tables = datasource.entities + const extraTablesToUpdate = [] + // check if relations need setup for (let schema of Object.values(tableToSave.schema)) { - // TODO: many to many handling - if (oneToManyRelationshipNeedsSetup(schema)) { - const relatedTable = Object.values(tables).find( - table => table._id === schema.tableId + if (schema.type !== FieldTypes.LINK || isRelationshipSetup(schema)) { + continue + } + const relatedTable = Object.values(tables).find( + table => table._id === schema.tableId + ) + const relatedColumnName = schema.fieldName + const relationType = schema.relationshipType + if (relationType === RelationshipTypes.MANY_TO_MANY) { + const junctionTable = generateManyLinkSchema( + datasource, + schema, + table, + relatedTable ) - // setup the schema in this table - const relatedField = schema.fieldName - const relatedPrimary = relatedTable.primary[0] - // generate a foreign key - const foreignKey = `fk_${relatedTable.name}_${schema.fieldName}` - - schema.relationshipType = RelationshipTypes.ONE_TO_MANY - schema.foreignKey = foreignKey - schema.fieldName = relatedPrimary - schema.main = true - - relatedTable.schema[relatedField] = generateRelatedSchema(schema, table) - tableToSave.schema[foreignKey] = { - type: FieldTypes.NUMBER, - constraints: {}, + if (tables[junctionTable.name]) { + throw "Junction table already exists, cannot create another relationship." + } + tables[junctionTable.name] = junctionTable + extraTablesToUpdate.push(junctionTable) + } else { + const fkTable = + relationType === RelationshipTypes.ONE_TO_MANY ? table : relatedTable + const foreignKey = generateLinkSchema( + schema, + table, + relatedTable, + relationType + ) + fkTable.schema[foreignKey] = foreignKeyStructure(foreignKey) + // foreign key is in other table, need to save it to external + if (fkTable._id !== table._id) { + extraTablesToUpdate.push(fkTable) } } + generateRelatedSchema(schema, relatedTable, table, relatedColumnName) + schema.main = true } cleanupRelationships(tableToSave, tables, oldTable) @@ -147,6 +225,14 @@ exports.save = async function (ctx) { ? DataSourceOperation.UPDATE_TABLE : DataSourceOperation.CREATE_TABLE await makeTableRequest(datasource, operation, tableToSave, tables, oldTable) + // update any extra tables (like foreign keys in other tables) + for (let extraTable of extraTablesToUpdate) { + const oldExtraTable = oldTables[extraTable.name] + let op = oldExtraTable + ? DataSourceOperation.UPDATE_TABLE + : DataSourceOperation.CREATE_TABLE + await makeTableRequest(datasource, op, extraTable, tables, oldExtraTable) + } // store it into couch now for budibase reference datasource.entities[tableToSave.name] = tableToSave diff --git a/packages/server/src/api/controllers/table/utils.js b/packages/server/src/api/controllers/table/utils.js index 65c081f90e..1cca4c41d2 100644 --- a/packages/server/src/api/controllers/table/utils.js +++ b/packages/server/src/api/controllers/table/utils.js @@ -315,4 +315,24 @@ exports.checkForViewUpdates = async (db, table, rename, deletedColumns) => { } } +exports.generateForeignKey = (column, relatedTable) => { + return `fk_${relatedTable.name}_${column.fieldName}` +} + +exports.generateJunctionTableName = (column, table, relatedTable) => { + return `jt_${table.name}_${relatedTable.name}_${column.name}_${column.fieldName}` +} + +exports.foreignKeyStructure = (keyName, meta = null) => { + const structure = { + type: FieldTypes.NUMBER, + constraints: {}, + name: keyName, + } + if (meta) { + structure.meta = meta + } + return structure +} + exports.TableSaveFunctions = TableSaveFunctions diff --git a/packages/server/src/definitions/common.ts b/packages/server/src/definitions/common.ts index b2ab203bee..4308aff0a9 100644 --- a/packages/server/src/definitions/common.ts +++ b/packages/server/src/definitions/common.ts @@ -17,6 +17,11 @@ export interface FieldSchema { autocolumn?: boolean throughFrom?: string throughTo?: string + main?: boolean + meta?: { + toTable: string + toKey: string + } constraints?: { type?: string email?: boolean diff --git a/packages/server/src/integrations/base/sqlTable.ts b/packages/server/src/integrations/base/sqlTable.ts index 87d0f1e3b3..6eb6d02c3f 100644 --- a/packages/server/src/integrations/base/sqlTable.ts +++ b/packages/server/src/integrations/base/sqlTable.ts @@ -8,9 +8,15 @@ const { FieldTypes, RelationshipTypes } = require("../../constants") function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record, oldTable: null | Table = null) { let primaryKey = table && table.primary ? table.primary[0] : null + const columns = Object.values(table.schema) + // all columns in a junction table will be meta + let metaCols = columns.filter(col => col.meta) + let isJunction = metaCols.length === columns.length // can't change primary once its set for now - if (primaryKey && !oldTable) { + if (primaryKey && !oldTable && !isJunction) { schema.increments(primaryKey).primary() + } else if (!oldTable && isJunction) { + schema.primary(metaCols.map(col => col.name)) } // check if any columns need added @@ -18,7 +24,7 @@ function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record for (let [key, column] of Object.entries(table.schema)) { // skip things that are already correct const oldColumn = oldTable ? oldTable.schema[key] : null - if ((oldColumn && oldColumn.type === column.type) || primaryKey === key) { + if ((oldColumn && oldColumn.type === column.type) || (primaryKey === key && !isJunction)) { continue } switch (column.type) { @@ -26,7 +32,12 @@ function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record schema.string(key) break case FieldTypes.NUMBER: - if (foreignKeys.indexOf(key) === -1) { + // if meta is specified then this is a junction table entry + if (column.meta && column.meta.toKey && column.meta.toTable) { + const { toKey, toTable } = column.meta + schema.integer(key).unsigned() + schema.foreign(key).references(`${toTable}.${toKey}`) + } else if (foreignKeys.indexOf(key) === -1) { schema.float(key) } break @@ -41,20 +52,23 @@ function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record break case FieldTypes.LINK: // this side of the relationship doesn't need any SQL work - if (column.relationshipType === RelationshipTypes.MANY_TO_ONE) { - break + if ( + column.relationshipType !== RelationshipTypes.MANY_TO_ONE && + column.relationshipType !== RelationshipTypes.MANY_TO_MANY + ) { + if (!column.foreignKey || !column.tableId) { + throw "Invalid relationship schema" + } + const { tableName } = breakExternalTableId(column.tableId) + // @ts-ignore + const relatedTable = tables[tableName] + if (!relatedTable) { + throw "Referenced table doesn't exist" + } + schema.integer(column.foreignKey).unsigned() + schema.foreign(column.foreignKey).references(`${tableName}.${relatedTable.primary[0]}`) } - if (!column.foreignKey || !column.tableId) { - throw "Invalid relationship schema" - } - const { tableName } = breakExternalTableId(column.tableId) - // @ts-ignore - const relatedTable = tables[tableName] - if (!relatedTable) { - throw "Referenced table doesn't exist" - } - schema.integer(column.foreignKey).unsigned() - schema.foreign(column.foreignKey).references(`${tableName}.${relatedTable.primary[0]}`) + break } } From 835b0efe740e6b7172750cd53fac1d1a009986de Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 29 Oct 2021 18:43:50 +0100 Subject: [PATCH 013/100] Adding mysql support. --- packages/server/src/integrations/mysql.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/server/src/integrations/mysql.ts b/packages/server/src/integrations/mysql.ts index eba288da56..c6f271b6fe 100644 --- a/packages/server/src/integrations/mysql.ts +++ b/packages/server/src/integrations/mysql.ts @@ -262,6 +262,13 @@ module MySQLModule { const operation = this._operation(json) this.client.connect() const input = this._query(json, { disableReturning: true }) + if (Array.isArray(input)) { + const responses = [] + for (let query of input) { + responses.push(await internalQuery(this.client, query)) + } + return responses + } let row // need to manage returning, a feature mySQL can't do if (operation === operation.DELETE) { From 7efa06901e220adb94204fddb2761f469344358f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 1 Nov 2021 16:03:53 +0000 Subject: [PATCH 014/100] Adding error scenario for table name already in use. --- .../modals/CreateExternalTableModal.svelte | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/modals/CreateExternalTableModal.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/modals/CreateExternalTableModal.svelte index c7a040d89a..1d9e246d20 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/modals/CreateExternalTableModal.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/modals/CreateExternalTableModal.svelte @@ -6,8 +6,9 @@ export let datasource let name = "" - - $: valid = name && name.length > 0 + $: valid = name && name.length > 0 && !datasource?.entities[name] + $: error = + name && datasource?.entities[name] ? "Table name already in use." : null function buildDefaultTable(tableName, datasourceId) { return { @@ -40,5 +41,5 @@ Provide a name for your new table; you can add columns once it is created. - + From e9dd47b562d1dc9775234655e4ebca714551d80c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 1 Nov 2021 17:18:38 +0000 Subject: [PATCH 015/100] Add initial structure of table with search block --- .../design/AppPreview/componentStructure.json | 7 ++ packages/client/manifest.json | 83 ++++++++++++++++ .../app/blocks/TableWithSearch.svelte | 99 +++++++++++++++++++ .../components/app/blocks/block-builder.js | 14 +++ .../app/blocks/component-builder.js | 49 +++++++++ .../client/src/components/app/blocks/index.js | 1 + packages/client/src/components/app/index.js | 1 + 7 files changed, 254 insertions(+) create mode 100644 packages/client/src/components/app/blocks/TableWithSearch.svelte create mode 100644 packages/client/src/components/app/blocks/block-builder.js create mode 100644 packages/client/src/components/app/blocks/component-builder.js create mode 100644 packages/client/src/components/app/blocks/index.js diff --git a/packages/builder/src/components/design/AppPreview/componentStructure.json b/packages/builder/src/components/design/AppPreview/componentStructure.json index 3bc2554fde..b6675c2480 100644 --- a/packages/builder/src/components/design/AppPreview/componentStructure.json +++ b/packages/builder/src/components/design/AppPreview/componentStructure.json @@ -1,4 +1,11 @@ [ + { + "name": "Blocks", + "icon": "Article", + "children": [ + "tablewithsearch" + ] + }, "section", "container", "dataprovider", diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 2adfd96626..9b0bf7605c 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -2574,5 +2574,88 @@ "label": "Horizontal" } ] + }, + "tablewithsearch": { + "block": true, + "name": "Table with search", + "icon": "Table", + "styles": ["size"], + "settings": [ + { + "type": "dataSource", + "label": "Data", + "key": "dataSource" + }, + { + "type": "multifield", + "label": "Search Columns", + "key": "searchColumns", + "dependsOn": "dataSource", + "placeholder": "All columns" + }, + { + "type": "filter", + "label": "Filtering", + "key": "filter" + }, + { + "type": "field", + "label": "Sort Column", + "key": "sortColumn" + }, + { + "type": "select", + "label": "Sort Order", + "key": "sortOrder", + "options": ["Ascending", "Descending"], + "defaultValue": "Descending" + }, + { + "type": "boolean", + "label": "Paginate", + "key": "paginate", + "defaultValue": true + }, + { + "type": "number", + "label": "Row Count", + "key": "rowCount", + "defaultValue": 8 + }, + { + "type": "multifield", + "label": "Table Columns", + "key": "tableColumns", + "dependsOn": "dataSource", + "placeholder": "All columns" + }, + { + "type": "select", + "label": "Size", + "key": "size", + "defaultValue": "spectrum--medium", + "options": [ + { + "label": "Medium", + "value": "spectrum--medium" + }, + { + "label": "Large", + "value": "spectrum--large" + } + ] + }, + { + "type": "boolean", + "label": "Quiet", + "key": "quiet" + }, + { + "type": "boolean", + "label": "Auto Columns", + "key": "showAutoColumns", + "defaultValue": false + } + ] } } diff --git a/packages/client/src/components/app/blocks/TableWithSearch.svelte b/packages/client/src/components/app/blocks/TableWithSearch.svelte new file mode 100644 index 0000000000..dba50eb4b8 --- /dev/null +++ b/packages/client/src/components/app/blocks/TableWithSearch.svelte @@ -0,0 +1,99 @@ + + +
+ +
diff --git a/packages/client/src/components/app/blocks/block-builder.js b/packages/client/src/components/app/blocks/block-builder.js new file mode 100644 index 0000000000..2d56094806 --- /dev/null +++ b/packages/client/src/components/app/blocks/block-builder.js @@ -0,0 +1,14 @@ +import { ComponentBuilder } from "./component-builder" + +export class BlockBuilder { + context + componentCount = 0 + + constructor(context) { + this.context = context + } + + createComponent(type, props) { + return new ComponentBuilder(this, type, props) + } +} diff --git a/packages/client/src/components/app/blocks/component-builder.js b/packages/client/src/components/app/blocks/component-builder.js new file mode 100644 index 0000000000..90ce30c066 --- /dev/null +++ b/packages/client/src/components/app/blocks/component-builder.js @@ -0,0 +1,49 @@ +export class ComponentBuilder { + context + + constructor(blockBuilder, type, props) { + this.blockBuilder = blockBuilder + this.type = type + this.id = `${blockBuilder.context.id}-${blockBuilder.componentCount++}` + this.props = props + this.children = [] + this.styles = null + } + + setProp(key, value) { + this.props[key] = value + return this + } + + setProps(props) { + this.props = { + ...this.props, + ...props, + } + return this + } + + setStyles(styles) { + this.styles = styles + return this + } + + addChild(component) { + this.children.push(component) + return this + } + + build() { + return { + _component: `@budibase/standard-components/${this.type}`, + _id: this.id, + _children: this.children?.map(child => child.build()), + _styles: { + normal: { + ...this.styles, + }, + }, + ...this.props, + } + } +} diff --git a/packages/client/src/components/app/blocks/index.js b/packages/client/src/components/app/blocks/index.js new file mode 100644 index 0000000000..16b123d17e --- /dev/null +++ b/packages/client/src/components/app/blocks/index.js @@ -0,0 +1 @@ +export { default as tablewithsearch } from "./TableWithSearch.svelte" diff --git a/packages/client/src/components/app/index.js b/packages/client/src/components/app/index.js index b01763e2f0..92725f0738 100644 --- a/packages/client/src/components/app/index.js +++ b/packages/client/src/components/app/index.js @@ -32,6 +32,7 @@ export { default as spectrumcard } from "./SpectrumCard.svelte" export * from "./charts" export * from "./forms" export * from "./table" +export * from "./blocks" // Deprecated component left for compatibility in old apps export { default as navigation } from "./deprecated/Navigation.svelte" From c82cf534d80e70e997595c81de234e7108d226bd Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 1 Nov 2021 21:15:46 +0000 Subject: [PATCH 016/100] Updating external table UI a bit, adding the concept of defining an existing relationship, updating the data sources UI to make it a bit less cluttered and make the creation of tables more obvious. --- .../backend/DataTable/DataTable.svelte | 7 +++ .../buttons/ExistingRelationshipButton.svelte | 54 +++++++++++++++++++ .../CreateEditRelationship.svelte | 30 ++++++++++- .../backend/Datasources}/TableSelect.svelte | 0 .../[selectedDatasource]/index.svelte | 32 ++++++++--- packages/builder/src/stores/backend/tables.js | 1 + 6 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 packages/builder/src/components/backend/DataTable/buttons/ExistingRelationshipButton.svelte rename packages/builder/src/{pages/builder/app/[application]/data/datasource/[selectedDatasource]/CreateEditRelationship => components/backend/Datasources}/CreateEditRelationship.svelte (92%) rename packages/builder/src/{pages/builder/app/[application]/data/datasource/[selectedDatasource]/CreateEditRelationship => components/backend/Datasources}/TableSelect.svelte (100%) diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte index 1af703800f..6bebf2ca02 100644 --- a/packages/builder/src/components/backend/DataTable/DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte @@ -4,6 +4,7 @@ import CreateRowButton from "./buttons/CreateRowButton.svelte" import CreateColumnButton from "./buttons/CreateColumnButton.svelte" import CreateViewButton from "./buttons/CreateViewButton.svelte" + import ExistingRelationshipButton from "./buttons/ExistingRelationshipButton.svelte" import ExportButton from "./buttons/ExportButton.svelte" import EditRolesButton from "./buttons/EditRolesButton.svelte" import ManageAccessButton from "./buttons/ManageAccessButton.svelte" @@ -114,6 +115,12 @@ {#if isUsersTable} {/if} + {#if !isInternal} + + {/if} diff --git a/packages/builder/src/components/backend/DataTable/buttons/ExistingRelationshipButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/ExistingRelationshipButton.svelte new file mode 100644 index 0000000000..4a7abf487f --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/buttons/ExistingRelationshipButton.svelte @@ -0,0 +1,54 @@ + + +{#if table.sourceId} +
+ + Define existing relationship + +
+ + + +{/if} diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/CreateEditRelationship/CreateEditRelationship.svelte b/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte similarity index 92% rename from packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/CreateEditRelationship/CreateEditRelationship.svelte rename to packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte index 583ca5e887..81032da716 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/CreateEditRelationship/CreateEditRelationship.svelte +++ b/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte @@ -18,10 +18,19 @@ export let fromRelationship = {} export let toRelationship = {} export let close + export let selectedFromTable let originalFromName = fromRelationship.name, originalToName = toRelationship.name + if (fromRelationship && !fromRelationship.relationshipType) { + fromRelationship.relationshipType = RelationshipTypes.MANY_TO_ONE + } + + if (toRelationship && selectedFromTable) { + toRelationship.tableId = selectedFromTable._id + } + function inSchema(table, prop, ogName) { if (!table || !prop || prop === ogName) { return false @@ -114,6 +123,7 @@ }, ] $: updateRelationshipType(fromRelationship?.relationshipType) + $: tableChanged(fromTable, toTable) function updateRelationshipType(fromType) { if (fromType === RelationshipTypes.MANY_TO_MANY) { @@ -205,7 +215,6 @@ originalToName = toRelationship.name originalFromName = fromRelationship.name await save() - await tables.fetch() } async function deleteRelationship() { @@ -215,10 +224,26 @@ await tables.fetch() close() } + + function tableChanged(fromTbl, toTbl) { + fromRelationship.name = toTbl?.name || "" + errors.fromCol = "" + toRelationship.name = fromTbl?.name || "" + errors.toCol = "" + if (toTbl || fromTbl) { + checkForErrors( + fromTable, + toTable, + through, + fromRelationship, + toRelationship + ) + } + } ($touched.from = true)} bind:error={errors.from} bind:value={toRelationship.tableId} diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/CreateEditRelationship/TableSelect.svelte b/packages/builder/src/components/backend/Datasources/TableSelect.svelte similarity index 100% rename from packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/CreateEditRelationship/TableSelect.svelte rename to packages/builder/src/components/backend/Datasources/TableSelect.svelte diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte index 7c56d502e4..9bd2e10a08 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte @@ -8,11 +8,12 @@ Layout, Modal, InlineAlert, + ActionButton, } from "@budibase/bbui" import { datasources, integrations, queries, tables } from "stores/backend" import { notifications } from "@budibase/bbui" import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte" - import CreateEditRelationship from "./CreateEditRelationship/CreateEditRelationship.svelte" + import CreateEditRelationship from "components/backend/Datasources/CreateEditRelationship.svelte" import CreateExternalTableModal from "./modals/CreateExternalTableModal.svelte" import DisplayColumnModal from "./modals/EditDisplayColumnsModal.svelte" import ICONS from "components/backend/DatasourceNavigator/icons" @@ -174,14 +175,24 @@ Tables
{#if plusTables && plusTables.length !== 0} - + {/if}
- +
@@ -214,9 +225,15 @@
Relationships - + Define existing relationship +
Tell budibase how your tables are related to get even more smart @@ -331,7 +348,6 @@ .table-buttons { display: grid; - grid-gap: var(--spacing-l); grid-template-columns: 1fr 1fr; } diff --git a/packages/builder/src/stores/backend/tables.js b/packages/builder/src/stores/backend/tables.js index 7f90a04a05..baa9e38ab9 100644 --- a/packages/builder/src/stores/backend/tables.js +++ b/packages/builder/src/stores/backend/tables.js @@ -11,6 +11,7 @@ export function createTablesStore() { const tablesResponse = await api.get(`/api/tables`) const tables = await tablesResponse.json() update(state => ({ ...state, list: tables })) + return tables } async function select(table) { From 5d40882c665af02494fdf8224f29411811a377cc Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 1 Nov 2021 21:17:51 +0000 Subject: [PATCH 017/100] Removing the edit display columns modal as it was very specific and a little confusing, these are defaulted now anyway like internal tables. --- .../[selectedDatasource]/index.svelte | 20 --------- .../modals/EditDisplayColumnsModal.svelte | 43 ------------------- 2 files changed, 63 deletions(-) delete mode 100644 packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/modals/EditDisplayColumnsModal.svelte diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte index 9bd2e10a08..b1867db248 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte @@ -15,12 +15,10 @@ import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte" import CreateEditRelationship from "components/backend/Datasources/CreateEditRelationship.svelte" import CreateExternalTableModal from "./modals/CreateExternalTableModal.svelte" - import DisplayColumnModal from "./modals/EditDisplayColumnsModal.svelte" import ICONS from "components/backend/DatasourceNavigator/icons" import { capitalise } from "helpers" let relationshipModal - let displayColumnModal let createExternalTableModal let selectedFromRelationship, selectedToRelationship @@ -113,10 +111,6 @@ relationshipModal.show() } - function openDisplayColumnModal() { - displayColumnModal.show() - } - function createNewTable() { createExternalTableModal.show() } @@ -133,10 +127,6 @@ /> - - - - @@ -174,16 +164,6 @@
Tables
- {#if plusTables && plusTables.length !== 0} - - Update display columns - - {/if}
- import { ModalContent, Select, Body } from "@budibase/bbui" - import { tables } from "stores/backend" - - export let datasource - export let plusTables - export let save - - async function saveDisplayColumns() { - // be explicit about copying over - for (let table of plusTables) { - datasource.entities[table.name].primaryDisplay = table.primaryDisplay - } - save() - await tables.fetch() - } - - function getColumnOptions(table) { - if (!table || !table.schema) { - return [] - } - return Object.entries(table.schema) - .filter(field => field[1].type !== "link") - .map(([fieldName]) => fieldName) - } - - - - Select the columns that will be shown when displaying relationships. - {#each plusTables as table} - From f8b2429bd0179dd47162a25f95c44a92e70dd77e Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 3 Nov 2021 13:12:20 +0000 Subject: [PATCH 035/100] Fixing REST PUT using POST as per #3227. --- packages/server/src/api/controllers/query.js | 3 --- packages/server/src/integrations/rest.ts | 14 ++++++-------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/server/src/api/controllers/query.js b/packages/server/src/api/controllers/query.js index 89bf1a8ab0..a22cda4ebb 100644 --- a/packages/server/src/api/controllers/query.js +++ b/packages/server/src/api/controllers/query.js @@ -23,9 +23,6 @@ function formatResponse(resp) { try { resp = JSON.parse(resp) } catch (err) { - console.error( - "Error parsing JSON response. Returning string in array instead." - ) resp = { response: resp } } } diff --git a/packages/server/src/integrations/rest.ts b/packages/server/src/integrations/rest.ts index 9c6ece52d6..cf234518d9 100644 --- a/packages/server/src/integrations/rest.ts +++ b/packages/server/src/integrations/rest.ts @@ -142,13 +142,11 @@ module RestModule { } async parseResponse(response: any) { - switch (this.headers.Accept) { - case "application/json": - return await response.json() - case "text/html": - return await response.text() - default: - return await response.json() + const contentType = response.headers.get("content-type") + if (contentType && contentType.indexOf("application/json") !== -1) { + return await response.json() + } else { + return await response.text() } } @@ -191,7 +189,7 @@ module RestModule { } const response = await fetch(this.getUrl(path, queryString), { - method: "POST", + method: "PUT", headers: this.headers, body: JSON.stringify(json), }) From ea6646f055cb9f2a9c3c6ea0750cf96c6a9df52d Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 3 Nov 2021 13:13:22 +0000 Subject: [PATCH 036/100] Fixing #3237 and #3235 - always apply headers on out going webhooks if they are specified and handle a range of response codes. --- .../src/automations/steps/outgoingWebhook.js | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/server/src/automations/steps/outgoingWebhook.js b/packages/server/src/automations/steps/outgoingWebhook.js index 34299d23b6..f0637c3351 100644 --- a/packages/server/src/automations/steps/outgoingWebhook.js +++ b/packages/server/src/automations/steps/outgoingWebhook.js @@ -85,6 +85,18 @@ exports.run = async function ({ inputs }) { const request = { method: requestMethod, } + if (headers) { + try { + const customHeaders = + typeof headers === "string" ? JSON.parse(headers) : headers + request.headers = { ...request.headers, ...customHeaders } + } catch (err) { + return { + success: false, + response: "Unable to process headers, must be a JSON object.", + } + } + } if ( requestBody && requestBody.length !== 0 && @@ -95,21 +107,9 @@ exports.run = async function ({ inputs }) { ? requestBody : JSON.stringify(requestBody) request.headers = { + ...request.headers, "Content-Type": "application/json", } - - if (headers) { - try { - const customHeaders = - typeof headers === "string" ? JSON.parse(headers) : headers - request.headers = { ...request.headers, ...customHeaders } - } catch (err) { - return { - success: false, - response: "Unable to process headers, must be a JSON object.", - } - } - } } try { @@ -122,7 +122,7 @@ exports.run = async function ({ inputs }) { return { httpStatus: status, response: message, - success: status === 200, + success: status >= 200 && status <= 206, } } catch (err) { /* istanbul ignore next */ From 88a729913cacdf91acdda3d22a8c5b763c5c2841 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 3 Nov 2021 14:08:47 +0000 Subject: [PATCH 037/100] Fixing an issue with webhooks, couldn't use them in development (like getting schema) and making sure trigger will always use production app #3143. --- .../server/src/api/controllers/webhook.js | 49 ++++++++++++------- packages/server/src/automations/utils.js | 7 ++- packages/server/src/middleware/authorized.js | 7 +-- packages/server/src/middleware/currentapp.js | 2 + packages/server/src/middleware/utils.js | 7 +++ 5 files changed, 47 insertions(+), 25 deletions(-) create mode 100644 packages/server/src/middleware/utils.js diff --git a/packages/server/src/api/controllers/webhook.js b/packages/server/src/api/controllers/webhook.js index c810f85004..15ee748d8d 100644 --- a/packages/server/src/api/controllers/webhook.js +++ b/packages/server/src/api/controllers/webhook.js @@ -3,6 +3,7 @@ const { generateWebhookID, getWebhookParams } = require("../../db/utils") const toJsonSchema = require("to-json-schema") const validate = require("jsonschema").validate const triggers = require("../../automations/triggers") +const { getDeployedAppID } = require("@budibase/auth/db") const AUTOMATION_DESCRIPTION = "Generated from Webhook Schema" @@ -76,24 +77,34 @@ exports.buildSchema = async ctx => { } exports.trigger = async ctx => { - const db = new CouchDB(ctx.params.instance) - const webhook = await db.get(ctx.params.id) - // validate against the schema - if (webhook.bodySchema) { - validate(ctx.request.body, webhook.bodySchema) - } - const target = await db.get(webhook.action.target) - if (webhook.action.type === exports.WebhookType.AUTOMATION) { - // trigger with both the pure request and then expand it - // incase the user has produced a schema to bind to - await triggers.externalTrigger(target, { - body: ctx.request.body, - ...ctx.request.body, - appId: ctx.params.instance, - }) - } - ctx.status = 200 - ctx.body = { - message: "Webhook trigger fired successfully", + const prodAppId = getDeployedAppID(ctx.params.instance) + try { + const db = new CouchDB(prodAppId) + const webhook = await db.get(ctx.params.id) + // validate against the schema + if (webhook.bodySchema) { + validate(ctx.request.body, webhook.bodySchema) + } + const target = await db.get(webhook.action.target) + if (webhook.action.type === exports.WebhookType.AUTOMATION) { + // trigger with both the pure request and then expand it + // incase the user has produced a schema to bind to + await triggers.externalTrigger(target, { + body: ctx.request.body, + ...ctx.request.body, + appId: prodAppId, + }) + } + ctx.status = 200 + ctx.body = { + message: "Webhook trigger fired successfully", + } + } catch (err) { + if (err.status === 404) { + ctx.status = 200 + ctx.body = { + message: "Application not deployed yet.", + } + } } } diff --git a/packages/server/src/automations/utils.js b/packages/server/src/automations/utils.js index 4bef91a153..f2d1bf5699 100644 --- a/packages/server/src/automations/utils.js +++ b/packages/server/src/automations/utils.js @@ -6,6 +6,7 @@ const { queue } = require("./bullboard") const newid = require("../db/newid") const { updateEntityMetadata } = require("../utilities") const { MetadataTypes } = require("../constants") +const { getDeployedAppID } = require("@budibase/auth/db") const WH_STEP_ID = definitions.WEBHOOK.stepId const CRON_STEP_ID = definitions.CRON.stepId @@ -150,9 +151,13 @@ exports.checkForWebhooks = async ({ appId, oldAuto, newAuto }) => { await webhooks.save(ctx) const id = ctx.body.webhook._id newTrigger.webhookId = id + // the app ID has to be development for this endpoint + // it can only be used when building the app + // but the trigger endpoint will always be used in production + const prodAppId = getDeployedAppID(appId) newTrigger.inputs = { schemaUrl: `api/webhooks/schema/${appId}/${id}`, - triggerUrl: `api/webhooks/trigger/${appId}/${id}`, + triggerUrl: `api/webhooks/trigger/${prodAppId}/${id}`, } } return newAuto diff --git a/packages/server/src/middleware/authorized.js b/packages/server/src/middleware/authorized.js index bd064f7e66..d91311e165 100644 --- a/packages/server/src/middleware/authorized.js +++ b/packages/server/src/middleware/authorized.js @@ -5,20 +5,17 @@ const { doesHaveBasePermission, } = require("@budibase/auth/permissions") const builderMiddleware = require("./builder") +const { isWebhookEndpoint } = require("./utils") function hasResource(ctx) { return ctx.resourceId != null } -const WEBHOOK_ENDPOINTS = new RegExp( - ["webhooks/trigger", "webhooks/schema"].join("|") -) - module.exports = (permType, permLevel = null) => async (ctx, next) => { // webhooks don't need authentication, each webhook unique - if (WEBHOOK_ENDPOINTS.test(ctx.request.url)) { + if (isWebhookEndpoint(ctx)) { return next() } diff --git a/packages/server/src/middleware/currentapp.js b/packages/server/src/middleware/currentapp.js index 01b9dcc248..5682a860b9 100644 --- a/packages/server/src/middleware/currentapp.js +++ b/packages/server/src/middleware/currentapp.js @@ -9,6 +9,7 @@ const { isUserInAppTenant } = require("@budibase/auth/tenancy") const { getCachedSelf } = require("../utilities/global") const CouchDB = require("../db") const env = require("../environment") +const { isWebhookEndpoint } = require("./utils") module.exports = async (ctx, next) => { // try to get the appID from the request @@ -38,6 +39,7 @@ module.exports = async (ctx, next) => { // deny access to application preview if ( isDevAppID(requestAppId) && + !isWebhookEndpoint(ctx) && (!ctx.user || !ctx.user.builder || !ctx.user.builder.global) ) { clearCookie(ctx, Cookies.CurrentApp) diff --git a/packages/server/src/middleware/utils.js b/packages/server/src/middleware/utils.js new file mode 100644 index 0000000000..b1eea8cd66 --- /dev/null +++ b/packages/server/src/middleware/utils.js @@ -0,0 +1,7 @@ +const WEBHOOK_ENDPOINTS = new RegExp( + ["webhooks/trigger", "webhooks/schema"].join("|") +) + +exports.isWebhookEndpoint = ctx => { + return WEBHOOK_ENDPOINTS.test(ctx.request.url) +} From fbaedd6a3472ab0396620ebdd5d0d0bfec344d3f Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 3 Nov 2021 15:04:05 +0000 Subject: [PATCH 038/100] Fix: Prevent user updates in multi tenant mode from deleting user password. Also forward the authentication error from the backend to the login page to warn when an sso user is trying to log in with a password when one is not present --- packages/auth/src/middleware/passport/local.js | 14 ++++++++++++++ packages/auth/src/utils.js | 4 ++-- .../builder/src/pages/builder/auth/login.svelte | 2 +- packages/builder/src/stores/portal/auth.js | 2 +- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/auth/src/middleware/passport/local.js b/packages/auth/src/middleware/passport/local.js index 0db40d64eb..50c2b18d87 100644 --- a/packages/auth/src/middleware/passport/local.js +++ b/packages/auth/src/middleware/passport/local.js @@ -9,6 +9,7 @@ const { createASession } = require("../../security/sessions") const { getTenantId } = require("../../tenancy") const INVALID_ERR = "Invalid Credentials" +const SSO_NO_PASSWORD = "SSO user does not have a password set" exports.options = { passReqToCallback: true, @@ -36,6 +37,19 @@ exports.authenticate = async function (ctx, email, password, done) { return authError(done, INVALID_ERR) } + // check that the user has a stored password before proceeding + if (!dbUser.password) { + if ( + (dbUser.account && dbUser.account.authType === "sso") || // root account sso + dbUser.thirdPartyProfile // internal sso + ) { + return authError(done, SSO_NO_PASSWORD) + } + + console.error("User has no password", dbUser) + return authError(done, INVALID_ERR) + } + // authenticate if (await compare(password, dbUser.password)) { const sessionId = newid() diff --git a/packages/auth/src/utils.js b/packages/auth/src/utils.js index e1df289d6e..f7ab5d6990 100644 --- a/packages/auth/src/utils.js +++ b/packages/auth/src/utils.js @@ -181,8 +181,8 @@ exports.saveUser = async ( // check budibase users in other tenants if (env.MULTI_TENANCY) { - dbUser = await getTenantUser(email) - if (dbUser != null && dbUser.tenantId !== tenantId) { + const tenantUser = await getTenantUser(email) + if (tenantUser != null && tenantUser.tenantId !== tenantId) { throw `Email address ${email} already in use.` } } diff --git a/packages/builder/src/pages/builder/auth/login.svelte b/packages/builder/src/pages/builder/auth/login.svelte index 7374678236..5a5a27eb6e 100644 --- a/packages/builder/src/pages/builder/auth/login.svelte +++ b/packages/builder/src/pages/builder/auth/login.svelte @@ -44,7 +44,7 @@ } } catch (err) { console.error(err) - notifications.error("Invalid credentials") + notifications.error(err.message ? err.message : "Invalid Credentials") } } diff --git a/packages/builder/src/stores/portal/auth.js b/packages/builder/src/stores/portal/auth.js index 333226e3ba..134232dd74 100644 --- a/packages/builder/src/stores/portal/auth.js +++ b/packages/builder/src/stores/portal/auth.js @@ -112,7 +112,7 @@ export function createAuthStore() { if (response.status === 200) { setUser(json.user) } else { - throw "Invalid credentials" + throw new Error(json.message ? json.message : "Invalid credentials") } return json }, From 58d7b13e9e7d8fb8d60fc4aeef348fd94e88bcea Mon Sep 17 00:00:00 2001 From: Budibase Release Bot <> Date: Wed, 3 Nov 2021 15:10:20 +0000 Subject: [PATCH 039/100] v0.9.174 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 6 +++--- packages/server/package.json | 8 ++++---- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lerna.json b/lerna.json index d879d3c65c..0848829304 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.173", + "version": "0.9.174", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index cecb16d00a..8d6ff8c6ab 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.173", + "version": "0.9.174", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 8cd9b14520..78edb81229 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.173", + "version": "0.9.174", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 1b337e2baf..79b4f3b6d7 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.173", + "version": "0.9.174", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.173", - "@budibase/client": "^0.9.173", + "@budibase/bbui": "^0.9.174", + "@budibase/client": "^0.9.174", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.173", + "@budibase/string-templates": "^0.9.174", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 0ed0c98266..1dac63f129 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.173", + "version": "0.9.174", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 9edca0f2a3..b8cc017d68 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.173", + "version": "0.9.174", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.173", + "@budibase/bbui": "^0.9.174", "@budibase/standard-components": "^0.9.139", - "@budibase/string-templates": "^0.9.173", + "@budibase/string-templates": "^0.9.174", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index a1271be56b..c96e8e5edf 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.173", + "version": "0.9.174", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -68,9 +68,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.173", - "@budibase/client": "^0.9.173", - "@budibase/string-templates": "^0.9.173", + "@budibase/auth": "^0.9.174", + "@budibase/client": "^0.9.174", + "@budibase/string-templates": "^0.9.174", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 1906bfde0d..0b29019c49 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.173", + "version": "0.9.174", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 05a322c822..ba75b2e7d1 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.173", + "version": "0.9.174", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -29,8 +29,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.173", - "@budibase/string-templates": "^0.9.173", + "@budibase/auth": "^0.9.174", + "@budibase/string-templates": "^0.9.174", "@koa/router": "^8.0.0", "@sentry/node": "^6.0.0", "@techpass/passport-openidconnect": "^0.3.0", From 512ed5aa2a1357655b7fc6281a390a9682b7d518 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 3 Nov 2021 15:26:15 +0000 Subject: [PATCH 040/100] Add support for tables updating server-side sorting --- .../src/components/app/DataProvider.svelte | 68 +++++++++++++++---- .../src/components/app/table/Table.svelte | 15 +++- packages/client/src/constants.js | 1 + 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/packages/client/src/components/app/DataProvider.svelte b/packages/client/src/components/app/DataProvider.svelte index 54e758ad3c..a8e53f4906 100644 --- a/packages/client/src/components/app/DataProvider.svelte +++ b/packages/client/src/components/app/DataProvider.svelte @@ -35,28 +35,36 @@ let pageNumber = 0 let query = null + // Sorting can be overridden at run time, so we can't use the prop directly + let currentSortColumn = sortColumn + let currentSortOrder = sortOrder + $: query = buildLuceneQuery(filter) $: internalTable = dataSource?.type === "table" $: nestedProvider = dataSource?.type === "provider" $: hasNextPage = bookmarks[pageNumber + 1] != null $: hasPrevPage = pageNumber > 0 $: getSchema(dataSource) - $: sortType = getSortType(schema, sortColumn) + $: sortType = getSortType(schema, currentSortColumn) + + // Wait until schema loads before loading data, so that we can determine + // the correct sort type first time $: { - // Wait until schema loads before loading data, so that we can determine - // the correct sort type first time if (schemaLoaded) { fetchData( dataSource, + schema, query, limit, - sortColumn, - sortOrder, + currentSortColumn, + currentSortOrder, sortType, paginate ) } } + + // Reactively filter and sort rows if required $: { if (internalTable) { // Internal tables are already processed server-side @@ -65,10 +73,17 @@ // For anything else we use client-side implementations to filter, sort // and limit const filtered = luceneQuery(allRows, query) - const sorted = luceneSort(filtered, sortColumn, sortOrder, sortType) + const sorted = luceneSort( + filtered, + currentSortColumn, + currentSortOrder, + sortType + ) rows = luceneLimit(sorted, limit) } } + + // Build our action context $: actions = [ { type: ActionTypes.RefreshDatasource, @@ -79,7 +94,20 @@ type: ActionTypes.SetDataProviderQuery, callback: newQuery => (query = newQuery), }, + { + type: ActionTypes.SetDataProviderSorting, + callback: ({ column, order }) => { + if (column) { + currentSortColumn = column + } + if (order) { + currentSortOrder = order + } + }, + }, ] + + // Build our data context $: dataContext = { rows, schema, @@ -88,7 +116,11 @@ // Undocumented properties. These aren't supposed to be used in builder // bindings, but are used internally by other components id: $component?.id, - state: { query }, + state: { + query, + sortColumn: currentSortColumn, + sortOrder: currentSortOrder, + }, loaded, } @@ -104,10 +136,11 @@ if (schemaLoaded && !nestedProvider) { fetchData( dataSource, + schema, query, limit, - sortColumn, - sortOrder, + currentSortColumn, + currentSortOrder, sortType, paginate ) @@ -116,6 +149,7 @@ const fetchData = async ( dataSource, + schema, query, limit, sortColumn, @@ -125,12 +159,16 @@ ) => { loading = true if (dataSource?.type === "table") { + // Sanity check sort column, as using a non-existant column will prevent + // results coming back at all + const sort = schema?.[sortColumn] ? sortColumn : undefined + // For internal tables we use server-side processing const res = await API.searchTable({ tableId: dataSource.tableId, query, limit, - sort: sortColumn, + sort, sortOrder: sortOrder?.toLowerCase() ?? "ascending", sortType, paginate, @@ -181,13 +219,14 @@ if (!hasNextPage || !internalTable) { return } + const sort = schema?.[currentSortColumn] ? currentSortColumn : undefined const res = await API.searchTable({ tableId: dataSource?.tableId, query, bookmark: bookmarks[pageNumber + 1], limit, - sort: sortColumn, - sortOrder: sortOrder?.toLowerCase() ?? "ascending", + sort, + sortOrder: currentSortOrder?.toLowerCase() ?? "ascending", sortType, paginate: true, }) @@ -202,13 +241,14 @@ if (!hasPrevPage || !internalTable) { return } + const sort = schema?.[currentSortColumn] ? currentSortColumn : undefined const res = await API.searchTable({ tableId: dataSource?.tableId, query, bookmark: bookmarks[pageNumber - 1], limit, - sort: sortColumn, - sortOrder: sortOrder?.toLowerCase() ?? "ascending", + sort, + sortOrder: currentSortOrder?.toLowerCase() ?? "ascending", sortType, paginate: true, }) diff --git a/packages/client/src/components/app/table/Table.svelte b/packages/client/src/components/app/table/Table.svelte index ba9f3aae97..a6508c5763 100644 --- a/packages/client/src/components/app/table/Table.svelte +++ b/packages/client/src/components/app/table/Table.svelte @@ -11,7 +11,11 @@ export let size const component = getContext("component") - const { styleable } = getContext("sdk") + const { styleable, getAction, ActionTypes } = getContext("sdk") + const setSorting = getAction( + dataProvider?.id, + ActionTypes.SetDataProviderSorting + ) const customColumnKey = `custom-${Math.random()}` const customRenderers = [ { @@ -70,6 +74,13 @@ }) return newSchema } + + const onSort = e => { + setSorting({ + column: e.detail.column, + order: e.detail.order, + }) + }
@@ -84,6 +95,8 @@ allowEditRows={false} allowEditColumns={false} showAutoColumns={true} + disableSorting + on:sort={onSort} > diff --git a/packages/client/src/constants.js b/packages/client/src/constants.js index d2237ba3b9..c660d8d86c 100644 --- a/packages/client/src/constants.js +++ b/packages/client/src/constants.js @@ -6,6 +6,7 @@ export const ActionTypes = { ValidateForm: "ValidateForm", RefreshDatasource: "RefreshDatasource", SetDataProviderQuery: "SetDataProviderQuery", + SetDataProviderSorting: "SetDataProviderSorting", ClearForm: "ClearForm", ChangeFormStep: "ChangeFormStep", } From b2bf5056b54f6213ca2ac2dbe13bc7e840d4d1b1 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 3 Nov 2021 15:45:19 +0000 Subject: [PATCH 041/100] Fixing rest test mocking. --- packages/server/src/integrations/tests/rest.spec.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/server/src/integrations/tests/rest.spec.js b/packages/server/src/integrations/tests/rest.spec.js index cb69d3c38d..7b128a6d14 100644 --- a/packages/server/src/integrations/tests/rest.spec.js +++ b/packages/server/src/integrations/tests/rest.spec.js @@ -1,5 +1,11 @@ jest.mock("node-fetch", () => - jest.fn(() => ({ json: jest.fn(), text: jest.fn() })) + jest.fn(() => ({ + headers: { + get: () => ["application/json"] + }, + json: jest.fn(), + text: jest.fn() + })) ) const fetch = require("node-fetch") const RestIntegration = require("../rest") From 044579b829d2f5ad2cfbe59abc18c0663da91965 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 3 Nov 2021 15:46:45 +0000 Subject: [PATCH 042/100] Add expired user notification --- packages/auth/src/middleware/passport/local.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/auth/src/middleware/passport/local.js b/packages/auth/src/middleware/passport/local.js index 50c2b18d87..f95c3a173e 100644 --- a/packages/auth/src/middleware/passport/local.js +++ b/packages/auth/src/middleware/passport/local.js @@ -10,6 +10,7 @@ const { getTenantId } = require("../../tenancy") const INVALID_ERR = "Invalid Credentials" const SSO_NO_PASSWORD = "SSO user does not have a password set" +const EXPIRED = "This account has expired. Please reset your password" exports.options = { passReqToCallback: true, @@ -46,8 +47,8 @@ exports.authenticate = async function (ctx, email, password, done) { return authError(done, SSO_NO_PASSWORD) } - console.error("User has no password", dbUser) - return authError(done, INVALID_ERR) + console.error("Non SSO usser has no password set", dbUser) + return authError(done, EXPIRED) } // authenticate From 4019eeaa26e848a5c4b498c025680977fbfe8b93 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 3 Nov 2021 15:53:45 +0000 Subject: [PATCH 043/100] Fix picker width in table with search block and enable autosizing of other search components --- .../client/src/components/app/blocks/TableWithSearch.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/components/app/blocks/TableWithSearch.svelte b/packages/client/src/components/app/blocks/TableWithSearch.svelte index b3b6598cee..09fa43934d 100644 --- a/packages/client/src/components/app/blocks/TableWithSearch.svelte +++ b/packages/client/src/components/app/blocks/TableWithSearch.svelte @@ -150,7 +150,7 @@ max-width: 100%; flex-wrap: wrap; } - .search :global(.component > *) { + .search:not(.mobile) :global(.spectrum-Form-itemField > .spectrum-Picker) { width: 200px; } .search :global(.spectrum-InputGroup .spectrum-InputGroup-input) { From 294dd232bc71276083714e333a96eef3cdf4eb8e Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 3 Nov 2021 16:17:45 +0000 Subject: [PATCH 044/100] Revert changes to medium sized checkbox --- packages/bbui/src/Form/Core/Checkbox.svelte | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/bbui/src/Form/Core/Checkbox.svelte b/packages/bbui/src/Form/Core/Checkbox.svelte index 4290984331..a5b366c262 100644 --- a/packages/bbui/src/Form/Core/Checkbox.svelte +++ b/packages/bbui/src/Form/Core/Checkbox.svelte @@ -51,11 +51,6 @@ diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 11aa033c1d..655f2f32e5 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -161,9 +161,15 @@ const confirmTextMap = { */ export const enrichButtonActions = (actions, context) => { // Prevent button actions in the builder preview - if (get(builderStore).inBuilder) { + if (!actions || get(builderStore).inBuilder) { return () => {} } + + // If this is a function then it has already been enriched + if (typeof actions === "function") { + return actions + } + const handlers = actions.map(def => handlerMap[def["##eventHandlerType"]]) return async () => { for (let i = 0; i < handlers.length; i++) { diff --git a/packages/client/src/utils/componentProps.js b/packages/client/src/utils/componentProps.js index a181d1ea4d..d600fdef04 100644 --- a/packages/client/src/utils/componentProps.js +++ b/packages/client/src/utils/componentProps.js @@ -36,17 +36,19 @@ export const enrichProps = (props, context) => { let enrichedProps = enrichDataBindings(props, totalContext) // Enrich click actions if they exist - if (enrichedProps.onClick) { - enrichedProps.onClick = enrichButtonActions( - enrichedProps.onClick, - totalContext - ) - } + Object.keys(enrichedProps).forEach(prop => { + if (prop?.toLowerCase().includes("onclick")) { + enrichedProps[prop] = enrichButtonActions( + enrichedProps[prop], + totalContext + ) + } + }) // Enrich any click actions in conditions if (enrichedProps._conditions) { enrichedProps._conditions.forEach(condition => { - if (condition.setting === "onClick") { + if (condition.setting?.toLowerCase().includes("onclick")) { condition.settingValue = enrichButtonActions( condition.settingValue, totalContext From d5983d56039686d478328e2b6f5b72baf0e5d334 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 4 Nov 2021 11:31:43 +0000 Subject: [PATCH 055/100] Lint --- packages/client/src/utils/styleable.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/client/src/utils/styleable.js b/packages/client/src/utils/styleable.js index f35e45d3fe..c93742d560 100644 --- a/packages/client/src/utils/styleable.js +++ b/packages/client/src/utils/styleable.js @@ -1,4 +1,3 @@ -import { get } from "svelte/store" import { builderStore } from "stores" /** From b80a93d6d5eda97cc8ed38296b9e93a7da005db7 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 4 Nov 2021 14:03:18 +0100 Subject: [PATCH 056/100] cookie based approach --- packages/auth/src/constants.js | 1 + .../components/start/CreateAppModal.svelte | 2 +- .../builder/src/pages/builder/_layout.svelte | 7 ++++++- .../src/pages/builder/auth/_layout.svelte | 6 +----- .../pages/builder/portal/apps/index.svelte | 7 ++++--- packages/builder/src/stores/portal/auth.js | 21 +++++++------------ .../worker/src/api/controllers/global/auth.js | 11 ++++++++++ packages/worker/src/api/routes/global/auth.js | 2 ++ 8 files changed, 33 insertions(+), 24 deletions(-) diff --git a/packages/auth/src/constants.js b/packages/auth/src/constants.js index 4b4aef5a42..9892275bec 100644 --- a/packages/auth/src/constants.js +++ b/packages/auth/src/constants.js @@ -6,6 +6,7 @@ exports.UserStatus = { exports.Cookies = { CurrentApp: "budibase:currentapp", Auth: "budibase:auth", + Init: "budibase:init", OIDC_CONFIG: "budibase:oidc:config", } diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index d25acf2495..1db5e46261 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -139,7 +139,7 @@ } const userResp = await api.post(`/api/users/metadata/self`, user) await userResp.json() - auth.resetInitTemplate() + await auth.setInitInfo({}) $goto(`/builder/app/${appJson.instance._id}`) } catch (error) { console.error(error) diff --git a/packages/builder/src/pages/builder/_layout.svelte b/packages/builder/src/pages/builder/_layout.svelte index 39c93f0fb0..9ef171d7c7 100644 --- a/packages/builder/src/pages/builder/_layout.svelte +++ b/packages/builder/src/pages/builder/_layout.svelte @@ -1,5 +1,5 @@ diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index 94198caeab..f9aac6e0c1 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -201,9 +201,10 @@ await apps.load() loaded = true // if the portal is loaded from an external URL with a template param - const templateKey = $auth.initTemplate - if (templateKey) { - createAppFromTemplateUrl(templateKey) + const initInfo = await auth.getInitInfo() + console.log(initInfo) + if (initInfo.init_template) { + createAppFromTemplateUrl(initInfo.init_template) } }) diff --git a/packages/builder/src/stores/portal/auth.js b/packages/builder/src/stores/portal/auth.js index 9723b64ffc..47ebbfe7ef 100644 --- a/packages/builder/src/stores/portal/auth.js +++ b/packages/builder/src/stores/portal/auth.js @@ -33,7 +33,6 @@ export function createAuthStore() { user: $store.user, tenantId: $store.tenantId, tenantSet: $store.tenantSet, - initTemplate: $store.initTemplate, loaded: $store.loaded, initials, isAdmin, @@ -81,28 +80,22 @@ export function createAuthStore() { } } - function updateInitTemplate(template) { - auth.update(store => { - store.initTemplate = template - return store - }) - } - return { subscribe: store.subscribe, - resetInitTemplate: () => updateInitTemplate(null), setOrganisation: setOrganisation, + getInitInfo: async () => { + const response = await api.get(`/api/global/auth/init`) + return await response.json() + }, + setInitInfo: async info => { + await api.post(`/api/global/auth/init`, info) + }, checkQueryString: async () => { const urlParams = new URLSearchParams(window.location.search) if (urlParams.has("tenantId")) { const tenantId = urlParams.get("tenantId") await setOrganisation(tenantId) } - - // set the template to create an app from - if (urlParams.has("template")) { - updateInitTemplate(urlParams.get("template")) - } }, setOrg: async tenantId => { await setOrganisation(tenantId) diff --git a/packages/worker/src/api/controllers/global/auth.js b/packages/worker/src/api/controllers/global/auth.js index 50b4ec969b..e111619041 100644 --- a/packages/worker/src/api/controllers/global/auth.js +++ b/packages/worker/src/api/controllers/global/auth.js @@ -77,6 +77,17 @@ exports.authenticate = async (ctx, next) => { })(ctx, next) } +exports.setInitInfo = ctx => { + const initInfo = ctx.request.body + setCookie(ctx, initInfo, Cookies.Init) + ctx.status = 200 +} + +exports.getInitInfo = ctx => { + const initInfo = getCookie(ctx, Cookies.Init) + ctx.body = initInfo +} + /** * Reset the user password, used as part of a forgotten password flow. */ diff --git a/packages/worker/src/api/routes/global/auth.js b/packages/worker/src/api/routes/global/auth.js index 9cd77ce153..7baad60ecd 100644 --- a/packages/worker/src/api/routes/global/auth.js +++ b/packages/worker/src/api/routes/global/auth.js @@ -56,6 +56,8 @@ router authController.resetUpdate ) .post("/api/global/auth/logout", authController.logout) + .post("/api/global/auth/init", authController.setInitInfo) + .get("/api/global/auth/init", authController.getInitInfo) .get( "/api/global/auth/:tenantId/google", updateTenant, From e8889719365c4fd588cd662b7e8ec9eb700f4c83 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 4 Nov 2021 14:04:58 +0100 Subject: [PATCH 057/100] remove logs --- packages/builder/src/pages/builder/_layout.svelte | 1 - packages/builder/src/pages/builder/portal/apps/index.svelte | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/builder/src/pages/builder/_layout.svelte b/packages/builder/src/pages/builder/_layout.svelte index 9ef171d7c7..179131107f 100644 --- a/packages/builder/src/pages/builder/_layout.svelte +++ b/packages/builder/src/pages/builder/_layout.svelte @@ -48,7 +48,6 @@ onMount(async () => { if ($params["?template"]) { - console.log("SETTING COOKIE", $params["?template"]) await auth.setInitInfo({ init_template: $params["?template"] }) } diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index f9aac6e0c1..2c8cbb1097 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -202,7 +202,6 @@ loaded = true // if the portal is loaded from an external URL with a template param const initInfo = await auth.getInitInfo() - console.log(initInfo) if (initInfo.init_template) { createAppFromTemplateUrl(initInfo.init_template) } From 8c4c0ae93ebc0dfc87edf09ffe4697ff5e883a2d Mon Sep 17 00:00:00 2001 From: Budibase Release Bot <> Date: Thu, 4 Nov 2021 13:56:13 +0000 Subject: [PATCH 058/100] v0.9.177 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 6 +++--- packages/server/package.json | 8 ++++---- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lerna.json b/lerna.json index ec4dd2b529..2177433557 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.176", + "version": "0.9.177", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 73215d66a4..5a6692c2a7 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.176", + "version": "0.9.177", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index dffea62d09..e4be1ddb39 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.176", + "version": "0.9.177", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 77951831bd..c86268f2b1 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.176", + "version": "0.9.177", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.176", - "@budibase/client": "^0.9.176", + "@budibase/bbui": "^0.9.177", + "@budibase/client": "^0.9.177", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.176", + "@budibase/string-templates": "^0.9.177", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 3d105de857..2b87fc0142 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.176", + "version": "0.9.177", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 610cd87c05..21825fb7d6 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.176", + "version": "0.9.177", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.176", + "@budibase/bbui": "^0.9.177", "@budibase/standard-components": "^0.9.139", - "@budibase/string-templates": "^0.9.176", + "@budibase/string-templates": "^0.9.177", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index c2fbd81414..08c5f71031 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.176", + "version": "0.9.177", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -68,9 +68,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.176", - "@budibase/client": "^0.9.176", - "@budibase/string-templates": "^0.9.176", + "@budibase/auth": "^0.9.177", + "@budibase/client": "^0.9.177", + "@budibase/string-templates": "^0.9.177", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 156e835dc6..e0b7c5558e 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.176", + "version": "0.9.177", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 5827027a95..6e8b25beaf 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.176", + "version": "0.9.177", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -29,8 +29,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.176", - "@budibase/string-templates": "^0.9.176", + "@budibase/auth": "^0.9.177", + "@budibase/string-templates": "^0.9.177", "@koa/router": "^8.0.0", "@sentry/node": "^6.0.0", "@techpass/passport-openidconnect": "^0.3.0", From 94ee13ffc4da6bffb7c919f9db23d50ffecb689a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 4 Nov 2021 14:53:03 +0000 Subject: [PATCH 059/100] Adding the sync call from the worker for creation, updating and deletion of users. Making sure that production and development apps are always up to date with user metadata. --- hosting/docker-compose.yaml | 1 + .../templates/worker-service-deployment.yaml | 2 + packages/server/src/api/controllers/user.js | 64 ++++++++++++++++--- packages/server/src/middleware/authorized.js | 3 +- packages/server/src/utilities/global.js | 1 + packages/server/src/utilities/index.js | 10 ++- packages/worker/scripts/dev/manage.js | 1 + .../src/api/controllers/global/users.js | 8 ++- packages/worker/src/environment.js | 8 +++ packages/worker/src/utilities/appService.js | 23 +++++++ 10 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 packages/worker/src/utilities/appService.js diff --git a/hosting/docker-compose.yaml b/hosting/docker-compose.yaml index 66b24f4e49..c94d1520a1 100644 --- a/hosting/docker-compose.yaml +++ b/hosting/docker-compose.yaml @@ -45,6 +45,7 @@ services: MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY} MINIO_SECRET_KEY: ${MINIO_SECRET_KEY} MINIO_URL: http://minio-service:9000 + APPS_URL: http://app-service:4002 COUCH_DB_USERNAME: ${COUCH_DB_USER} COUCH_DB_PASSWORD: ${COUCH_DB_PASSWORD} COUCH_DB_URL: http://${COUCH_DB_USER}:${COUCH_DB_PASSWORD}@couchdb-service:5984 diff --git a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml index d7d8702567..6cded8545f 100644 --- a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml @@ -113,6 +113,8 @@ spec: value: {{ .Values.globals.smtp.port | quote }} - name: SMTP_FROM_ADDRESS value: {{ .Values.globals.smtp.from | quote }} + - name: APPS_URL + value: http://app-service:{{ .Values.services.apps.port }} image: budibase/worker imagePullPolicy: Always name: bbworker diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index 0362d1298b..5faf821349 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -8,8 +8,13 @@ const { getGlobalUsers, getRawGlobalUser } = require("../../utilities/global") const { getFullUser } = require("../../utilities/users") const { isEqual } = require("lodash") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") -const { getDevelopmentAppID } = require("@budibase/auth/db") +const { + getDevelopmentAppID, + getAllApps, + isDevAppID, +} = require("@budibase/auth/db") const { doesDatabaseExist } = require("../../utilities") +const { UserStatus } = require("@budibase/auth/constants") async function rawMetadata(db) { return ( @@ -21,7 +26,7 @@ async function rawMetadata(db) { ).rows.map(row => row.doc) } -async function combineMetadataAndUser(user, metadata) { +function combineMetadataAndUser(user, metadata) { // skip users with no access if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) { return null @@ -67,22 +72,61 @@ exports.syncGlobalUsers = async appId => { } exports.syncUser = async function (ctx) { - const user = await getRawGlobalUser(ctx.params.id) + let deleting = false, + user + const userId = ctx.params.id + try { + user = await getRawGlobalUser(userId) + } catch (err) { + user = {} + deleting = true + } const roles = user.roles + // remove props which aren't useful to metadata + delete user.password + delete user.forceResetPassword delete user.roles - for (let [prodAppId, roleId] of Object.entries(roles)) { - if (roleId === BUILTIN_ROLE_IDS.PUBLIC) { - continue - } + // run through all production appIDs in the users roles + let prodAppIds + // if they are a builder then get all production app IDs + if ((user.builder && user.builder.global) || deleting) { + prodAppIds = (await getAllApps(CouchDB, { idsOnly: true })).filter( + id => !isDevAppID(id) + ) + } else { + prodAppIds = Object.entries(roles) + .filter(entry => entry[1] !== BUILTIN_ROLE_IDS.PUBLIC) + .map(([appId]) => appId) + } + for (let prodAppId of prodAppIds) { const devAppId = getDevelopmentAppID(prodAppId) for (let appId of [prodAppId, devAppId]) { if (!(await doesDatabaseExist(appId))) { continue } const db = new CouchDB(appId) - const userId = generateUserMetadataID(user._id) - const metadata = await db.get(userId) - const combined = combineMetadataAndUser(user, metadata) + const metadataId = generateUserMetadataID(userId) + let metadata + try { + metadata = await db.get(metadataId) + } catch (err) { + if (deleting) { + continue + } + metadata = { + tableId: InternalTables.USER_METADATA, + } + } + let combined + if (deleting) { + combined = { + ...metadata, + status: UserStatus.INACTIVE, + metadata: BUILTIN_ROLE_IDS.PUBLIC, + } + } else { + combined = combineMetadataAndUser(user, metadata) + } await db.put(combined) } } diff --git a/packages/server/src/middleware/authorized.js b/packages/server/src/middleware/authorized.js index bd064f7e66..a30e034be2 100644 --- a/packages/server/src/middleware/authorized.js +++ b/packages/server/src/middleware/authorized.js @@ -18,7 +18,8 @@ module.exports = (permType, permLevel = null) => async (ctx, next) => { // webhooks don't need authentication, each webhook unique - if (WEBHOOK_ENDPOINTS.test(ctx.request.url)) { + // also internal requests (between services) don't need authorized + if (WEBHOOK_ENDPOINTS.test(ctx.request.url) || ctx.internal) { return next() } diff --git a/packages/server/src/utilities/global.js b/packages/server/src/utilities/global.js index eb64aa7f77..6527aa0601 100644 --- a/packages/server/src/utilities/global.js +++ b/packages/server/src/utilities/global.js @@ -77,6 +77,7 @@ exports.getGlobalUsers = async (appId = null, users = null) => { .filter(user => user != null) .map(user => { delete user.password + delete user.forceResetPassword return user }) if (!appId) { diff --git a/packages/server/src/utilities/index.js b/packages/server/src/utilities/index.js index d67c8f1da0..266ee09f9f 100644 --- a/packages/server/src/utilities/index.js +++ b/packages/server/src/utilities/index.js @@ -136,7 +136,11 @@ exports.stringToReadStream = string => { } exports.doesDatabaseExist = async dbName => { - const db = new CouchDB(dbName, { skip_setup: true }) - const info = await db.info() - return info && !info.error + try { + const db = new CouchDB(dbName, { skip_setup: true }) + const info = await db.info() + return info && !info.error + } catch (err) { + return false + } } diff --git a/packages/worker/scripts/dev/manage.js b/packages/worker/scripts/dev/manage.js index e0b8c3586a..179167883f 100644 --- a/packages/worker/scripts/dev/manage.js +++ b/packages/worker/scripts/dev/manage.js @@ -25,6 +25,7 @@ async function init() { ACCOUNT_PORTAL_URL: "http://localhost:10001", ACCOUNT_PORTAL_API_KEY: "budibase", PLATFORM_URL: "http://localhost:10000", + APPS_URL: "http://localhost:4001", } let envFile = "" Object.keys(envFileJson).forEach(key => { diff --git a/packages/worker/src/api/controllers/global/users.js b/packages/worker/src/api/controllers/global/users.js index ed70d6122e..42166faad7 100644 --- a/packages/worker/src/api/controllers/global/users.js +++ b/packages/worker/src/api/controllers/global/users.js @@ -19,6 +19,7 @@ const { } = require("@budibase/auth/tenancy") const { removeUserFromInfoDB } = require("@budibase/auth/deprovision") const env = require("../../../environment") +const { syncUserInApps } = require("../../../utilities/appService") async function allUsers() { const db = getGlobalDB() @@ -32,7 +33,10 @@ async function allUsers() { exports.save = async ctx => { try { - ctx.body = await saveUser(ctx.request.body, getTenantId()) + const user = await saveUser(ctx.request.body, getTenantId()) + // let server know to sync user + await syncUserInApps(user._id) + ctx.body = user } catch (err) { ctx.throw(err.status || 400, err) } @@ -129,6 +133,8 @@ exports.destroy = async ctx => { await db.remove(dbUser._id, dbUser._rev) await userCache.invalidateUser(dbUser._id) await invalidateSessions(dbUser._id) + // let server know to sync user + await syncUserInApps(dbUser._id) ctx.body = { message: `User ${ctx.params.id} deleted.`, } diff --git a/packages/worker/src/environment.js b/packages/worker/src/environment.js index a1fab84112..91f06ea46d 100644 --- a/packages/worker/src/environment.js +++ b/packages/worker/src/environment.js @@ -42,6 +42,7 @@ module.exports = { SMTP_FROM_ADDRESS: process.env.SMTP_FROM_ADDRESS, PLATFORM_URL: process.env.PLATFORM_URL, COOKIE_DOMAIN: process.env.COOKIE_DOMAIN, + APPS_URL: process.env.APPS_URL, _set(key, value) { process.env[key] = value module.exports[key] = value @@ -53,6 +54,13 @@ module.exports = { }, } +// if some var haven't been set, define them +if (!module.exports.APPS_URL) { + module.exports.APPS_URL = isDev() + ? "http://localhost:4001" + : "http://app-service:4002" +} + // clean up any environment variable edge cases for (let [key, value] of Object.entries(module.exports)) { // handle the edge case of "0" to disable an environment variable diff --git a/packages/worker/src/utilities/appService.js b/packages/worker/src/utilities/appService.js new file mode 100644 index 0000000000..a7be05b227 --- /dev/null +++ b/packages/worker/src/utilities/appService.js @@ -0,0 +1,23 @@ +const fetch = require("node-fetch") +const { Headers } = require("@budibase/auth/constants") +const { getTenantId, isTenantIdSet } = require("@budibase/auth/tenancy") +const { checkSlashesInUrl } = require("../utilities") +const env = require("../environment") + +exports.syncUserInApps = async userId => { + const request = { headers: {} } + request.headers[Headers.API_KEY] = env.INTERNAL_API_KEY + if (isTenantIdSet()) { + request.headers[Headers.TENANT_ID] = getTenantId() + } + request.headers["Content-Type"] = "application/json" + request.body = JSON.stringify({}) + request.method = "POST" + const response = await fetch( + checkSlashesInUrl(env.APPS_URL + `/api/users/sync/${userId}`), + request + ) + if (response.status !== 200) { + throw "Unable to sync user." + } +} From 193d40bbf3f1efb941359fcee801ac94c579297d Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 28 Oct 2021 19:40:46 +0200 Subject: [PATCH 060/100] refactor switch into a key value handler object tidy up - remove logs and comments update windowed modals to use postMessage --- .../AppPreview/CurrentItemPreview.svelte | 55 +++++++++++-------- .../design/AppPreview/iframeTemplate.js | 11 ++-- .../overlay/PeekScreenDisplay.svelte | 47 +++++++++------- packages/client/src/stores/builder.js | 6 +- packages/client/src/stores/notification.js | 18 ++++-- packages/server/src/integrations/utils.ts | 5 +- 6 files changed, 84 insertions(+), 58 deletions(-) diff --git a/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte b/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte index d2d67bf99f..99de9f7942 100644 --- a/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte +++ b/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte @@ -32,6 +32,16 @@ .component("@budibase/standard-components/screenslot") .instanceName("Content Placeholder") .json() + + // Messages that can be sent from the iframe preview to the builder + // Budibase events are and initalisation events + const MessageTypes = { + IFRAME_LOADED: "iframe-loaded", + READY: "ready", + ERROR: "error", + BUDIBASE: "type", + KEYDOWN: "keydown" + } // Construct iframe template $: template = iframeTemplate.replace( @@ -80,46 +90,44 @@ // Refresh the preview when required $: refreshContent(strippedJson) - onMount(() => { - // Initialise the app when mounted - iframe.contentWindow.addEventListener( - "ready", - () => { + function receiveMessage(message) { + const handlers = { + [MessageTypes.READY]: () => { + // Initialise the app when mounted // Display preview immediately if the intelligent loading feature // is not supported + if (!loading) return + if (!$store.clientFeatures.intelligentLoading) { loading = false } refreshContent(strippedJson) }, - { once: true } - ) - - // Catch any app errors - iframe.contentWindow.addEventListener( - "error", - event => { + [MessageTypes.ERROR]: event => { + // Catch any app errors loading = false - error = event.detail || "An unknown error occurred" + error = event.error || "An unknown error occurred" }, - { once: true } - ) + [MessageTypes.KEYDOWN]: handleKeydownEvent + } - // Add listener for events sent by client library in preview - iframe.contentWindow.addEventListener("bb-event", handleBudibaseEvent) - iframe.contentWindow.addEventListener("keydown", handleKeydownEvent) + const messageHandler = handlers[message.data.type] || handleBudibaseEvent + messageHandler(message) + } + + onMount(() => { + window.addEventListener("message", receiveMessage) }) // Remove all iframe event listeners on component destroy onDestroy(() => { if (iframe.contentWindow) { - iframe.contentWindow.removeEventListener("bb-event", handleBudibaseEvent) - iframe.contentWindow.removeEventListener("keydown", handleKeydownEvent) + window.removeEventListener("message", receiveMessage) // } }) const handleBudibaseEvent = event => { - const { type, data } = event.detail + const { type, data } = event.data if (type === "select-component" && data.id) { store.actions.components.select({ _id: data.id }) } else if (type === "update-prop") { @@ -151,13 +159,14 @@ store.actions.components.paste(destination, data.mode) } } else { - console.warning(`Client sent unknown event type: ${type}`) + console.warn(`Client sent unknown event type: ${type}`) } } const handleKeydownEvent = event => { + const { key } = event.data if ( - (event.key === "Delete" || event.key === "Backspace") && + (key === "Delete" || key === "Backspace") && selectedComponentId && ["input", "textarea"].indexOf( iframe.contentWindow.document.activeElement?.tagName.toLowerCase() diff --git a/packages/builder/src/components/design/AppPreview/iframeTemplate.js b/packages/builder/src/components/design/AppPreview/iframeTemplate.js index 58557273f2..89fa63012d 100644 --- a/packages/builder/src/components/design/AppPreview/iframeTemplate.js +++ b/packages/builder/src/components/design/AppPreview/iframeTemplate.js @@ -54,7 +54,7 @@ export default ` if (!parsed) { return } - + // Extract data from message const { selectedComponentId, @@ -84,17 +84,20 @@ export default ` if (window.loadBudibase) { window.loadBudibase() document.documentElement.classList.add("loaded") - window.dispatchEvent(new Event("iframe-loaded")) + window.parent.postMessage({ type: "iframe-loaded" }) } else { throw "The client library couldn't be loaded" } } catch (error) { - window.dispatchEvent(new CustomEvent("error", { detail: error })) + window.parent.postMessage({ type: "error", error }) } } window.addEventListener("message", receiveMessage) - window.dispatchEvent(new Event("ready")) + window.addEventListener("keydown", evt => { + window.parent.postMessage({ type: "keydown", key: event.key }) + }) + window.parent.postMessage({ type: "ready" }) diff --git a/packages/client/src/components/overlay/PeekScreenDisplay.svelte b/packages/client/src/components/overlay/PeekScreenDisplay.svelte index 0af1ba499e..51ff4412dc 100644 --- a/packages/client/src/components/overlay/PeekScreenDisplay.svelte +++ b/packages/client/src/components/overlay/PeekScreenDisplay.svelte @@ -8,6 +8,12 @@ import { Modal, ModalContent, ActionButton } from "@budibase/bbui" import { onDestroy } from "svelte" + const MessageTypes = { + NOTIFICATION: "notification", + CLOSE_SCREEN_MODAL: "close-screen-modal", + INVALIDATE_DATASOURCE: "invalidate-datasource", + } + let iframe let listenersAttached = false @@ -21,32 +27,33 @@ notificationStore.actions.send(message, type, icon) } + function receiveMessage(message) { + const handlers = { + [MessageTypes.NOTIFICATION]: () => { + proxyNotification(message.data) + }, + [MessageTypes.CLOSE_SCREEN_MODAL]: peekStore.actions.hidePeek, + [MessageTypes.INVALIDATE_DATASOURCE]: () => { + invalidateDataSource(message.data) + }, + } + + const messageHandler = handlers[message.data.type] + if (messageHandler) { + messageHandler(message) + } else { + console.warning("Unknown event type", message?.data?.type) + } + } + const attachListeners = () => { // Mirror datasource invalidation to keep the parent window up to date - iframe.contentWindow.addEventListener( - "invalidate-datasource", - invalidateDataSource - ) - // Listen for a close event to close the screen peek - iframe.contentWindow.addEventListener( - "close-screen-modal", - peekStore.actions.hidePeek - ) - // Proxy notifications back to the parent window instead of iframe - iframe.contentWindow.addEventListener("notification", proxyNotification) + window.addEventListener("message", receiveMessage) } const handleCancel = () => { peekStore.actions.hidePeek() - iframe.contentWindow.removeEventListener( - "invalidate-datasource", - invalidateDataSource - ) - iframe.contentWindow.removeEventListener( - "close-screen-modal", - peekStore.actions.hidePeek - ) - iframe.contentWindow.removeEventListener("notification", proxyNotification) + window.removeEventListener("message", receiveMessage) } const handleFullscreen = () => { diff --git a/packages/client/src/stores/builder.js b/packages/client/src/stores/builder.js index 2740a4551c..baea5cb831 100644 --- a/packages/client/src/stores/builder.js +++ b/packages/client/src/stores/builder.js @@ -4,11 +4,7 @@ import { findComponentById, findComponentPathById } from "../utils/components" import { pingEndUser } from "../api" const dispatchEvent = (type, data = {}) => { - window.dispatchEvent( - new CustomEvent("bb-event", { - detail: { type, data }, - }) - ) + window.parent.postMessage({ type, data }) } const createBuilderStore = () => { diff --git a/packages/client/src/stores/notification.js b/packages/client/src/stores/notification.js index e3af9aacd1..97193b2092 100644 --- a/packages/client/src/stores/notification.js +++ b/packages/client/src/stores/notification.js @@ -26,11 +26,19 @@ const createNotificationStore = () => { // If peeking, pass notifications back to parent window if (get(routeStore).queryParams?.peek) { - window.dispatchEvent( - new CustomEvent("notification", { - detail: { message, type, icon }, - }) - ) + window.parent.postMessage({ + type: "notification", + detail: { + message, + type, + icon, + }, + }) + // window.dispatchEvent( + // new CustomEvent("notification", { + // detail: { message, type, icon }, + // }) + // ) return } diff --git a/packages/server/src/integrations/utils.ts b/packages/server/src/integrations/utils.ts index 4d139fa28b..f91d25423c 100644 --- a/packages/server/src/integrations/utils.ts +++ b/packages/server/src/integrations/utils.ts @@ -124,7 +124,10 @@ function copyExistingPropsOver( return table } -export function finaliseExternalTables(tables: { [key: string]: any }, entities: { [key: string]: any }) { +export function finaliseExternalTables( + tables: { [key: string]: any }, + entities: { [key: string]: any } +) { const finalTables: { [key: string]: any } = {} const errors: { [key: string]: string } = {} for (let [name, table] of Object.entries(tables)) { From 1219c532509cce945b48682c6be54eea021a7d7e Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 4 Nov 2021 17:22:02 +0100 Subject: [PATCH 061/100] fix safari --- .../AppPreview/CurrentItemPreview.svelte | 55 +++++++++++-------- .../design/AppPreview/iframeTemplate.js | 11 ++-- .../overlay/PeekScreenDisplay.svelte | 47 +++++++++------- packages/client/src/stores/builder.js | 6 +- packages/client/src/stores/notification.js | 18 ++++-- 5 files changed, 80 insertions(+), 57 deletions(-) diff --git a/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte b/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte index d2d67bf99f..99de9f7942 100644 --- a/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte +++ b/packages/builder/src/components/design/AppPreview/CurrentItemPreview.svelte @@ -32,6 +32,16 @@ .component("@budibase/standard-components/screenslot") .instanceName("Content Placeholder") .json() + + // Messages that can be sent from the iframe preview to the builder + // Budibase events are and initalisation events + const MessageTypes = { + IFRAME_LOADED: "iframe-loaded", + READY: "ready", + ERROR: "error", + BUDIBASE: "type", + KEYDOWN: "keydown" + } // Construct iframe template $: template = iframeTemplate.replace( @@ -80,46 +90,44 @@ // Refresh the preview when required $: refreshContent(strippedJson) - onMount(() => { - // Initialise the app when mounted - iframe.contentWindow.addEventListener( - "ready", - () => { + function receiveMessage(message) { + const handlers = { + [MessageTypes.READY]: () => { + // Initialise the app when mounted // Display preview immediately if the intelligent loading feature // is not supported + if (!loading) return + if (!$store.clientFeatures.intelligentLoading) { loading = false } refreshContent(strippedJson) }, - { once: true } - ) - - // Catch any app errors - iframe.contentWindow.addEventListener( - "error", - event => { + [MessageTypes.ERROR]: event => { + // Catch any app errors loading = false - error = event.detail || "An unknown error occurred" + error = event.error || "An unknown error occurred" }, - { once: true } - ) + [MessageTypes.KEYDOWN]: handleKeydownEvent + } - // Add listener for events sent by client library in preview - iframe.contentWindow.addEventListener("bb-event", handleBudibaseEvent) - iframe.contentWindow.addEventListener("keydown", handleKeydownEvent) + const messageHandler = handlers[message.data.type] || handleBudibaseEvent + messageHandler(message) + } + + onMount(() => { + window.addEventListener("message", receiveMessage) }) // Remove all iframe event listeners on component destroy onDestroy(() => { if (iframe.contentWindow) { - iframe.contentWindow.removeEventListener("bb-event", handleBudibaseEvent) - iframe.contentWindow.removeEventListener("keydown", handleKeydownEvent) + window.removeEventListener("message", receiveMessage) // } }) const handleBudibaseEvent = event => { - const { type, data } = event.detail + const { type, data } = event.data if (type === "select-component" && data.id) { store.actions.components.select({ _id: data.id }) } else if (type === "update-prop") { @@ -151,13 +159,14 @@ store.actions.components.paste(destination, data.mode) } } else { - console.warning(`Client sent unknown event type: ${type}`) + console.warn(`Client sent unknown event type: ${type}`) } } const handleKeydownEvent = event => { + const { key } = event.data if ( - (event.key === "Delete" || event.key === "Backspace") && + (key === "Delete" || key === "Backspace") && selectedComponentId && ["input", "textarea"].indexOf( iframe.contentWindow.document.activeElement?.tagName.toLowerCase() diff --git a/packages/builder/src/components/design/AppPreview/iframeTemplate.js b/packages/builder/src/components/design/AppPreview/iframeTemplate.js index 58557273f2..89fa63012d 100644 --- a/packages/builder/src/components/design/AppPreview/iframeTemplate.js +++ b/packages/builder/src/components/design/AppPreview/iframeTemplate.js @@ -54,7 +54,7 @@ export default ` if (!parsed) { return } - + // Extract data from message const { selectedComponentId, @@ -84,17 +84,20 @@ export default ` if (window.loadBudibase) { window.loadBudibase() document.documentElement.classList.add("loaded") - window.dispatchEvent(new Event("iframe-loaded")) + window.parent.postMessage({ type: "iframe-loaded" }) } else { throw "The client library couldn't be loaded" } } catch (error) { - window.dispatchEvent(new CustomEvent("error", { detail: error })) + window.parent.postMessage({ type: "error", error }) } } window.addEventListener("message", receiveMessage) - window.dispatchEvent(new Event("ready")) + window.addEventListener("keydown", evt => { + window.parent.postMessage({ type: "keydown", key: event.key }) + }) + window.parent.postMessage({ type: "ready" }) diff --git a/packages/client/src/components/overlay/PeekScreenDisplay.svelte b/packages/client/src/components/overlay/PeekScreenDisplay.svelte index 0af1ba499e..51ff4412dc 100644 --- a/packages/client/src/components/overlay/PeekScreenDisplay.svelte +++ b/packages/client/src/components/overlay/PeekScreenDisplay.svelte @@ -8,6 +8,12 @@ import { Modal, ModalContent, ActionButton } from "@budibase/bbui" import { onDestroy } from "svelte" + const MessageTypes = { + NOTIFICATION: "notification", + CLOSE_SCREEN_MODAL: "close-screen-modal", + INVALIDATE_DATASOURCE: "invalidate-datasource", + } + let iframe let listenersAttached = false @@ -21,32 +27,33 @@ notificationStore.actions.send(message, type, icon) } + function receiveMessage(message) { + const handlers = { + [MessageTypes.NOTIFICATION]: () => { + proxyNotification(message.data) + }, + [MessageTypes.CLOSE_SCREEN_MODAL]: peekStore.actions.hidePeek, + [MessageTypes.INVALIDATE_DATASOURCE]: () => { + invalidateDataSource(message.data) + }, + } + + const messageHandler = handlers[message.data.type] + if (messageHandler) { + messageHandler(message) + } else { + console.warning("Unknown event type", message?.data?.type) + } + } + const attachListeners = () => { // Mirror datasource invalidation to keep the parent window up to date - iframe.contentWindow.addEventListener( - "invalidate-datasource", - invalidateDataSource - ) - // Listen for a close event to close the screen peek - iframe.contentWindow.addEventListener( - "close-screen-modal", - peekStore.actions.hidePeek - ) - // Proxy notifications back to the parent window instead of iframe - iframe.contentWindow.addEventListener("notification", proxyNotification) + window.addEventListener("message", receiveMessage) } const handleCancel = () => { peekStore.actions.hidePeek() - iframe.contentWindow.removeEventListener( - "invalidate-datasource", - invalidateDataSource - ) - iframe.contentWindow.removeEventListener( - "close-screen-modal", - peekStore.actions.hidePeek - ) - iframe.contentWindow.removeEventListener("notification", proxyNotification) + window.removeEventListener("message", receiveMessage) } const handleFullscreen = () => { diff --git a/packages/client/src/stores/builder.js b/packages/client/src/stores/builder.js index 2740a4551c..baea5cb831 100644 --- a/packages/client/src/stores/builder.js +++ b/packages/client/src/stores/builder.js @@ -4,11 +4,7 @@ import { findComponentById, findComponentPathById } from "../utils/components" import { pingEndUser } from "../api" const dispatchEvent = (type, data = {}) => { - window.dispatchEvent( - new CustomEvent("bb-event", { - detail: { type, data }, - }) - ) + window.parent.postMessage({ type, data }) } const createBuilderStore = () => { diff --git a/packages/client/src/stores/notification.js b/packages/client/src/stores/notification.js index e3af9aacd1..97193b2092 100644 --- a/packages/client/src/stores/notification.js +++ b/packages/client/src/stores/notification.js @@ -26,11 +26,19 @@ const createNotificationStore = () => { // If peeking, pass notifications back to parent window if (get(routeStore).queryParams?.peek) { - window.dispatchEvent( - new CustomEvent("notification", { - detail: { message, type, icon }, - }) - ) + window.parent.postMessage({ + type: "notification", + detail: { + message, + type, + icon, + }, + }) + // window.dispatchEvent( + // new CustomEvent("notification", { + // detail: { message, type, icon }, + // }) + // ) return } From ff9130c49c3ae0433f1858986cc8b18577f67bfe Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 4 Nov 2021 17:28:07 +0100 Subject: [PATCH 062/100] tidy up --- packages/client/src/stores/dataSource.js | 9 ++++----- packages/client/src/stores/notification.js | 5 ----- packages/client/src/utils/buttonActions.js | 2 +- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/client/src/stores/dataSource.js b/packages/client/src/stores/dataSource.js index c60039aa8a..247ba18835 100644 --- a/packages/client/src/stores/dataSource.js +++ b/packages/client/src/stores/dataSource.js @@ -59,11 +59,10 @@ export const createDataSourceStore = () => { // Emit this as a window event, so parent screens which are iframing us in // can also invalidate the same datasource - window.dispatchEvent( - new CustomEvent("invalidate-datasource", { - detail: { dataSourceId }, - }) - ) + window.parent.postMessage({ + type: "close-screen-modal", + detail: { dataSourceId }, + }) let invalidations = [dataSourceId] diff --git a/packages/client/src/stores/notification.js b/packages/client/src/stores/notification.js index 97193b2092..64178328c0 100644 --- a/packages/client/src/stores/notification.js +++ b/packages/client/src/stores/notification.js @@ -34,11 +34,6 @@ const createNotificationStore = () => { icon, }, }) - // window.dispatchEvent( - // new CustomEvent("notification", { - // detail: { message, type, icon }, - // }) - // ) return } diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 11aa033c1d..1fb2284375 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -120,7 +120,7 @@ const changeFormStepHandler = async (action, context) => { const closeScreenModalHandler = () => { // Emit this as a window event, so parent screens which are iframing us in // can close the modal - window.dispatchEvent(new Event("close-screen-modal")) + window.parent.postMessage({ type: "close-screen-modal" }) } const updateStateHandler = action => { From 7db07ed081ec06fb9b10ed52bb441dc0555965e5 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Thu, 4 Nov 2021 16:30:06 +0000 Subject: [PATCH 063/100] v0.9.176-alpha.3 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 6 +++--- packages/server/package.json | 8 ++++---- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lerna.json b/lerna.json index f7730dd4f2..18f9328afd 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.176-alpha.2", + "version": "0.9.176-alpha.3", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 78c48af85e..b5f27ca059 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.176-alpha.2", + "version": "0.9.176-alpha.3", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 44f0ac437d..0d1c5c15bc 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.176-alpha.2", + "version": "0.9.176-alpha.3", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 80d466cc8b..56811c6d11 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.176-alpha.2", + "version": "0.9.176-alpha.3", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.176-alpha.2", - "@budibase/client": "^0.9.176-alpha.2", + "@budibase/bbui": "^0.9.176-alpha.3", + "@budibase/client": "^0.9.176-alpha.3", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.176-alpha.2", + "@budibase/string-templates": "^0.9.176-alpha.3", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index dbb3ac8d14..f6f873edc1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.176-alpha.2", + "version": "0.9.176-alpha.3", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 03bf4895f5..dc487f6bf5 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.176-alpha.2", + "version": "0.9.176-alpha.3", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.176-alpha.2", + "@budibase/bbui": "^0.9.176-alpha.3", "@budibase/standard-components": "^0.9.139", - "@budibase/string-templates": "^0.9.176-alpha.2", + "@budibase/string-templates": "^0.9.176-alpha.3", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index ecdebfb3cd..cee6a2a2e8 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.176-alpha.2", + "version": "0.9.176-alpha.3", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -68,9 +68,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.176-alpha.2", - "@budibase/client": "^0.9.176-alpha.2", - "@budibase/string-templates": "^0.9.176-alpha.2", + "@budibase/auth": "^0.9.176-alpha.3", + "@budibase/client": "^0.9.176-alpha.3", + "@budibase/string-templates": "^0.9.176-alpha.3", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index d024c45245..3a7268920f 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.176-alpha.2", + "version": "0.9.176-alpha.3", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 760200bb9d..8d1c7141b4 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.176-alpha.2", + "version": "0.9.176-alpha.3", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -29,8 +29,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.176-alpha.2", - "@budibase/string-templates": "^0.9.176-alpha.2", + "@budibase/auth": "^0.9.176-alpha.3", + "@budibase/string-templates": "^0.9.176-alpha.3", "@koa/router": "^8.0.0", "@sentry/node": "^6.0.0", "@techpass/passport-openidconnect": "^0.3.0", From 6489d61fd1bd674f847f27a1bb21893f90cdedc9 Mon Sep 17 00:00:00 2001 From: Budibase Release Bot <> Date: Thu, 4 Nov 2021 16:37:06 +0000 Subject: [PATCH 064/100] v0.9.178 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 6 +++--- packages/server/package.json | 8 ++++---- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lerna.json b/lerna.json index 2177433557..df4c58df40 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.177", + "version": "0.9.178", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 5a6692c2a7..0d5171458c 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.177", + "version": "0.9.178", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index e4be1ddb39..4f7c369e1f 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.177", + "version": "0.9.178", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index c86268f2b1..d0376d6116 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.177", + "version": "0.9.178", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.177", - "@budibase/client": "^0.9.177", + "@budibase/bbui": "^0.9.178", + "@budibase/client": "^0.9.178", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.177", + "@budibase/string-templates": "^0.9.178", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 2b87fc0142..5cb584aa42 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.177", + "version": "0.9.178", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 21825fb7d6..ddfd50264f 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.177", + "version": "0.9.178", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.177", + "@budibase/bbui": "^0.9.178", "@budibase/standard-components": "^0.9.139", - "@budibase/string-templates": "^0.9.177", + "@budibase/string-templates": "^0.9.178", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index 08c5f71031..d55cdbdabe 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.177", + "version": "0.9.178", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -68,9 +68,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.177", - "@budibase/client": "^0.9.177", - "@budibase/string-templates": "^0.9.177", + "@budibase/auth": "^0.9.178", + "@budibase/client": "^0.9.178", + "@budibase/string-templates": "^0.9.178", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index e0b7c5558e..196ec9a0c0 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.177", + "version": "0.9.178", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 6e8b25beaf..90da0e8cbf 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.177", + "version": "0.9.178", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -29,8 +29,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.177", - "@budibase/string-templates": "^0.9.177", + "@budibase/auth": "^0.9.178", + "@budibase/string-templates": "^0.9.178", "@koa/router": "^8.0.0", "@sentry/node": "^6.0.0", "@techpass/passport-openidconnect": "^0.3.0", From 6e76a7dd1af0056c2a885bc43be94e12c8e57106 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 4 Nov 2021 20:28:26 +0000 Subject: [PATCH 065/100] Adding a check to disable user sync in test when server not available. --- packages/worker/src/utilities/appService.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/worker/src/utilities/appService.js b/packages/worker/src/utilities/appService.js index a7be05b227..166a82f78b 100644 --- a/packages/worker/src/utilities/appService.js +++ b/packages/worker/src/utilities/appService.js @@ -5,6 +5,9 @@ const { checkSlashesInUrl } = require("../utilities") const env = require("../environment") exports.syncUserInApps = async userId => { + if (env.isTest()) { + return + } const request = { headers: {} } request.headers[Headers.API_KEY] = env.INTERNAL_API_KEY if (isTenantIdSet()) { From 9c933b629f9d4f9beb91d9f5c02fec59c839e16e Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 5 Nov 2021 12:33:48 +0000 Subject: [PATCH 066/100] Linting and updating SQL Server schema generation to include auto column and primary key recognition. --- .../scripts/integrations/mssql/data/setup.sql | 28 ++++++-- .../api/controllers/row/ExternalRequest.ts | 7 +- packages/server/src/integrations/base/sql.ts | 4 +- .../server/src/integrations/base/sqlTable.ts | 44 +++++++++---- .../server/src/integrations/base/utils.ts | 5 +- .../src/integrations/microsoftSqlServer.ts | 66 ++++++++++++++++--- 6 files changed, 123 insertions(+), 31 deletions(-) diff --git a/packages/server/scripts/integrations/mssql/data/setup.sql b/packages/server/scripts/integrations/mssql/data/setup.sql index f6c94ee2c1..766388f46a 100644 --- a/packages/server/scripts/integrations/mssql/data/setup.sql +++ b/packages/server/scripts/integrations/mssql/data/setup.sql @@ -7,15 +7,30 @@ CREATE TABLE products ( id int IDENTITY(1,1), name varchar (20), - description varchar(30) + description varchar(30), + CONSTRAINT pk_products PRIMARY KEY NONCLUSTERED (id) ); + IF OBJECT_ID ('dbo.tasks', 'U') IS NOT NULL DROP TABLE tasks; GO CREATE TABLE tasks ( taskid int IDENTITY(1,1), - taskname varchar (20) + taskname varchar (20), + productid int, + CONSTRAINT pk_tasks PRIMARY KEY NONCLUSTERED (taskid), + CONSTRAINT fk_products FOREIGN KEY (productid) REFERENCES products (id), +); + +IF OBJECT_ID ('dbo.people', 'U') IS NOT NULL + DROP TABLE people; +GO +CREATE TABLE people +( + name varchar(30), + age varchar(20), + CONSTRAINT pk_people PRIMARY KEY NONCLUSTERED (name, age) ); INSERT products @@ -29,6 +44,11 @@ VALUES ('Meat', 'Animal thing'); INSERT tasks - (taskname) + (taskname, productid) VALUES - ('Processing'); + ('Processing', 1); + +INSERT people + (name, age) +VALUES + ('Bob', '30'); diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index f538e01f73..23d8deb259 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -226,7 +226,12 @@ module External { manyRelationships: ManyRelationship[] = [] for (let [key, field] of Object.entries(table.schema)) { // if set already, or not set just skip it - if (row[key] == null || newRow[key] || field.autocolumn || field.type === FieldTypes.FORMULA) { + if ( + row[key] == null || + newRow[key] || + field.autocolumn || + field.type === FieldTypes.FORMULA + ) { continue } // if its an empty string then it means return the column to null (if possible) diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 738b44afcc..6c64d5c38f 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -279,7 +279,9 @@ class SqlQueryBuilder extends SqlTableQueryBuilder { case Operation.DELETE: query = buildDelete(client, json, opts) break - case Operation.CREATE_TABLE: case Operation.UPDATE_TABLE: case Operation.DELETE_TABLE: + case Operation.CREATE_TABLE: + case Operation.UPDATE_TABLE: + case Operation.DELETE_TABLE: return this._tableQuery(json) default: throw `Operation type is not supported by SQL query builder` diff --git a/packages/server/src/integrations/base/sqlTable.ts b/packages/server/src/integrations/base/sqlTable.ts index e5249dfe7c..974f395063 100644 --- a/packages/server/src/integrations/base/sqlTable.ts +++ b/packages/server/src/integrations/base/sqlTable.ts @@ -6,7 +6,12 @@ import SchemaBuilder = Knex.SchemaBuilder import CreateTableBuilder = Knex.CreateTableBuilder const { FieldTypes, RelationshipTypes } = require("../../constants") -function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record, oldTable: null | Table = null) { +function generateSchema( + schema: CreateTableBuilder, + table: Table, + tables: Record, + oldTable: null | Table = null +) { let primaryKey = table && table.primary ? table.primary[0] : null const columns = Object.values(table.schema) // all columns in a junction table will be meta @@ -19,17 +24,21 @@ function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record schema.primary(metaCols.map(col => col.name)) } - // check if any columns need added const foreignKeys = Object.values(table.schema).map(col => col.foreignKey) for (let [key, column] of Object.entries(table.schema)) { // skip things that are already correct const oldColumn = oldTable ? oldTable.schema[key] : null - if ((oldColumn && oldColumn.type === column.type) || (primaryKey === key && !isJunction)) { + if ( + (oldColumn && oldColumn.type === column.type) || + (primaryKey === key && !isJunction) + ) { continue } switch (column.type) { - case FieldTypes.STRING: case FieldTypes.OPTIONS: case FieldTypes.LONGFORM: + case FieldTypes.STRING: + case FieldTypes.OPTIONS: + case FieldTypes.LONGFORM: schema.string(key) break case FieldTypes.NUMBER: @@ -67,7 +76,9 @@ function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record throw "Referenced table doesn't exist" } schema.integer(column.foreignKey).unsigned() - schema.foreign(column.foreignKey).references(`${tableName}.${relatedTable.primary[0]}`) + schema + .foreign(column.foreignKey) + .references(`${tableName}.${relatedTable.primary[0]}`) } break } @@ -76,7 +87,10 @@ function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record // need to check if any columns have been deleted if (oldTable) { const deletedColumns = Object.entries(oldTable.schema) - .filter(([key, schema]) => schema.type !== FieldTypes.LINK && table.schema[key] == null) + .filter( + ([key, schema]) => + schema.type !== FieldTypes.LINK && table.schema[key] == null + ) .map(([key]) => key) deletedColumns.forEach(key => { if (oldTable.constrained && oldTable.constrained.indexOf(key) !== -1) { @@ -92,7 +106,7 @@ function generateSchema(schema: CreateTableBuilder, table: Table, tables: Record function buildCreateTable( knex: Knex, table: Table, - tables: Record, + tables: Record ): SchemaBuilder { return knex.schema.createTable(table.name, schema => { generateSchema(schema, table, tables) @@ -103,17 +117,14 @@ function buildUpdateTable( knex: Knex, table: Table, tables: Record, - oldTable: Table, + oldTable: Table ): SchemaBuilder { return knex.schema.alterTable(table.name, schema => { generateSchema(schema, table, tables, oldTable) }) } -function buildDeleteTable( - knex: Knex, - table: Table, -): SchemaBuilder { +function buildDeleteTable(knex: Knex, table: Table): SchemaBuilder { return knex.schema.dropTable(table.name) } @@ -151,7 +162,12 @@ class SqlTableQueryBuilder { if (!json.meta || !json.meta.table) { throw "Must specify old table for update" } - query = buildUpdateTable(client, json.table, json.meta.tables, json.meta.table) + query = buildUpdateTable( + client, + json.table, + json.meta.tables, + json.meta.table + ) break case Operation.DELETE_TABLE: query = buildDeleteTable(client, json.table) @@ -164,4 +180,4 @@ class SqlTableQueryBuilder { } export default SqlTableQueryBuilder -module.exports = SqlTableQueryBuilder \ No newline at end of file +module.exports = SqlTableQueryBuilder diff --git a/packages/server/src/integrations/base/utils.ts b/packages/server/src/integrations/base/utils.ts index 5757232bc7..086912b920 100644 --- a/packages/server/src/integrations/base/utils.ts +++ b/packages/server/src/integrations/base/utils.ts @@ -4,7 +4,10 @@ import { Datasource } from "../../definitions/common" module DatasourceUtils { const { integrations } = require("../index") - export async function makeExternalQuery(datasource: Datasource, json: QueryJson) { + export async function makeExternalQuery( + datasource: Datasource, + json: QueryJson + ) { const Integration = integrations[datasource.source] // query is the opinionated function if (Integration.prototype.query) { diff --git a/packages/server/src/integrations/microsoftSqlServer.ts b/packages/server/src/integrations/microsoftSqlServer.ts index d97c5f36b6..f8fd638082 100644 --- a/packages/server/src/integrations/microsoftSqlServer.ts +++ b/packages/server/src/integrations/microsoftSqlServer.ts @@ -7,7 +7,7 @@ import { } from "../definitions/datasource" import { getSqlQuery } from "./utils" import { DatasourcePlus } from "./base/datasourcePlus" -import { Table, TableSchema } from "../definitions/common"; +import { Table, TableSchema } from "../definitions/common" module MSSQLModule { const sqlServer = require("mssql") @@ -129,9 +129,10 @@ module MSSQLModule { "spt_fallback_dev", "spt_fallback_usg", "spt_monitor", - "MSreplication_options" + "MSreplication_options", ] - TABLES_SQL = "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'" + TABLES_SQL = + "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'" getDefinitionSQL(tableName: string) { return `select * @@ -139,6 +140,28 @@ module MSSQLModule { where TABLE_NAME='${tableName}'` } + getConstraintsSQL(tableName: string) { + return `SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC + INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KU + ON TC.CONSTRAINT_TYPE = 'PRIMARY KEY' + AND TC.CONSTRAINT_NAME = KU.CONSTRAINT_NAME + AND KU.table_name='${tableName}' + ORDER BY + KU.TABLE_NAME, + KU.ORDINAL_POSITION;` + } + + getAutoColumnsSQL(tableName: string) { + return `SELECT + COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA+'.'+TABLE_NAME),COLUMN_NAME,'IsComputed') + AS IS_COMPUTED, + COLUMNPROPERTY(object_id(TABLE_SCHEMA+'.'+TABLE_NAME), COLUMN_NAME, 'IsIdentity') + AS IS_IDENTITY, + * + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME='${tableName}'` + } + constructor(config: MSSQLConfig) { super("mssql") this.config = config @@ -171,16 +194,39 @@ module MSSQLModule { * @param entities - the tables that are to be built */ async buildSchema(datasourceId: string, entities: Record) { - await this.connect() - let tableNames = await internalQuery(this.client, getSqlQuery(this.TABLES_SQL)) + let tableNames = await internalQuery( + this.client, + getSqlQuery(this.TABLES_SQL) + ) if (tableNames == null || !Array.isArray(tableNames.recordset)) { throw "Unable to get list of tables in database" } - tableNames = tableNames.recordset.map((record: any) => record.TABLE_NAME).filter((name: string) => this.MASTER_TABLES.indexOf(name) === -1) + tableNames = tableNames.recordset + .map((record: any) => record.TABLE_NAME) + .filter((name: string) => this.MASTER_TABLES.indexOf(name) === -1) const tables: Record = {} for (let tableName of tableNames) { - const definition = await internalQuery(this.client, getSqlQuery(this.getDefinitionSQL(tableName))) + const definition = await internalQuery( + this.client, + getSqlQuery(this.getDefinitionSQL(tableName)) + ) + const constraints = await internalQuery( + this.client, + getSqlQuery(this.getConstraintsSQL(tableName)) + ) + const columns = await internalQuery( + this.client, + getSqlQuery(this.getAutoColumnsSQL(tableName)) + ) + const autoColumns = columns.recordset + .filter((col: any) => col.IS_COMPUTED || col.IS_IDENTITY) + .map((col: any) => col.COLUMN_NAME) + const primaryKeys = constraints.recordset + .filter( + (constraint: any) => constraint.CONSTRAINT_TYPE === "PRIMARY KEY" + ) + .map((constraint: any) => constraint.COLUMN_NAME) let schema: TableSchema = {} for (let def of definition.recordset) { const name = def.COLUMN_NAME @@ -188,16 +234,16 @@ module MSSQLModule { continue } const type: string = convertType(def.DATA_TYPE, TYPE_MAP) - const identity = false + schema[name] = { - autocolumn: identity, + autocolumn: !!autoColumns.find((col: string) => col === name), name: name, type, } } tables[tableName] = { _id: buildExternalTableId(datasourceId, tableName), - primary: ["id"], + primary: primaryKeys, name: tableName, schema, } From 1f1786c2b342040e6fab00b9ed66f385224a1b24 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 5 Nov 2021 12:37:16 +0000 Subject: [PATCH 067/100] Fix issue saving a doc with a multi-options datatype that was not defined --- packages/server/src/api/controllers/row/utils.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/row/utils.js b/packages/server/src/api/controllers/row/utils.js index ca6c782713..6bbc6474ee 100644 --- a/packages/server/src/api/controllers/row/utils.js +++ b/packages/server/src/api/controllers/row/utils.js @@ -68,7 +68,11 @@ exports.validate = async ({ appId, tableId, row, table }) => { let res // Validate.js doesn't seem to handle array - if (table.schema[fieldName].type === FieldTypes.ARRAY) { + if ( + table.schema[fieldName].type === FieldTypes.ARRAY && + row[fieldName] && + row[fieldName].length + ) { row[fieldName].map(val => { if (!constraints.inclusion.includes(val)) { errors[fieldName] = "Field not in list" From 117a7f7ce8849477d1dc9a7661f8e8607898074f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 5 Nov 2021 12:37:42 +0000 Subject: [PATCH 068/100] Fix issue getting bindable properties when selecting a screen --- packages/builder/src/builderStore/dataBinding.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index 9003b3e06c..742c9052ad 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -83,7 +83,7 @@ export const getActionProviderComponents = (asset, componentId, actionType) => { * Gets a datasource object for a certain data provider component */ export const getDatasourceForProvider = (asset, component) => { - const settings = getComponentSettings(component._component) + const settings = getComponentSettings(component?._component) // If this component has a dataProvider setting, go up the stack and use it const dataProviderSetting = settings.find(setting => { From 8c63b147fb07a8192204c2d09423c195bb6275cc Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 5 Nov 2021 12:38:15 +0000 Subject: [PATCH 069/100] Remove forced capitalisation of setting labels --- .../PropertiesPanel/PropertyControls/PropertyControl.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte index 91e67c1685..ea0fdead78 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte @@ -122,7 +122,6 @@ } .label { - text-transform: capitalize; padding-bottom: var(--spectrum-global-dimension-size-65); } From d5002d3dda46039654e0b921d354f81252b24399 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 5 Nov 2021 12:38:33 +0000 Subject: [PATCH 070/100] Add ability to link rows in tables, and link rows in the table with search block --- packages/client/manifest.json | 65 +++++++++++++++++-- .../app/blocks/TableWithSearch.svelte | 8 +++ .../src/components/app/table/Table.svelte | 20 +++++- packages/client/src/stores/routes.js | 24 ++++++- packages/client/src/utils/buttonActions.js | 15 +---- 5 files changed, 109 insertions(+), 23 deletions(-) diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 46f87818ff..982f96ef90 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -2457,12 +2457,12 @@ "settings": [ { "type": "dataProvider", - "label": "Provider", + "label": "Data provider", "key": "dataProvider" }, { "type": "number", - "label": "Row Count", + "label": "Row count", "key": "rowCount", "defaultValue": 8 }, @@ -2496,9 +2496,36 @@ }, { "type": "boolean", - "label": "Auto Columns", + "label": "Show auto columns", "key": "showAutoColumns", "defaultValue": false + }, + { + "type": "boolean", + "label": "Link table rows", + "key": "linkRows" + }, + { + "type": "boolean", + "label": "Open link screens in modal", + "key": "linkPeek" + }, + { + "type": "url", + "label": "Link screen", + "key": "linkURL" + }, + { + "section": true, + "name": "Advanced", + "settings": [ + { + "type": "field", + "label": "ID column for linking (appended to URL)", + "key": "linkColumn", + "placeholder": "Default" + } + ] } ], "context": { @@ -2662,8 +2689,22 @@ { "type": "boolean", "label": "Show auto columns", - "key": "showAutoColumns", - "defaultValue": false + "key": "showAutoColumns" + }, + { + "type": "boolean", + "label": "Link table rows", + "key": "linkRows" + }, + { + "type": "boolean", + "label": "Open link in modal", + "key": "linkPeek" + }, + { + "type": "url", + "label": "Link screen", + "key": "linkURL" } ] }, @@ -2684,10 +2725,22 @@ }, { "type": "event", - "label": "Title Button Action", + "label": "Title button action", "key": "titleButtonOnClick" } ] + }, + { + "section": true, + "name": "Advanced", + "settings": [ + { + "type": "field", + "label": "ID column for linking (appended to URL)", + "key": "linkColumn", + "placeholder": "Default" + } + ] } ], "context": { diff --git a/packages/client/src/components/app/blocks/TableWithSearch.svelte b/packages/client/src/components/app/blocks/TableWithSearch.svelte index c4143df986..378a111571 100644 --- a/packages/client/src/components/app/blocks/TableWithSearch.svelte +++ b/packages/client/src/components/app/blocks/TableWithSearch.svelte @@ -16,6 +16,10 @@ export let rowCount export let quiet export let size + export let linkRows + export let linkURL + export let linkColumn + export let linkPeek export let showTitleButton export let titleButtonText export let titleButtonOnClick @@ -138,6 +142,10 @@ rowCount, quiet, size, + linkRows, + linkURL, + linkColumn, + linkPeek, }} /> diff --git a/packages/client/src/components/app/table/Table.svelte b/packages/client/src/components/app/table/Table.svelte index a6508c5763..4178ca2bb2 100644 --- a/packages/client/src/components/app/table/Table.svelte +++ b/packages/client/src/components/app/table/Table.svelte @@ -9,9 +9,13 @@ export let rowCount export let quiet export let size + export let linkRows + export let linkURL + export let linkColumn + export let linkPeek const component = getContext("component") - const { styleable, getAction, ActionTypes } = getContext("sdk") + const { styleable, getAction, ActionTypes, routeStore } = getContext("sdk") const setSorting = getAction( dataProvider?.id, ActionTypes.SetDataProviderSorting @@ -81,6 +85,19 @@ order: e.detail.order, }) } + + const onClick = e => { + if (!linkRows || !linkURL) { + return + } + const col = linkColumn || "_id" + const id = e.detail?.[col] + if (!id) { + return + } + const split = linkURL.split("/:") + routeStore.actions.navigate(`${split[0]}/${id}`, linkPeek) + }
@@ -97,6 +114,7 @@ showAutoColumns={true} disableSorting on:sort={onSort} + on:click={onClick} > diff --git a/packages/client/src/stores/routes.js b/packages/client/src/stores/routes.js index e6473fb40c..1d5dca1645 100644 --- a/packages/client/src/stores/routes.js +++ b/packages/client/src/stores/routes.js @@ -1,6 +1,8 @@ -import { writable } from "svelte/store" +import { get, writable } from "svelte/store" import { push } from "svelte-spa-router" import * as API from "../api" +import { peekStore } from "./peek" +import { builderStore } from "./builder" const createRouteStore = () => { const initialState = { @@ -59,7 +61,25 @@ const createRouteStore = () => { return state }) } - const navigate = push + const navigate = (url, peek) => { + if (get(builderStore).inBuilder) { + return + } + if (url) { + // If we're already peeking, don't peek again + const isPeeking = get(store).queryParams?.peek + if (peek && !isPeeking) { + peekStore.actions.showPeek(url) + } else { + const external = !url.startsWith("/") + if (external) { + window.location.href = url + } else { + push(url) + } + } + } + } const setRouterLoaded = () => { store.update(state => ({ ...state, routerLoaded: true })) } diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 655f2f32e5..01b330d74f 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -42,20 +42,7 @@ const triggerAutomationHandler = async action => { const navigationHandler = action => { const { url, peek } = action.parameters - if (url) { - // If we're already peeking, don't peek again - const isPeeking = get(routeStore).queryParams?.peek - if (peek && !isPeeking) { - peekStore.actions.showPeek(url) - } else { - const external = !url.startsWith("/") - if (external) { - window.location.href = url - } else { - routeStore.actions.navigate(action.parameters.url) - } - } - } + routeStore.actions.navigate(url, peek) } const queryExecutionHandler = async action => { From 5ec0d803afdaf6c7465fa0dfb731e574718fc3dc Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 5 Nov 2021 12:41:26 +0000 Subject: [PATCH 071/100] Cleaning up repeated work in sql server building of schema. --- .../src/integrations/microsoftSqlServer.ts | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/packages/server/src/integrations/microsoftSqlServer.ts b/packages/server/src/integrations/microsoftSqlServer.ts index f8fd638082..7071ce6f75 100644 --- a/packages/server/src/integrations/microsoftSqlServer.ts +++ b/packages/server/src/integrations/microsoftSqlServer.ts @@ -188,6 +188,10 @@ module MSSQLModule { } } + async runSQL(sql: string) { + return (await internalQuery(this.client, getSqlQuery(sql))).recordset + } + /** * Fetches the tables from the sql server database and assigns them to the datasource. * @param {*} datasourceId - datasourceId to fetch @@ -195,40 +199,33 @@ module MSSQLModule { */ async buildSchema(datasourceId: string, entities: Record) { await this.connect() - let tableNames = await internalQuery( - this.client, - getSqlQuery(this.TABLES_SQL) - ) + let tableNames = await this.runSQL(this.TABLES_SQL) if (tableNames == null || !Array.isArray(tableNames.recordset)) { throw "Unable to get list of tables in database" } tableNames = tableNames.recordset .map((record: any) => record.TABLE_NAME) .filter((name: string) => this.MASTER_TABLES.indexOf(name) === -1) + const tables: Record = {} for (let tableName of tableNames) { - const definition = await internalQuery( - this.client, - getSqlQuery(this.getDefinitionSQL(tableName)) - ) - const constraints = await internalQuery( - this.client, - getSqlQuery(this.getConstraintsSQL(tableName)) - ) - const columns = await internalQuery( - this.client, - getSqlQuery(this.getAutoColumnsSQL(tableName)) - ) - const autoColumns = columns.recordset - .filter((col: any) => col.IS_COMPUTED || col.IS_IDENTITY) - .map((col: any) => col.COLUMN_NAME) - const primaryKeys = constraints.recordset + // get the column definition (type) + const definition = await this.runSQL(this.getDefinitionSQL(tableName)) + // find primary key constraints + const constraints = await this.runSQL(this.getConstraintsSQL(tableName)) + // find the computed and identity columns (auto columns) + const columns = await this.runSQL(this.getAutoColumnsSQL(tableName)) + const primaryKeys = constraints .filter( (constraint: any) => constraint.CONSTRAINT_TYPE === "PRIMARY KEY" ) .map((constraint: any) => constraint.COLUMN_NAME) + const autoColumns = columns + .filter((col: any) => col.IS_COMPUTED || col.IS_IDENTITY) + .map((col: any) => col.COLUMN_NAME) + let schema: TableSchema = {} - for (let def of definition.recordset) { + for (let def of definition) { const name = def.COLUMN_NAME if (typeof name !== "string") { continue From e2cbf054ce9fb522a950471ead2680494b02cb36 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 5 Nov 2021 12:43:26 +0000 Subject: [PATCH 072/100] Lint --- packages/client/src/utils/buttonActions.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 01b330d74f..233a526062 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -4,7 +4,6 @@ import { builderStore, confirmationStore, authStore, - peekStore, stateStore, } from "stores" import { saveRow, deleteRow, executeQuery, triggerAutomation } from "api" From 3474f3ae8e45455022db504e600c8aaeda4f4100 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 5 Nov 2021 13:48:13 +0000 Subject: [PATCH 073/100] Updating underlying sql to not use ilike unless in postgres client. --- .../api/controllers/row/ExternalRequest.ts | 2 +- packages/server/src/integrations/base/sql.ts | 421 +++++++++--------- .../src/integrations/microsoftSqlServer.ts | 4 +- 3 files changed, 220 insertions(+), 207 deletions(-) diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 23d8deb259..d4e8d475a2 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -342,7 +342,7 @@ module External { table: Table, relationships: RelationshipsJson[] ) { - if (rows[0].read === true) { + if (!rows || rows.length === 0 || rows[0].read === true) { return [] } let finalRows: { [key: string]: Row } = {} diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 6c64d5c38f..06a9d0aa10 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -29,222 +29,232 @@ function parseBody(body: any) { return body } -// right now we only do filters on the specific table being queried -function addFilters( - tableName: string, - query: KnexQuery, - filters: SearchFilters | undefined -): KnexQuery { - function iterate( - structure: { [key: string]: any }, - fn: (key: string, value: any) => void - ) { - for (let [key, value] of Object.entries(structure)) { - fn(`${tableName}.${key}`, value) - } +class InternalBuilder { + private readonly client: string + + constructor(client: string) { + this.client = client } - if (!filters) { - return query - } - // if all or specified in filters, then everything is an or - const allOr = filters.allOr - if (filters.oneOf) { - iterate(filters.oneOf, (key, array) => { - const fnc = allOr ? "orWhereIn" : "whereIn" - query = query[fnc](key, array) - }) - } - if (filters.string) { - iterate(filters.string, (key, value) => { - const fnc = allOr ? "orWhere" : "where" - query = query[fnc](key, "ilike", `${value}%`) - }) - } - if (filters.fuzzy) { - iterate(filters.fuzzy, (key, value) => { - const fnc = allOr ? "orWhere" : "where" - query = query[fnc](key, "ilike", `%${value}%`) - }) - } - if (filters.range) { - iterate(filters.range, (key, value) => { - if (!value.high || !value.low) { - return + + // right now we only do filters on the specific table being queried + addFilters( + tableName: string, + query: KnexQuery, + filters: SearchFilters | undefined + ): KnexQuery { + function iterate( + structure: { [key: string]: any }, + fn: (key: string, value: any) => void + ) { + for (let [key, value] of Object.entries(structure)) { + fn(`${tableName}.${key}`, value) } - const fnc = allOr ? "orWhereBetween" : "whereBetween" - query = query[fnc](key, [value.low, value.high]) - }) - } - if (filters.equal) { - iterate(filters.equal, (key, value) => { - const fnc = allOr ? "orWhere" : "where" - query = query[fnc]({ [key]: value }) - }) - } - if (filters.notEqual) { - iterate(filters.notEqual, (key, value) => { - const fnc = allOr ? "orWhereNot" : "whereNot" - query = query[fnc]({ [key]: value }) - }) - } - if (filters.empty) { - iterate(filters.empty, key => { - const fnc = allOr ? "orWhereNull" : "whereNull" - query = query[fnc](key) - }) - } - if (filters.notEmpty) { - iterate(filters.notEmpty, key => { - const fnc = allOr ? "orWhereNotNull" : "whereNotNull" - query = query[fnc](key) - }) - } - return query -} - -function addRelationships( - knex: Knex, - query: KnexQuery, - fields: string | string[], - fromTable: string, - relationships: RelationshipsJson[] | undefined -): KnexQuery { - if (!relationships) { + } + if (!filters) { + return query + } + // if all or specified in filters, then everything is an or + const allOr = filters.allOr + if (filters.oneOf) { + iterate(filters.oneOf, (key, array) => { + const fnc = allOr ? "orWhereIn" : "whereIn" + query = query[fnc](key, array) + }) + } + if (filters.string) { + iterate(filters.string, (key, value) => { + const fnc = allOr ? "orWhere" : "where" + // postgres supports ilike, nothing else does + if (this.client === "pg") { + query = query[fnc](key, "ilike", `${value}%`) + } else { + const rawFnc = `${fnc}Raw` + // @ts-ignore + query = query[rawFnc](`LOWER(${key}) LIKE ?`, [`${value}%`]) + } + }) + } + if (filters.fuzzy) { + iterate(filters.fuzzy, (key, value) => { + const fnc = allOr ? "orWhere" : "where" + // postgres supports ilike, nothing else does + if (this.client === "pg") { + query = query[fnc](key, "ilike", `%${value}%`) + } else { + const rawFnc = `${fnc}Raw` + // @ts-ignore + query = query[rawFnc](`LOWER(${key}) LIKE ?`, [`%${value}%`]) + } + }) + } + if (filters.range) { + iterate(filters.range, (key, value) => { + if (!value.high || !value.low) { + return + } + const fnc = allOr ? "orWhereBetween" : "whereBetween" + query = query[fnc](key, [value.low, value.high]) + }) + } + if (filters.equal) { + iterate(filters.equal, (key, value) => { + const fnc = allOr ? "orWhere" : "where" + query = query[fnc]({ [key]: value }) + }) + } + if (filters.notEqual) { + iterate(filters.notEqual, (key, value) => { + const fnc = allOr ? "orWhereNot" : "whereNot" + query = query[fnc]({ [key]: value }) + }) + } + if (filters.empty) { + iterate(filters.empty, key => { + const fnc = allOr ? "orWhereNull" : "whereNull" + query = query[fnc](key) + }) + } + if (filters.notEmpty) { + iterate(filters.notEmpty, key => { + const fnc = allOr ? "orWhereNotNull" : "whereNotNull" + query = query[fnc](key) + }) + } return query } - for (let relationship of relationships) { - const from = relationship.from, - to = relationship.to, - toTable = relationship.tableName - if (!relationship.through) { - // @ts-ignore - query = query.leftJoin( - toTable, - `${fromTable}.${from}`, - `${toTable}.${to}` - ) - } else { - const throughTable = relationship.through - const fromPrimary = relationship.fromPrimary - const toPrimary = relationship.toPrimary - query = query + + addRelationships( + knex: Knex, + query: KnexQuery, + fields: string | string[], + fromTable: string, + relationships: RelationshipsJson[] | undefined + ): KnexQuery { + if (!relationships) { + return query + } + for (let relationship of relationships) { + const from = relationship.from, + to = relationship.to, + toTable = relationship.tableName + if (!relationship.through) { // @ts-ignore - .leftJoin( - throughTable, - `${fromTable}.${fromPrimary}`, - `${throughTable}.${from}` + query = query.leftJoin( + toTable, + `${fromTable}.${from}`, + `${toTable}.${to}` ) - .leftJoin(toTable, `${toTable}.${toPrimary}`, `${throughTable}.${to}`) + } else { + const throughTable = relationship.through + const fromPrimary = relationship.fromPrimary + const toPrimary = relationship.toPrimary + query = query + // @ts-ignore + .leftJoin( + throughTable, + `${fromTable}.${fromPrimary}`, + `${throughTable}.${from}` + ) + .leftJoin(toTable, `${toTable}.${toPrimary}`, `${throughTable}.${to}`) + } + } + return query.limit(BASE_LIMIT) + } + + create(knex: Knex, json: QueryJson, opts: QueryOptions): KnexQuery { + const { endpoint, body } = json + let query: KnexQuery = knex(endpoint.entityId) + const parsedBody = parseBody(body) + // make sure no null values in body for creation + for (let [key, value] of Object.entries(parsedBody)) { + if (value == null) { + delete parsedBody[key] + } + } + // mysql can't use returning + if (opts.disableReturning) { + return query.insert(parsedBody) + } else { + return query.insert(parsedBody).returning("*") } } - return query.limit(BASE_LIMIT) -} -function buildCreate( - knex: Knex, - json: QueryJson, - opts: QueryOptions -): KnexQuery { - const { endpoint, body } = json - let query: KnexQuery = knex(endpoint.entityId) - const parsedBody = parseBody(body) - // make sure no null values in body for creation - for (let [key, value] of Object.entries(parsedBody)) { - if (value == null) { - delete parsedBody[key] + read(knex: Knex, json: QueryJson, limit: number): KnexQuery { + let { endpoint, resource, filters, sort, paginate, relationships } = json + const tableName = endpoint.entityId + // select all if not specified + if (!resource) { + resource = { fields: [] } } - } - // mysql can't use returning - if (opts.disableReturning) { - return query.insert(parsedBody) - } else { - return query.insert(parsedBody).returning("*") - } -} - -function buildRead(knex: Knex, json: QueryJson, limit: number): KnexQuery { - let { endpoint, resource, filters, sort, paginate, relationships } = json - const tableName = endpoint.entityId - // select all if not specified - if (!resource) { - resource = { fields: [] } - } - let selectStatement: string | string[] = "*" - // handle select - if (resource.fields && resource.fields.length > 0) { - // select the resources as the format "table.columnName" - this is what is provided - // by the resource builder further up - selectStatement = resource.fields.map(field => `${field} as ${field}`) - } - let foundLimit = limit || BASE_LIMIT - // handle pagination - let foundOffset: number | null = null - if (paginate && paginate.page && paginate.limit) { + let selectStatement: string | string[] = "*" + // handle select + if (resource.fields && resource.fields.length > 0) { + // select the resources as the format "table.columnName" - this is what is provided + // by the resource builder further up + selectStatement = resource.fields.map(field => `${field} as ${field}`) + } + let foundLimit = limit || BASE_LIMIT + // handle pagination + let foundOffset: number | null = null + if (paginate && paginate.page && paginate.limit) { + // @ts-ignore + const page = paginate.page <= 1 ? 0 : paginate.page - 1 + const offset = page * paginate.limit + foundLimit = paginate.limit + foundOffset = offset + } else if (paginate && paginate.limit) { + foundLimit = paginate.limit + } + // start building the query + let query: KnexQuery = knex(tableName).limit(foundLimit) + if (foundOffset) { + query = query.offset(foundOffset) + } + if (sort) { + for (let [key, value] of Object.entries(sort)) { + const direction = value === SortDirection.ASCENDING ? "asc" : "desc" + query = query.orderBy(key, direction) + } + } + query = this.addFilters(tableName, query, filters) // @ts-ignore - const page = paginate.page <= 1 ? 0 : paginate.page - 1 - const offset = page * paginate.limit - foundLimit = paginate.limit - foundOffset = offset - } else if (paginate && paginate.limit) { - foundLimit = paginate.limit + let preQuery: KnexQuery = knex({ + // @ts-ignore + [tableName]: query, + }).select(selectStatement) + // handle joins + return this.addRelationships( + knex, + preQuery, + selectStatement, + tableName, + relationships + ) } - // start building the query - let query: KnexQuery = knex(tableName).limit(foundLimit) - if (foundOffset) { - query = query.offset(foundOffset) - } - if (sort) { - for (let [key, value] of Object.entries(sort)) { - const direction = value === SortDirection.ASCENDING ? "asc" : "desc" - query = query.orderBy(key, direction) + + update(knex: Knex, json: QueryJson, opts: QueryOptions): KnexQuery { + const { endpoint, body, filters } = json + let query: KnexQuery = knex(endpoint.entityId) + const parsedBody = parseBody(body) + query = this.addFilters(endpoint.entityId, query, filters) + // mysql can't use returning + if (opts.disableReturning) { + return query.update(parsedBody) + } else { + return query.update(parsedBody).returning("*") } } - query = addFilters(tableName, query, filters) - // @ts-ignore - let preQuery: KnexQuery = knex({ - // @ts-ignore - [tableName]: query, - }).select(selectStatement) - // handle joins - return addRelationships( - knex, - preQuery, - selectStatement, - tableName, - relationships - ) -} -function buildUpdate( - knex: Knex, - json: QueryJson, - opts: QueryOptions -): KnexQuery { - const { endpoint, body, filters } = json - let query: KnexQuery = knex(endpoint.entityId) - const parsedBody = parseBody(body) - query = addFilters(endpoint.entityId, query, filters) - // mysql can't use returning - if (opts.disableReturning) { - return query.update(parsedBody) - } else { - return query.update(parsedBody).returning("*") - } -} - -function buildDelete( - knex: Knex, - json: QueryJson, - opts: QueryOptions -): KnexQuery { - const { endpoint, filters } = json - let query: KnexQuery = knex(endpoint.entityId) - query = addFilters(endpoint.entityId, query, filters) - // mysql can't use returning - if (opts.disableReturning) { - return query.delete() - } else { - return query.delete().returning("*") + delete(knex: Knex, json: QueryJson, opts: QueryOptions): KnexQuery { + const { endpoint, filters } = json + let query: KnexQuery = knex(endpoint.entityId) + query = this.addFilters(endpoint.entityId, query, filters) + // mysql can't use returning + if (opts.disableReturning) { + return query.delete() + } else { + return query.delete().returning("*") + } } } @@ -266,18 +276,19 @@ class SqlQueryBuilder extends SqlTableQueryBuilder { const sqlClient = this.getSqlClient() const client = knex({ client: sqlClient }) let query + const builder = new InternalBuilder(sqlClient) switch (this._operation(json)) { case Operation.CREATE: - query = buildCreate(client, json, opts) + query = builder.create(client, json, opts) break case Operation.READ: - query = buildRead(client, json, this.limit) + query = builder.read(client, json, this.limit) break case Operation.UPDATE: - query = buildUpdate(client, json, opts) + query = builder.update(client, json, opts) break case Operation.DELETE: - query = buildDelete(client, json, opts) + query = builder.delete(client, json, opts) break case Operation.CREATE_TABLE: case Operation.UPDATE_TABLE: diff --git a/packages/server/src/integrations/microsoftSqlServer.ts b/packages/server/src/integrations/microsoftSqlServer.ts index 7071ce6f75..bcb22f50a9 100644 --- a/packages/server/src/integrations/microsoftSqlServer.ts +++ b/packages/server/src/integrations/microsoftSqlServer.ts @@ -245,7 +245,9 @@ module MSSQLModule { schema, } } - this.tables = tables + const final = finaliseExternalTables(tables) + this.tables = final.tables + this.schemaErrors = final.errors } async read(query: SqlQuery | string) { From 61a4a240f18e7299161793779de81023be0f27f7 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 5 Nov 2021 14:59:28 +0000 Subject: [PATCH 074/100] Updating usage quota middleware to fix issue presented in #3258 where anything with an _id and a _rev is considered to exist in CouchDB, which won't always be the case. Handle the scenario of an external database ID and don't error. --- packages/server/src/middleware/usageQuota.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/server/src/middleware/usageQuota.js b/packages/server/src/middleware/usageQuota.js index c62f0078cd..2b189b8660 100644 --- a/packages/server/src/middleware/usageQuota.js +++ b/packages/server/src/middleware/usageQuota.js @@ -2,6 +2,10 @@ const CouchDB = require("../db") const usageQuota = require("../utilities/usageQuota") const env = require("../environment") const { getTenantId } = require("@budibase/auth/tenancy") +const { + isExternalTable, + isRowId: isExternalRowId, +} = require("../integrations/utils") // tenants without limits const EXCLUDED_TENANTS = ["bb", "default", "bbtest", "bbstaging"] @@ -46,14 +50,24 @@ module.exports = async (ctx, next) => { } // post request could be a save of a pre-existing entry if (ctx.request.body && ctx.request.body._id && ctx.request.body._rev) { + const usageId = ctx.request.body._id try { if (ctx.appId) { const db = new CouchDB(ctx.appId) - await db.get(ctx.request.body._id) + await db.get(usageId) } return next() } catch (err) { - ctx.throw(404, `${ctx.request.body._id} does not exist`) + if ( + isExternalTable(usageId) || + (ctx.request.body.tableId && + isExternalTable(ctx.request.body.tableId)) || + isExternalRowId(usageId) + ) { + return next() + } else { + ctx.throw(404, `${usageId} does not exist`) + } } } From b34cef26c304f67e2b8b3d0e3c76ea004db672bf Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 5 Nov 2021 18:55:36 +0000 Subject: [PATCH 075/100] Fixing an issue with relationship modal breaking when multiple data sources available to relate to, also fixing an pile of issues with creating and reading rows from SQL server plus. --- .../DataTable/modals/CreateEditColumn.svelte | 13 ++- packages/server/src/definitions/datasource.ts | 2 +- packages/server/src/integrations/base/sql.ts | 83 +++++++++++++++++++ .../src/integrations/microsoftSqlServer.ts | 35 +++++--- packages/server/src/integrations/mysql.ts | 61 +------------- packages/server/src/integrations/utils.ts | 2 +- 6 files changed, 121 insertions(+), 75 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index ebfea9cee6..6ee0c48d2a 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -59,9 +59,6 @@ let deletion $: checkConstraints(field) - $: tableOptions = $tables.list.filter( - opt => opt._id !== $tables.draft._id && opt.type === table.type - ) $: required = !!field?.constraints?.presence || primaryDisplay $: uneditable = $tables.selected?._id === TableNames.USERS && @@ -88,6 +85,14 @@ field.type !== LINK_TYPE && !uneditable && field.type !== AUTO_TYPE $: relationshipOptions = getRelationshipOptions(field) $: external = table.type === "external" + // in the case of internal tables the sourceId will just be undefined + $: tableOptions = $tables.list.filter( + opt => + opt._id !== $tables.draft._id && + opt.type === table.type && + table.sourceId === opt.sourceId + ) + $: console.log(tableOptions) async function saveColumn() { if (field.type === AUTO_TYPE) { @@ -174,7 +179,7 @@ if (!field || !field.tableId) { return null } - const linkTable = tableOptions.find(table => table._id === field.tableId) + const linkTable = tableOptions?.find(table => table._id === field.tableId) if (!linkTable) { return null } diff --git a/packages/server/src/definitions/datasource.ts b/packages/server/src/definitions/datasource.ts index a82e50b140..6e6cb02f4f 100644 --- a/packages/server/src/definitions/datasource.ts +++ b/packages/server/src/definitions/datasource.ts @@ -119,7 +119,7 @@ export interface SortJson { export interface PaginationJson { limit: number - page: string | number + page?: string | number } export interface RelationshipsJson { diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 06a9d0aa10..d72f87958b 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -216,6 +216,10 @@ class InternalBuilder { query = query.orderBy(key, direction) } } + if (this.client === "mssql" && !sort && paginate?.limit) { + // @ts-ignore + query = query.orderBy(json.meta?.table?.primary[0]) + } query = this.addFilters(tableName, query, filters) // @ts-ignore let preQuery: KnexQuery = knex({ @@ -301,6 +305,85 @@ class SqlQueryBuilder extends SqlTableQueryBuilder { // @ts-ignore return query.toSQL().toNative() } + + async getReturningRow(queryFn: Function, json: QueryJson) { + if (!json.extra || !json.extra.idFilter) { + return {} + } + const input = this._query({ + endpoint: { + ...json.endpoint, + operation: Operation.READ, + }, + resource: { + fields: [], + }, + filters: json.extra.idFilter, + paginate: { + limit: 1, + }, + meta: json.meta, + }) + return queryFn(input, Operation.READ) + } + + // when creating if an ID has been inserted need to make sure + // the id filter is enriched with it before trying to retrieve the row + checkLookupKeys(id: any, json: QueryJson) { + if (!id || !json.meta?.table || !json.meta.table.primary) { + return json + } + const primaryKey = json.meta.table.primary?.[0] + json.extra = { + idFilter: { + equal: { + [primaryKey]: id, + }, + }, + } + return json + } + + // this function recreates the returning functionality of postgres + async queryWithReturning( + json: QueryJson, + queryFn: Function, + processFn: Function = (result: any) => result + ) { + const sqlClient = this.getSqlClient() + const operation = this._operation(json) + const input = this._query(json, { disableReturning: true }) + if (Array.isArray(input)) { + const responses = [] + for (let query of input) { + responses.push(await queryFn(query, operation)) + } + return responses + } + let row + // need to manage returning, a feature mySQL can't do + if (operation === Operation.DELETE) { + row = processFn(await this.getReturningRow(queryFn, json)) + } + const response = await queryFn(input, operation) + const results = processFn(response) + // same as delete, manage returning + if (operation === Operation.CREATE || operation === Operation.UPDATE) { + let id + if (sqlClient === "mssql") { + id = results?.[0].id + } else if (sqlClient === "mysql") { + id = results?.insertId + } + row = processFn( + await this.getReturningRow(queryFn, this.checkLookupKeys(id, json)) + ) + } + if (operation !== Operation.READ) { + return row + } + return results.length ? results : [{ [operation.toLowerCase()]: true }] + } } module.exports = SqlQueryBuilder diff --git a/packages/server/src/integrations/microsoftSqlServer.ts b/packages/server/src/integrations/microsoftSqlServer.ts index bcb22f50a9..1ee64759ef 100644 --- a/packages/server/src/integrations/microsoftSqlServer.ts +++ b/packages/server/src/integrations/microsoftSqlServer.ts @@ -1,8 +1,9 @@ import { - Integration, DatasourceFieldTypes, - QueryTypes, + Integration, + Operation, QueryJson, + QueryTypes, SqlQuery, } from "../definitions/datasource" import { getSqlQuery } from "./utils" @@ -103,15 +104,26 @@ module MSSQLModule { json: DatasourceFieldTypes.JSON, } - async function internalQuery(client: any, query: SqlQuery) { + async function internalQuery( + client: any, + query: SqlQuery, + operation: string | undefined = undefined + ) { + const request = client.request() try { if (Array.isArray(query.bindings)) { let count = 0 for (let binding of query.bindings) { - client.input(`p${count++}`, binding) + request.input(`p${count++}`, binding) } } - return await client.query(query.sql) + // this is a hack to get the inserted ID back, + // no way to do this with Knex nicely + const sql = + operation === Operation.CREATE + ? `${query.sql}; SELECT SCOPE_IDENTITY() AS id;` + : query.sql + return await request.query(sql) } catch (err) { // @ts-ignore throw new Error(err) @@ -180,8 +192,7 @@ module MSSQLModule { async connect() { try { - const client = await this.pool.connect() - this.client = client.request() + this.client = await this.pool.connect() } catch (err) { // @ts-ignore throw new Error(err) @@ -276,10 +287,12 @@ module MSSQLModule { async query(json: QueryJson) { await this.connect() - const operation = this._operation(json).toLowerCase() - const input = this._query(json) - const response = await internalQuery(this.client, input) - return response.recordset ? response.recordset : [{ [operation]: true }] + const operation = this._operation(json) + const queryFn = (query: any, op: string) => + internalQuery(this.client, query, op) + const processFn = (result: any) => + result.recordset ? result.recordset : [{ [operation]: true }] + return this.queryWithReturning(json, queryFn, processFn) } } diff --git a/packages/server/src/integrations/mysql.ts b/packages/server/src/integrations/mysql.ts index 30cbd836b5..ad313d9302 100644 --- a/packages/server/src/integrations/mysql.ts +++ b/packages/server/src/integrations/mysql.ts @@ -223,67 +223,12 @@ module MySQLModule { return results.length ? results : [{ deleted: true }] } - async getReturningRow(json: QueryJson) { - if (!json.extra || !json.extra.idFilter) { - return {} - } - const input = this._query({ - endpoint: { - ...json.endpoint, - operation: Operation.READ, - }, - fields: [], - filters: json.extra.idFilter, - paginate: { - limit: 1, - }, - }) - return internalQuery(this.client, input, false) - } - - // when creating if an ID has been inserted need to make sure - // the id filter is enriched with it before trying to retrieve the row - checkLookupKeys(results: any, json: QueryJson) { - if (!results?.insertId || !json.meta?.table || !json.meta.table.primary) { - return json - } - const primaryKey = json.meta.table.primary?.[0] - json.extra = { - idFilter: { - equal: { - [primaryKey]: results.insertId, - }, - }, - } - return json - } - async query(json: QueryJson) { - const operation = this._operation(json) this.client.connect() - const input = this._query(json, { disableReturning: true }) - if (Array.isArray(input)) { - const responses = [] - for (let query of input) { - responses.push(await internalQuery(this.client, query, false)) - } - return responses - } - let row - // need to manage returning, a feature mySQL can't do - if (operation === operation.DELETE) { - row = this.getReturningRow(json) - } - const results = await internalQuery(this.client, input, false) - // same as delete, manage returning - if (operation === Operation.CREATE || operation === Operation.UPDATE) { - row = this.getReturningRow(this.checkLookupKeys(results, json)) - } + const queryFn = (query: any) => internalQuery(this.client, query, false) + const output = await this.queryWithReturning(json, queryFn) this.client.end() - if (operation !== Operation.READ) { - return row - } - return results.length ? results : [{ [operation.toLowerCase()]: true }] + return output } } diff --git a/packages/server/src/integrations/utils.ts b/packages/server/src/integrations/utils.ts index f91d25423c..55ec086b7e 100644 --- a/packages/server/src/integrations/utils.ts +++ b/packages/server/src/integrations/utils.ts @@ -1,4 +1,4 @@ -import { SqlQuery } from "../definitions/datasource" +import { Operation, SqlQuery } from "../definitions/datasource" import { Datasource, Table } from "../definitions/common" import { SourceNames } from "../definitions/datasource" const { DocumentTypes, SEPARATOR } = require("../db/utils") From 25965696fcca6f38b06f0c78a65bb31dfb3883f2 Mon Sep 17 00:00:00 2001 From: Budibase Release Bot <> Date: Mon, 8 Nov 2021 12:29:59 +0000 Subject: [PATCH 076/100] v0.9.179 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 6 +++--- packages/server/package.json | 8 ++++---- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lerna.json b/lerna.json index df4c58df40..cea115bf10 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.178", + "version": "0.9.179", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 0d5171458c..aeea64e2d5 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.178", + "version": "0.9.179", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 4f7c369e1f..00bd4051a8 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.178", + "version": "0.9.179", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index d0376d6116..2b88ce9776 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.178", + "version": "0.9.179", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.178", - "@budibase/client": "^0.9.178", + "@budibase/bbui": "^0.9.179", + "@budibase/client": "^0.9.179", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.178", + "@budibase/string-templates": "^0.9.179", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 5cb584aa42..24c2c70b72 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.178", + "version": "0.9.179", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index ddfd50264f..7f8a0a5469 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.178", + "version": "0.9.179", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.178", + "@budibase/bbui": "^0.9.179", "@budibase/standard-components": "^0.9.139", - "@budibase/string-templates": "^0.9.178", + "@budibase/string-templates": "^0.9.179", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index d55cdbdabe..236e5175de 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.178", + "version": "0.9.179", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -68,9 +68,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.178", - "@budibase/client": "^0.9.178", - "@budibase/string-templates": "^0.9.178", + "@budibase/auth": "^0.9.179", + "@budibase/client": "^0.9.179", + "@budibase/string-templates": "^0.9.179", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 196ec9a0c0..a3ed17a1a8 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.178", + "version": "0.9.179", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 90da0e8cbf..05a652e079 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.178", + "version": "0.9.179", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -29,8 +29,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.178", - "@budibase/string-templates": "^0.9.178", + "@budibase/auth": "^0.9.179", + "@budibase/string-templates": "^0.9.179", "@koa/router": "^8.0.0", "@sentry/node": "^6.0.0", "@techpass/passport-openidconnect": "^0.3.0", From 7df3227432b4cc0ea6c0dad0526dcef7a514de07 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Mon, 8 Nov 2021 14:13:09 +0000 Subject: [PATCH 077/100] v0.9.180-alpha.0 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 6 +++--- packages/server/package.json | 8 ++++---- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lerna.json b/lerna.json index cea115bf10..2b7b35e6fd 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.179", + "version": "0.9.180-alpha.0", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index aeea64e2d5..91305f3cb9 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.179", + "version": "0.9.180-alpha.0", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 66b1f9ad96..ba325bcd0b 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.179", + "version": "0.9.180-alpha.0", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 2b88ce9776..c362ce6912 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.179", + "version": "0.9.180-alpha.0", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.179", - "@budibase/client": "^0.9.179", + "@budibase/bbui": "^0.9.180-alpha.0", + "@budibase/client": "^0.9.180-alpha.0", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.179", + "@budibase/string-templates": "^0.9.180-alpha.0", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 24c2c70b72..02c0c8e08e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.179", + "version": "0.9.180-alpha.0", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 7f8a0a5469..c925087740 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.179", + "version": "0.9.180-alpha.0", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.179", + "@budibase/bbui": "^0.9.180-alpha.0", "@budibase/standard-components": "^0.9.139", - "@budibase/string-templates": "^0.9.179", + "@budibase/string-templates": "^0.9.180-alpha.0", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index 236e5175de..27c274f2d4 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.179", + "version": "0.9.180-alpha.0", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -68,9 +68,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.179", - "@budibase/client": "^0.9.179", - "@budibase/string-templates": "^0.9.179", + "@budibase/auth": "^0.9.180-alpha.0", + "@budibase/client": "^0.9.180-alpha.0", + "@budibase/string-templates": "^0.9.180-alpha.0", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index a3ed17a1a8..187f720407 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.179", + "version": "0.9.180-alpha.0", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 05a652e079..84650b7713 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.179", + "version": "0.9.180-alpha.0", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -29,8 +29,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.179", - "@budibase/string-templates": "^0.9.179", + "@budibase/auth": "^0.9.180-alpha.0", + "@budibase/string-templates": "^0.9.180-alpha.0", "@koa/router": "^8.0.0", "@sentry/node": "^6.0.0", "@techpass/passport-openidconnect": "^0.3.0", From a7849eac7d354b04b3f353110c1f4f4c3a659642 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 8 Nov 2021 14:35:58 +0000 Subject: [PATCH 078/100] Add card list with search block. Add concept of nested settings which can consume their own contexts and are not enriched at the top level --- .../builder/src/builderStore/dataBinding.js | 48 +++- .../design/AppPreview/componentStructure.json | 3 +- .../ComponentSettingsSection.svelte | 83 +++--- .../PropertiesPanel/PropertiesPanel.svelte | 10 +- .../PropertyControls/PropertyControl.svelte | 18 +- packages/client/manifest.json | 155 ++++++++++- .../src/components/BlockComponent.svelte | 3 +- .../client/src/components/Component.svelte | 135 +++++++--- .../src/components/app/SpectrumCard.svelte | 13 + .../app/blocks/CardListWithSearch.svelte | 240 ++++++++++++++++++ .../client/src/components/app/blocks/index.js | 1 + packages/client/src/stores/context.js | 3 +- 12 files changed, 616 insertions(+), 96 deletions(-) create mode 100644 packages/client/src/components/app/blocks/CardListWithSearch.svelte diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index 742c9052ad..165ed37fbb 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -39,6 +39,25 @@ export const getBindableProperties = (asset, componentId) => { ] } +/** + * Gets the bindable properties exposed by a certain component. + */ +export const getComponentBindableProperties = (asset, componentId) => { + if (!asset || !componentId) { + return [] + } + + // Ensure that the component exists and exposes context + const component = findComponent(asset.props, componentId) + const def = store.actions.components.getDefinition(component?._component) + if (!def?.context) { + return [] + } + + // Get the bindings for the component + return getProviderContextBindings(asset, component) +} + /** * Gets all data provider components above a component. */ @@ -125,9 +144,26 @@ export const getDatasourceForProvider = (asset, component) => { const getContextBindings = (asset, componentId) => { // Extract any components which provide data contexts const dataProviders = getDataProviderComponents(asset, componentId) - let bindings = [] + + // Generate bindings for all matching components + return getProviderContextBindings(asset, dataProviders) +} + +/** + * Gets the context bindings exposed by a set of data provider components. + */ +const getProviderContextBindings = (asset, dataProviders) => { + if (!asset || !dataProviders) { + return [] + } + + // Ensure providers is an array + if (!Array.isArray(dataProviders)) { + dataProviders = [dataProviders] + } // Create bindings for each data provider + let bindings = [] dataProviders.forEach(component => { const def = store.actions.components.getDefinition(component._component) const contexts = Array.isArray(def.context) ? def.context : [def.context] @@ -140,6 +176,7 @@ const getContextBindings = (asset, componentId) => { let schema let readablePrefix + let runtimeSuffix = context.suffix if (context.type === "form") { // Forms do not need table schemas @@ -169,8 +206,14 @@ const getContextBindings = (asset, componentId) => { const keys = Object.keys(schema).sort() + // Generate safe unique runtime prefix + let runtimeId = component._id + if (runtimeSuffix) { + runtimeId += `-${runtimeSuffix}` + } + const safeComponentId = makePropSafe(runtimeId) + // Create bindable properties for each schema field - const safeComponentId = makePropSafe(component._id) keys.forEach(key => { const fieldSchema = schema[key] @@ -182,6 +225,7 @@ const getContextBindings = (asset, componentId) => { } else if (fieldSchema.type === "attachment") { runtimeBoundKey = `${key}_first` } + const runtimeBinding = `${safeComponentId}.${makePropSafe( runtimeBoundKey )}` diff --git a/packages/builder/src/components/design/AppPreview/componentStructure.json b/packages/builder/src/components/design/AppPreview/componentStructure.json index b6675c2480..9a87bb5761 100644 --- a/packages/builder/src/components/design/AppPreview/componentStructure.json +++ b/packages/builder/src/components/design/AppPreview/componentStructure.json @@ -3,7 +3,8 @@ "name": "Blocks", "icon": "Article", "children": [ - "tablewithsearch" + "tablewithsearch", + "cardlistwithsearch" ] }, "section", diff --git a/packages/builder/src/components/design/PropertiesPanel/ComponentSettingsSection.svelte b/packages/builder/src/components/design/PropertiesPanel/ComponentSettingsSection.svelte index f7123eb9e3..356e86b20a 100644 --- a/packages/builder/src/components/design/PropertiesPanel/ComponentSettingsSection.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/ComponentSettingsSection.svelte @@ -12,6 +12,7 @@ export let componentInstance export let assetInstance export let bindings + export let componentBindings const layoutDefinition = [] const screenDefinition = [ @@ -21,12 +22,24 @@ { key: "layoutId", label: "Layout", control: LayoutSelect }, ] - $: settings = componentDefinition?.settings ?? [] - $: generalSettings = settings.filter(setting => !setting.section) - $: sections = settings.filter(setting => setting.section) + $: sections = getSections(componentDefinition) $: isLayout = assetInstance && assetInstance.favicon $: assetDefinition = isLayout ? layoutDefinition : screenDefinition + const getSections = definition => { + const settings = definition?.settings ?? [] + const generalSettings = settings.filter(setting => !setting.section) + const customSections = settings.filter(setting => setting.section) + return [ + { + name: "General", + info: componentDefinition?.info, + settings: generalSettings, + }, + ...(customSections || []), + ] + } + const updateProp = store.actions.components.updateProp const canRenderControl = setting => { @@ -61,53 +74,18 @@ } - - {#if !componentInstance._component.endsWith("/layout")} - updateProp("_instanceName", val)} - /> - {/if} - {#if generalSettings.length} - {#each generalSettings as setting (setting.key)} - {#if canRenderControl(setting)} - updateProp(setting.key, val)} - props={{ - options: setting.options || [], - placeholder: setting.placeholder || null, - min: setting.min || null, - max: setting.max || null, - }} - {bindings} - {componentDefinition} - /> - {/if} - {/each} - {/if} - {#if componentDefinition?.component?.endsWith("/fieldgroup")} - - {/if} - {#if componentDefinition?.info} -
- {@html componentDefinition.info} -
- {/if} -
- -{#each sections as section (section.name)} +{#each sections as section, idx (section.name)} + {#if idx === 0 && !componentInstance._component.endsWith("/layout")} + updateProp("_instanceName", val)} + /> + {/if} {#each section.settings as setting (setting.key)} {#if canRenderControl(setting)} updateProp(setting.key, val)} props={{ options: setting.options || [], @@ -126,10 +104,15 @@ max: setting.max || null, }} {bindings} + {componentBindings} + {componentInstance} {componentDefinition} /> {/if} {/each} + {#if idx === 0 && componentDefinition?.component?.endsWith("/fieldgroup")} + + {/if} {#if section?.info}
{@html section.info} diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertiesPanel.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertiesPanel.svelte index 27f6650cde..df91a87456 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertiesPanel.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertiesPanel.svelte @@ -6,13 +6,20 @@ import DesignSection from "./DesignSection.svelte" import CustomStylesSection from "./CustomStylesSection.svelte" import ConditionalUISection from "./ConditionalUISection.svelte" - import { getBindableProperties } from "builderStore/dataBinding" + import { + getBindableProperties, + getComponentBindableProperties, + } from "builderStore/dataBinding" $: componentInstance = $selectedComponent $: componentDefinition = store.actions.components.getDefinition( $selectedComponent?._component ) $: bindings = getBindableProperties($currentAsset, $store.selectedComponentId) + $: componentBindings = getComponentBindableProperties( + $currentAsset, + $store.selectedComponentId + ) @@ -28,6 +35,7 @@ {componentInstance} {componentDefinition} {bindings} + {componentBindings} /> {} export let bindings = [] + export let componentBindings = [] + export let nested = false let bindingDrawer let anchor let valid - $: safeValue = getSafeValue(value, props.defaultValue, bindings) + $: allBindings = getAllBindings(bindings, componentBindings, nested) + $: safeValue = getSafeValue(value, props.defaultValue, allBindings) $: tempValue = safeValue - $: replaceBindings = val => readableToRuntimeBinding(bindings, val) + $: replaceBindings = val => readableToRuntimeBinding(allBindings, val) + + const getAllBindings = (bindings, componentBindings, nested) => { + if (!nested) { + return bindings + } + return [...(bindings || []), ...(componentBindings || [])] + } const handleClose = () => { handleChange(tempValue) @@ -78,7 +88,7 @@ updateOnChange={false} on:change={handleChange} onChange={handleChange} - {bindings} + bindings={allBindings} name={key} text={label} {type} @@ -104,7 +114,7 @@ bind:valid value={safeValue} on:change={e => (tempValue = e.detail)} - bindableProperties={bindings} + bindableProperties={allBindings} allowJS /> diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 982f96ef90..f3ba71223c 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -2599,6 +2599,21 @@ "type": "boolean", "key": "horizontal", "label": "Horizontal" + }, + { + "type": "boolean", + "label": "Show button", + "key": "showButton" + }, + { + "type": "text", + "key": "buttonText", + "label": "Button text" + }, + { + "type": "event", + "label": "Button action", + "key": "buttonOnClick" } ] }, @@ -2710,22 +2725,22 @@ }, { "section": true, - "name": "Button", + "name": "Title button", "settings": [ { "type": "boolean", "key": "showTitleButton", - "label": "Show title button", + "label": "Show button", "defaultValue": false }, { "type": "text", "key": "titleButtonText", - "label": "Title button text" + "label": "Button text" }, { "type": "event", - "label": "Title button action", + "label": "Button action", "key": "titleButtonOnClick" } ] @@ -2742,9 +2757,139 @@ } ] } + ] + }, + "cardlistwithsearch": { + "block": true, + "name": "Card list with search", + "icon": "Table", + "styles": ["size"], + "info": "Only the first 3 search columns will be used.", + "settings": [ + { + "type": "text", + "label": "Title", + "key": "title" + }, + { + "type": "dataSource", + "label": "Data", + "key": "dataSource" + }, + { + "type": "multifield", + "label": "Search Columns", + "key": "searchColumns", + "placeholder": "Choose search columns" + }, + { + "type": "filter", + "label": "Filtering", + "key": "filter" + }, + { + "type": "field", + "label": "Sort Column", + "key": "sortColumn" + }, + { + "type": "select", + "label": "Sort Order", + "key": "sortOrder", + "options": ["Ascending", "Descending"], + "defaultValue": "Descending" + }, + { + "type": "number", + "label": "Limit", + "key": "limit", + "defaultValue": 10 + }, + { + "type": "boolean", + "label": "Paginate", + "key": "paginate" + }, + { + "section": true, + "name": "Cards", + "settings": [ + { + "type": "text", + "key": "cardTitle", + "label": "Title", + "nested": true + }, + { + "type": "text", + "key": "cardSubtitle", + "label": "Subtitle", + "nested": true + }, + { + "type": "text", + "key": "cardDescription", + "label": "Description", + "nested": true + + }, + { + "type": "text", + "key": "cardImageURL", + "label": "Image URL", + "nested": true + + }, + { + "type": "boolean", + "key": "cardHorizontal", + "label": "Horizontal" + }, + { + "type": "boolean", + "label": "Show button", + "key": "showCardButton" + }, + { + "type": "text", + "key": "cardButtonText", + "label": "Button text", + "nested": true + + }, + { + "type": "event", + "label": "Button action", + "key": "cardButtonOnClick", + "nested": true + } + ] + }, + { + "section": true, + "name": "Title button", + "settings": [ + { + "type": "boolean", + "key": "showTitleButton", + "label": "Show button" + }, + { + "type": "text", + "key": "titleButtonText", + "label": "Button text" + }, + { + "type": "event", + "label": "Button action", + "key": "titleButtonOnClick" + } + ] + } ], "context": { - "type": "schema" + "type": "schema", + "suffix": "repeater" } } } diff --git a/packages/client/src/components/BlockComponent.svelte b/packages/client/src/components/BlockComponent.svelte index 02456322da..589998994d 100644 --- a/packages/client/src/components/BlockComponent.svelte +++ b/packages/client/src/components/BlockComponent.svelte @@ -6,6 +6,7 @@ export let type export let props export let styles + export let context // ID is only exposed as a prop so that it can be bound to from parent // block components @@ -16,7 +17,7 @@ // Create a fake component instance so that we can use the core Component // to render this part of the block, taking advantage of binding enrichment - $: id = block.id + rand + $: id = `${block.id}-${context ?? rand}` $: instance = { _component: `@budibase/standard-components/${type}`, _id: id, diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index c13a50d52e..346de98f2f 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -1,3 +1,7 @@ + + {#key renderKey} - {#if constructor && componentSettings && (visible || inSelectedPath)} + {#if constructor && settings && (visible || inSelectedPath)}
- + {#if children.length} {#each children as child (child._id)} diff --git a/packages/client/src/components/app/SpectrumCard.svelte b/packages/client/src/components/app/SpectrumCard.svelte index bac0a81fb3..ba3e919cdd 100644 --- a/packages/client/src/components/app/SpectrumCard.svelte +++ b/packages/client/src/components/app/SpectrumCard.svelte @@ -1,6 +1,7 @@ + + +
+ + {#if title || enrichedSearchColumns?.length || showTitleButton} +
+
+ {title || ""} +
+
+ {#if enrichedSearchColumns?.length} + + {/if} + {#if showTitleButton} + + {/if} +
+
+ {/if} + + + + + + +
+
+ + diff --git a/packages/client/src/components/app/blocks/index.js b/packages/client/src/components/app/blocks/index.js index 16b123d17e..7d0d15080a 100644 --- a/packages/client/src/components/app/blocks/index.js +++ b/packages/client/src/components/app/blocks/index.js @@ -1 +1,2 @@ export { default as tablewithsearch } from "./TableWithSearch.svelte" +export { default as cardlistwithsearch } from "./CardListWithSearch.svelte" diff --git a/packages/client/src/stores/context.js b/packages/client/src/stores/context.js index e372b837bd..fcbcf0f592 100644 --- a/packages/client/src/stores/context.js +++ b/packages/client/src/stores/context.js @@ -1,4 +1,5 @@ import { writable, derived } from "svelte/store" +import { hashString } from "../utils/helpers" export const createContextStore = oldContext => { const newContext = writable({}) @@ -9,7 +10,7 @@ export const createContextStore = oldContext => { for (let i = 0; i < $contexts.length - 1; i++) { key += $contexts[i].key } - key += JSON.stringify($contexts[$contexts.length - 1]) + key = hashString(key + JSON.stringify($contexts[$contexts.length - 1])) // Reduce global state const reducer = (total, context) => ({ ...total, ...context }) From 37c2690a316544b19d835319aba01f50a5bd8c77 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 8 Nov 2021 14:51:13 +0000 Subject: [PATCH 079/100] Fix card button text potentially showing as undefined --- packages/client/src/components/app/SpectrumCard.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/components/app/SpectrumCard.svelte b/packages/client/src/components/app/SpectrumCard.svelte index ba3e919cdd..981ac98d9e 100644 --- a/packages/client/src/components/app/SpectrumCard.svelte +++ b/packages/client/src/components/app/SpectrumCard.svelte @@ -66,7 +66,7 @@ {/if} {#if showButton} {/if}
From 9883fdb623140d24bd8d2276283949f1ea76d758 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 8 Nov 2021 16:40:45 +0000 Subject: [PATCH 080/100] Fix card list block empty state and update size --- .../client/src/components/app/Repeater.svelte | 1 + .../src/components/app/SpectrumCard.svelte | 16 ++++++++++++++-- .../app/blocks/CardListWithSearch.svelte | 9 +++------ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/client/src/components/app/Repeater.svelte b/packages/client/src/components/app/Repeater.svelte index 79f044f402..ff077f359b 100644 --- a/packages/client/src/components/app/Repeater.svelte +++ b/packages/client/src/components/app/Repeater.svelte @@ -38,6 +38,7 @@ padding: var(--spacing-l); display: grid; place-items: center; + grid-column: 1 / -1; } .noRows i { margin-bottom: var(--spacing-m); diff --git a/packages/client/src/components/app/SpectrumCard.svelte b/packages/client/src/components/app/SpectrumCard.svelte index 981ac98d9e..d8353eea7e 100644 --- a/packages/client/src/components/app/SpectrumCard.svelte +++ b/packages/client/src/components/app/SpectrumCard.svelte @@ -74,7 +74,7 @@ diff --git a/packages/client/src/components/app/blocks/CardListWithSearch.svelte b/packages/client/src/components/app/blocks/CardListWithSearch.svelte index 3e320bfee4..a750a37361 100644 --- a/packages/client/src/components/app/blocks/CardListWithSearch.svelte +++ b/packages/client/src/components/app/blocks/CardListWithSearch.svelte @@ -41,7 +41,7 @@ $: enrichedSearchColumns = enrichSearchColumns(searchColumns, schema) $: enrichedFilter = enrichFilter(filter, enrichedSearchColumns, formId) - $: cardWidth = cardHorizontal ? 420 : 240 + $: cardWidth = cardHorizontal ? 420 : 300 // Enrich the default filter with the specified search fields const enrichFilter = (filter, columns, formId) => { @@ -140,9 +140,10 @@ props={{ dataProvider: `{{ literal [${dataProviderId}] }}`, direction: "row", - hAlign: "left", + hAlign: "stretch", vAlign: "top", gap: "M", + noRowsMessage: "No rows found", }} styles={{ display: "grid", @@ -172,10 +173,6 @@ diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/URLSelect.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/URLSelect.svelte index fb8b48b9ed..dc2fa7ad89 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/URLSelect.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/URLSelect.svelte @@ -10,4 +10,10 @@ .filter(x => x != null) - + diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js index 69bb3f8b47..b9b227bef0 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js @@ -15,10 +15,10 @@ import URLSelect from "./URLSelect.svelte" import OptionsEditor from "./OptionsEditor/OptionsEditor.svelte" import FormFieldSelect from "./FormFieldSelect.svelte" import ValidationEditor from "./ValidationEditor/ValidationEditor.svelte" -import Input from "./Input.svelte" +import DrawerBindableCombobox from "components/common/bindings/DrawerBindableCombobox.svelte" const componentMap = { - text: Input, + text: DrawerBindableCombobox, select: Select, dataSource: DataSourceSelect, dataProvider: DataProviderSelect, diff --git a/packages/builder/src/components/design/PropertiesPanel/ScreenSettingsSection.svelte b/packages/builder/src/components/design/PropertiesPanel/ScreenSettingsSection.svelte index efe51ebdac..4a7c77746e 100644 --- a/packages/builder/src/components/design/PropertiesPanel/ScreenSettingsSection.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/ScreenSettingsSection.svelte @@ -54,7 +54,6 @@ {#each screenSettings as def (`${componentInstance._id}-${def.key}`)} Date: Wed, 10 Nov 2021 11:48:02 +0000 Subject: [PATCH 093/100] Update block search so that string fields do a 'starts with' search rather than an exact match --- .../client/src/components/app/blocks/CardListWithSearch.svelte | 3 ++- .../client/src/components/app/blocks/TableWithSearch.svelte | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/client/src/components/app/blocks/CardListWithSearch.svelte b/packages/client/src/components/app/blocks/CardListWithSearch.svelte index a750a37361..7c9a067bd6 100644 --- a/packages/client/src/components/app/blocks/CardListWithSearch.svelte +++ b/packages/client/src/components/app/blocks/CardListWithSearch.svelte @@ -49,7 +49,7 @@ columns?.forEach(column => { enrichedFilter.push({ field: column.name, - operator: "equal", + operator: column.type === "string" ? "string" : "equal", type: "string", valueType: "Binding", value: `{{ [${formId}].[${column.name}] }}`, @@ -68,6 +68,7 @@ enrichedColumns.push({ name: column, componentType, + type: schemaType, }) } }) diff --git a/packages/client/src/components/app/blocks/TableWithSearch.svelte b/packages/client/src/components/app/blocks/TableWithSearch.svelte index 378a111571..12a2022686 100644 --- a/packages/client/src/components/app/blocks/TableWithSearch.svelte +++ b/packages/client/src/components/app/blocks/TableWithSearch.svelte @@ -48,7 +48,7 @@ columns?.forEach(column => { enrichedFilter.push({ field: column.name, - operator: "equal", + operator: column.type === "string" ? "string" : "equal", type: "string", valueType: "Binding", value: `{{ [${formId}].[${column.name}] }}`, @@ -67,6 +67,7 @@ enrichedColumns.push({ name: column, componentType, + type: schemaType, }) } }) From d6051e7067c2a676c6bade22316fa0915184abc9 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 10 Nov 2021 11:58:27 +0000 Subject: [PATCH 094/100] Add cypress data tag to binding icon in drawer bindable combo box --- .../common/bindings/DrawerBindableCombobox.svelte | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/components/common/bindings/DrawerBindableCombobox.svelte b/packages/builder/src/components/common/bindings/DrawerBindableCombobox.svelte index ba5a869e72..af0732eb84 100644 --- a/packages/builder/src/components/common/bindings/DrawerBindableCombobox.svelte +++ b/packages/builder/src/components/common/bindings/DrawerBindableCombobox.svelte @@ -62,7 +62,11 @@ options={allOptions} /> {#if !disabled} -
+
{/if} From 47122f76fd1fb961cd929ed963acf51cbe7f9512 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 10 Nov 2021 12:00:29 +0000 Subject: [PATCH 095/100] Updating per review comments. --- packages/auth/src/db/utils.js | 18 ++++++++++++++++++ packages/server/src/api/controllers/user.js | 18 ++++++++---------- packages/server/src/api/routes/user.js | 2 +- packages/worker/src/utilities/appService.js | 21 ++++++++++++++------- 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index 03bd773922..b956089660 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -259,6 +259,24 @@ exports.getAllApps = async (CouchDB, { dev, all, idsOnly } = {}) => { } } +/** + * Utility function for getAllApps but filters to production apps only. + */ +exports.getDeployedAppIDs = async CouchDB => { + return (await exports.getAllApps(CouchDB, { idsOnly: true })).filter( + id => !exports.isDevAppID(id) + ) +} + +/** + * Utility function for the inverse of above. + */ +exports.getDevAppIDs = async CouchDB => { + return (await exports.getAllApps(CouchDB, { idsOnly: true })).filter(id => + exports.isDevAppID(id) + ) +} + exports.dbExists = async (CouchDB, dbName) => { let exists = false try { diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index 5faf821349..908018fe51 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -8,11 +8,7 @@ const { getGlobalUsers, getRawGlobalUser } = require("../../utilities/global") const { getFullUser } = require("../../utilities/users") const { isEqual } = require("lodash") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") -const { - getDevelopmentAppID, - getAllApps, - isDevAppID, -} = require("@budibase/auth/db") +const { getDevelopmentAppID, getDeployedAppIDs } = require("@budibase/auth/db") const { doesDatabaseExist } = require("../../utilities") const { UserStatus } = require("@budibase/auth/constants") @@ -78,8 +74,12 @@ exports.syncUser = async function (ctx) { try { user = await getRawGlobalUser(userId) } catch (err) { - user = {} - deleting = true + if (err && err.status === 404) { + user = {} + deleting = true + } else { + throw err + } } const roles = user.roles // remove props which aren't useful to metadata @@ -90,9 +90,7 @@ exports.syncUser = async function (ctx) { let prodAppIds // if they are a builder then get all production app IDs if ((user.builder && user.builder.global) || deleting) { - prodAppIds = (await getAllApps(CouchDB, { idsOnly: true })).filter( - id => !isDevAppID(id) - ) + prodAppIds = await getDeployedAppIDs(CouchDB) } else { prodAppIds = Object.entries(roles) .filter(entry => entry[1] !== BUILTIN_ROLE_IDS.PUBLIC) diff --git a/packages/server/src/api/routes/user.js b/packages/server/src/api/routes/user.js index 43c08a7f33..a3043b5af1 100644 --- a/packages/server/src/api/routes/user.js +++ b/packages/server/src/api/routes/user.js @@ -35,7 +35,7 @@ router controller.destroyMetadata ) .post( - "/api/users/sync/:id", + "/api/users/metadata/sync/:id", authorized(PermissionTypes.USER, PermissionLevels.WRITE), controller.syncUser ) diff --git a/packages/worker/src/utilities/appService.js b/packages/worker/src/utilities/appService.js index 166a82f78b..92809c9046 100644 --- a/packages/worker/src/utilities/appService.js +++ b/packages/worker/src/utilities/appService.js @@ -4,7 +4,7 @@ const { getTenantId, isTenantIdSet } = require("@budibase/auth/tenancy") const { checkSlashesInUrl } = require("../utilities") const env = require("../environment") -exports.syncUserInApps = async userId => { +async function makeAppRequest(url, method, body) { if (env.isTest()) { return } @@ -13,12 +13,19 @@ exports.syncUserInApps = async userId => { if (isTenantIdSet()) { request.headers[Headers.TENANT_ID] = getTenantId() } - request.headers["Content-Type"] = "application/json" - request.body = JSON.stringify({}) - request.method = "POST" - const response = await fetch( - checkSlashesInUrl(env.APPS_URL + `/api/users/sync/${userId}`), - request + if (body) { + request.headers["Content-Type"] = "application/json" + request.body = JSON.stringify(body) + } + request.method = method + return fetch(checkSlashesInUrl(env.APPS_URL + url), request) +} + +exports.syncUserInApps = async userId => { + const response = await makeAppRequest( + `/api/users/metadata/sync/${userId}`, + "POST", + {} ) if (response.status !== 200) { throw "Unable to sync user." From c62741a31103885a53a54a5184838097bb47daf8 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 10 Nov 2021 12:17:43 +0000 Subject: [PATCH 096/100] v0.9.180-alpha.4 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 6 +++--- packages/server/package.json | 8 ++++---- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lerna.json b/lerna.json index 3410e9fd80..a99572473d 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.180-alpha.3", + "version": "0.9.180-alpha.4", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 1552bece1f..8187b87919 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.180-alpha.3", + "version": "0.9.180-alpha.4", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index bdc524a39b..8cdaafcdfa 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.180-alpha.3", + "version": "0.9.180-alpha.4", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 6f68915cb0..3cc2758d96 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.180-alpha.3", + "version": "0.9.180-alpha.4", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.180-alpha.3", - "@budibase/client": "^0.9.180-alpha.3", + "@budibase/bbui": "^0.9.180-alpha.4", + "@budibase/client": "^0.9.180-alpha.4", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.180-alpha.3", + "@budibase/string-templates": "^0.9.180-alpha.4", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index f788892017..922e60e285 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.180-alpha.3", + "version": "0.9.180-alpha.4", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index f0ccea1ec7..c93e5e80fe 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.180-alpha.3", + "version": "0.9.180-alpha.4", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.180-alpha.3", + "@budibase/bbui": "^0.9.180-alpha.4", "@budibase/standard-components": "^0.9.139", - "@budibase/string-templates": "^0.9.180-alpha.3", + "@budibase/string-templates": "^0.9.180-alpha.4", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index 36a3efd91a..5c790e8033 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.180-alpha.3", + "version": "0.9.180-alpha.4", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -68,9 +68,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.180-alpha.3", - "@budibase/client": "^0.9.180-alpha.3", - "@budibase/string-templates": "^0.9.180-alpha.3", + "@budibase/auth": "^0.9.180-alpha.4", + "@budibase/client": "^0.9.180-alpha.4", + "@budibase/string-templates": "^0.9.180-alpha.4", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index eae4595d91..9cab05b62b 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.180-alpha.3", + "version": "0.9.180-alpha.4", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 07b8644abe..261107db77 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.180-alpha.3", + "version": "0.9.180-alpha.4", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -29,8 +29,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.180-alpha.3", - "@budibase/string-templates": "^0.9.180-alpha.3", + "@budibase/auth": "^0.9.180-alpha.4", + "@budibase/string-templates": "^0.9.180-alpha.4", "@koa/router": "^8.0.0", "@sentry/node": "^6.0.0", "@techpass/passport-openidconnect": "^0.3.0", From a20d21ebf58bc7ed0daa07f77dea8d58efd17fa7 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 10 Nov 2021 12:29:11 +0000 Subject: [PATCH 097/100] v0.9.180-alpha.5 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 6 +++--- packages/server/package.json | 8 ++++---- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lerna.json b/lerna.json index a99572473d..f32daef8bd 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.180-alpha.4", + "version": "0.9.180-alpha.5", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 8187b87919..45957402d6 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.180-alpha.4", + "version": "0.9.180-alpha.5", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 8cdaafcdfa..1a80c23caf 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.180-alpha.4", + "version": "0.9.180-alpha.5", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 3cc2758d96..279bb0ec36 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.180-alpha.4", + "version": "0.9.180-alpha.5", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.180-alpha.4", - "@budibase/client": "^0.9.180-alpha.4", + "@budibase/bbui": "^0.9.180-alpha.5", + "@budibase/client": "^0.9.180-alpha.5", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.180-alpha.4", + "@budibase/string-templates": "^0.9.180-alpha.5", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 922e60e285..1b7487b0ed 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.180-alpha.4", + "version": "0.9.180-alpha.5", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index c93e5e80fe..6b6c3d1722 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.180-alpha.4", + "version": "0.9.180-alpha.5", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.180-alpha.4", + "@budibase/bbui": "^0.9.180-alpha.5", "@budibase/standard-components": "^0.9.139", - "@budibase/string-templates": "^0.9.180-alpha.4", + "@budibase/string-templates": "^0.9.180-alpha.5", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index 5c790e8033..1e7351b8c6 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.180-alpha.4", + "version": "0.9.180-alpha.5", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -68,9 +68,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.180-alpha.4", - "@budibase/client": "^0.9.180-alpha.4", - "@budibase/string-templates": "^0.9.180-alpha.4", + "@budibase/auth": "^0.9.180-alpha.5", + "@budibase/client": "^0.9.180-alpha.5", + "@budibase/string-templates": "^0.9.180-alpha.5", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 9cab05b62b..d56cc34662 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.180-alpha.4", + "version": "0.9.180-alpha.5", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 261107db77..6dccc5e525 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.180-alpha.4", + "version": "0.9.180-alpha.5", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -29,8 +29,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.180-alpha.4", - "@budibase/string-templates": "^0.9.180-alpha.4", + "@budibase/auth": "^0.9.180-alpha.5", + "@budibase/string-templates": "^0.9.180-alpha.5", "@koa/router": "^8.0.0", "@sentry/node": "^6.0.0", "@techpass/passport-openidconnect": "^0.3.0", From 18b98a158a034f059b01009bc47720865a687ab4 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 10 Nov 2021 12:52:23 +0000 Subject: [PATCH 098/100] Fixing issue found by test case. --- packages/worker/src/utilities/appService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/worker/src/utilities/appService.js b/packages/worker/src/utilities/appService.js index 92809c9046..23c5581510 100644 --- a/packages/worker/src/utilities/appService.js +++ b/packages/worker/src/utilities/appService.js @@ -27,7 +27,7 @@ exports.syncUserInApps = async userId => { "POST", {} ) - if (response.status !== 200) { + if (response && response.status !== 200) { throw "Unable to sync user." } } From 953e41479e88fe31182aea08d5c275abe9acf5c0 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 10 Nov 2021 13:50:18 +0000 Subject: [PATCH 099/100] v0.9.180-alpha.6 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 6 +++--- packages/server/package.json | 8 ++++---- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lerna.json b/lerna.json index f32daef8bd..6c39265db8 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.180-alpha.5", + "version": "0.9.180-alpha.6", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 45957402d6..2cbb76f441 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.180-alpha.5", + "version": "0.9.180-alpha.6", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 1a80c23caf..6a407d2852 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.180-alpha.5", + "version": "0.9.180-alpha.6", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 279bb0ec36..4b19517628 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.180-alpha.5", + "version": "0.9.180-alpha.6", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.180-alpha.5", - "@budibase/client": "^0.9.180-alpha.5", + "@budibase/bbui": "^0.9.180-alpha.6", + "@budibase/client": "^0.9.180-alpha.6", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.180-alpha.5", + "@budibase/string-templates": "^0.9.180-alpha.6", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 1b7487b0ed..70f800e852 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.180-alpha.5", + "version": "0.9.180-alpha.6", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 6b6c3d1722..83240ea6cf 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.180-alpha.5", + "version": "0.9.180-alpha.6", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.180-alpha.5", + "@budibase/bbui": "^0.9.180-alpha.6", "@budibase/standard-components": "^0.9.139", - "@budibase/string-templates": "^0.9.180-alpha.5", + "@budibase/string-templates": "^0.9.180-alpha.6", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index 1e7351b8c6..5df56f7be1 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.180-alpha.5", + "version": "0.9.180-alpha.6", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -68,9 +68,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.180-alpha.5", - "@budibase/client": "^0.9.180-alpha.5", - "@budibase/string-templates": "^0.9.180-alpha.5", + "@budibase/auth": "^0.9.180-alpha.6", + "@budibase/client": "^0.9.180-alpha.6", + "@budibase/string-templates": "^0.9.180-alpha.6", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index d56cc34662..f9d280e7a2 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.180-alpha.5", + "version": "0.9.180-alpha.6", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 6dccc5e525..606337b78d 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.180-alpha.5", + "version": "0.9.180-alpha.6", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -29,8 +29,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.180-alpha.5", - "@budibase/string-templates": "^0.9.180-alpha.5", + "@budibase/auth": "^0.9.180-alpha.6", + "@budibase/string-templates": "^0.9.180-alpha.6", "@koa/router": "^8.0.0", "@sentry/node": "^6.0.0", "@techpass/passport-openidconnect": "^0.3.0", From 4e6a9c81548ae00b4d968d09e8e16473f995a259 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 10 Nov 2021 14:43:07 +0000 Subject: [PATCH 100/100] fix width of automation blocks --- .../automation/AutomationBuilder/FlowChart/FlowItem.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte index 74e82a2da7..7ddc5c3e48 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte @@ -202,7 +202,7 @@ display: inline-block; } .block { - width: 360px; + width: 480px; font-size: 16px; background-color: var(--background); border: 1px solid var(--spectrum-global-color-gray-300);