@budibase/worker 2.23.10 → 2.23.12

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": "2.23.10",
4
+ "version": "2.23.12",
5
5
  "description": "Budibase background service",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -37,10 +37,10 @@
37
37
  "author": "Budibase",
38
38
  "license": "GPL-3.0",
39
39
  "dependencies": {
40
- "@budibase/backend-core": "2.23.10",
41
- "@budibase/pro": "2.23.10",
42
- "@budibase/string-templates": "2.23.10",
43
- "@budibase/types": "2.23.10",
40
+ "@budibase/backend-core": "2.23.12",
41
+ "@budibase/pro": "2.23.12",
42
+ "@budibase/string-templates": "2.23.12",
43
+ "@budibase/types": "2.23.12",
44
44
  "@koa/router": "8.0.8",
45
45
  "@techpass/passport-openidconnect": "0.3.2",
46
46
  "@types/global-agent": "2.1.1",
@@ -108,5 +108,5 @@
108
108
  }
109
109
  }
110
110
  },
111
- "gitHead": "b588d75a8af5c4f3ab2641997282879a0207265e"
111
+ "gitHead": "35dcb2465a0a116c4a6169a01b6154990747627a"
112
112
  }
@@ -35,8 +35,7 @@ async function passportCallback(
35
35
  info: { message: string } | null = null
36
36
  ) {
37
37
  if (err) {
38
- console.error("Authentication error")
39
- console.error(err)
38
+ console.error("Authentication error", err)
40
39
  console.trace(err)
41
40
  return ctx.throw(403, info ? info : "Unauthorized")
42
41
  }
@@ -15,6 +15,7 @@ export async function sendEmail(ctx: BBContext) {
15
15
  bcc,
16
16
  automation,
17
17
  invite,
18
+ attachments,
18
19
  } = ctx.request.body
19
20
  let user: any
20
21
  if (userId) {
@@ -31,6 +32,7 @@ export async function sendEmail(ctx: BBContext) {
31
32
  bcc,
32
33
  automation,
33
34
  invite,
35
+ attachments,
34
36
  })
35
37
  ctx.body = {
36
38
  ...response,
@@ -1,10 +1,16 @@
1
1
  jest.unmock("node-fetch")
2
+ jest.unmock("aws-sdk")
2
3
  import { TestConfiguration } from "../../../../tests"
3
4
  import { EmailTemplatePurpose } from "../../../../constants"
5
+ import { objectStoreTestProviders } from "@budibase/backend-core/tests"
6
+ import { objectStore } from "@budibase/backend-core"
7
+ import tk from "timekeeper"
8
+ import { EmailAttachment } from "@budibase/types"
4
9
 
5
- const nodemailer = require("nodemailer")
6
10
  const fetch = require("node-fetch")
7
11
 
12
+ const nodemailer = require("nodemailer")
13
+
8
14
  // for the real email tests give them a long time to try complete/fail
9
15
  jest.setTimeout(30000)
10
16
 
@@ -12,14 +18,20 @@ describe("/api/global/email", () => {
12
18
  const config = new TestConfiguration()
13
19
 
14
20
  beforeAll(async () => {
21
+ tk.reset()
22
+ await objectStoreTestProviders.minio.start()
15
23
  await config.beforeAll()
16
24
  })
17
25
 
18
26
  afterAll(async () => {
27
+ await objectStoreTestProviders.minio.stop()
19
28
  await config.afterAll()
20
29
  })
21
30
 
22
- async function sendRealEmail(purpose: string) {
31
+ async function sendRealEmail(
32
+ purpose: string,
33
+ attachments?: EmailAttachment[]
34
+ ) {
23
35
  let response, text
24
36
  try {
25
37
  const timeout = () =>
@@ -35,8 +47,14 @@ describe("/api/global/email", () => {
35
47
  )
36
48
  await Promise.race([config.saveEtherealSmtpConfig(), timeout()])
37
49
  await Promise.race([config.saveSettingsConfig(), timeout()])
38
-
39
- const res = await config.api.emails.sendEmail(purpose).timeout(20000)
50
+ let res
51
+ if (attachments) {
52
+ res = await config.api.emails
53
+ .sendEmail(purpose, attachments)
54
+ .timeout(20000)
55
+ } else {
56
+ res = await config.api.emails.sendEmail(purpose).timeout(20000)
57
+ }
40
58
  // ethereal hiccup, can't test right now
41
59
  if (res.status >= 300) {
42
60
  return
@@ -80,4 +98,25 @@ describe("/api/global/email", () => {
80
98
  it("should be able to send a password recovery email", async () => {
81
99
  await sendRealEmail(EmailTemplatePurpose.PASSWORD_RECOVERY)
82
100
  })
101
+
102
+ it("should be able to send an email with attachments", async () => {
103
+ let bucket = "testbucket"
104
+ let filename = "test.txt"
105
+ await objectStore.upload({
106
+ bucket,
107
+ filename,
108
+ body: Buffer.from("test data"),
109
+ })
110
+ let presignedUrl = await objectStore.getPresignedUrl(
111
+ bucket,
112
+ filename,
113
+ 60000
114
+ )
115
+
116
+ let attachmentObject = {
117
+ url: presignedUrl,
118
+ filename,
119
+ }
120
+ await sendRealEmail(EmailTemplatePurpose.WELCOME, [attachmentObject])
121
+ })
83
122
  })
@@ -1,3 +1,4 @@
1
+ import { EmailAttachment } from "@budibase/types"
1
2
  import TestConfiguration from "../TestConfiguration"
2
3
  import { TestAPI } from "./base"
3
4
 
@@ -6,11 +7,12 @@ export class EmailAPI extends TestAPI {
6
7
  super(config)
7
8
  }
8
9
 
9
- sendEmail = (purpose: string) => {
10
+ sendEmail = (purpose: string, attachments?: EmailAttachment[]) => {
10
11
  return this.request
11
12
  .post(`/api/global/email/send`)
12
13
  .send({
13
14
  email: "test@example.com",
15
+ attachments,
14
16
  purpose,
15
17
  tenantId: this.config.getTenantId(),
16
18
  userId: this.config.user?._id!,
@@ -4,8 +4,8 @@ process.env.JWT_SECRET = "test-jwtsecret"
4
4
  process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error"
5
5
  process.env.MULTI_TENANCY = "1"
6
6
  process.env.MINIO_URL = "http://localhost"
7
- process.env.MINIO_ACCESS_KEY = "test"
8
- process.env.MINIO_SECRET_KEY = "test"
7
+ process.env.MINIO_ACCESS_KEY = "budibase"
8
+ process.env.MINIO_SECRET_KEY = "budibase"
9
9
  process.env.PLATFORM_URL = "http://localhost:10000"
10
10
  process.env.INTERNAL_API_KEY = "tet"
11
11
  process.env.DISABLE_ACCOUNT_PORTAL = "0"
@@ -62,8 +62,8 @@ export function smtpEthereal(): SMTPConfig {
62
62
  from: "testfrom@example.com",
63
63
  secure: false,
64
64
  auth: {
65
- user: "wyatt.zulauf29@ethereal.email",
66
- pass: "tEwDtHBWWxusVWAPfa",
65
+ user: "mortimer.leuschke@ethereal.email",
66
+ pass: "5hSjsPbzRv7gEUsfzx",
67
67
  },
68
68
  connectionTimeout: 1000, // must be less than the jest default of 5000
69
69
  },
@@ -4,8 +4,10 @@ import { getTemplateByPurpose, EmailTemplates } from "../constants/templates"
4
4
  import { getSettingsTemplateContext } from "./templates"
5
5
  import { processString } from "@budibase/string-templates"
6
6
  import { User, SendEmailOpts, SMTPInnerConfig } from "@budibase/types"
7
- import { configs, cache } from "@budibase/backend-core"
7
+ import { configs, cache, objectStore } from "@budibase/backend-core"
8
8
  import ical from "ical-generator"
9
+ import fetch from "node-fetch"
10
+ import path from "path"
9
11
 
10
12
  const nodemailer = require("nodemailer")
11
13
 
@@ -162,6 +164,42 @@ export async function sendEmail(
162
164
  contents: opts?.contents,
163
165
  }),
164
166
  }
167
+ if (opts?.attachments) {
168
+ const attachments = await Promise.all(
169
+ opts.attachments?.map(async attachment => {
170
+ const isFullyFormedUrl =
171
+ attachment.url.startsWith("http://") ||
172
+ attachment.url.startsWith("https://")
173
+ if (isFullyFormedUrl) {
174
+ const response = await fetch(attachment.url)
175
+ if (!response.ok) {
176
+ throw new Error(`unexpected response ${response.statusText}`)
177
+ }
178
+ const fallbackFilename = path.basename(
179
+ new URL(attachment.url).pathname
180
+ )
181
+ return {
182
+ filename: attachment.filename || fallbackFilename,
183
+ content: response?.body,
184
+ }
185
+ } else {
186
+ const url = attachment.url
187
+ const result = objectStore.extractBucketAndPath(url)
188
+ if (result === null) {
189
+ throw new Error("Invalid signed URL")
190
+ }
191
+ const { bucket, path } = result
192
+ const readStream = await objectStore.getReadStream(bucket, path)
193
+ const fallbackFilename = path.split("/").pop() || ""
194
+ return {
195
+ filename: attachment.filename || fallbackFilename,
196
+ content: readStream,
197
+ }
198
+ }
199
+ })
200
+ )
201
+ message = { ...message, attachments }
202
+ }
165
203
 
166
204
  message = {
167
205
  ...message,