@budibase/worker 3.11.1 → 3.12.0
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.
|
|
4
|
+
"version": "3.12.0",
|
|
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": "
|
|
126
|
+
"gitHead": "4d3f7719ab1ae02d3d01908b8a14a31a0c81445d"
|
|
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 {
|
|
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<
|
|
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,
|
|
@@ -839,9 +839,8 @@ describe("scim", () => {
|
|
|
839
839
|
|
|
840
840
|
it("creating an external group that conflicts an internal one syncs the existing group", async () => {
|
|
841
841
|
const groupToSave = structures.userGroups.userGroup()
|
|
842
|
-
const { body: internalGroup } =
|
|
843
|
-
groupToSave
|
|
844
|
-
)
|
|
842
|
+
const { body: internalGroup } =
|
|
843
|
+
await config.api.groups.saveGroup(groupToSave)
|
|
845
844
|
|
|
846
845
|
const scimGroupData = {
|
|
847
846
|
externalId: structures.uuid(),
|
package/src/utilities/email.ts
CHANGED
|
@@ -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 || {},
|