diff --git a/packages/server/src/db/linkedRows/linkUtils.js b/packages/server/src/db/linkedRows/linkUtils.js index 6549f9b61b..cb669cf5c7 100644 --- a/packages/server/src/db/linkedRows/linkUtils.js +++ b/packages/server/src/db/linkedRows/linkUtils.js @@ -84,6 +84,13 @@ exports.getLinkDocuments = async function({ // filter to get unique entries const foundIds = [] linkRows = linkRows.filter(link => { + // make sure anything unique is the correct key + if ( + (tableId && link.key[0] !== tableId) || + (rowId && link.key[1] !== rowId) + ) { + return false + } const unique = foundIds.indexOf(link.id) === -1 if (unique) { foundIds.push(link.id) diff --git a/packages/string-templates/manifest.json b/packages/string-templates/manifest.json index 294eeeec5e..af1f4e3d54 100644 --- a/packages/string-templates/manifest.json +++ b/packages/string-templates/manifest.json @@ -1097,8 +1097,8 @@ "format" ], "numArgs": 2, - "example": "{{date now \"YYYY\"}}", - "description": "

Format a date using moment.js data formatting.

\n" + "example": "{{date now \"DD-MM-YYYY\"}}", + "description": "

Format a date using moment.js date formatting.

\n" } } } \ No newline at end of file diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 8bbcccf3fc..dc2bcf7f54 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -15,9 +15,9 @@ }, "dependencies": { "@budibase/handlebars-helpers": "^0.11.3", + "dayjs": "^1.10.4", "handlebars": "^4.7.6", "handlebars-utils": "^1.0.6", - "helper-date": "^1.0.1", "lodash": "^4.17.20" }, "devDependencies": { diff --git a/packages/string-templates/scripts/gen-collection-info.js b/packages/string-templates/scripts/gen-collection-info.js index 413e870519..76b5c89e0c 100644 --- a/packages/string-templates/scripts/gen-collection-info.js +++ b/packages/string-templates/scripts/gen-collection-info.js @@ -139,8 +139,8 @@ function run() { date: { args: ["datetime", "format"], numArgs: 2, - example: '{{date now "YYYY"}}', - description: "Format a date using moment.js data formatting.", + example: '{{date now "DD-MM-YYYY"}}', + description: "Format a date using moment.js date formatting.", }, } // convert all markdown to HTML diff --git a/packages/string-templates/src/helpers/date.js b/packages/string-templates/src/helpers/date.js new file mode 100644 index 0000000000..1c929a758f --- /dev/null +++ b/packages/string-templates/src/helpers/date.js @@ -0,0 +1,78 @@ +const dayjs = require("dayjs") + +/** + * This file was largely taken from the helper-date package - we did this for two reasons: + * 1. It made use of both moment of date.js - this caused some weird bugs with some relatively simple + * syntax and didn't offer much in return. + * 2. Replacing moment with dayjs helps massively reduce bundle size. + * The original package can be found here: + * https://github.com/helpers/helper-date + */ + +function isOptions(val) { + return typeof val === "object" && typeof val.hash === "object" +} + +function isApp(thisArg) { + return ( + typeof thisArg === "object" && + typeof thisArg.options === "object" && + typeof thisArg.app === "object" + ) +} + +function getContext(thisArg, locals, options) { + if (isOptions(thisArg)) { + return getContext({}, locals, thisArg) + } + // ensure args are in the correct order + if (isOptions(locals)) { + return getContext(thisArg, options, locals) + } + const appContext = isApp(thisArg) ? thisArg.context : {} + options = options || {} + + // if "options" is not handlebars options, merge it onto locals + if (!isOptions(options)) { + locals = Object.assign({}, locals, options) + } + // merge handlebars root data onto locals if specified on the hash + if (isOptions(options) && options.hash.root === true) { + locals = Object.assign({}, options.data.root, locals) + } + let context = Object.assign({}, appContext, locals, options.hash) + if (!isApp(thisArg)) { + context = Object.assign({}, thisArg, context) + } + if (isApp(thisArg) && thisArg.view && thisArg.view.data) { + context = Object.assign({}, context, thisArg.view.data) + } + return context +} + +module.exports = function dateHelper(str, pattern, options) { + if (isOptions(pattern)) { + options = pattern + pattern = null + } + + if (isOptions(str)) { + options = str + pattern = null + str = null + } + + // if no args are passed, return a formatted date + if (str == null && pattern == null) { + dayjs.locale("en") + return dayjs().format("MMMM DD, YYYY") + } + + const defaults = { lang: "en", date: new Date(str) } + const opts = getContext(this, defaults, options) + + // set the language to use + dayjs.locale(opts.lang || opts.language) + + return dayjs(new Date(str)).format(pattern) +} diff --git a/packages/string-templates/src/helpers/external.js b/packages/string-templates/src/helpers/external.js index 3256134371..138565889d 100644 --- a/packages/string-templates/src/helpers/external.js +++ b/packages/string-templates/src/helpers/external.js @@ -1,5 +1,5 @@ const helpers = require("@budibase/handlebars-helpers") -const dateHelper = require("helper-date") +const dateHelper = require("./date") const { HelperFunctionBuiltin } = require("./constants") /** diff --git a/packages/string-templates/src/index.js b/packages/string-templates/src/index.js index e7d9b9d7e7..e662f253c6 100644 --- a/packages/string-templates/src/index.js +++ b/packages/string-templates/src/index.js @@ -2,7 +2,11 @@ const handlebars = require("handlebars") const { registerAll } = require("./helpers/index") const processors = require("./processors") const { cloneDeep } = require("lodash/fp") -const { removeNull, addConstants, removeHandlebarsStatements } = require("./utilities") +const { + removeNull, + addConstants, + removeHandlebarsStatements, +} = require("./utilities") const manifest = require("../manifest.json") const hbsInstance = handlebars.create() @@ -121,7 +125,13 @@ module.exports.makePropSafe = property => { * @returns {boolean} Whether or not the input string is valid. */ module.exports.isValid = string => { - const validCases = ["string", "number", "object", "array", "cannot read property"] + const validCases = [ + "string", + "number", + "object", + "array", + "cannot read property", + ] // this is a portion of a specific string always output by handlebars in the case of a syntax error const invalidCases = [`expecting '`] // don't really need a real context to check if its valid diff --git a/packages/string-templates/test/helpers.spec.js b/packages/string-templates/test/helpers.spec.js index cd659fcc78..b8b44a0929 100644 --- a/packages/string-templates/test/helpers.spec.js +++ b/packages/string-templates/test/helpers.spec.js @@ -1,5 +1,6 @@ const { processString, + processObject, isValid, } = require("../src/index") @@ -330,4 +331,11 @@ describe("Cover a few complex use cases", () => { const output = await processString(input, {}) expect(output).toBe(input) }) + + it("getting a nice date from the user", async () => { + const input = {text: `{{ date user.subscriptionDue "DD-MM" }}`} + const context = JSON.parse(`{"user":{"email":"test@test.com","roleId":"ADMIN","type":"user","tableId":"ta_users","subscriptionDue":"2021-01-12T12:00:00.000Z","_id":"ro_ta_users_us_test@test.com","_rev":"2-24cc794985eb54183ecb93e148563f3d"}}`) + const output = await processObject(input, context) + expect(output.text).toBe("12-01") + }) }) \ No newline at end of file diff --git a/packages/string-templates/yarn.lock b/packages/string-templates/yarn.lock index a3d5ac638d..cb96e4e81c 100644 --- a/packages/string-templates/yarn.lock +++ b/packages/string-templates/yarn.lock @@ -1596,6 +1596,11 @@ date.js@^0.3.1: dependencies: debug "~3.1.0" +dayjs@^1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2" + integrity sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw== + debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"