@budibase/worker 3.11.1 → 3.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/worker",
3
3
  "email": "hi@budibase.com",
4
- "version": "3.11.1",
4
+ "version": "3.11.2",
5
5
  "description": "Budibase background service",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -69,6 +69,7 @@
69
69
  "koa-static": "5.0.0",
70
70
  "koa-useragent": "^4.1.0",
71
71
  "lodash": "4.17.21",
72
+ "marked": "^15.0.11",
72
73
  "node-fetch": "2.6.7",
73
74
  "nodemailer": "6.9.9",
74
75
  "passport-google-oauth": "2.0.0",
@@ -92,6 +93,7 @@
92
93
  "@types/server-destroy": "1.0.1",
93
94
  "@types/supertest": "2.0.14",
94
95
  "@types/uuid": "8.3.4",
96
+ "cheerio": "^1.0.0",
95
97
  "jest": "29.7.0",
96
98
  "maildev": "^2.2.1",
97
99
  "nock": "^13.5.4",
@@ -121,5 +123,5 @@
121
123
  }
122
124
  }
123
125
  },
124
- "gitHead": "721e06c1447afa703270d0c1020ca792c23590b7"
126
+ "gitHead": "4c2f13e1b36fa559124df22462d83d2b4d1e7e9d"
125
127
  }
@@ -9,6 +9,7 @@ import {
9
9
  stopMailserver,
10
10
  } from "../../../../tests/mocks/email"
11
11
  import { objectStore } from "@budibase/backend-core"
12
+ import * as cheerio from "cheerio"
12
13
 
13
14
  describe("/api/global/email", () => {
14
15
  const config = new TestConfiguration()
@@ -266,4 +267,118 @@ describe("/api/global/email", () => {
266
267
  `DTEND:${formatDate(endTime)}`
267
268
  )
268
269
  })
270
+
271
+ it("Should parse valid markdown content from automation steps into valid HTML.", async () => {
272
+ // Basic verification that the markdown is being processed.
273
+ const email = await captureEmail(mailserver, async () => {
274
+ const res = await config.api.emails.sendEmail({
275
+ email: "to@example.com",
276
+ subject: "Test",
277
+ userId: config.user!._id,
278
+ contents: `test@home.com [Call Me!](tel:1111111)`,
279
+ purpose: EmailTemplatePurpose.CUSTOM,
280
+ })
281
+ expect(res.message).toBeDefined()
282
+ })
283
+
284
+ const $ = cheerio.load(email.html)
285
+
286
+ // Verify the email body rendered
287
+ const emailBody = $("td.email-body").first()
288
+ expect(emailBody.length).toBe(1)
289
+
290
+ // Verify a valid link was generated and is queryable
291
+ const emailLink = $("a[href^='mailto:']").first()
292
+ expect(emailLink.length).toBe(1)
293
+ expect(emailLink.text()).toBe("test@home.com")
294
+
295
+ // Verify the markdown link has been built correctly
296
+ const phoneLink = $("a[href^='tel:']").first()
297
+ expect(phoneLink.length).toBe(1)
298
+ expect(phoneLink.text()).toBe("Call Me!")
299
+ expect(phoneLink.attr("href")).toBe("tel:1111111")
300
+ })
301
+
302
+ it("Should ignore invalid markdown content and return nothing", async () => {
303
+ // The only failure case for a parse with marked is 'undefined'
304
+ // It should be caught and resolve to nothing.
305
+ const email = await captureEmail(mailserver, async () => {
306
+ const res = await config.api.emails.sendEmail({
307
+ email: "to@example.com",
308
+ subject: "Test",
309
+ userId: config.user!._id,
310
+ contents: undefined,
311
+ purpose: EmailTemplatePurpose.CUSTOM,
312
+ })
313
+ expect(res.message).toBeDefined()
314
+ })
315
+
316
+ const $ = cheerio.load(email.html)
317
+ const emailBody = $("td.email-body").first()
318
+ expect(emailBody.length).toBe(1)
319
+
320
+ const bodyText = emailBody.text().trim()
321
+ expect(bodyText).toBe("")
322
+ })
323
+
324
+ it("Should render a mixture of content. Plain text, markdown and HTML", async () => {
325
+ // A more involved check to ensure all content types are still respected
326
+ const email = await captureEmail(mailserver, async () => {
327
+ const res = await config.api.emails.sendEmail({
328
+ email: "to@example.com",
329
+ subject: "Test",
330
+ userId: config.user!._id,
331
+ contents: `<div class="html-content"><strong>Some content</strong></div>
332
+
333
+ # A heading
334
+ - This should be list entry 1
335
+ - This should be list entry 2
336
+
337
+ Some plain text`,
338
+ purpose: EmailTemplatePurpose.CUSTOM,
339
+ })
340
+ expect(res.message).toBeDefined()
341
+ })
342
+
343
+ const $ = cheerio.load(email.html)
344
+ const emailBody = $("td.email-body").first()
345
+ expect(emailBody.length).toBe(1)
346
+
347
+ const divEle = emailBody.find("div.html-content").first()
348
+ expect(divEle.length).toBe(1)
349
+ expect(divEle.text()).toBe("Some content")
350
+
351
+ const heading = emailBody.find("h1").first()
352
+ expect(heading.length).toBe(1)
353
+ expect(heading.text()).toBe("A heading")
354
+
355
+ // Both list items rendered
356
+ const listEles = emailBody.find("ul li")
357
+ expect(listEles.length).toBe(2)
358
+
359
+ const plainText = emailBody.find("p")
360
+ expect(plainText.length).toBe(1)
361
+ expect(plainText.text()).toBe("Some plain text")
362
+ })
363
+
364
+ it("Should only parse markdown content for the CUSTOM email template used in automation steps", async () => {
365
+ const email = await captureEmail(mailserver, async () => {
366
+ const res = await config.api.emails.sendEmail({
367
+ email: "to@example.com",
368
+ subject: "Test",
369
+ userId: config.user!._id,
370
+ purpose: EmailTemplatePurpose.INVITATION,
371
+ })
372
+ expect(res.message).toBeDefined()
373
+ })
374
+
375
+ const $ = cheerio.load(email.html)
376
+ const emailBody = $("td.email-body").first()
377
+ expect(emailBody.length).toBe(1)
378
+
379
+ const heading = emailBody.find("h1").first()
380
+ expect(heading.length).toBe(1)
381
+ // The email should not be parsed as markdown.
382
+ expect(heading.text()).toBe("Hi, to@example.com!")
383
+ })
269
384
  })
