diff --git a/packages/builder/package.json b/packages/builder/package.json index 129d8e1dc6..ea555007a9 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -63,7 +63,7 @@ } }, "dependencies": { - "@budibase/bbui": "^1.29.3", + "@budibase/bbui": "^1.32.0", "@budibase/client": "^0.1.19", "@budibase/colorpicker": "^1.0.1", "@sentry/browser": "5.19.1", @@ -73,7 +73,6 @@ "deepmerge": "^4.2.2", "fast-sort": "^2.2.0", "feather-icons": "^4.21.0", - "flatpickr": "^4.5.7", "lodash": "^4.17.13", "mustache": "^4.0.1", "posthog-js": "1.3.1", @@ -105,6 +104,7 @@ "rollup-plugin-alias": "^1.5.2", "rollup-plugin-commonjs": "^10.0.0", "rollup-plugin-copy": "^3.0.0", + "rollup-plugin-css-only": "^2.1.0", "rollup-plugin-livereload": "^1.0.0", "rollup-plugin-node-builtins": "^2.1.2", "rollup-plugin-node-globals": "^1.4.0", diff --git a/packages/builder/rollup.config.js b/packages/builder/rollup.config.js index a269b11bec..3d73d968f3 100644 --- a/packages/builder/rollup.config.js +++ b/packages/builder/rollup.config.js @@ -8,6 +8,7 @@ import { terser } from "rollup-plugin-terser" import builtins from "rollup-plugin-node-builtins" import nodeglobals from "rollup-plugin-node-globals" import copy from "rollup-plugin-copy" +import css from "rollup-plugin-css-only" import replace from "rollup-plugin-replace" import json from "@rollup/plugin-json" @@ -200,6 +201,11 @@ export default { }, }), + // export all CSS imported in the JS to it's own bundle + css({ + output: `${outputpath}/external.css`, + }), + resolve({ browser: true, dedupe: importee => { @@ -223,12 +229,6 @@ export default { fileName: "[dirname][name][extname]", emitFiles: true, }), - url({ - limit: 0, - include: ["**/*.css"], - fileName: "[name][extname]", - emitFiles: true, - }), builtins(), nodeglobals(), diff --git a/packages/builder/src/builderStore/store/index.js b/packages/builder/src/builderStore/store/index.js index ac13c90dbe..b64bf78624 100644 --- a/packages/builder/src/builderStore/store/index.js +++ b/packages/builder/src/builderStore/store/index.js @@ -104,6 +104,10 @@ const setPackage = (store, initial) => async pkg => { initial.pages = pkg.pages initial.hasAppPackage = true initial.screens = values(pkg.screens) + initial.allScreens = [ + ...Object.values(main_screens), + ...Object.values(unauth_screens), + ] initial.builtins = [getBuiltin("##builtin/screenslot")] initial.appInstances = pkg.application.instances initial.appId = pkg.application._id @@ -132,6 +136,7 @@ const _saveScreen = async (store, s, screen) => { innerState.pages[s.currentPageName]._screens = screens innerState.screens = screens innerState.currentPreviewItem = screen + innerState.allScreens = [...innerState.allScreens, screen] const safeProps = makePropsSafe( innerState.components[screen.props._component], screen.props diff --git a/packages/builder/src/components/common/DatePicker.svelte b/packages/builder/src/components/common/DatePicker.svelte index 8fc03decda..ded25b5d87 100644 --- a/packages/builder/src/components/common/DatePicker.svelte +++ b/packages/builder/src/components/common/DatePicker.svelte @@ -1,32 +1,17 @@
- +
- - diff --git a/packages/builder/src/components/database/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/database/DataTable/modals/CreateEditColumn.svelte index f71db9aada..270e4b6e25 100644 --- a/packages/builder/src/components/database/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/database/DataTable/modals/CreateEditColumn.svelte @@ -83,10 +83,10 @@ bind:values={field.constraints.inclusion} /> {:else if field.type === 'datetime' && field.constraints} {:else if field.type === 'number' && field.constraints} import { Input, Select } from "@budibase/bbui" + import DatePicker from "components/common/DatePicker.svelte" export let meta export let value = meta.type === "boolean" ? false : "" @@ -9,6 +10,7 @@ meta.constraints && meta.constraints.inclusion && meta.constraints.inclusion.length > 0 + let type = determineInputType(meta) function determineInputType(meta) { @@ -42,6 +44,8 @@ {/each} +{:else if type === 'date'} + {:else} {#if type === 'checkbox'} @@ -53,7 +57,6 @@ checked={value} {type} {value} - on:input={handleInput} on:change={handleInput} /> {/if} diff --git a/packages/builder/src/components/start/AppList.svelte b/packages/builder/src/components/start/AppList.svelte index c07437ed9e..efd448b94a 100644 --- a/packages/builder/src/components/start/AppList.svelte +++ b/packages/builder/src/components/start/AppList.svelte @@ -1,11 +1,6 @@
diff --git a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte index e2f08404b1..7aff1e366e 100644 --- a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte +++ b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte @@ -28,6 +28,13 @@ {/each} + {:else if parameter.name === 'url'} + {:else} + import { Select } from "@budibase/bbui" + import { createEventDispatcher } from "svelte" + import { store } from "builderStore" + + const dispatch = createEventDispatcher() + + export let value = "" + + const handleBlur = () => dispatch("change", value) + + + diff --git a/packages/builder/src/components/userInterface/temporaryPanelStructure.js b/packages/builder/src/components/userInterface/temporaryPanelStructure.js index 26ec25929e..5fef88d3af 100644 --- a/packages/builder/src/components/userInterface/temporaryPanelStructure.js +++ b/packages/builder/src/components/userInterface/temporaryPanelStructure.js @@ -5,6 +5,7 @@ import ModelSelect from "components/userInterface/ModelSelect.svelte" import ModelViewSelect from "components/userInterface/ModelViewSelect.svelte" import ModelViewFieldSelect from "components/userInterface/ModelViewFieldSelect.svelte" import Event from "components/userInterface/EventsEditor/EventPropertyControl.svelte" +import ScreenSelect from "components/userInterface/ScreenSelect.svelte" import { all } from "./propertyCategories.js" /* @@ -239,7 +240,7 @@ export default { design: { ...all }, settings: [ { label: "Text", key: "text", control: Input }, - { label: "Url", key: "url", control: Input }, + { label: "Url", key: "url", control: ScreenSelect }, { label: "Open New Tab", key: "openInNewTab", @@ -249,6 +250,19 @@ export default { ], }, }, + { + _component: "@budibase/standard-components/datepicker", + name: "Date Picker", + description: "A basic date picker component", + icon: "ri-calendar-line", + children: [], + properties: { + design: { ...all }, + settings: [ + { label: "Placeholder", key: "placeholder", control: Input }, + ], + }, + }, ], }, { diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index d13e8b60cd..43a01786f4 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -37,21 +37,20 @@ export const FIELDS = { // presence: { allowEmpty: true }, // }, // }, - // DATETIME: { - // name: "Date/Time", - // icon: "ri-calendar-event-fill", - // type: "string", - // value: "datetime", - // constraints: { - // type: "string", - // length: {}, - // presence: { allowEmpty: true }, - // datetime: { - // latest: "", - // earliest: "", - // }, - // }, - // }, + DATETIME: { + name: "Date/Time", + icon: "ri-calendar-event-fill", + type: "datetime", + constraints: { + type: "string", + length: {}, + presence: { allowEmpty: true }, + datetime: { + latest: "", + earliest: "", + }, + }, + }, // IMAGE: { // name: "File", // icon: "ri-image-line", diff --git a/packages/builder/src/index.html b/packages/builder/src/index.html index 40172ce6ca..150d9e23b9 100644 --- a/packages/builder/src/index.html +++ b/packages/builder/src/index.html @@ -10,11 +10,9 @@ - - + - diff --git a/packages/builder/src/main.js b/packages/builder/src/main.js index cfc568b564..bb6700eb5b 100644 --- a/packages/builder/src/main.js +++ b/packages/builder/src/main.js @@ -1,6 +1,7 @@ import "./global.css" import "./fonts.css" import "./budibase.css" +import "./fonts.css" import "/assets/Inter-Regular" import "/assets/Inter-Medium" import "/assets/Inter-SemiBold" @@ -9,10 +10,6 @@ import "/assets/Inter-ExtraBold" import "/assets/Inter-Black" import "/_builder/assets/budibase-logo.png" import "/_builder/assets/budibase-logo-only.png" -import "uikit/dist/css/uikit.min.css" -import "uikit/dist/js/uikit.min.js" -import "codemirror/lib/codemirror.css" -import "codemirror/theme/monokai.css" import App from "./App.svelte" /* eslint-disable */ diff --git a/packages/builder/yarn.lock b/packages/builder/yarn.lock index eb315d66fa..5fc90c91c5 100644 --- a/packages/builder/yarn.lock +++ b/packages/builder/yarn.lock @@ -688,12 +688,27 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@budibase/bbui@^1.29.3": - version "1.29.3" - resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.29.3.tgz#02d84d5e0f9ca936d62b1e0e9bc943711d124f7a" - integrity sha512-X5QBZX2CNccRiW6fbj6/fnUYhlilc6I6ae0QT8oIuv9DozDnUUbdd6j4RMxW1DQBZuvWH54OGq4nwkq9f37hKg== +"@budibase/bbui@^1.32.0": + version "1.32.0" + resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.32.0.tgz#4b099e51cf8aebfc963a763bb9687994a2ee26a8" + integrity sha512-pY4bhoBhE3EAdaO/wWkbOXaHtGpPxaRFFVUBe9DyoLZ6bv6Pg6y3SDMoMNaKtr3ybtum7pk9eDZOjMMYlGC4AQ== dependencies: sirv-cli "^0.4.6" + svelte-flatpickr "^2.4.0" + +"@budibase/client@^0.1.19": + version "0.1.19" + resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.1.19.tgz#3906781423ab4626118c981657ecf7a4578c547c" + dependencies: + "@nx-js/compiler-util" "^2.0.0" + bcryptjs "^2.4.3" + deep-equal "^2.0.1" + lodash "^4.17.15" + lunr "^2.3.5" + mustache "^4.0.1" + regexparam "^1.3.0" + shortid "^2.2.8" + svelte "^3.9.2" "@budibase/colorpicker@^1.0.1": version "1.0.1" @@ -944,6 +959,10 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@nx-js/compiler-util@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@nx-js/compiler-util/-/compiler-util-2.0.0.tgz#c74c12165fa2f017a292bb79af007e8fce0af297" + "@polka/url@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@polka/url/-/url-0.5.0.tgz#b21510597fd601e5d7c95008b76bf0d254ebfd31" @@ -960,6 +979,15 @@ dependencies: "@rollup/pluginutils" "^3.0.8" +"@rollup/pluginutils@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== + dependencies: + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" + "@rollup/pluginutils@^3.0.8": version "3.0.10" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.0.10.tgz#a659b9025920378494cd8f8c59fbf9b3a50d5f12" @@ -1362,6 +1390,10 @@ array-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -1416,6 +1448,12 @@ atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" +available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" + dependencies: + array-filter "^1.0.0" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -1490,6 +1528,10 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bcryptjs@^2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" + binary-extensions@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" @@ -2373,6 +2415,25 @@ decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" +deep-equal@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.3.tgz#cad1c15277ad78a5c01c49c2dee0f54de8a6a7b0" + dependencies: + es-abstract "^1.17.5" + es-get-iterator "^1.1.0" + is-arguments "^1.0.4" + is-date-object "^1.0.2" + is-regex "^1.0.5" + isarray "^2.0.5" + object-is "^1.1.2" + object-keys "^1.1.1" + object.assign "^4.1.0" + regexp.prototype.flags "^1.3.0" + side-channel "^1.0.2" + which-boxed-primitive "^1.0.1" + which-collection "^1.0.1" + which-typed-array "^1.1.2" + deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -2542,6 +2603,51 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: string.prototype.trimleft "^2.1.1" string.prototype.trimright "^2.1.1" +es-abstract@^1.17.4: + version "1.17.6" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-regex "^1.1.0" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-abstract@^1.18.0-next.0: + version "1.18.0-next.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.0.tgz#b302834927e624d8e5837ed48224291f2c66e6fc" + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-negative-zero "^2.0.0" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-get-iterator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8" + dependencies: + es-abstract "^1.17.4" + has-symbols "^1.0.1" + is-arguments "^1.0.4" + is-map "^2.0.1" + is-set "^2.0.1" + is-string "^1.0.5" + isarray "^2.0.5" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -2843,9 +2949,10 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -flatpickr@^4.5.7: - version "4.6.3" - resolved "https://registry.yarnpkg.com/flatpickr/-/flatpickr-4.6.3.tgz#15a8b76b6e34e3a072861250503a5995b9d3bc60" +flatpickr@^4.5.2: + version "4.6.6" + resolved "https://registry.yarnpkg.com/flatpickr/-/flatpickr-4.6.6.tgz#34d2ad80adfa34254e62583a34264d472f1038d6" + integrity sha512-EZ48CJMttMg3maMhJoX+GvTuuEhX/RbA1YeuI19attP3pwBdbYy6+yqAEVm0o0hSBFYBiLbVxscLW6gJXq6H3A== fn-name@~3.0.0: version "3.0.0" @@ -2855,10 +2962,9 @@ for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" -foreach@~2.0.1: +foreach@^2.0.5, foreach@~2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= forever-agent@~0.6.1: version "0.6.1" @@ -3229,16 +3335,28 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" +is-bigint@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" dependencies: binary-extensions "^2.0.0" +is-boolean-object@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -3247,6 +3365,10 @@ is-callable@^1.1.4, is-callable@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" +is-callable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -3265,7 +3387,7 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-date-object@^1.0.1: +is-date-object@^1.0.1, is-date-object@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" @@ -3330,10 +3452,22 @@ is-installed-globally@^0.3.2: global-dirs "^2.0.1" is-path-inside "^3.0.1" +is-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" +is-negative-zero@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" + +is-number-object@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3390,6 +3524,16 @@ is-regex@^1.0.5: dependencies: has "^1.0.3" +is-regex@^1.1.0, is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + dependencies: + has-symbols "^1.0.1" + +is-set@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -3398,16 +3542,37 @@ is-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" +is-string@^1.0.4, is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" dependencies: has-symbols "^1.0.1" +is-typed-array@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.3.tgz#a4ff5a5e672e1a55f99c7f54e59597af5c1df04d" + dependencies: + available-typed-arrays "^1.0.0" + es-abstract "^1.17.4" + foreach "^2.0.5" + has-symbols "^1.0.1" + is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + +is-weakset@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83" + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -3428,6 +3593,10 @@ isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + isbuffer@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/isbuffer/-/isbuffer-0.0.0.tgz#38c146d9df528b8bf9b0701c3d43cf12df3fc39b" @@ -4241,6 +4410,10 @@ ltgt@^2.1.2: version "2.2.1" resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" +lunr@^2.3.5: + version "2.3.9" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" + magic-string@^0.22.5: version "0.22.5" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e" @@ -4538,6 +4711,17 @@ object-inspect@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" +object-inspect@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + +object-is@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6" + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5048,6 +5232,17 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexp.prototype.flags@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +regexparam@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-1.3.0.tgz#2fe42c93e32a40eff6235d635e0ffa344b92965f" + regexpu-core@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938" @@ -5233,6 +5428,14 @@ rollup-plugin-copy@^3.0.0: globby "10.0.1" is-plain-object "^3.0.0" +rollup-plugin-css-only@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-css-only/-/rollup-plugin-css-only-2.1.0.tgz#b9e8505eb01c5257b5eab65bd51eec9050bed9a3" + integrity sha512-pfdcqAWEmRMFy+ABXAQPA/DKyPqLuBTOf+lWSOgtrVs1v/q7DSXzYa9QZg4myd8/1F7NHcdvPkWnfWqMxq9vrw== + dependencies: + "@rollup/pluginutils" "^3.0.0" + fs-extra "^9.0.0" + rollup-plugin-livereload@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/rollup-plugin-livereload/-/rollup-plugin-livereload-1.3.0.tgz#8da90df13df6502b9d982997d6ac871092f15fdd" @@ -5438,13 +5641,19 @@ shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" -shortid@^2.2.15: +shortid@^2.2.15, shortid@^2.2.8: version "2.2.15" resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.15.tgz#2b902eaa93a69b11120373cd42a1f1fe4437c122" - integrity sha512-5EaCy2mx2Jgc/Fdn9uuDuNIIfWBpzY4XIlhoqtXF6qsf+/+SGZ+FxDdX/ZsMZiWupIWNqAEmiNY4RC+LSmCeOw== dependencies: nanoid "^2.1.0" +side-channel@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.3.tgz#cdc46b057550bbab63706210838df5d4c19519c3" + dependencies: + es-abstract "^1.18.0-next.0" + object-inspect "^1.8.0" + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -5671,7 +5880,7 @@ string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string.prototype.trimend@^1.0.0: +string.prototype.trimend@^1.0.0, string.prototype.trimend@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" dependencies: @@ -5694,7 +5903,7 @@ string.prototype.trimright@^2.1.1: es-abstract "^1.17.5" string.prototype.trimend "^1.0.0" -string.prototype.trimstart@^1.0.0: +string.prototype.trimstart@^1.0.0, string.prototype.trimstart@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" dependencies: @@ -5781,6 +5990,13 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +svelte-flatpickr@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/svelte-flatpickr/-/svelte-flatpickr-2.4.0.tgz#190871fc3305956c8c8fd3601cd036b8ac71ef49" + integrity sha512-UUC5Te+b0qi4POg7VDwfGh0m5W3Hf64OwkfOTj6FEe/dYZN4cBzpQ82EuuQl0CTbbBAsMkcjJcixV1d2V6EHCQ== + dependencies: + flatpickr "^4.5.2" + svelte-jester@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/svelte-jester/-/svelte-jester-1.0.6.tgz#a95da31acdcdd339468745c05c63fd0b52acff93" @@ -5790,7 +6006,6 @@ svelte-jester@^1.0.6: svelte-loading-spinners@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/svelte-loading-spinners/-/svelte-loading-spinners-0.1.1.tgz#a35a811b7db0389ec2a5de6904c718c58c36e1f9" - integrity sha512-or4zs10VOdczOJo3u25IINXQOkZbLNAxMrXK0PRbzVoJtPQq/QZPNxI32383bpe+soYcEKmESbmW+JlW3MbUKQ== svelte-portal@^0.1.0: version "0.1.0" @@ -5804,6 +6019,10 @@ svelte@3.23.x: version "3.23.0" resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.23.0.tgz#bbcd6887cf588c24a975b14467455abfff9acd3f" +svelte@^3.9.2: + version "3.24.1" + resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.24.1.tgz#aca364937dd1df27fe131e2a4c234acb6061db4b" + symbol-observable@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -6135,10 +6354,40 @@ whatwg-url@^8.0.0: tr46 "^2.0.2" webidl-conversions "^5.0.0" +which-boxed-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1" + dependencies: + is-bigint "^1.0.0" + is-boolean-object "^1.0.0" + is-number-object "^1.0.3" + is-string "^1.0.4" + is-symbol "^1.0.2" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" +which-typed-array@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2" + dependencies: + available-typed-arrays "^1.0.2" + es-abstract "^1.17.5" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" diff --git a/packages/server/src/api/controllers/record.js b/packages/server/src/api/controllers/record.js index 4685079f68..df3c93687a 100644 --- a/packages/server/src/api/controllers/record.js +++ b/packages/server/src/api/controllers/record.js @@ -2,6 +2,16 @@ const CouchDB = require("../../db") const validateJs = require("validate.js") const newid = require("../../db/newid") +validateJs.extend(validateJs.validators.datetime, { + parse: function(value) { + return new Date(value).getTime() + }, + // Input is a unix timestamp + format: function(value) { + return new Date(value).toISOString() + }, +}) + exports.save = async function(ctx) { const db = new CouchDB(ctx.user.instanceId) const record = ctx.request.body diff --git a/packages/server/src/api/controllers/workflow/actions/CREATE_USER.js b/packages/server/src/api/controllers/workflow/actions/CREATE_USER.js deleted file mode 100644 index be78275133..0000000000 --- a/packages/server/src/api/controllers/workflow/actions/CREATE_USER.js +++ /dev/null @@ -1,24 +0,0 @@ -const userController = require("../../user") - -module.exports = async function createUser({ args, instanceId }) { - const ctx = { - params: { - instanceId, - }, - request: { - body: args.user, - }, - } - - try { - const response = await userController.create(ctx) - return { - user: response, - } - } catch (err) { - console.error(err) - return { - user: null, - } - } -} diff --git a/packages/server/src/api/controllers/workflow/actions/DELAY.js b/packages/server/src/api/controllers/workflow/actions/DELAY.js deleted file mode 100644 index 97c70db8ae..0000000000 --- a/packages/server/src/api/controllers/workflow/actions/DELAY.js +++ /dev/null @@ -1,5 +0,0 @@ -const wait = ms => new Promise(resolve => setTimeout(resolve, ms)) - -module.exports = async function delay({ args }) { - await wait(args.time) -} diff --git a/packages/server/src/api/controllers/workflow/actions/FILTER.js b/packages/server/src/api/controllers/workflow/actions/FILTER.js deleted file mode 100644 index 94b7d8de81..0000000000 --- a/packages/server/src/api/controllers/workflow/actions/FILTER.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = async function filter({ args }) { - const { field, condition, value } = args - switch (condition) { - case "equals": - if (field !== value) return - break - default: - return - } -} diff --git a/packages/server/src/api/controllers/workflow/actions/SAVE_RECORD.js b/packages/server/src/api/controllers/workflow/actions/SAVE_RECORD.js deleted file mode 100644 index 11bbb94f4d..0000000000 --- a/packages/server/src/api/controllers/workflow/actions/SAVE_RECORD.js +++ /dev/null @@ -1,29 +0,0 @@ -const recordController = require("../../record") - -module.exports = async function saveRecord({ args, context }) { - const { model, ...record } = args.record - - const ctx = { - params: { - instanceId: context.instanceId, - modelId: model._id, - }, - request: { - body: record, - }, - user: { instanceId: context.instanceId }, - } - - try { - await recordController.save(ctx) - return { - record: ctx.body, - } - } catch (err) { - console.error(err) - return { - record: null, - error: err.message, - } - } -} diff --git a/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js b/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js deleted file mode 100644 index 39cb7a8432..0000000000 --- a/packages/server/src/api/controllers/workflow/actions/SEND_EMAIL.js +++ /dev/null @@ -1,26 +0,0 @@ -const sgMail = require("@sendgrid/mail") - -sgMail.setApiKey(process.env.SENDGRID_API_KEY) - -module.exports = async function sendEmail({ args }) { - const msg = { - to: args.to, - from: args.from, - subject: args.subject, - text: args.text, - } - - try { - await sgMail.send(msg) - return { - success: true, - ...args, - } - } catch (err) { - console.error(err) - return { - success: false, - error: err.message, - } - } -} diff --git a/packages/server/src/api/controllers/workflow/blockDefinitions.js b/packages/server/src/api/controllers/workflow/blockDefinitions.js index 5df44e307d..df1a7ffe9c 100644 --- a/packages/server/src/api/controllers/workflow/blockDefinitions.js +++ b/packages/server/src/api/controllers/workflow/blockDefinitions.js @@ -4,61 +4,76 @@ const ACTION = { tagline: "Save a {{record.model.name}} record", icon: "ri-save-3-fill", description: "Save a record to your database.", - environment: "SERVER", params: { record: "record", }, args: { record: {}, }, + type: "ACTION", }, DELETE_RECORD: { description: "Delete a record from your database.", icon: "ri-delete-bin-line", name: "Delete Record", tagline: "Delete a {{record.model.name}} record", - environment: "SERVER", params: { record: "record", }, args: { record: {}, }, + type: "ACTION", }, - // FIND_RECORD: { - // description: "Find a record in your database.", - // tagline: "Find a {{record.model.name}} record", - // icon: "ri-search-line", - // name: "Find Record", - // environment: "SERVER", - // params: { - // record: "string", - // }, - // }, CREATE_USER: { description: "Create a new user.", tagline: "Create user {{username}}", icon: "ri-user-add-fill", name: "Create User", - environment: "SERVER", params: { username: "string", password: "password", accessLevelId: "accessLevel", }, + type: "ACTION", }, SEND_EMAIL: { description: "Send an email.", tagline: "Send email to {{to}}", icon: "ri-mail-open-fill", name: "Send Email", - environment: "SERVER", params: { to: "string", from: "string", subject: "longText", text: "longText", }, + type: "ACTION", + }, +} + +const LOGIC = { + FILTER: { + name: "Filter", + tagline: "{{field}} {{condition}} {{value}}", + icon: "ri-git-branch-line", + description: "Filter any workflows which do not meet certain conditions.", + params: { + filter: "string", + condition: ["equals"], + value: "string", + }, + type: "LOGIC", + }, + DELAY: { + name: "Delay", + icon: "ri-time-fill", + tagline: "Delay for {{time}} milliseconds", + description: "Delay the workflow until an amount of time has passed.", + params: { + time: "number", + }, + type: "LOGIC", }, } @@ -69,10 +84,10 @@ const TRIGGER = { icon: "ri-save-line", tagline: "Record is added to {{model.name}}", description: "Save a record to your database.", - environment: "SERVER", params: { model: "model", }, + type: "TRIGGER", }, RECORD_DELETED: { name: "Record Deleted", @@ -80,40 +95,17 @@ const TRIGGER = { icon: "ri-delete-bin-line", tagline: "Record is deleted from {{model.name}}", description: "Fired when a record is deleted from your database.", - environment: "SERVER", params: { model: "model", }, + type: "TRIGGER", }, } -const LOGIC = { - FILTER: { - name: "Filter", - tagline: "{{field}} {{condition}} {{value}}", - icon: "ri-git-branch-line", - description: "Filter any workflows which do not meet certain conditions.", - environment: "CLIENT", - params: { - filter: "string", - condition: ["equals"], - value: "string", - }, - }, - DELAY: { - name: "Delay", - icon: "ri-time-fill", - tagline: "Delay for {{time}} milliseconds", - description: "Delay the workflow until an amount of time has passed.", - environment: "CLIENT", - params: { - time: "number", - }, - }, -} - +// This contains the definitions for the steps and triggers that make up a workflow, a workflow comprises +// of many steps and a single trigger module.exports = { ACTION, - TRIGGER, LOGIC, + TRIGGER, } diff --git a/packages/server/src/api/controllers/workflow/index.js b/packages/server/src/api/controllers/workflow/index.js index 3f489ceede..f22afe537c 100644 --- a/packages/server/src/api/controllers/workflow/index.js +++ b/packages/server/src/api/controllers/workflow/index.js @@ -1,6 +1,7 @@ const CouchDB = require("../../../db") const newid = require("../../../db/newid") const blockDefinitions = require("./blockDefinitions") +const triggers = require("../../../workflows/triggers") /************************* * * @@ -60,24 +61,11 @@ exports.find = async function(ctx) { ctx.body = await db.get(ctx.params.id) } -exports.fetchActionScript = async function(ctx) { - ctx.body = require(`./actions/${ctx.action}`) -} - exports.destroy = async function(ctx) { const db = new CouchDB(ctx.user.instanceId) ctx.body = await db.remove(ctx.params.id, ctx.params.rev) } -exports.executeAction = async function(ctx) { - const { args, action } = ctx.request.body - const workflowAction = require(`./actions/${action}`) - ctx.body = await workflowAction({ - args, - instanceId: ctx.user.instanceId, - }) -} - exports.getActionList = async function(ctx) { ctx.body = blockDefinitions.ACTION } @@ -87,7 +75,7 @@ exports.getTriggerList = async function(ctx) { } exports.getLogicList = async function(ctx) { - ctx.body = blockDefinitions.ACTION + ctx.body = blockDefinitions.LOGIC } /********************* @@ -97,4 +85,7 @@ exports.getLogicList = async function(ctx) { *********************/ exports.trigger = async function(ctx) { + const db = new CouchDB(ctx.user.instanceId) + let workflow = await db.get(ctx.params.id) + await triggers.externalTrigger(workflow, ctx.request.body) } diff --git a/packages/server/src/api/routes/tests/workflow.spec.js b/packages/server/src/api/routes/tests/workflow.spec.js index f8b38c53ec..5991d06d9d 100644 --- a/packages/server/src/api/routes/tests/workflow.spec.js +++ b/packages/server/src/api/routes/tests/workflow.spec.js @@ -23,7 +23,7 @@ const TEST_WORKFLOW = { ], next: { - actionId: "abc123", + stepId: "abc123", type: "SERVER", conditions: { } diff --git a/packages/server/src/api/routes/workflow.js b/packages/server/src/api/routes/workflow.js index a2191e44d4..de1d49d191 100644 --- a/packages/server/src/api/routes/workflow.js +++ b/packages/server/src/api/routes/workflow.js @@ -11,10 +11,9 @@ router .get("/api/workflows/logic/list", authorized(BUILDER), controller.getLogicList) .get("/api/workflows", authorized(BUILDER), controller.fetch) .get("/api/workflows/:id", authorized(BUILDER), controller.find) - .get("/api/workflows/:id/:action", authorized(BUILDER), controller.fetchActionScript) .put("/api/workflows", authorized(BUILDER), controller.update) .post("/api/workflows", authorized(BUILDER), controller.create) - .post("/api/workflows/trigger", controller.trigger) + .post("/api/workflows/:id/trigger", controller.trigger) .delete("/api/workflows/:id/:rev", authorized(BUILDER), controller.destroy) module.exports = router diff --git a/packages/server/src/app.js b/packages/server/src/app.js index ee6fb3172d..5f720ad865 100644 --- a/packages/server/src/app.js +++ b/packages/server/src/app.js @@ -6,6 +6,7 @@ const http = require("http") const api = require("./api") const env = require("./environment") const eventEmitter = require("./events") +const workflows = require("./workflows/index") const Sentry = require("@sentry/node") const app = new Koa() @@ -49,4 +50,5 @@ process.on("SIGINT", () => process.exit(1)) module.exports = server.listen(env.PORT || 4001, () => { console.log(`Budibase running on ${JSON.stringify(server.address())}`) + workflows.init() }) diff --git a/packages/server/src/events/index.js b/packages/server/src/events/index.js index 0ad4e986bd..f627532e09 100644 --- a/packages/server/src/events/index.js +++ b/packages/server/src/events/index.js @@ -1,33 +1,11 @@ const EventEmitter = require("events").EventEmitter -const CouchDB = require("../db") -const { Orchestrator, serverStrategy } = require("./workflow") + +/** + * keeping event emitter in one central location as it might be used for things other than + * workflows (what it was for originally) - having a central emitter will be useful in the + * future. + */ const emitter = new EventEmitter() -async function executeRelevantWorkflows(event, eventType) { - const db = new CouchDB(event.instanceId) - const workflowsToTrigger = await db.query("database/by_workflow_trigger", { - key: [eventType], - include_docs: true, - }) - - const workflows = workflowsToTrigger.rows.map(wf => wf.doc) - - // Create orchestrator - const workflowOrchestrator = new Orchestrator() - workflowOrchestrator.strategy = serverStrategy - - for (let workflow of workflows) { - workflowOrchestrator.execute(workflow, event) - } -} - -emitter.on("record:save", async function(event) { - await executeRelevantWorkflows(event, "record:save") -}) - -emitter.on("record:delete", async function(event) { - await executeRelevantWorkflows(event, "record:delete") -}) - module.exports = emitter diff --git a/packages/server/src/events/workflow.js b/packages/server/src/events/workflow.js deleted file mode 100644 index d76f8e0e24..0000000000 --- a/packages/server/src/events/workflow.js +++ /dev/null @@ -1,52 +0,0 @@ -const mustache = require("mustache") - -/** - * The workflow orchestrator is a class responsible for executing workflows. - * It relies on the strategy pattern, which allows composable behaviour to be - * passed into its execute() function. This allows custom execution behaviour based - * on where the orchestrator is run. - * - */ -exports.Orchestrator = class Orchestrator { - set strategy(strategy) { - this._strategy = strategy() - } - - async execute(workflow, context) { - if (workflow.live) { - this._strategy.run(workflow.definition, context) - } - } -} - -exports.serverStrategy = () => ({ - context: {}, - bindContextArgs: function(args) { - const mappedArgs = { ...args } - - // bind the workflow action args to the workflow context, if required - for (let arg in args) { - const argValue = args[arg] - // We don't want to render mustache templates on non-strings - if (typeof argValue !== "string") continue - - mappedArgs[arg] = mustache.render(argValue, { context: this.context }) - } - - return mappedArgs - }, - run: async function(workflow, context) { - for (let block of workflow.steps) { - const action = require(`../api/controllers/workflow/actions/${block.actionId}`) - const response = await action({ - args: this.bindContextArgs(block.args), - context, - }) - - this.context = { - ...this.context, - [block.id]: response, - } - } - }, -}) diff --git a/packages/server/src/workflows/actions.js b/packages/server/src/workflows/actions.js new file mode 100644 index 0000000000..b20465b24e --- /dev/null +++ b/packages/server/src/workflows/actions.js @@ -0,0 +1,86 @@ +const userController = require("../api/controllers/user") +const recordController = require("../api/controllers/record") +const sgMail = require("@sendgrid/mail") + +sgMail.setApiKey(process.env.SENDGRID_API_KEY) + +let BUILTIN_ACTIONS = { + CREATE_USER: async function({ args, instanceId }) { + const ctx = { + params: { + instanceId, + }, + request: { + body: args.user, + }, + } + + try { + const response = await userController.create(ctx) + return { + user: response, + } + } catch (err) { + console.error(err) + return { + user: null, + } + } + }, + SAVE_RECORD: async function({ args, context }) { + const { model, ...record } = args.record + + const ctx = { + params: { + instanceId: context.instanceId, + modelId: model._id, + }, + request: { + body: record, + }, + user: { instanceId: context.instanceId }, + } + + try { + await recordController.save(ctx) + return { + record: ctx.body, + } + } catch (err) { + console.error(err) + return { + record: null, + error: err.message, + } + } + }, + SEND_EMAIL: async function({ args }) { + const msg = { + to: args.to, + from: args.from, + subject: args.subject, + text: args.text, + } + + try { + await sgMail.send(msg) + return { + success: true, + ...args, + } + } catch (err) { + console.error(err) + return { + success: false, + error: err.message, + } + } + }, +} + +module.exports.getAction = async function(actionName) { + if (BUILTIN_ACTIONS[actionName] != null) { + return BUILTIN_ACTIONS[actionName] + } + // TODO: load async actions here +} diff --git a/packages/server/src/workflows/index.js b/packages/server/src/workflows/index.js new file mode 100644 index 0000000000..9459f325b2 --- /dev/null +++ b/packages/server/src/workflows/index.js @@ -0,0 +1,67 @@ +const mustache = require("mustache") +const actions = require("./actions") +const logic = require("./logic") +const triggers = require("./triggers") + +/** + * The workflow orchestrator is a class responsible for executing workflows. + * It relies on the strategy pattern, which allows composable behaviour to be + * passed into its execute() function. This allows custom execution behaviour based + * on where the orchestrator is run. + * + */ +class Orchestrator { + constructor(workflow) { + this._context = {} + this._workflow = workflow + } + + async getStep(type, stepId) { + let step = null + if (type === "ACTION") { + step = await actions.getAction(stepId) + } else if (type === "LOGIC") { + step = logic.getLogic(stepId) + } + if (step == null) { + throw `Cannot find workflow step by name ${stepId}` + } + return step + } + + async execute(context) { + let workflow = this._workflow + if (!workflow.live) { + return + } + for (let block of workflow.steps) { + let step = this.getStep(block.type, block.stepId) + let args = { ...block.args } + // bind the workflow action args to the workflow context, if required + for (let arg of Object.keys(args)) { + const argValue = args[arg] + // We don't want to render mustache templates on non-strings + if (typeof argValue !== "string") continue + + args[arg] = mustache.render(argValue, { context: this._context }) + } + const response = await step({ + args, + context, + }) + + this._context = { + ...this._context, + [block.id]: response, + } + } + } +} + +module.exports.init = function() { + triggers.workflowQueue.process(async job => { + // Create orchestrator for each individual workflow (their own context) + const workflowOrchestrator = new Orchestrator(job.data.workflow) + await workflowOrchestrator.execute(job.data.event) + }) +} diff --git a/packages/server/src/workflows/logic.js b/packages/server/src/workflows/logic.js new file mode 100644 index 0000000000..a1d71bc0f2 --- /dev/null +++ b/packages/server/src/workflows/logic.js @@ -0,0 +1,24 @@ +const wait = ms => new Promise(resolve => setTimeout(resolve, ms)) + +let LOGIC = { + DELAY: async function delay({ args }) { + await wait(args.time) + }, + + FILTER: async function filter({ args }) { + const { field, condition, value } = args + switch (condition) { + case "equals": + if (field !== value) return + break + default: + return + } + }, +} + +module.exports.getLogic = function(logicName) { + if (LOGIC[logicName] != null) { + return LOGIC[logicName] + } +} diff --git a/packages/server/src/workflows/queue/inMemoryQueue.js b/packages/server/src/workflows/queue/inMemoryQueue.js new file mode 100644 index 0000000000..927eeb60b6 --- /dev/null +++ b/packages/server/src/workflows/queue/inMemoryQueue.js @@ -0,0 +1,44 @@ +let events = require("events") + +// Bull works with a Job wrapper around all messages that contains a lot more information about +// the state of the message, implement this for the sake of maintaining API consistency +function newJob(queue, message) { + return { + timestamp: Date.now(), + queue: queue, + data: message, + } +} + +// designed to replicate Bull (https://github.com/OptimalBits/bull) in memory as a sort of mock +class InMemoryQueue { + // opts is not used by this as there is no real use case when in memory, but is the same API as Bull + constructor(name, opts) { + this._name = name + this._opts = opts + this._messages = [] + this._emitter = new events.EventEmitter() + } + + // same API as bull, provide a callback and it will respond when messages are available + process(func) { + this._emitter.on("message", async () => { + if (this._messages.length <= 0) { + return + } + let msg = this._messages.shift() + let resp = func(msg) + if (resp.then != null) { + await resp + } + }) + } + + // simply puts a message to the queue and emits to the queue for processing + add(msg) { + this._messages.push(newJob(this._name, msg)) + this._emitter.emit("message") + } +} + +module.exports = InMemoryQueue diff --git a/packages/server/src/workflows/triggers.js b/packages/server/src/workflows/triggers.js new file mode 100644 index 0000000000..c72afca301 --- /dev/null +++ b/packages/server/src/workflows/triggers.js @@ -0,0 +1,32 @@ +const CouchDB = require("../db") +const emitter = require("../events/index") +const InMemoryQueue = require("./queue/inMemoryQueue") + +let workflowQueue = new InMemoryQueue() + +async function queueRelevantWorkflows(event, eventType) { + const db = new CouchDB(event.instanceId) + const workflowsToTrigger = await db.query("database/by_workflow_trigger", { + key: [eventType], + include_docs: true, + }) + + const workflows = workflowsToTrigger.rows.map(wf => wf.doc) + for (let workflow of workflows) { + workflowQueue.add({ workflow, event }) + } +} + +emitter.on("record:save", async function(event) { + await queueRelevantWorkflows(event, "record:save") +}) + +emitter.on("record:delete", async function(event) { + await queueRelevantWorkflows(event, "record:delete") +}) + +module.exports.externalTrigger = async function(workflow, params) { + workflowQueue.add({ workflow, event: params }) +} + +module.exports.workflowQueue = workflowQueue diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 3d374707b7..77bbbecb62 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -172,20 +172,6 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@budibase/client@^0.1.19": - version "0.1.19" - resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.1.19.tgz#3906781423ab4626118c981657ecf7a4578c547c" - dependencies: - "@nx-js/compiler-util" "^2.0.0" - bcryptjs "^2.4.3" - deep-equal "^2.0.1" - lodash "^4.17.15" - lunr "^2.3.5" - mustache "^4.0.1" - regexparam "^1.3.0" - shortid "^2.2.8" - svelte "^3.9.2" - "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -368,10 +354,6 @@ path-to-regexp "1.x" urijs "^1.19.2" -"@nx-js/compiler-util@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@nx-js/compiler-util/-/compiler-util-2.0.0.tgz#c74c12165fa2f017a292bb79af007e8fce0af297" - "@sendgrid/client@^7.1.1": version "7.1.1" resolved "https://registry.yarnpkg.com/@sendgrid/client/-/client-7.1.1.tgz#09a25e58ac7e5321d66807e7110ff0fb61bb534f" @@ -829,10 +811,6 @@ array-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" -array-filter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" - array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -891,12 +869,6 @@ atomic-sleep@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" -available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" - dependencies: - array-filter "^1.0.0" - aws-sdk@^2.706.0: version "2.706.0" resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.706.0.tgz#09f65e9a91ecac5a635daf934082abae30eca953" @@ -1565,25 +1537,6 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" -deep-equal@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.3.tgz#cad1c15277ad78a5c01c49c2dee0f54de8a6a7b0" - dependencies: - es-abstract "^1.17.5" - es-get-iterator "^1.1.0" - is-arguments "^1.0.4" - is-date-object "^1.0.2" - is-regex "^1.0.5" - isarray "^2.0.5" - object-is "^1.1.2" - object-keys "^1.1.1" - object.assign "^4.1.0" - regexp.prototype.flags "^1.3.0" - side-channel "^1.0.2" - which-boxed-primitive "^1.0.1" - which-collection "^1.0.1" - which-typed-array "^1.1.2" - deep-equal@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" @@ -1910,51 +1863,6 @@ es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: string.prototype.trimleft "^2.1.1" string.prototype.trimright "^2.1.1" -es-abstract@^1.17.4: - version "1.17.6" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.2.0" - is-regex "^1.1.0" - object-inspect "^1.7.0" - object-keys "^1.1.1" - object.assign "^4.1.0" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" - -es-abstract@^1.18.0-next.0: - version "1.18.0-next.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.0.tgz#b302834927e624d8e5837ed48224291f2c66e6fc" - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.2.0" - is-negative-zero "^2.0.0" - is-regex "^1.1.1" - object-inspect "^1.8.0" - object-keys "^1.1.1" - object.assign "^4.1.0" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" - -es-get-iterator@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8" - dependencies: - es-abstract "^1.17.4" - has-symbols "^1.0.1" - is-arguments "^1.0.4" - is-map "^2.0.1" - is-set "^2.0.1" - is-string "^1.0.5" - isarray "^2.0.5" - es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -2845,28 +2753,16 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-arguments@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" -is-bigint@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4" - is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" dependencies: binary-extensions "^2.0.0" -is-boolean-object@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" - is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -2875,10 +2771,6 @@ is-callable@^1.1.4, is-callable@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" -is-callable@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" - is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -2901,7 +2793,7 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-date-object@^1.0.1, is-date-object@^1.0.2: +is-date-object@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" @@ -2964,22 +2856,10 @@ is-installed-globally@^0.3.1: global-dirs "^2.0.1" is-path-inside "^3.0.1" -is-map@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" - -is-negative-zero@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" - is-npm@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" -is-number-object@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" - is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3010,24 +2890,10 @@ is-regex@^1.0.5: dependencies: has "^1.0.3" -is-regex@^1.1.0, is-regex@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" - dependencies: - has-symbols "^1.0.1" - -is-set@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" - is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" -is-string@^1.0.4, is-string@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" - is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -3042,27 +2908,10 @@ is-type-of@^1.0.0: is-class-hotfix "~0.0.6" isstream "~0.1.2" -is-typed-array@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.3.tgz#a4ff5a5e672e1a55f99c7f54e59597af5c1df04d" - dependencies: - available-typed-arrays "^1.0.0" - es-abstract "^1.17.4" - foreach "^2.0.5" - has-symbols "^1.0.1" - is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" -is-weakmap@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" - -is-weakset@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83" - is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -3083,10 +2932,6 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - isbinaryfile@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz#edcb62b224e2b4710830b67498c8e4e5a4d2610b" @@ -4026,10 +3871,6 @@ ltgt@~2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.1.3.tgz#10851a06d9964b971178441c23c9e52698eece34" -lunr@^2.3.5: - version "2.3.9" - resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" - make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -4194,10 +4035,6 @@ nan@^2.12.1: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" -nanoid@^2.1.0: - version "2.1.11" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" - nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -4345,17 +4182,6 @@ object-inspect@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" -object-inspect@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" - -object-is@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6" - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5002,17 +4828,6 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - -regexparam@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-1.3.0.tgz#2fe42c93e32a40eff6235d635e0ffa344b92965f" - regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" @@ -5308,19 +5123,6 @@ shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" -shortid@^2.2.8: - version "2.2.15" - resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.15.tgz#2b902eaa93a69b11120373cd42a1f1fe4437c122" - dependencies: - nanoid "^2.1.0" - -side-channel@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.3.tgz#cdc46b057550bbab63706210838df5d4c19519c3" - dependencies: - es-abstract "^1.18.0-next.0" - object-inspect "^1.8.0" - signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" @@ -5537,7 +5339,7 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string.prototype.trimend@^1.0.0, string.prototype.trimend@^1.0.1: +string.prototype.trimend@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" dependencies: @@ -5560,7 +5362,7 @@ string.prototype.trimright@^2.1.1: es-abstract "^1.17.5" string.prototype.trimend "^1.0.0" -string.prototype.trimstart@^1.0.0, string.prototype.trimstart@^1.0.1: +string.prototype.trimstart@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" dependencies: @@ -5678,10 +5480,6 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -svelte@^3.9.2: - version "3.24.1" - resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.24.1.tgz#aca364937dd1df27fe131e2a4c234acb6061db4b" - symbol-tree@^3.2.2: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -6114,40 +5912,10 @@ whatwg-url@^7.0.0: tr46 "^1.0.1" webidl-conversions "^4.0.2" -which-boxed-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1" - dependencies: - is-bigint "^1.0.0" - is-boolean-object "^1.0.0" - is-number-object "^1.0.3" - is-string "^1.0.4" - is-symbol "^1.0.2" - -which-collection@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" - dependencies: - is-map "^2.0.1" - is-set "^2.0.1" - is-weakmap "^2.0.1" - is-weakset "^2.0.1" - which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" -which-typed-array@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2" - dependencies: - available-typed-arrays "^1.0.2" - es-abstract "^1.17.5" - foreach "^2.0.5" - function-bind "^1.1.1" - has-symbols "^1.0.1" - is-typed-array "^1.1.3" - which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" diff --git a/packages/standard-components/components.json b/packages/standard-components/components.json index dff7fe4434..8c41618aee 100644 --- a/packages/standard-components/components.json +++ b/packages/standard-components/components.json @@ -620,6 +620,13 @@ "width": "number" } }, + "datepicker": { + "description": "Date Picker", + "bindable": "value", + "props": { + "placeholder": "string" + } + }, "datachart": { "description": "shiny chart", "data": true, diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index 1c2467cba0..1325a3ad32 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -36,11 +36,12 @@ "gitHead": "284cceb9b703c38566c6e6363c022f79a08d5691", "dependencies": { "@beyonk/svelte-googlemaps": "^2.2.0", - "@budibase/bbui": "^1.29.3", + "@budibase/bbui": "^1.32.0", "britecharts": "^2.16.1", "d3-selection": "^1.4.2", "fast-sort": "^2.2.0", "fusioncharts": "^3.15.1-sr.1", + "svelte-flatpickr": "^2.4.0", "svelte-fusioncharts": "^1.0.0" } } diff --git a/packages/standard-components/src/Chart/index.js b/packages/standard-components/src/Chart/index.js index baf1d17488..a94abf8ac8 100644 --- a/packages/standard-components/src/Chart/index.js +++ b/packages/standard-components/src/Chart/index.js @@ -1,4 +1,5 @@ import "britecharts/dist/css/britecharts.min.css" + export { default as donut } from "./Donut.svelte" export { default as bar } from "./Bar.svelte" export { default as line } from "./Line.svelte" diff --git a/packages/standard-components/src/DatePicker.svelte b/packages/standard-components/src/DatePicker.svelte new file mode 100644 index 0000000000..30b07e6d8f --- /dev/null +++ b/packages/standard-components/src/DatePicker.svelte @@ -0,0 +1,18 @@ + + + diff --git a/packages/standard-components/src/index.js b/packages/standard-components/src/index.js index 598f372988..91498002c0 100644 --- a/packages/standard-components/src/index.js +++ b/packages/standard-components/src/index.js @@ -26,4 +26,5 @@ export { default as stackedlist } from "./StackedList.svelte" export { default as card } from "./Card.svelte" export { default as cardhorizontal } from "./CardHorizontal.svelte" export { default as recorddetail } from "./RecordDetail.svelte" +export { default as datepicker } from "./DatePicker.svelte" export * from "./Chart"