From 72de4dcab497d77f3d2b6df8771d8b6fa7b13d2b Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 6 Oct 2020 19:13:41 +0100 Subject: [PATCH 01/38] First work towards implementing Dynamo usage in the server when running in the cloud; this is for tracking usage against API keys. --- packages/server/package.json | 2 +- packages/server/src/api/controllers/auth.js | 6 + packages/server/src/api/controllers/model.js | 14 +- packages/server/src/api/controllers/record.js | 5 +- packages/server/src/api/routes/model.js | 4 +- packages/server/src/api/routes/record.js | 3 + .../src/api/routes/tests/couchTestUtils.js | 3 + packages/server/src/db/dynamoClient.js | 108 ++++++++ packages/server/src/environment.js | 3 + .../server/src/middleware/authenticated.js | 1 + packages/server/src/middleware/usageQuota.js | 77 ++++++ packages/server/yarn.lock | 256 +++++++++++++++++- 12 files changed, 461 insertions(+), 21 deletions(-) create mode 100644 packages/server/src/db/dynamoClient.js create mode 100644 packages/server/src/middleware/usageQuota.js diff --git a/packages/server/package.json b/packages/server/package.json index 1875e44285..6905685324 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -46,7 +46,7 @@ "@koa/router": "^8.0.0", "@sendgrid/mail": "^7.1.1", "@sentry/node": "^5.19.2", - "aws-sdk": "^2.706.0", + "aws-sdk": "^2.767.0", "bcryptjs": "^2.4.3", "chmodr": "^1.2.0", "dotenv": "^8.2.0", diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js index 828a88bb9b..b7a7c116b9 100644 --- a/packages/server/src/api/controllers/auth.js +++ b/packages/server/src/api/controllers/auth.js @@ -2,6 +2,8 @@ const jwt = require("jsonwebtoken") const CouchDB = require("../../db") const ClientDb = require("../../db/clientDb") const bcrypt = require("../../utilities/bcrypt") +const environment = require("../../environment") +const { apiKeyTable } = require("../../db/dynamoClient") const { generateUserID } = require("../../db/utils") exports.authenticate = async ctx => { @@ -51,6 +53,10 @@ exports.authenticate = async ctx => { appId: ctx.user.appId, instanceId, } + // if in cloud add the user api key + if (environment.CLOUD) { + payload.apiKey = await apiKeyTable.get({ primary: ctx.user.appId }) + } const token = jwt.sign(payload, ctx.config.jwtSecret, { expiresIn: "1 day", diff --git a/packages/server/src/api/controllers/model.js b/packages/server/src/api/controllers/model.js index 2593002d72..29bf2dd90f 100644 --- a/packages/server/src/api/controllers/model.js +++ b/packages/server/src/api/controllers/model.js @@ -30,17 +30,9 @@ exports.save = async function(ctx) { views: {}, ...ctx.request.body, } - // get the model in its previous state for differencing - let oldModel - let oldModelId = ctx.request.body._id - if (oldModelId) { - // if it errors then the oldModelId is invalid - can't diff it - try { - oldModel = await db.get(oldModelId) - } catch (err) { - oldModel = null - } - } + + // if the model obj had an _id then it will have been retrieved + const oldModel = ctx.preExisting // rename record fields when table column is renamed const { _rename } = modelToSave diff --git a/packages/server/src/api/controllers/record.js b/packages/server/src/api/controllers/record.js index 45d983eb2b..7422cc9fc8 100644 --- a/packages/server/src/api/controllers/record.js +++ b/packages/server/src/api/controllers/record.js @@ -70,6 +70,9 @@ exports.save = async function(ctx) { record._id = generateRecordID(record.modelId) } + // if the record obj had an _id then it will have been retrieved + const existingRecord = ctx.preExisting + const model = await db.get(record.modelId) const validateResult = await validate({ @@ -86,8 +89,6 @@ exports.save = async function(ctx) { return } - const existingRecord = record._rev && (await db.get(record._id)) - // make sure link records are up to date record = await linkRecords.updateLinks({ instanceId, diff --git a/packages/server/src/api/routes/model.js b/packages/server/src/api/routes/model.js index 00eb46d515..118870cc61 100644 --- a/packages/server/src/api/routes/model.js +++ b/packages/server/src/api/routes/model.js @@ -1,6 +1,7 @@ const Router = require("@koa/router") const modelController = require("../controllers/model") const authorized = require("../../middleware/authorized") +const usage = require("../../middleware/usageQuota") const { BUILDER, READ_MODEL } = require("../../utilities/accessLevels") const router = Router() @@ -12,10 +13,11 @@ router authorized(READ_MODEL, ctx => ctx.params.id), modelController.find ) - .post("/api/models", authorized(BUILDER), modelController.save) + .post("/api/models", authorized(BUILDER), usage, modelController.save) .delete( "/api/models/:modelId/:revId", authorized(BUILDER), + usage, modelController.destroy ) diff --git a/packages/server/src/api/routes/record.js b/packages/server/src/api/routes/record.js index ddc26a55af..b3b5e9ed56 100644 --- a/packages/server/src/api/routes/record.js +++ b/packages/server/src/api/routes/record.js @@ -1,6 +1,7 @@ const Router = require("@koa/router") const recordController = require("../controllers/record") const authorized = require("../../middleware/authorized") +const usage = require("../../middleware/usageQuota") const { READ_MODEL, WRITE_MODEL } = require("../../utilities/accessLevels") const router = Router() @@ -25,6 +26,7 @@ router .post( "/api/:modelId/records", authorized(WRITE_MODEL, ctx => ctx.params.modelId), + usage, recordController.save ) .patch( @@ -40,6 +42,7 @@ router .delete( "/api/:modelId/records/:recordId/:revId", authorized(WRITE_MODEL, ctx => ctx.params.modelId), + usage, recordController.destroy ) diff --git a/packages/server/src/api/routes/tests/couchTestUtils.js b/packages/server/src/api/routes/tests/couchTestUtils.js index a22a2a427f..c269c14e4d 100644 --- a/packages/server/src/api/routes/tests/couchTestUtils.js +++ b/packages/server/src/api/routes/tests/couchTestUtils.js @@ -40,6 +40,9 @@ exports.defaultHeaders = (appId, instanceId) => { } exports.createModel = async (request, appId, instanceId, model) => { + if (model != null && model._id) { + delete model._id + } model = model || { name: "TestModel", type: "model", diff --git a/packages/server/src/db/dynamoClient.js b/packages/server/src/db/dynamoClient.js new file mode 100644 index 0000000000..9f30b151da --- /dev/null +++ b/packages/server/src/db/dynamoClient.js @@ -0,0 +1,108 @@ +let _ = require("lodash") +let environment = require("../environment") + +const TableInfo = { + API_KEYS: { + name: "beta-api-key-table", + primary: "pk", + }, + USERS: { + name: "prod-budi-table", + primary: "pk", + sort: "sk", + }, +} + +let docClient = null + +class Table { + constructor(tableInfo) { + if (!tableInfo.name || !tableInfo.primary) { + throw "Table info must specify a name and a primary key" + } + this._name = tableInfo.name + this._primary = tableInfo.primary + this._sort = tableInfo.sort + } + + async get({ primary, sort, otherProps }) { + let params = { + TableName: this._name, + Key: { + [this._primary]: primary, + }, + } + if (this._sort && sort) { + params.Key[this._sort] = sort + } + if (otherProps) { + params = _.merge(params, otherProps) + } + let response = await docClient.get(params).promise() + return response.Item + } + + async update({ + primary, + sort, + expression, + condition, + names, + values, + otherProps, + }) { + let params = { + TableName: this._name, + Key: { + [this._primary]: primary, + }, + ExpressionAttributeNames: names, + ExpressionAttributeValues: values, + UpdateExpression: expression, + } + if (condition) { + params.ConditionExpression = condition + } + if (this._sort && sort) { + params.Key[this._sort] = sort + } + if (otherProps) { + params = _.merge(params, otherProps) + } + return docClient.update(params).promise() + } + + async put({ item, otherProps }) { + if ( + item[this._primary] == null || + (this._sort && item[this._sort] == null) + ) { + throw "Cannot put item without primary and sort key (if required)" + } + let params = { + TableName: this._name, + Item: item, + } + if (otherProps) { + params = _.merge(params, otherProps) + } + return docClient.put(params).promise() + } +} + +exports.init = () => { + if (!environment.CLOUD) { + return + } + let AWS = require("aws-sdk") + let docClientParams = { + correctClockSkew: true, + } + if (environment.DYNAMO_ENDPOINT) { + docClientParams.endpoint = environment.DYNAMO_ENDPOINT + } + docClient = new AWS.DynamoDB.DocumentClient(docClientParams) +} + +exports.apiKeyTable = new Table(TableInfo.API_KEYS) +exports.userTable = new Table(TableInfo.USERS) diff --git a/packages/server/src/environment.js b/packages/server/src/environment.js index bd08f191aa..f24d495397 100644 --- a/packages/server/src/environment.js +++ b/packages/server/src/environment.js @@ -11,4 +11,7 @@ module.exports = { AUTOMATION_BUCKET: process.env.AUTOMATION_BUCKET, BUDIBASE_ENVIRONMENT: process.env.BUDIBASE_ENVIRONMENT, SENDGRID_API_KEY: process.env.SENDGRID_API_KEY, + CLOUD: process.env.CLOUD, + DYNAMO_ENDPOINT: process.env.DYNAMO_ENDPOINT, + AWS_REGION: process.env.AWS_REGION, } diff --git a/packages/server/src/middleware/authenticated.js b/packages/server/src/middleware/authenticated.js index 53cb0b2c13..126d616e3b 100644 --- a/packages/server/src/middleware/authenticated.js +++ b/packages/server/src/middleware/authenticated.js @@ -20,6 +20,7 @@ module.exports = async (ctx, next) => { if (builderToken) { try { const jwtPayload = jwt.verify(builderToken, ctx.config.jwtSecret) + ctx.apiKey = jwtPayload.apiKey ctx.isAuthenticated = jwtPayload.accessLevelId === BUILDER_LEVEL_ID ctx.user = { ...jwtPayload, diff --git a/packages/server/src/middleware/usageQuota.js b/packages/server/src/middleware/usageQuota.js new file mode 100644 index 0000000000..1d119f25c8 --- /dev/null +++ b/packages/server/src/middleware/usageQuota.js @@ -0,0 +1,77 @@ +const CouchDB = require("../db") +const environment = require("../environment") +const { apiKeyTable } = require("../db/dynamoClient") + +// a normalised month in milliseconds +const QUOTA_RESET = 2592000000 + +// currently only counting new writes and deletes +const METHOD_MAP = { + POST: 1, + DELETE: -1, +} + +const DOMAIN_MAP = { + models: "model", + records: "record", +} + +function buildUpdateParams(key, property, usage) { + return { + primary: key, + condition: "#quota.#prop + :usage < #limits.model AND #quotaReset < :now", + expression: "ADD #quota.#prop :usage", + names: { + "#quota": "usageQuota", + "#prop": property, + "#limits": "limits", + "#quotaReset": "quotaReset", + }, + values: { + ":usage": usage, + ":now": Date.now(), + }, + } +} + +module.exports = async (ctx, next) => { + const db = new CouchDB(ctx.user.instanceId) + const usage = METHOD_MAP[ctx.req.method] + const domainParts = ctx.req.url.split("/") + const property = DOMAIN_MAP[domainParts[domainParts.length - 1]] + if (usage == null || property == null) { + return next() + } + // post request could be a save of a pre-existing entry + if (ctx.request.body && ctx.request.body._id) { + try { + ctx.preExisting = await db.get(ctx.request.body._id) + return next() + } catch (err) { + ctx.throw(404, `${ctx.request.body._id} does not exist`) + return + } + } + // don't try validate in builder + if (!environment.CLOUD) { + return next() + } + try { + await apiKeyTable.update(buildUpdateParams(ctx.apiKey, property, usage)) + } catch (err) { + if (err.code !== "ConditionalCheckFailedException") { + // get the API key so we can check it + let apiKey = await apiKeyTable.get({ primary: ctx.apiKey }) + // we have infact breached the reset period + if (apiKey && apiKey.quotaReset >= Date.now()) { + // update the quota reset period and reset the values for all properties + apiKey.quotaReset = Date.now() + QUOTA_RESET + for (let prop of Object.keys(apiKey.usageQuota)) { + apiKey.usageQuota[prop] = 0 + } + await apiKeyTable.put({ item: apiKey }) + } + ctx.throw(403, `Resource limits have been reached`) + } + } +} \ No newline at end of file diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index de283385a8..cb22d75c7b 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -172,6 +172,15 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@budibase/client@^0.1.23": + version "0.1.23" + resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.1.23.tgz#d72d2b26ff3a2d99f2b6c1b71020b1136880937d" + integrity sha512-pZdwdCq5kKLZfZYxasIHBNnqu3BFFrqJLxXMFs0K9ddCVZ0UNons59nn73nFGbeRgNVdWp6yyW71XyMQr8NOEw== + dependencies: + deep-equal "^2.0.1" + mustache "^4.0.1" + regexparam "^1.3.0" + "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -867,6 +876,11 @@ 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" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -925,9 +939,17 @@ atomic-sleep@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" -aws-sdk@^2.706.0: - version "2.706.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.706.0.tgz#09f65e9a91ecac5a635daf934082abae30eca953" +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" + integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== + dependencies: + array-filter "^1.0.0" + +aws-sdk@^2.767.0: + version "2.767.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.767.0.tgz#9863c8bfd5990106b95f38e9345a547fee782470" + integrity sha512-soPZxjNpat0CtuIqm54GO/FDT4SZTlQG0icSptWYfMFYdkXe8b0tJqaPssNn9TzlgoWDCNTdaoepM6TN0rNHkQ== dependencies: buffer "4.9.2" events "1.1.1" @@ -1725,6 +1747,26 @@ decompress@^4.2.1: pify "^2.3.0" strip-dirs "^2.0.0" +deep-equal@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.4.tgz#6b0b407a074666033169df3acaf128e1c6f3eab6" + integrity sha512-BUfaXrVoCfgkOQY/b09QdO9L3XNoF2XH0A3aY9IQwQL/ZjLOe8FQgCNVl1wiolhsFo8kFdO9zdPViCPbmaJA5w== + dependencies: + es-abstract "^1.18.0-next.1" + es-get-iterator "^1.1.0" + is-arguments "^1.0.4" + is-date-object "^1.0.2" + is-regex "^1.1.1" + isarray "^2.0.5" + object-is "^1.1.3" + object-keys "^1.1.1" + object.assign "^4.1.1" + regexp.prototype.flags "^1.3.0" + side-channel "^1.0.3" + 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" @@ -2073,6 +2115,54 @@ 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.7" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c" + integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g== + 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.2" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-abstract@^1.18.0-next.0, es-abstract@^1.18.0-next.1: + version "1.18.0-next.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" + integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== + 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.2" + 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.1" + 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" + integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ== + 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" @@ -3112,6 +3202,11 @@ 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" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3121,12 +3216,22 @@ is-arrayish@^0.3.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== +is-bigint@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4" + integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g== + 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" + integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -3135,6 +3240,11 @@ 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.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" + integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -3157,7 +3267,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" @@ -3227,15 +3337,30 @@ 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" + integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== + is-natural-number@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= +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" + integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= + 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" + integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3276,15 +3401,32 @@ is-regex@^1.0.5: dependencies: has "^1.0.3" +is-regex@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + is-retry-allowed@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== +is-set@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" + integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== + 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" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -3299,10 +3441,30 @@ 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" + integrity sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ== + 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" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + +is-weakset@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83" + integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw== + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -3323,6 +3485,11 @@ 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" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isbinaryfile@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz#edcb62b224e2b4710830b67498c8e4e5a4d2610b" @@ -4668,6 +4835,19 @@ 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" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + +object-is@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.3.tgz#2e3b9e65560137455ee3bd62aec4d90a2ea1cc81" + integrity sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + 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" @@ -4687,6 +4867,16 @@ object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" +object.assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd" + integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.0" + has-symbols "^1.0.1" + object-keys "^1.1.1" + object.getownpropertydescriptors@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" @@ -5374,6 +5564,19 @@ 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" + integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== + 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" + 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" @@ -5692,6 +5895,14 @@ shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" +side-channel@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.3.tgz#cdc46b057550bbab63706210838df5d4c19519c3" + integrity sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g== + 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" @@ -5981,7 +6192,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.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: @@ -6004,7 +6215,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: @@ -6611,6 +6822,27 @@ 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" + integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ== + 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" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + 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" @@ -6620,6 +6852,18 @@ which-pm-runs@^1.0.0: resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= +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" + integrity sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ== + 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" From 77552a074740a0d02bce8f9c53f462eb89dcd9a7 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Wed, 7 Oct 2020 15:40:06 +0100 Subject: [PATCH 02/38] Screen templates - initial commit --- .../builder/src/builderStore/store/index.js | 7 +- .../store/screenTemplates/editRecordScreen.js | 8 + .../store/screenTemplates/index.js | 17 ++ .../CreateScreen/ChooseTemplate.svelte | 59 +++++ .../CreateScreen/CreateScreen.svelte | 45 ++++ .../CreateScreen/NoTemplate.svelte | 92 +++++++ .../userInterface/FrontendNavigatePane.svelte | 4 +- .../components/userInterface/NewScreen.svelte | 117 -------- packages/server/yarn.lock | 249 +----------------- 9 files changed, 230 insertions(+), 368 deletions(-) create mode 100644 packages/builder/src/builderStore/store/screenTemplates/editRecordScreen.js create mode 100644 packages/builder/src/builderStore/store/screenTemplates/index.js create mode 100644 packages/builder/src/components/userInterface/CreateScreen/ChooseTemplate.svelte create mode 100644 packages/builder/src/components/userInterface/CreateScreen/CreateScreen.svelte create mode 100644 packages/builder/src/components/userInterface/CreateScreen/NoTemplate.svelte delete mode 100644 packages/builder/src/components/userInterface/NewScreen.svelte diff --git a/packages/builder/src/builderStore/store/index.js b/packages/builder/src/builderStore/store/index.js index 70b88eb778..794d9436ee 100644 --- a/packages/builder/src/builderStore/store/index.js +++ b/packages/builder/src/builderStore/store/index.js @@ -153,11 +153,12 @@ const _saveScreen = async (store, s, screen) => { return s } -const createScreen = store => (screenName, route, layoutComponentName) => { +const createScreen = store => ({ screenName, route, screen }) => { store.update(state => { - const rootComponent = state.components[layoutComponentName] + const rootComponent = + state.components["@budibase/standard-components/container"] - const newScreen = { + const newScreen = screen || { description: "", url: "", _css: "", diff --git a/packages/builder/src/builderStore/store/screenTemplates/editRecordScreen.js b/packages/builder/src/builderStore/store/screenTemplates/editRecordScreen.js new file mode 100644 index 0000000000..1666d6d3c5 --- /dev/null +++ b/packages/builder/src/builderStore/store/screenTemplates/editRecordScreen.js @@ -0,0 +1,8 @@ +export default function(models) { + return models.map(model => ({ + name: `Edit ${model.name}`, + create: () => createScreen(model), + })) +} + +const createScreen = model => ({}) diff --git a/packages/builder/src/builderStore/store/screenTemplates/index.js b/packages/builder/src/builderStore/store/screenTemplates/index.js new file mode 100644 index 0000000000..6118df42a6 --- /dev/null +++ b/packages/builder/src/builderStore/store/screenTemplates/index.js @@ -0,0 +1,17 @@ +import editRecordScreen from "./editRecordScreen" +import { generateNewIdsForComponent } from "../../storeUtils" + +const allTemplates = models => [...editRecordScreen(models)] + +// allows us to apply common behaviour to all create() functions +const createTemplateOverride = (frontendState, create) => () => { + const screen = create() + generateNewIdsForComponent(screen.props, frontendState) + return screen +} + +export default (frontendState, models) => + allTemplates(models).map(template => ({ + ...template, + create: createTemplateOverride(frontendState, template.create), + })) diff --git a/packages/builder/src/components/userInterface/CreateScreen/ChooseTemplate.svelte b/packages/builder/src/components/userInterface/CreateScreen/ChooseTemplate.svelte new file mode 100644 index 0000000000..8f806b6b05 --- /dev/null +++ b/packages/builder/src/components/userInterface/CreateScreen/ChooseTemplate.svelte @@ -0,0 +1,59 @@ + + +
+ +
+ + +
+
+ + + + + + diff --git a/packages/builder/src/components/userInterface/CreateScreen/CreateScreen.svelte b/packages/builder/src/components/userInterface/CreateScreen/CreateScreen.svelte new file mode 100644 index 0000000000..166ddcf0a9 --- /dev/null +++ b/packages/builder/src/components/userInterface/CreateScreen/CreateScreen.svelte @@ -0,0 +1,45 @@ + + + +

New Screen

+ + + {#if template === 'none'} +
+ +
+ {:else} +
+ (template = 'none')} /> +
+ {/if} +
+ + diff --git a/packages/builder/src/components/userInterface/CreateScreen/NoTemplate.svelte b/packages/builder/src/components/userInterface/CreateScreen/NoTemplate.svelte new file mode 100644 index 0000000000..37bcd0cc0d --- /dev/null +++ b/packages/builder/src/components/userInterface/CreateScreen/NoTemplate.svelte @@ -0,0 +1,92 @@ + + +

New Screen

+ + +
+
+ +
+ +
+ +
+ +
+ + + + + + diff --git a/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte b/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte index 9495799990..265630b88b 100644 --- a/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte +++ b/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte @@ -3,7 +3,7 @@ import ComponentsHierarchy from "components/userInterface/ComponentsHierarchy.svelte" import PageLayout from "components/userInterface/PageLayout.svelte" import PagesList from "components/userInterface/PagesList.svelte" - import NewScreen from "components/userInterface/NewScreen.svelte" + import CreateScreen from "components/userInterface/CreateScreen/CreateScreen.svelte" const newScreen = () => { newScreenPicker.show() @@ -22,7 +22,7 @@ - + diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 40eb7edf4c..5c68d56319 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -172,15 +172,6 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@budibase/client@^0.1.24": - version "0.1.24" - resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.1.24.tgz#d2967c050af9f559791e0189137f80e621ea2d69" - integrity sha512-2Plu9PpF3TOPTDkAAIkPFEjZFolGkty0Sc0vbLk8lee4yqeonBj5paXT44O6kpxLFW47YjN5VCA4+EnwGl358w== - dependencies: - deep-equal "^2.0.1" - mustache "^4.0.1" - regexparam "^1.3.0" - "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -876,11 +867,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" - integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= - array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -939,13 +925,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" - integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== - 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" @@ -1755,26 +1734,6 @@ decompress@^4.2.1: pify "^2.3.0" strip-dirs "^2.0.0" -deep-equal@^2.0.1: - version "2.0.4" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.4.tgz#6b0b407a074666033169df3acaf128e1c6f3eab6" - integrity sha512-BUfaXrVoCfgkOQY/b09QdO9L3XNoF2XH0A3aY9IQwQL/ZjLOe8FQgCNVl1wiolhsFo8kFdO9zdPViCPbmaJA5w== - dependencies: - es-abstract "^1.18.0-next.1" - es-get-iterator "^1.1.0" - is-arguments "^1.0.4" - is-date-object "^1.0.2" - is-regex "^1.1.1" - isarray "^2.0.5" - object-is "^1.1.3" - object-keys "^1.1.1" - object.assign "^4.1.1" - regexp.prototype.flags "^1.3.0" - side-channel "^1.0.3" - 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" @@ -2123,54 +2082,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.7" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c" - integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g== - 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.2" - is-regex "^1.1.1" - object-inspect "^1.8.0" - object-keys "^1.1.1" - object.assign "^4.1.1" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" - -es-abstract@^1.18.0-next.0, es-abstract@^1.18.0-next.1: - version "1.18.0-next.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" - integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== - 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.2" - 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.1" - 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" - integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ== - 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" @@ -3210,11 +3121,6 @@ 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" - integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3224,22 +3130,12 @@ is-arrayish@^0.3.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== -is-bigint@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4" - integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g== - 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" - integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== - is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -3248,11 +3144,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.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" - integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== - is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -3275,7 +3166,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" @@ -3345,30 +3236,15 @@ 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" - integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== - is-natural-number@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= -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" - integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= - 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" - integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== - is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3409,32 +3285,15 @@ is-regex@^1.0.5: dependencies: has "^1.0.3" -is-regex@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" - integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== - dependencies: - has-symbols "^1.0.1" - is-retry-allowed@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== -is-set@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" - integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== - 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" - integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== - is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -3449,16 +3308,6 @@ 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" - integrity sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ== - 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" @@ -3468,16 +3317,6 @@ is-utf8@^0.2.0: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= -is-weakmap@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" - integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== - -is-weakset@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83" - integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw== - is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -3498,11 +3337,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" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - isbinaryfile@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz#edcb62b224e2b4710830b67498c8e4e5a4d2610b" @@ -4875,19 +4709,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" - integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== - -object-is@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.3.tgz#2e3b9e65560137455ee3bd62aec4d90a2ea1cc81" - integrity sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" - 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" @@ -4907,16 +4728,6 @@ object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd" - integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.18.0-next.0" - has-symbols "^1.0.1" - object-keys "^1.1.1" - object.getownpropertydescriptors@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" @@ -5642,19 +5453,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" - integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== - 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" - 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" @@ -5973,14 +5771,6 @@ shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" -side-channel@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.3.tgz#cdc46b057550bbab63706210838df5d4c19519c3" - integrity sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g== - 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" @@ -6277,7 +6067,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: @@ -6300,7 +6090,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: @@ -6922,27 +6712,6 @@ 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" - integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ== - 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" - integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== - 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" @@ -6952,18 +6721,6 @@ which-pm-runs@^1.0.0: resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= -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" - integrity sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ== - 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" From 922e214dca902f9f6a07ba031eaae50df0208bd6 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 7 Oct 2020 17:56:47 +0100 Subject: [PATCH 03/38] Fixing up middleware to handle uploads, views, records, automation runs and users. --- packages/server/src/api/controllers/auth.js | 4 +- .../server/src/api/controllers/automation.js | 2 + packages/server/src/api/routes/model.js | 4 +- packages/server/src/api/routes/static.js | 3 +- packages/server/src/api/routes/user.js | 4 +- packages/server/src/api/routes/view.js | 10 ++- packages/server/src/automations/index.js | 25 +++++-- packages/server/src/middleware/usageQuota.js | 71 +++++++----------- .../server/src/utilities/fileProcessor.js | 18 ++--- packages/server/src/utilities/usageQuota.js | 72 +++++++++++++++++++ 10 files changed, 147 insertions(+), 66 deletions(-) create mode 100644 packages/server/src/utilities/usageQuota.js diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js index b7a7c116b9..c16f76caca 100644 --- a/packages/server/src/api/controllers/auth.js +++ b/packages/server/src/api/controllers/auth.js @@ -3,7 +3,7 @@ const CouchDB = require("../../db") const ClientDb = require("../../db/clientDb") const bcrypt = require("../../utilities/bcrypt") const environment = require("../../environment") -const { apiKeyTable } = require("../../db/dynamoClient") +const { getAPIKey } = require("../../utilities/usageQuota") const { generateUserID } = require("../../db/utils") exports.authenticate = async ctx => { @@ -55,7 +55,7 @@ exports.authenticate = async ctx => { } // if in cloud add the user api key if (environment.CLOUD) { - payload.apiKey = await apiKeyTable.get({ primary: ctx.user.appId }) + payload.apiKey = getAPIKey(ctx.user.appId) } const token = jwt.sign(payload, ctx.config.jwtSecret, { diff --git a/packages/server/src/api/controllers/automation.js b/packages/server/src/api/controllers/automation.js index 5414f3878c..92391da7e9 100644 --- a/packages/server/src/api/controllers/automation.js +++ b/packages/server/src/api/controllers/automation.js @@ -33,6 +33,7 @@ function cleanAutomationInputs(automation) { exports.create = async function(ctx) { const db = new CouchDB(ctx.user.instanceId) let automation = ctx.request.body + automation.appId = ctx.user.appId automation._id = generateAutomationID() @@ -54,6 +55,7 @@ exports.create = async function(ctx) { exports.update = async function(ctx) { const db = new CouchDB(ctx.user.instanceId) let automation = ctx.request.body + automation.appId = ctx.user.appId automation = cleanAutomationInputs(automation) const response = await db.put(automation) diff --git a/packages/server/src/api/routes/model.js b/packages/server/src/api/routes/model.js index 3c667df520..fe782d4cf5 100644 --- a/packages/server/src/api/routes/model.js +++ b/packages/server/src/api/routes/model.js @@ -1,7 +1,6 @@ const Router = require("@koa/router") const modelController = require("../controllers/model") const authorized = require("../../middleware/authorized") -const usage = require("../../middleware/usageQuota") const { BUILDER, READ_MODEL } = require("../../utilities/accessLevels") const router = Router() @@ -13,7 +12,7 @@ router authorized(READ_MODEL, ctx => ctx.params.id), modelController.find ) - .post("/api/models", authorized(BUILDER), usage, modelController.save) + .post("/api/models", authorized(BUILDER), modelController.save) .post( "/api/models/csv/validate", authorized(BUILDER), @@ -22,7 +21,6 @@ router .delete( "/api/models/:modelId/:revId", authorized(BUILDER), - usage, modelController.destroy ) diff --git a/packages/server/src/api/routes/static.js b/packages/server/src/api/routes/static.js index aa136a3d15..5c33900eca 100644 --- a/packages/server/src/api/routes/static.js +++ b/packages/server/src/api/routes/static.js @@ -4,6 +4,7 @@ const { budibaseTempDir } = require("../../utilities/budibaseDir") const env = require("../../environment") const authorized = require("../../middleware/authorized") const { BUILDER } = require("../../utilities/accessLevels") +const usage = require("../../middleware/usageQuota") const router = Router() @@ -28,7 +29,7 @@ router authorized(BUILDER), controller.performLocalFileProcessing ) - .post("/api/attachments/upload", controller.uploadFile) + .post("/api/attachments/upload", usage, controller.uploadFile) .get("/componentlibrary", controller.serveComponentLibrary) .get("/assets/:file*", controller.serveAppAsset) .get("/attachments/:file*", controller.serveAttachment) diff --git a/packages/server/src/api/routes/user.js b/packages/server/src/api/routes/user.js index 532943ea62..5289439e41 100644 --- a/packages/server/src/api/routes/user.js +++ b/packages/server/src/api/routes/user.js @@ -2,6 +2,7 @@ const Router = require("@koa/router") const controller = require("../controllers/user") const authorized = require("../../middleware/authorized") const { USER_MANAGEMENT, LIST_USERS } = require("../../utilities/accessLevels") +const usage = require("../../middleware/usageQuota") const router = Router() @@ -9,10 +10,11 @@ router .get("/api/users", authorized(LIST_USERS), controller.fetch) .get("/api/users/:username", authorized(USER_MANAGEMENT), controller.find) .put("/api/users/", authorized(USER_MANAGEMENT), controller.update) - .post("/api/users", authorized(USER_MANAGEMENT), controller.create) + .post("/api/users", authorized(USER_MANAGEMENT), usage, controller.create) .delete( "/api/users/:username", authorized(USER_MANAGEMENT), + usage, controller.destroy ) diff --git a/packages/server/src/api/routes/view.js b/packages/server/src/api/routes/view.js index 2c88f6d19a..571e4494f1 100644 --- a/packages/server/src/api/routes/view.js +++ b/packages/server/src/api/routes/view.js @@ -3,6 +3,7 @@ const viewController = require("../controllers/view") const recordController = require("../controllers/record") const authorized = require("../../middleware/authorized") const { BUILDER, READ_VIEW } = require("../../utilities/accessLevels") +const usage = require("../../middleware/usageQuota") const router = Router() @@ -13,8 +14,13 @@ router recordController.fetchView ) .get("/api/views", authorized(BUILDER), viewController.fetch) - .delete("/api/views/:viewName", authorized(BUILDER), viewController.destroy) - .post("/api/views", authorized(BUILDER), viewController.save) + .delete( + "/api/views/:viewName", + authorized(BUILDER), + usage, + viewController.destroy + ) + .post("/api/views", authorized(BUILDER), usage, viewController.save) .post("/api/views/export", authorized(BUILDER), viewController.exportView) .get( "/api/views/export/download/:fileName", diff --git a/packages/server/src/automations/index.js b/packages/server/src/automations/index.js index e419985ce2..882f24391b 100644 --- a/packages/server/src/automations/index.js +++ b/packages/server/src/automations/index.js @@ -3,6 +3,7 @@ const actions = require("./actions") const environment = require("../environment") const workerFarm = require("worker-farm") const singleThread = require("./thread") +const { getAPIKey, update, Properties } = require("../utilities/usageQuota") let workers = workerFarm(require.resolve("./thread")) @@ -18,16 +19,32 @@ function runWorker(job) { }) } +async function updateQuota(automation) { + const appId = automation.appId + const apiKey = await getAPIKey(appId) + // this will fail, causing automation to escape if limits reached + await update(apiKey, Properties.AUTOMATION, 1) +} + /** * This module is built purely to kick off the worker farm and manage the inputs/outputs */ module.exports.init = function() { actions.init().then(() => { triggers.automationQueue.process(async job => { - if (environment.BUDIBASE_ENVIRONMENT === "PRODUCTION") { - await runWorker(job) - } else { - await singleThread(job) + try { + if (environment.CLOUD) { + await updateQuota(job.data.automation) + } + if (environment.BUDIBASE_ENVIRONMENT === "PRODUCTION") { + await runWorker(job) + } else { + await singleThread(job) + } + } catch (err) { + console.error( + `${job.data.automation.appId} automation ${job.data.automation._id} was unable to run - ${err}` + ) } }) }) diff --git a/packages/server/src/middleware/usageQuota.js b/packages/server/src/middleware/usageQuota.js index 1d119f25c8..74df19a015 100644 --- a/packages/server/src/middleware/usageQuota.js +++ b/packages/server/src/middleware/usageQuota.js @@ -1,9 +1,5 @@ const CouchDB = require("../db") -const environment = require("../environment") -const { apiKeyTable } = require("../db/dynamoClient") - -// a normalised month in milliseconds -const QUOTA_RESET = 2592000000 +const usageQuota = require("../utilities/usageQuota") // currently only counting new writes and deletes const METHOD_MAP = { @@ -12,36 +8,32 @@ const METHOD_MAP = { } const DOMAIN_MAP = { - models: "model", - records: "record", + records: usageQuota.Properties.RECORD, + upload: usageQuota.Properties.UPLOAD, + views: usageQuota.Properties.VIEW, + users: usageQuota.Properties.USER, + // this will not be updated by endpoint calls + // instead it will be updated by triggers + automationRuns: usageQuota.Properties.AUTOMATION, } -function buildUpdateParams(key, property, usage) { - return { - primary: key, - condition: "#quota.#prop + :usage < #limits.model AND #quotaReset < :now", - expression: "ADD #quota.#prop :usage", - names: { - "#quota": "usageQuota", - "#prop": property, - "#limits": "limits", - "#quotaReset": "quotaReset", - }, - values: { - ":usage": usage, - ":now": Date.now(), - }, +function getProperty(url) { + for (let domain of Object.keys(DOMAIN_MAP)) { + if (url.indexOf(domain) !== -1) { + return DOMAIN_MAP[domain] + } } } module.exports = async (ctx, next) => { const db = new CouchDB(ctx.user.instanceId) - const usage = METHOD_MAP[ctx.req.method] - const domainParts = ctx.req.url.split("/") - const property = DOMAIN_MAP[domainParts[domainParts.length - 1]] + let usage = METHOD_MAP[ctx.req.method] + const property = getProperty(ctx.req.url) + console.log(ctx.req.url) if (usage == null || property == null) { return next() } + console.log(`${usage} to ${property}`) // post request could be a save of a pre-existing entry if (ctx.request.body && ctx.request.body._id) { try { @@ -52,26 +44,17 @@ module.exports = async (ctx, next) => { return } } - // don't try validate in builder - if (!environment.CLOUD) { - return next() + // update usage for uploads to be the total size + if (property === usageQuota.Properties.UPLOAD) { + const files = + ctx.request.files.file.length > 1 + ? Array.from(ctx.request.files.file) + : [ctx.request.files.file] + usage = files.map(file => file.size).reduce((total, size) => total + size) } try { - await apiKeyTable.update(buildUpdateParams(ctx.apiKey, property, usage)) + await usageQuota.update(ctx.apiKey, property, usage) } catch (err) { - if (err.code !== "ConditionalCheckFailedException") { - // get the API key so we can check it - let apiKey = await apiKeyTable.get({ primary: ctx.apiKey }) - // we have infact breached the reset period - if (apiKey && apiKey.quotaReset >= Date.now()) { - // update the quota reset period and reset the values for all properties - apiKey.quotaReset = Date.now() + QUOTA_RESET - for (let prop of Object.keys(apiKey.usageQuota)) { - apiKey.usageQuota[prop] = 0 - } - await apiKeyTable.put({ item: apiKey }) - } - ctx.throw(403, `Resource limits have been reached`) - } + ctx.throw(403, err) } -} \ No newline at end of file +} diff --git a/packages/server/src/utilities/fileProcessor.js b/packages/server/src/utilities/fileProcessor.js index 3e580e9e37..734209733d 100644 --- a/packages/server/src/utilities/fileProcessor.js +++ b/packages/server/src/utilities/fileProcessor.js @@ -1,5 +1,5 @@ const fs = require("fs") -const sharp = require("sharp") +// const sharp = require("sharp") const fsPromises = fs.promises const FORMATS = { @@ -7,14 +7,14 @@ const FORMATS = { } async function processImage(file) { - const imgMeta = await sharp(file.path) - .resize(300) - .toFile(file.outputPath) - - return { - ...file, - ...imgMeta, - } + // const imgMeta = await sharp(file.path) + // .resize(300) + // .toFile(file.outputPath) + // + // return { + // ...file, + // ...imgMeta, + // } } async function process(file) { diff --git a/packages/server/src/utilities/usageQuota.js b/packages/server/src/utilities/usageQuota.js new file mode 100644 index 0000000000..7ba0005213 --- /dev/null +++ b/packages/server/src/utilities/usageQuota.js @@ -0,0 +1,72 @@ +const environment = require("../environment") +const { apiKeyTable } = require("../db/dynamoClient") + +function buildUpdateParams(key, property, usage) { + return { + primary: key, + condition: "#quota.#prop + :usage < #limits.model AND #quotaReset < :now", + expression: "ADD #quota.#prop :usage", + names: { + "#quota": "usageQuota", + "#prop": property, + "#limits": "limits", + "#quotaReset": "quotaReset", + }, + values: { + ":usage": usage, + ":now": Date.now(), + }, + } +} + +// a normalised month in milliseconds +const QUOTA_RESET = 2592000000 + +exports.Properties = { + RECORD: "records", + UPLOAD: "storage", + VIEW: "views", + USER: "users", + AUTOMATION: "automationRuns", +} + +exports.getAPIKey = async appId => { + return apiKeyTable.get({ primary: appId }) +} + +/** + * Given a specified API key this will add to the usage object for the specified property. + * @param {string} apiKey The API key which is to be updated. + * @param {string} property The property which is to be added to (within the nested usageQuota object). + * @param {number} usage The amount (this can be negative) to adjust the number by. + * @returns {Promise} When this completes the API key will now be up to date - the quota period may have + * also been reset after this call. + */ +exports.update = async (apiKey, property, usage) => { + // don't try validate in builder + if (!environment.CLOUD) { + return + } + try { + await apiKeyTable.update(buildUpdateParams(apiKey, property, usage)) + } catch (err) { + if (err.code !== "ConditionalCheckFailedException") { + // get the API key so we can check it + let apiKey = await apiKeyTable.get({ primary: apiKey }) + // we have infact breached the reset period + if (apiKey && apiKey.quotaReset >= Date.now()) { + // update the quota reset period and reset the values for all properties + apiKey.quotaReset = Date.now() + QUOTA_RESET + for (let prop of Object.keys(apiKey.usageQuota)) { + if (prop === property) { + apiKey.usageQuota[prop] = usage > 0 ? usage : 0 + } else { + apiKey.usageQuota[prop] = 0 + } + } + await apiKeyTable.put({ item: apiKey }) + } + throw "Resource limits have been reached" + } + } +} From 3dca82a5ff016598dca48cbe6da7e2b32dfc7081 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 7 Oct 2020 20:37:55 +0100 Subject: [PATCH 04/38] check that deployment is possible using lambda API --- .../server/src/api/controllers/deploy/aws.js | 12 +++++- .../src/api/controllers/deploy/index.js | 37 +++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/controllers/deploy/aws.js b/packages/server/src/api/controllers/deploy/aws.js index 2f89d97742..6b73751695 100644 --- a/packages/server/src/api/controllers/deploy/aws.js +++ b/packages/server/src/api/controllers/deploy/aws.js @@ -21,11 +21,21 @@ async function invalidateCDN(cfDistribution, appId) { .promise() } -exports.fetchTemporaryCredentials = async function() { +/** + * Verifies the users API key and + * Verifies that the deployment fits within the quota of the user, + * @param {String} instanceId - instanceId being deployed + * @param {String} appId - appId being deployed + * @param {quota} quota - current quota being changed with this application + */ +exports.verifyDeployment = async function({ instanceId, appId, quota }) { const response = await fetch(process.env.DEPLOYMENT_CREDENTIALS_URL, { method: "POST", body: JSON.stringify({ apiKey: process.env.BUDIBASE_API_KEY, + instanceId, + appId, + quota, }), }) diff --git a/packages/server/src/api/controllers/deploy/index.js b/packages/server/src/api/controllers/deploy/index.js index 34579b64c7..a155fbbf81 100644 --- a/packages/server/src/api/controllers/deploy/index.js +++ b/packages/server/src/api/controllers/deploy/index.js @@ -1,10 +1,17 @@ const CouchDB = require("pouchdb") const PouchDB = require("../../../db") -const { uploadAppAssets, fetchTemporaryCredentials } = require("./aws") +const { + uploadAppAssets, + verifyDeployment, + determineDeploymentAllowed, +} = require("./aws") +const { getRecordParams } = require("../../db/utils") function replicate(local, remote) { return new Promise((resolve, reject) => { - const replication = local.sync(remote) + const replication = local.sync(remote, { + retry: true, + }) replication.on("complete", () => resolve()) replication.on("error", err => reject(err)) @@ -31,13 +38,37 @@ async function replicateCouch({ instanceId, clientId, credentials }) { await Promise.all(replications) } +async function getCurrentInstanceQuota(instanceId) { + const db = new PouchDB(instanceId) + const records = await db.allDocs( + getRecordParams("", null, { + include_docs: true, + }) + ) + const existingRecords = records.rows.length + return { + records: existingRecords, + } +} + exports.deployApp = async function(ctx) { try { const clientAppLookupDB = new PouchDB("client_app_lookup") const { clientId } = await clientAppLookupDB.get(ctx.user.appId) + const instanceQuota = await getCurrentInstanceQuota(ctx.user.instanceId) + const credentials = await verifyDeployment({ + instanceId: ctx.user.instanceId, + appId: ctx.user.appId, + quota: instanceQuota, + }) + ctx.log.info(`Uploading assets for appID ${ctx.user.appId} assets to s3..`) - const credentials = await fetchTemporaryCredentials() + + if (credentials.errors) { + ctx.throw(500, credentials.errors) + return + } await uploadAppAssets({ clientId, From 716f42deab9a6c1a63fb8bd8df0155dfee167554 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Wed, 7 Oct 2020 22:30:00 +0100 Subject: [PATCH 05/38] Screen templates working --- .../src/builderStore/getNewComponentName.js | 5 ++- .../builder/src/builderStore/store/index.js | 18 ++++++-- .../store/screenTemplates/editRecordScreen.js | 8 ---- .../store/screenTemplates/index.js | 16 +++++-- .../store/screenTemplates/newRecordScreen.js | 44 +++++++++++++++++++ .../screenTemplates/recordDetailScreen.js | 44 +++++++++++++++++++ .../ComponentDropdownMenu.svelte | 14 +----- .../CreateScreen/ChooseTemplate.svelte | 9 ++-- .../CreateScreen/CreateScreen.svelte | 11 +++-- .../CreateScreen/NoTemplate.svelte | 30 ++++++++++++- packages/standard-components/components.json | 13 +++++- 11 files changed, 174 insertions(+), 38 deletions(-) delete mode 100644 packages/builder/src/builderStore/store/screenTemplates/editRecordScreen.js create mode 100644 packages/builder/src/builderStore/store/screenTemplates/newRecordScreen.js create mode 100644 packages/builder/src/builderStore/store/screenTemplates/recordDetailScreen.js diff --git a/packages/builder/src/builderStore/getNewComponentName.js b/packages/builder/src/builderStore/getNewComponentName.js index b3ddc4e953..9ab8ef16dc 100644 --- a/packages/builder/src/builderStore/getNewComponentName.js +++ b/packages/builder/src/builderStore/getNewComponentName.js @@ -8,8 +8,9 @@ export default function(component, state) { const findMatches = props => { walkProps(props, c => { - if ((c._instanceName || "").startsWith(capitalised)) { - matchingComponents.push(c._instanceName) + const thisInstanceName = get_capitalised_name(c._instanceName) + if ((thisInstanceName || "").startsWith(capitalised)) { + matchingComponents.push(thisInstanceName) } }) } diff --git a/packages/builder/src/builderStore/store/index.js b/packages/builder/src/builderStore/store/index.js index 794d9436ee..dadba69e44 100644 --- a/packages/builder/src/builderStore/store/index.js +++ b/packages/builder/src/builderStore/store/index.js @@ -153,20 +153,30 @@ const _saveScreen = async (store, s, screen) => { return s } -const createScreen = store => ({ screenName, route, screen }) => { +const createScreen = store => ({ + screenName, + route, + baseComponent, + screen, +}) => { store.update(state => { const rootComponent = - state.components["@budibase/standard-components/container"] + state.components[ + baseComponent || "@budibase/standard-components/container" + ] const newScreen = screen || { description: "", url: "", _css: "", props: createProps(rootComponent).props, + route, } - newScreen.route = route newScreen.name = newScreen.props._id - newScreen.props._instanceName = screenName || "" + if (screenName) { + newScreen.props._instanceName = screenName + } + state.currentPreviewItem = newScreen state.currentComponentInfo = newScreen.props state.currentFrontEndType = "screen" diff --git a/packages/builder/src/builderStore/store/screenTemplates/editRecordScreen.js b/packages/builder/src/builderStore/store/screenTemplates/editRecordScreen.js deleted file mode 100644 index 1666d6d3c5..0000000000 --- a/packages/builder/src/builderStore/store/screenTemplates/editRecordScreen.js +++ /dev/null @@ -1,8 +0,0 @@ -export default function(models) { - return models.map(model => ({ - name: `Edit ${model.name}`, - create: () => createScreen(model), - })) -} - -const createScreen = model => ({}) diff --git a/packages/builder/src/builderStore/store/screenTemplates/index.js b/packages/builder/src/builderStore/store/screenTemplates/index.js index 6118df42a6..6825b8b973 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/index.js +++ b/packages/builder/src/builderStore/store/screenTemplates/index.js @@ -1,12 +1,22 @@ -import editRecordScreen from "./editRecordScreen" +import newRecordScreen from "./newRecordScreen" +import recordDetailScreen from "./recordDetailScreen" import { generateNewIdsForComponent } from "../../storeUtils" +import { uuid } from "builderStore/uuid" -const allTemplates = models => [...editRecordScreen(models)] +const allTemplates = models => [ + ...newRecordScreen(models), + ...recordDetailScreen(models), +] // allows us to apply common behaviour to all create() functions const createTemplateOverride = (frontendState, create) => () => { const screen = create() - generateNewIdsForComponent(screen.props, frontendState) + for (let component in screen.props.children) { + generateNewIdsForComponent(component, frontendState) + } + screen.props._id = uuid() + screen.name = screen.props._id + screen.route = screen.route.toLowerCase() return screen } diff --git a/packages/builder/src/builderStore/store/screenTemplates/newRecordScreen.js b/packages/builder/src/builderStore/store/screenTemplates/newRecordScreen.js new file mode 100644 index 0000000000..4c3466ca1c --- /dev/null +++ b/packages/builder/src/builderStore/store/screenTemplates/newRecordScreen.js @@ -0,0 +1,44 @@ +export default function(models) { + return models.map(model => ({ + name: `New ${model.name}`, + create: () => createScreen(model), + })) +} + +const createScreen = model => ({ + props: { + _id: "f684460e-1f79-42b4-8ffd-1f708bca93ed", + _component: "@budibase/standard-components/container", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + className: "", + onLoad: [], + type: "div", + _children: [ + { + _id: "7d1d6b43-b444-46a5-a75c-267fd6b5baf6", + _component: "@budibase/standard-components/heading", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + className: "", + text: `Create New ${model.name}`, + type: "h1", + _instanceId: "inst_cf8ace4_69efc0d72e6f443db2d4c902c14d9394", + _instanceName: `Heading 1`, + _children: [], + }, + ], + _instanceName: `New ${model.name}`, + }, + route: `/${model.name}/new`, + name: "screen-id", +}) diff --git a/packages/builder/src/builderStore/store/screenTemplates/recordDetailScreen.js b/packages/builder/src/builderStore/store/screenTemplates/recordDetailScreen.js new file mode 100644 index 0000000000..252e0f5e20 --- /dev/null +++ b/packages/builder/src/builderStore/store/screenTemplates/recordDetailScreen.js @@ -0,0 +1,44 @@ +export default function(models) { + return models.map(model => ({ + name: `New ${model.name}`, + create: () => createScreen(model), + })) +} + +const createScreen = model => ({ + props: { + _id: "f684460e-1f79-42b4-8ffd-1f708bca93ed", + _component: "@budibase/standard-components/recorddetail", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + className: "", + onLoad: [], + type: "div", + _children: [ + { + _id: "7d1d6b43-b444-46a5-a75c-267fd6b5baf6", + _component: "@budibase/standard-components/heading", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + className: "", + text: `${model.name} Detail`, + type: "h1", + _instanceId: "inst_cf8ace4_69efc0d72e6f443db2d4c902c14d9394", + _instanceName: `Heading 1`, + _children: [], + }, + ], + _instanceName: `${model.name} Detail`, + }, + route: `/${model.name}/new`, + name: "screen-id", +}) diff --git a/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte b/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte index 581bee5ef0..1c3353c888 100644 --- a/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte +++ b/packages/builder/src/components/userInterface/ComponentDropdownMenu.svelte @@ -69,18 +69,8 @@ } const copyComponent = () => { - store.update(s => { - const parent = getParent(s.currentPreviewItem.props, component) - const copiedComponent = cloneDeep(component) - walkProps(copiedComponent, p => { - p._id = uuid() - }) - parent._children = [...parent._children, copiedComponent] - saveCurrentPreviewItem(s) - s.currentComponentInfo = copiedComponent - regenerateCssForCurrentScreen(s) - return s - }) + storeComponentForCopy(false) + pasteComponent("below") } const deleteComponent = () => { diff --git a/packages/builder/src/components/userInterface/CreateScreen/ChooseTemplate.svelte b/packages/builder/src/components/userInterface/CreateScreen/ChooseTemplate.svelte index 8f806b6b05..a0b1e88ac8 100644 --- a/packages/builder/src/components/userInterface/CreateScreen/ChooseTemplate.svelte +++ b/packages/builder/src/components/userInterface/CreateScreen/ChooseTemplate.svelte @@ -2,6 +2,7 @@ import { store, backendUiStore } from "builderStore" import { Input, Select, Button, Spacer } from "@budibase/bbui" import getTemplates from "builderStore/store/screenTemplates" + import { createEventDispatcher } from "svelte" const dispatch = createEventDispatcher() let screens @@ -35,9 +36,11 @@ diff --git a/packages/builder/src/components/userInterface/CreateScreen/CreateScreen.svelte b/packages/builder/src/components/userInterface/CreateScreen/CreateScreen.svelte index 166ddcf0a9..4a5ec9f69d 100644 --- a/packages/builder/src/components/userInterface/CreateScreen/CreateScreen.svelte +++ b/packages/builder/src/components/userInterface/CreateScreen/CreateScreen.svelte @@ -9,6 +9,11 @@ dialog.show() } + const finished = () => { + dialog.hide() + template = undefined + } + let dialog let template @@ -19,12 +24,12 @@ {#if template === 'none'}
- +
{:else} -
+
(template = 'none')} />
{/if} diff --git a/packages/builder/src/components/userInterface/CreateScreen/NoTemplate.svelte b/packages/builder/src/components/userInterface/CreateScreen/NoTemplate.svelte index 37bcd0cc0d..f83970cdb0 100644 --- a/packages/builder/src/components/userInterface/CreateScreen/NoTemplate.svelte +++ b/packages/builder/src/components/userInterface/CreateScreen/NoTemplate.svelte @@ -1,17 +1,23 @@ -

New Screen

@@ -68,6 +85,15 @@ on:change={routeChanged} />
+
+ + +
+
diff --git a/packages/standard-components/components.json b/packages/standard-components/components.json index e600b264be..e8715dfbef 100644 --- a/packages/standard-components/components.json +++ b/packages/standard-components/components.json @@ -274,6 +274,17 @@ "context": "model", "children": true, "data": true, + "baseComponent": true, + "props": { + "model": "models" + } + }, + "newrecord": { + "description": "Loads a record, using an ID in the url", + "context": "model", + "children": true, + "data": true, + "baseComponent": true, "props": { "model": "models" } @@ -715,7 +726,7 @@ "default": "div" } }, - "container": true, + "baseComponent": true, "tags": [ "div", "container", From ae0069fa5d0106f0331d247823b0e35b0821cc12 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Wed, 7 Oct 2020 22:30:51 +0100 Subject: [PATCH 06/38] RecordDetail & NewRecord now baseComponents --- .../standard-components/src/NewRecord.svelte | 25 +++++++++++++++++++ .../src/RecordDetail.svelte | 13 +++++----- packages/standard-components/src/index.js | 1 + 3 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 packages/standard-components/src/NewRecord.svelte diff --git a/packages/standard-components/src/NewRecord.svelte b/packages/standard-components/src/NewRecord.svelte new file mode 100644 index 0000000000..923611d760 --- /dev/null +++ b/packages/standard-components/src/NewRecord.svelte @@ -0,0 +1,25 @@ + + +
diff --git a/packages/standard-components/src/RecordDetail.svelte b/packages/standard-components/src/RecordDetail.svelte index 95723782bd..30ab336427 100644 --- a/packages/standard-components/src/RecordDetail.svelte +++ b/packages/standard-components/src/RecordDetail.svelte @@ -8,6 +8,7 @@ let store = _bb.store let target + // this function should only get called when in builder preview mode async function fetchFirstRecord() { const FETCH_RECORDS_URL = `/api/views/all_${model}` const response = await _bb.api.get(FETCH_RECORDS_URL) @@ -24,22 +25,22 @@ // if srcdoc, then we assume this is the builder preview if (pathParts.length === 0 || pathParts[0] === "srcdoc") { record = await fetchFirstRecord() - } else { - const id = pathParts[pathParts.length - 1] - const GET_RECORD_URL = `/api/${model}/records/${id}` + } else if (_bb.routeParams().id) { + const GET_RECORD_URL = `/api/${model}/records/${_bb.routeParams().id}` const response = await _bb.api.get(GET_RECORD_URL) if (response.status === 200) { record = await response.json() + } else { + throw new Error("Failed to fetch record.", response) } + } else { + throw new Exception("Record ID was not supplied to RecordDetail") } if (record) { _bb.attachChildren(target, { - hydrate: false, context: record, }) - } else { - throw new Error("Failed to fetch record.", response) } } diff --git a/packages/standard-components/src/index.js b/packages/standard-components/src/index.js index 0ce5088cb8..2ad0db166b 100644 --- a/packages/standard-components/src/index.js +++ b/packages/standard-components/src/index.js @@ -27,6 +27,7 @@ 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 newrecord } from "./NewRecord.svelte" export { default as datepicker } from "./DatePicker.svelte" export * from "./Chart" export { default as icon } from "./Icon.svelte" From 39d2adb9e3f4e48be76ef04a5a0344812ea78e68 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 8 Oct 2020 10:56:32 +0100 Subject: [PATCH 07/38] hitting deployment success endpoint --- .../server/src/api/controllers/deploy/aws.js | 21 +++++++++++++++++++ .../src/api/controllers/deploy/index.js | 7 ++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/deploy/aws.js b/packages/server/src/api/controllers/deploy/aws.js index 6b73751695..b56d3c7788 100644 --- a/packages/server/src/api/controllers/deploy/aws.js +++ b/packages/server/src/api/controllers/deploy/aws.js @@ -21,6 +21,27 @@ async function invalidateCDN(cfDistribution, appId) { .promise() } +exports.updateDeploymentQuota = async function(quota) { + const response = await fetch( + `${process.env.DEPLOYMENT_CREDENTIALS_URL}/deploy/success`, + { + method: "POST", + body: JSON.stringify({ + apiKey: process.env.BUDIBASE_API_KEY, + quota, + }), + } + ) + + if (response.status !== 200) { + throw new Error(`Error updating deployment quota for app`) + } + + const json = await response.json() + + return json +} + /** * Verifies the users API key and * Verifies that the deployment fits within the quota of the user, diff --git a/packages/server/src/api/controllers/deploy/index.js b/packages/server/src/api/controllers/deploy/index.js index a155fbbf81..c5d1169962 100644 --- a/packages/server/src/api/controllers/deploy/index.js +++ b/packages/server/src/api/controllers/deploy/index.js @@ -3,7 +3,7 @@ const PouchDB = require("../../../db") const { uploadAppAssets, verifyDeployment, - determineDeploymentAllowed, + updateDeploymentQuota, } = require("./aws") const { getRecordParams } = require("../../db/utils") @@ -85,6 +85,11 @@ exports.deployApp = async function(ctx) { credentials: credentials.couchDbCreds, }) + const deployedInstanceQuota = await getCurrentInstanceQuota( + ctx.user.instanceId + ) + updateDeploymentQuota(deployedInstanceQuota) + ctx.body = { status: "SUCCESS", completed: Date.now(), From f6d2e48dcc8635170e78c095202f25eaeb2fab0c Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Thu, 8 Oct 2020 14:53:06 +0100 Subject: [PATCH 08/38] Screen Template - pre merge --- .../CreateScreen/NewScreen.svelte | 148 ++++++++++++++++++ .../CreateScreen/NoTemplate.svelte | 2 +- 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 packages/builder/src/components/userInterface/CreateScreen/NewScreen.svelte diff --git a/packages/builder/src/components/userInterface/CreateScreen/NewScreen.svelte b/packages/builder/src/components/userInterface/CreateScreen/NewScreen.svelte new file mode 100644 index 0000000000..50aa935146 --- /dev/null +++ b/packages/builder/src/components/userInterface/CreateScreen/NewScreen.svelte @@ -0,0 +1,148 @@ + + + + +

New Screen

+ + +
+ + +
+
+
+ +
+ +
+ +
+ +
+ + +
+ +
+ + + + + +
+ + diff --git a/packages/builder/src/components/userInterface/CreateScreen/NoTemplate.svelte b/packages/builder/src/components/userInterface/CreateScreen/NoTemplate.svelte index f83970cdb0..6ddb0ae951 100644 --- a/packages/builder/src/components/userInterface/CreateScreen/NoTemplate.svelte +++ b/packages/builder/src/components/userInterface/CreateScreen/NoTemplate.svelte @@ -86,7 +86,7 @@
- + - - - {#if templates} - {#each templates as template} - - {/each} - {/if} - -
- - - - - - - diff --git a/packages/builder/src/components/userInterface/CreateScreen/CreateScreen.svelte b/packages/builder/src/components/userInterface/CreateScreen/CreateScreen.svelte deleted file mode 100644 index 4a5ec9f69d..0000000000 --- a/packages/builder/src/components/userInterface/CreateScreen/CreateScreen.svelte +++ /dev/null @@ -1,50 +0,0 @@ - - - -

New Screen

- - - {#if template === 'none'} -
- -
- {:else} -
- (template = 'none')} /> -
- {/if} -
- - diff --git a/packages/builder/src/components/userInterface/CreateScreen/NewScreen.svelte b/packages/builder/src/components/userInterface/CreateScreen/NewScreen.svelte index 50aa935146..9a48ffe530 100644 --- a/packages/builder/src/components/userInterface/CreateScreen/NewScreen.svelte +++ b/packages/builder/src/components/userInterface/CreateScreen/NewScreen.svelte @@ -1,6 +1,7 @@ - - - -
-
- -
- -
- -
- -
- - -
- -
- - - - - - diff --git a/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte b/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte index 265630b88b..41e077ecd6 100644 --- a/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte +++ b/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte @@ -3,7 +3,7 @@ import ComponentsHierarchy from "components/userInterface/ComponentsHierarchy.svelte" import PageLayout from "components/userInterface/PageLayout.svelte" import PagesList from "components/userInterface/PagesList.svelte" - import CreateScreen from "components/userInterface/CreateScreen/CreateScreen.svelte" + import NewScreen from "components/userInterface/CreateScreen/NewScreen.svelte" const newScreen = () => { newScreenPicker.show() @@ -22,7 +22,7 @@ - + diff --git a/packages/builder/src/components/automation/SetupPanel/ParamInputs/RecordSelector.svelte b/packages/builder/src/components/automation/SetupPanel/ParamInputs/RecordSelector.svelte index d560107642..d002aaa0a1 100644 --- a/packages/builder/src/components/automation/SetupPanel/ParamInputs/RecordSelector.svelte +++ b/packages/builder/src/components/automation/SetupPanel/ParamInputs/RecordSelector.svelte @@ -28,42 +28,34 @@ {#if schemaFields.length} -
+
{#each schemaFields as [field, schema]} -
- {#if schemaHasOptions(schema)} -
{field}
- - {:else if schema.type === 'string' || schema.type === 'number'} - - {/if} -
+ {#if schemaHasOptions(schema)} + + {:else if schema.type === 'string' || schema.type === 'number'} + + {/if} {/each}
{/if} diff --git a/packages/builder/src/components/automation/SetupPanel/SetupPanel.svelte b/packages/builder/src/components/automation/SetupPanel/SetupPanel.svelte index 39ffaa370b..f2b7a8b711 100644 --- a/packages/builder/src/components/automation/SetupPanel/SetupPanel.svelte +++ b/packages/builder/src/components/automation/SetupPanel/SetupPanel.svelte @@ -1,27 +1,19 @@
@@ -93,11 +93,18 @@ on:click={saveAutomation}> Save Automation - + {/if}
-
+ diff --git a/packages/builder/src/components/database/DataTable/TablePagination.svelte b/packages/builder/src/components/backend/DataTable/TablePagination.svelte similarity index 57% rename from packages/builder/src/components/database/DataTable/TablePagination.svelte rename to packages/builder/src/components/backend/DataTable/TablePagination.svelte index 508ee60b23..1435148ebc 100644 --- a/packages/builder/src/components/database/DataTable/TablePagination.svelte +++ b/packages/builder/src/components/backend/DataTable/TablePagination.svelte @@ -1,14 +1,11 @@ + + + + + {#if view.calculation} + + {/if} + +
diff --git a/packages/builder/src/components/database/DataTable/api.js b/packages/builder/src/components/backend/DataTable/api.js similarity index 100% rename from packages/builder/src/components/database/DataTable/api.js rename to packages/builder/src/components/backend/DataTable/api.js diff --git a/packages/builder/src/components/backend/DataTable/buttons/CalculateButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/CalculateButton.svelte new file mode 100644 index 0000000000..efe3dcd79d --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/buttons/CalculateButton.svelte @@ -0,0 +1,19 @@ + + +
+ + + Calculate + +
+ + + diff --git a/packages/builder/src/components/database/DataTable/popovers/Row.svelte b/packages/builder/src/components/backend/DataTable/buttons/CreateColumnButton.svelte similarity index 63% rename from packages/builder/src/components/database/DataTable/popovers/Row.svelte rename to packages/builder/src/components/backend/DataTable/buttons/CreateColumnButton.svelte index 843c636063..7ddced256e 100644 --- a/packages/builder/src/components/database/DataTable/popovers/Row.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/CreateColumnButton.svelte @@ -1,20 +1,21 @@
-
Add New Row
- +
Create Column
+
diff --git a/packages/builder/src/components/database/DataTable/popovers/View.svelte b/packages/builder/src/components/backend/DataTable/popovers/CreateViewPopover.svelte similarity index 58% rename from packages/builder/src/components/database/DataTable/popovers/View.svelte rename to packages/builder/src/components/backend/DataTable/popovers/CreateViewPopover.svelte index 90208a0025..8cd0785044 100644 --- a/packages/builder/src/components/database/DataTable/popovers/View.svelte +++ b/packages/builder/src/components/backend/DataTable/popovers/CreateViewPopover.svelte @@ -1,19 +1,11 @@ -
- - - Create New View - -
- +
Create View
-
- -
-
- + + - +
diff --git a/packages/builder/src/components/backend/DataTable/popovers/ExportPopover.svelte b/packages/builder/src/components/backend/DataTable/popovers/ExportPopover.svelte new file mode 100644 index 0000000000..0d52689a6e --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/popovers/ExportPopover.svelte @@ -0,0 +1,61 @@ + + +
+
Export Data
+ + +
+ + diff --git a/packages/builder/src/components/database/DataTable/popovers/Filter.svelte b/packages/builder/src/components/backend/DataTable/popovers/FilterPopover.svelte similarity index 55% rename from packages/builder/src/components/database/DataTable/popovers/Filter.svelte rename to packages/builder/src/components/backend/DataTable/popovers/FilterPopover.svelte index fbf3b59db1..3d6241f0ab 100644 --- a/packages/builder/src/components/database/DataTable/popovers/Filter.svelte +++ b/packages/builder/src/components/backend/DataTable/popovers/FilterPopover.svelte @@ -1,13 +1,5 @@ -
- - - Filter - -
- +
Filter
-
- {#each view.filters as filter, idx} - {#if idx === 0} -

Where

- {:else} - + + {#each CONJUNCTIONS as conjunction} + + {/each} + + {/if} + - {/if} - - - {#if filter.key && isMultipleChoice(filter.key)} - - {#each fieldOptions(filter.key) as option} - + {#each CONDITIONS as condition} + {/each} - {:else if filter.key && isDate(filter.key)} - - {:else if filter.key && isNumber(filter.key)} - - {:else} - - {/if} - removeFilter(idx)} /> - {/each} -
-
+ {#if filter.key && isMultipleChoice(filter.key)} + + {:else if filter.key && isDate(filter.key)} + + {:else if filter.key && isNumber(filter.key)} + + {:else} + + {/if} + removeFilter(idx)} /> + {/each} +
+ {/if} + diff --git a/packages/builder/src/components/nav/ModelNavigator/ListItem.svelte b/packages/builder/src/components/backend/ModelNavigator/ListItem.svelte similarity index 74% rename from packages/builder/src/components/nav/ModelNavigator/ListItem.svelte rename to packages/builder/src/components/backend/ModelNavigator/ListItem.svelte index 54d9eef0e9..31dd5b5ac4 100644 --- a/packages/builder/src/components/nav/ModelNavigator/ListItem.svelte +++ b/packages/builder/src/components/backend/ModelNavigator/ListItem.svelte @@ -19,26 +19,24 @@ diff --git a/packages/builder/src/components/nav/ModelNavigator/ModelNavigator.svelte b/packages/builder/src/components/backend/ModelNavigator/ModelNavigator.svelte similarity index 74% rename from packages/builder/src/components/nav/ModelNavigator/ModelNavigator.svelte rename to packages/builder/src/components/backend/ModelNavigator/ModelNavigator.svelte index a055c0dc67..627acab415 100644 --- a/packages/builder/src/components/nav/ModelNavigator/ModelNavigator.svelte +++ b/packages/builder/src/components/backend/ModelNavigator/ModelNavigator.svelte @@ -1,16 +1,12 @@ + + + + + +
+ + +
+
+
diff --git a/packages/builder/src/components/backend/ModelNavigator/popovers/EditTablePopover.svelte b/packages/builder/src/components/backend/ModelNavigator/popovers/EditTablePopover.svelte new file mode 100644 index 0000000000..028b78a1e8 --- /dev/null +++ b/packages/builder/src/components/backend/ModelNavigator/popovers/EditTablePopover.svelte @@ -0,0 +1,141 @@ + + +
+ +
+ + {#if editing} +
+
Edit Table
+ + +
+ + +
+
+ {:else} +
    +
  • + + Edit +
  • +
  • + + Delete +
  • +
+ {/if} +
+ + + diff --git a/packages/builder/src/components/nav/ModelNavigator/EditView.svelte b/packages/builder/src/components/backend/ModelNavigator/popovers/EditViewPopover.svelte similarity index 51% rename from packages/builder/src/components/nav/ModelNavigator/EditView.svelte rename to packages/builder/src/components/backend/ModelNavigator/popovers/EditViewPopover.svelte index 7faf9b980e..bd1e184e06 100644 --- a/packages/builder/src/components/nav/ModelNavigator/EditView.svelte +++ b/packages/builder/src/components/backend/ModelNavigator/popovers/EditViewPopover.svelte @@ -1,20 +1,18 @@ -
+
- + {#if editing} -
Edit View
-
- -
-
-
+
+
Edit View
+ +
-
-
-
-
+ +
{:else}
  • Edit
  • -
  • +
  • Delete
{/if} + diff --git a/packages/builder/src/components/common/ConfirmDialog.svelte b/packages/builder/src/components/common/ConfirmDialog.svelte index 31e2d3ad86..2cd27573f4 100644 --- a/packages/builder/src/components/common/ConfirmDialog.svelte +++ b/packages/builder/src/components/common/ConfirmDialog.svelte @@ -1,57 +1,31 @@ - -

{title}

- - {body} - - + + +
{body}
+
diff --git a/packages/builder/src/components/common/DatePicker.svelte b/packages/builder/src/components/common/DatePicker.svelte index ec9d41a17b..913e00fc75 100644 --- a/packages/builder/src/components/common/DatePicker.svelte +++ b/packages/builder/src/components/common/DatePicker.svelte @@ -11,6 +11,4 @@ } -
- -
+ diff --git a/packages/builder/src/components/common/ErrorsBox.svelte b/packages/builder/src/components/common/ErrorsBox.svelte index 0e565a4ad8..9e29784c66 100644 --- a/packages/builder/src/components/common/ErrorsBox.svelte +++ b/packages/builder/src/components/common/ErrorsBox.svelte @@ -5,9 +5,25 @@ {#if hasErrors} -
+
{#each errors as error} -
{error.dataPath} {error.message}
+
{error.dataPath} {error.message}
{/each}
{/if} + + diff --git a/packages/builder/src/components/common/LinkedRecordSelector.svelte b/packages/builder/src/components/common/LinkedRecordSelector.svelte index 2976ffd913..e7cf245eb8 100644 --- a/packages/builder/src/components/common/LinkedRecordSelector.svelte +++ b/packages/builder/src/components/common/LinkedRecordSelector.svelte @@ -2,101 +2,52 @@ import { onMount } from "svelte" import { backendUiStore } from "builderStore" import api from "builderStore/api" + import { Select, Label, Multiselect } from "@budibase/bbui" + import { capitalise } from "../../helpers" - export let modelId - export let linkName - export let linked = [] + export let schema + export let linkedRecords = [] let records = [] - let model = {} - let linkedRecords = new Set(linked) + $: label = capitalise(schema.name) + $: linkedModelId = schema.modelId + $: linkedModel = $backendUiStore.models.find( + model => model._id === linkedModelId + ) + $: fetchRecords(linkedModelId) - $: linked = [...linkedRecords] - $: FIELDS_TO_HIDE = [$backendUiStore.selectedModel.name] - $: schema = $backendUiStore.selectedModel.schema - - async function fetchRecords() { - const FETCH_RECORDS_URL = `/api/${modelId}/records` - const response = await api.get(FETCH_RECORDS_URL) - const modelResponse = await api.get(`/api/models/${modelId}`) - - model = await modelResponse.json() - records = await response.json() - } - - function linkRecord(id) { - if (linkedRecords.has(id)) { - linkedRecords.delete(id) - } else { - linkedRecords.add(id) + async function fetchRecords(linkedModelId) { + const FETCH_RECORDS_URL = `/api/${linkedModelId}/records` + try { + const response = await api.get(FETCH_RECORDS_URL) + records = await response.json() + } catch (error) { + console.log(error) + records = [] } - - linkedRecords = linkedRecords } - onMount(() => { - fetchRecords() - }) + function getPrettyName(record) { + return record[linkedModel.primaryDisplay || "_id"] + } -
-
-

{linkName}

-
- {#each records as record} -
linkRecord(record._id)}> -
- {#each Object.keys(model.schema).filter(key => !FIELDS_TO_HIDE.includes(key)) as key} -
- {model.schema[key].name} -

{record[key]}

-
- {/each} -
-
- {/each} -
- - +{#if linkedModel.primaryDisplay == null} + + +{:else} + + {#each records as record} + + {/each} + +{/if} diff --git a/packages/builder/src/components/common/Notification/NotificationDisplay.svelte b/packages/builder/src/components/common/Notification/NotificationDisplay.svelte index 425eb60fb2..ad32f228cb 100644 --- a/packages/builder/src/components/common/Notification/NotificationDisplay.svelte +++ b/packages/builder/src/components/common/Notification/NotificationDisplay.svelte @@ -1,7 +1,7 @@ -
    +
    {#each $notificationStore.notifications as notification (notification.id)} -
  • + transition:fly={{ y: -30 }}>
    {notification.message}
    {#if notification.icon} {/if} -
  • +
    {/each} -
+
diff --git a/packages/builder/src/components/database/DataTable/LinkedRecord.svelte b/packages/builder/src/components/database/DataTable/LinkedRecord.svelte deleted file mode 100644 index 0cbf9ab307..0000000000 --- a/packages/builder/src/components/database/DataTable/LinkedRecord.svelte +++ /dev/null @@ -1,124 +0,0 @@ - - -
- {records.length} - {#if open} -
-
-

{field.name}

- -
- {#each records as record} -
-
- {#each Object.keys(model.schema).filter(key => !FIELDS_TO_HIDE.includes(key)) as key} -
- {model.schema[key].name} -

{record[key]}

-
- {/each} -
-
- {/each} -
- {/if} -
- - diff --git a/packages/builder/src/components/database/DataTable/Table.svelte b/packages/builder/src/components/database/DataTable/Table.svelte deleted file mode 100644 index 5b167c71f7..0000000000 --- a/packages/builder/src/components/database/DataTable/Table.svelte +++ /dev/null @@ -1,149 +0,0 @@ - - -
-
-

{title}

-
- -
-
- - - - {#each columns as header} - - {/each} - - - - {#if paginatedData.length === 0} -
No Data.
- {/if} - {#each paginatedData as row} - - {#each columns as header} - - {/each} - - {/each} - -
{header}
- {#if schema[header].type === 'attachment'} - - {:else}{getOr('', header, row)}{/if} -
- -
- - diff --git a/packages/builder/src/components/database/DataTable/ViewDataTable.svelte b/packages/builder/src/components/database/DataTable/ViewDataTable.svelte deleted file mode 100644 index 164683ef5f..0000000000 --- a/packages/builder/src/components/database/DataTable/ViewDataTable.svelte +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - {#if view.calculation} - - {/if} - -
diff --git a/packages/builder/src/components/database/DataTable/index.js b/packages/builder/src/components/database/DataTable/index.js deleted file mode 100644 index bd9df8885c..0000000000 --- a/packages/builder/src/components/database/DataTable/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./ModelDataTable.svelte" diff --git a/packages/builder/src/components/database/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/database/DataTable/modals/CreateEditColumn.svelte deleted file mode 100644 index 1e5e23823c..0000000000 --- a/packages/builder/src/components/database/DataTable/modals/CreateEditColumn.svelte +++ /dev/null @@ -1,161 +0,0 @@ - - -
- - - - -
-
- - -
- - {#if field.type === 'string' && field.constraints} - - - {:else if field.type === 'datetime' && field.constraints} - - - {:else if field.type === 'number' && field.constraints} - - - {:else if field.type === 'link'} -
- - -
- {/if} -
- -
-
-
- -
-
- -
-
- - diff --git a/packages/builder/src/components/database/DataTable/modals/CreateEditRecord.svelte b/packages/builder/src/components/database/DataTable/modals/CreateEditRecord.svelte deleted file mode 100644 index 8edecd913c..0000000000 --- a/packages/builder/src/components/database/DataTable/modals/CreateEditRecord.svelte +++ /dev/null @@ -1,93 +0,0 @@ - - -
- -
- {#each modelSchema as [key, meta]} -
- {#if meta.type === 'link'} - - {:else} - - {/if} -
- {/each} -
-
-
-
- -
-
- -
-
- - diff --git a/packages/builder/src/components/database/DataTable/modals/DeleteRecord.svelte b/packages/builder/src/components/database/DataTable/modals/DeleteRecord.svelte deleted file mode 100644 index 4b4d51329e..0000000000 --- a/packages/builder/src/components/database/DataTable/modals/DeleteRecord.svelte +++ /dev/null @@ -1,61 +0,0 @@ - - -
-
-
- -

Delete Record

-
-

- Are you sure you want to delete this record? All of your data will be - permanently removed. This action cannot be undone. -

-
- -
- - diff --git a/packages/builder/src/components/database/DataTable/modals/DeleteTable.svelte b/packages/builder/src/components/database/DataTable/modals/DeleteTable.svelte deleted file mode 100644 index c47b9e632a..0000000000 --- a/packages/builder/src/components/database/DataTable/modals/DeleteTable.svelte +++ /dev/null @@ -1,64 +0,0 @@ - - -
-
-
- -

Delete Table

-
-

- Are you sure you want to delete this table? All of your data will be - permanently removed. This action cannot be undone. -

-
- -
- - diff --git a/packages/builder/src/components/database/DataTable/modals/DeleteView.svelte b/packages/builder/src/components/database/DataTable/modals/DeleteView.svelte deleted file mode 100644 index 9d6c11cdb0..0000000000 --- a/packages/builder/src/components/database/DataTable/modals/DeleteView.svelte +++ /dev/null @@ -1,62 +0,0 @@ - - -
-
-
- -

Delete View

-
-

- Are you sure you want to delete this view? All of your data will be - permanently removed. This action cannot be undone. -

-
- -
- - diff --git a/packages/builder/src/components/database/DataTable/modals/RecordFieldControl.svelte b/packages/builder/src/components/database/DataTable/modals/RecordFieldControl.svelte deleted file mode 100644 index 991a5fea63..0000000000 --- a/packages/builder/src/components/database/DataTable/modals/RecordFieldControl.svelte +++ /dev/null @@ -1,75 +0,0 @@ - - -{#if type === 'select'} - -{:else if type === 'date'} - - -{:else if type === 'file'} - - -{:else} - {#if type === 'checkbox'} - - {/if} - -{/if} - - diff --git a/packages/builder/src/components/database/DataTable/modals/index.js b/packages/builder/src/components/database/DataTable/modals/index.js deleted file mode 100644 index af0b3cd70b..0000000000 --- a/packages/builder/src/components/database/DataTable/modals/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default as DeleteRecordModal } from "./DeleteRecord.svelte" -export { default as CreateEditRecordModal } from "./CreateEditRecord.svelte" diff --git a/packages/builder/src/components/database/DataTable/popovers/Column.svelte b/packages/builder/src/components/database/DataTable/popovers/Column.svelte deleted file mode 100644 index 2047a9a31b..0000000000 --- a/packages/builder/src/components/database/DataTable/popovers/Column.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - -
- -
- -
Create Column
- -
- - diff --git a/packages/builder/src/components/database/DataTable/popovers/EditRow.svelte b/packages/builder/src/components/database/DataTable/popovers/EditRow.svelte deleted file mode 100644 index a5a04c79c8..0000000000 --- a/packages/builder/src/components/database/DataTable/popovers/EditRow.svelte +++ /dev/null @@ -1,103 +0,0 @@ - - -
- -
- - {#if editing} -
Edit Row
- - {:else} -
    -
  • - - Edit -
  • -
  • - - Delete -
  • -
- {/if} -
- - diff --git a/packages/builder/src/components/database/DataTable/popovers/Export.svelte b/packages/builder/src/components/database/DataTable/popovers/Export.svelte deleted file mode 100644 index 00514edb9c..0000000000 --- a/packages/builder/src/components/database/DataTable/popovers/Export.svelte +++ /dev/null @@ -1,71 +0,0 @@ - - -
- - - Export - -
- -
Export Format
- -
- - -
-
- - diff --git a/packages/builder/src/components/nav/ModelNavigator/CreateTable.svelte b/packages/builder/src/components/nav/ModelNavigator/CreateTable.svelte deleted file mode 100644 index fe723843b3..0000000000 --- a/packages/builder/src/components/nav/ModelNavigator/CreateTable.svelte +++ /dev/null @@ -1,113 +0,0 @@ - - -
- -
- -
-
Create Table
- - - - - -
-
-
- -
-
- -
-
-
- - diff --git a/packages/builder/src/components/nav/ModelNavigator/EditTable.svelte b/packages/builder/src/components/nav/ModelNavigator/EditTable.svelte deleted file mode 100644 index e7926063dd..0000000000 --- a/packages/builder/src/components/nav/ModelNavigator/EditTable.svelte +++ /dev/null @@ -1,137 +0,0 @@ - - -
- -
- - {#if editing} -
Edit Table
-
- -
-
-
- -
-
- -
-
- {:else} -
    -
  • - - Edit -
  • -
  • - - Delete -
  • -
- {/if} -
- - diff --git a/packages/builder/src/components/nav/NavItem.svelte b/packages/builder/src/components/nav/NavItem.svelte deleted file mode 100644 index 002c4e0802..0000000000 --- a/packages/builder/src/components/nav/NavItem.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - -
$goto(href)} - class="budibase__nav-item backend-nav-item" - class:selected={$isActive(href)}> - {label} -
- - diff --git a/packages/builder/src/components/settings/Link.svelte b/packages/builder/src/components/settings/Link.svelte index 9d669bc7b2..3bb6d6e40d 100644 --- a/packages/builder/src/components/settings/Link.svelte +++ b/packages/builder/src/components/settings/Link.svelte @@ -1,32 +1,17 @@ - + + + + diff --git a/packages/builder/src/components/settings/SettingsModal.svelte b/packages/builder/src/components/settings/SettingsModal.svelte new file mode 100644 index 0000000000..44ab581521 --- /dev/null +++ b/packages/builder/src/components/settings/SettingsModal.svelte @@ -0,0 +1,52 @@ + + + +
+ + + +
+
+ + diff --git a/packages/builder/src/components/settings/UserRow.svelte b/packages/builder/src/components/settings/UserRow.svelte index 43ffc33fe2..30b03d9755 100644 --- a/packages/builder/src/components/settings/UserRow.svelte +++ b/packages/builder/src/components/settings/UserRow.svelte @@ -14,7 +14,7 @@ bind:value={user.username} name="Name" placeholder="Username" /> - @@ -37,19 +37,7 @@ .inputs { display: grid; justify-items: stretch; - grid-gap: 18px; - grid-template-columns: 1fr 1fr 1fr; - } - .inputs :global(input) { - padding: 10px 12px; - border-radius: var(--rounded-small); - } - .inputs :global(select) { - padding: 9px 12px; - border-radius: var(--rounded-small); - } - .inputs :global(button) { - border-radius: var(--rounded-small); - height: initial; + grid-gap: var(--spacing-m); + grid-template-columns: 1fr 1fr 140px; } diff --git a/packages/builder/src/components/settings/tabs/APIKeys.svelte b/packages/builder/src/components/settings/tabs/APIKeys.svelte index 7645548348..8a3c9fc710 100644 --- a/packages/builder/src/components/settings/tabs/APIKeys.svelte +++ b/packages/builder/src/components/settings/tabs/APIKeys.svelte @@ -1,8 +1,6 @@
-
- updateKey(['budibase', e.detail])} - thin - edit - value={keys.budibase} - label="Budibase" /> -
+ updateKey(['budibase', e.detail])} + thin + edit + value={keys.budibase} + label="Budibase API Key" />
diff --git a/packages/builder/src/components/settings/tabs/DangerZone.svelte b/packages/builder/src/components/settings/tabs/DangerZone.svelte index e87c5c4cd9..ee592ec6b1 100644 --- a/packages/builder/src/components/settings/tabs/DangerZone.svelte +++ b/packages/builder/src/components/settings/tabs/DangerZone.svelte @@ -1,6 +1,6 @@
-

- Type DELETE into the textbox, then click the following button to delete your - web app: -

+ + Type + DELETE + into the textbox, then click the following button to delete your entire web + app. + (value = e.target.value)} on:input={e => (value = e.target.value)} thin disabled={loading} placeholder="" /> - - +
+ +
diff --git a/packages/builder/src/components/settings/tabs/General.svelte b/packages/builder/src/components/settings/tabs/General.svelte index 7069dd2812..04d4672d50 100644 --- a/packages/builder/src/components/settings/tabs/General.svelte +++ b/packages/builder/src/components/settings/tabs/General.svelte @@ -22,19 +22,18 @@ thin edit value={$store.name} - label="Name" /> + label="App Name" />