@@ -1,6 +1,11 @@
1
1
  import { structures, TestConfiguration } from "../../../../tests"
2
2
  import { context, db, roles } from "@budibase/backend-core"
3
- import { App, Database, BuiltinPermissionID } from "@budibase/types"
3
+ import {
4
+ App,
5
+ Database,
6
+ BuiltinPermissionID,
7
+ WithoutDocMetadata,
8
+ } from "@budibase/types"
4
9
 
5
10
  jest.mock("@budibase/backend-core", () => {
6
11
  const core = jest.requireActual("@budibase/backend-core")
@@ -30,7 +35,7 @@ async function addAppMetadata() {
30
35
  })
31
36
  }
32
37
 
33
- async function updateAppMetadata(update: Partial<Omit<App, "_id" | "_rev">>) {
38
+ async function updateAppMetadata(update: Partial<WithoutDocMetadata<App>>) {
34
39
  const app = await appDb.get("app_metadata")
35
40
  await appDb.put({
36
41
  ...app,
@@ -47,7 +47,7 @@
47
47
  body,
48
48
  td,
49
49
  th {
50
- font-family: "Source Sans Pro", Helvetica, Arial, sans-serif;
50
+ font-family: "Source Sans 3", Helvetica, Arial, sans-serif;
51
51
  }
52
52
 
53
53
  h1 {
@@ -12,6 +12,7 @@ import {
12
12
  import { configs, cache, objectStore, HTTPError } from "@budibase/backend-core"
13
13
  import ical from "ical-generator"
14
14
  import _ from "lodash"
15
+ import { marked } from "marked"
15
16
 
16
17
  import nodemailer from "nodemailer"
17
18
  import SMTPTransport from "nodemailer/lib/smtp-transport"
@@ -97,7 +98,10 @@ async function buildEmail(
97
98
  }
98
99
  context = {
99
100
  ...context,
100
- contents,
101
+ contents:
102
+ purpose === EmailTemplatePurpose.CUSTOM
103
+ ? marked.parse(contents || "")
104
+ : contents,
101
105
  email,
102
106
  name,
103
107
  user: user || {},