From cd98882127f4ba5f818685e5b8b41462946b6ddd Mon Sep 17 00:00:00 2001
From: Sam Rose <hello@samwho.dev>
Date: Wed, 3 Jul 2024 16:30:23 +0100
Subject: [PATCH 1/4] Move secrets into backend-core.

---
 packages/backend-core/src/environment.ts      |  3 ++
 .../server/src/automations/steps/openai.ts    |  6 +--
 .../src/automations/tests/openai.spec.ts      | 37 ++++++++++---------
 packages/server/src/environment.ts            |  3 --
 packages/server/src/startup/index.ts          |  5 ++-
 .../server/src/startup/tests/startup.spec.ts  | 29 +++++++++------
 6 files changed, 47 insertions(+), 36 deletions(-)

diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts
index e58660a889..685f2988ad 100644
--- a/packages/backend-core/src/environment.ts
+++ b/packages/backend-core/src/environment.ts
@@ -200,6 +200,9 @@ const environment = {
   },
   ROLLING_LOG_MAX_SIZE: process.env.ROLLING_LOG_MAX_SIZE || "10M",
   DISABLE_SCIM_CALLS: process.env.DISABLE_SCIM_CALLS,
+  BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL,
+  BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD,
+  OPENAI_API_KEY: process.env.OPENAI_API_KEY,
 }
 
 // clean up any environment variable edge cases
diff --git a/packages/server/src/automations/steps/openai.ts b/packages/server/src/automations/steps/openai.ts
index 45ef5ef703..380a6b9536 100644
--- a/packages/server/src/automations/steps/openai.ts
+++ b/packages/server/src/automations/steps/openai.ts
@@ -7,8 +7,8 @@ import {
   AutomationStepType,
   AutomationIOType,
 } from "@budibase/types"
+import { env } from "@budibase/backend-core"
 import * as automationUtils from "../automationUtils"
-import environment from "../../environment"
 
 enum Model {
   GPT_35_TURBO = "gpt-3.5-turbo",
@@ -60,7 +60,7 @@ export const definition: AutomationStepSchema = {
 }
 
 export async function run({ inputs }: AutomationStepInput) {
-  if (!environment.OPENAI_API_KEY) {
+  if (!env.OPENAI_API_KEY) {
     return {
       success: false,
       response:
@@ -77,7 +77,7 @@ export async function run({ inputs }: AutomationStepInput) {
 
   try {
     const openai = new OpenAI({
-      apiKey: environment.OPENAI_API_KEY,
+      apiKey: env.OPENAI_API_KEY,
     })
 
     const completion = await openai.chat.completions.create({
diff --git a/packages/server/src/automations/tests/openai.spec.ts b/packages/server/src/automations/tests/openai.spec.ts
index 618c2d7754..44b410a0f1 100644
--- a/packages/server/src/automations/tests/openai.spec.ts
+++ b/packages/server/src/automations/tests/openai.spec.ts
@@ -1,6 +1,4 @@
-const setup = require("./utilities")
-
-import environment from "../../environment"
+import { getConfig, runStep, afterAll as _afterAll } from "./utilities"
 import { OpenAI } from "openai"
 
 jest.mock("openai", () => ({
@@ -26,32 +24,37 @@ const mockedOpenAI = OpenAI as jest.MockedClass<typeof OpenAI>
 const OPENAI_PROMPT = "What is the meaning of life?"
 
 describe("test the openai action", () => {
-  let config = setup.getConfig()
+  let config = getConfig()
+  let resetEnv: () => void | undefined
 
   beforeAll(async () => {
     await config.init()
   })
 
   beforeEach(() => {
-    environment.OPENAI_API_KEY = "abc123"
+    resetEnv = config.setCoreEnv({ OPENAI_API_KEY: "abc123" })
   })
 
-  afterAll(setup.afterAll)
+  afterEach(() => {
+    resetEnv()
+  })
+
+  afterAll(_afterAll)
 
   it("should present the correct error message when the OPENAI_API_KEY variable isn't set", async () => {
-    delete environment.OPENAI_API_KEY
-
-    let res = await setup.runStep("OPENAI", {
-      prompt: OPENAI_PROMPT,
+    await config.withCoreEnv({ OPENAI_API_KEY: "" }, async () => {
+      let res = await runStep("OPENAI", {
+        prompt: OPENAI_PROMPT,
+      })
+      expect(res.response).toEqual(
+        "OpenAI API Key not configured - please add the OPENAI_API_KEY environment variable."
+      )
+      expect(res.success).toBeFalsy()
     })
-    expect(res.response).toEqual(
-      "OpenAI API Key not configured - please add the OPENAI_API_KEY environment variable."
-    )
-    expect(res.success).toBeFalsy()
   })
 
   it("should be able to receive a response from ChatGPT given a prompt", async () => {
-    const res = await setup.runStep("OPENAI", {
+    const res = await runStep("OPENAI", {
       prompt: OPENAI_PROMPT,
     })
     expect(res.response).toEqual("This is a test")
@@ -59,7 +62,7 @@ describe("test the openai action", () => {
   })
 
   it("should present the correct error message when a prompt is not provided", async () => {
-    const res = await setup.runStep("OPENAI", {
+    const res = await runStep("OPENAI", {
       prompt: null,
     })
     expect(res.response).toEqual(
@@ -84,7 +87,7 @@ describe("test the openai action", () => {
         } as any)
     )
 
-    const res = await setup.runStep("OPENAI", {
+    const res = await runStep("OPENAI", {
       prompt: OPENAI_PROMPT,
     })
 
diff --git a/packages/server/src/environment.ts b/packages/server/src/environment.ts
index 341483d861..91e99b8850 100644
--- a/packages/server/src/environment.ts
+++ b/packages/server/src/environment.ts
@@ -81,10 +81,7 @@ const environment = {
   AUTOMATION_THREAD_TIMEOUT:
     parseIntSafe(process.env.AUTOMATION_THREAD_TIMEOUT) ||
     DEFAULT_AUTOMATION_TIMEOUT,
-  BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL,
-  BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD,
   PLUGINS_DIR: process.env.PLUGINS_DIR || DEFAULTS.PLUGINS_DIR,
-  OPENAI_API_KEY: process.env.OPENAI_API_KEY,
   MAX_IMPORT_SIZE_MB: process.env.MAX_IMPORT_SIZE_MB,
   SESSION_EXPIRY_SECONDS: process.env.SESSION_EXPIRY_SECONDS,
   // SQL
diff --git a/packages/server/src/startup/index.ts b/packages/server/src/startup/index.ts
index c86b0eb264..a5e26ca48e 100644
--- a/packages/server/src/startup/index.ts
+++ b/packages/server/src/startup/index.ts
@@ -8,6 +8,7 @@ import {
   tenancy,
   users,
   cache,
+  env as coreEnv,
 } from "@budibase/backend-core"
 import { watch } from "../watch"
 import * as automations from "../automations"
@@ -132,8 +133,8 @@ export async function startup(
   // check and create admin user if required
   // this must be run after the api has been initialised due to
   // the app user sync
-  const bbAdminEmail = env.BB_ADMIN_USER_EMAIL,
-    bbAdminPassword = env.BB_ADMIN_USER_PASSWORD
+  const bbAdminEmail = coreEnv.BB_ADMIN_USER_EMAIL,
+    bbAdminPassword = coreEnv.BB_ADMIN_USER_PASSWORD
   if (
     env.SELF_HOSTED &&
     !env.MULTI_TENANCY &&
diff --git a/packages/server/src/startup/tests/startup.spec.ts b/packages/server/src/startup/tests/startup.spec.ts
index ed31bc45b7..fef9270bb5 100644
--- a/packages/server/src/startup/tests/startup.spec.ts
+++ b/packages/server/src/startup/tests/startup.spec.ts
@@ -14,20 +14,27 @@ describe("check BB_ADMIN environment variables", () => {
     await tenancy.doInTenant(tenancy.DEFAULT_TENANT_ID, async () => {
       await config.withEnv(
         {
-          BB_ADMIN_USER_EMAIL: EMAIL,
-          BB_ADMIN_USER_PASSWORD: PASSWORD,
           MULTI_TENANCY: "0",
           SELF_HOSTED: "1",
         },
-        async () => {
-          await startup({ rerun: true })
-          const user = await users.getGlobalUserByEmail(EMAIL, {
-            cleanup: false,
-          })
-          expect(user).toBeDefined()
-          expect(user?.password).toBeDefined()
-          expect(await utils.compare(PASSWORD, user?.password!)).toEqual(true)
-        }
+        () =>
+          config.withCoreEnv(
+            {
+              BB_ADMIN_USER_EMAIL: EMAIL,
+              BB_ADMIN_USER_PASSWORD: PASSWORD,
+            },
+            async () => {
+              await startup({ rerun: true })
+              const user = await users.getGlobalUserByEmail(EMAIL, {
+                cleanup: false,
+              })
+              expect(user).toBeDefined()
+              expect(user?.password).toBeDefined()
+              expect(await utils.compare(PASSWORD, user?.password!)).toEqual(
+                true
+              )
+            }
+          )
       )
     })
   })

From d14cccb4c611eaaab777574206742efd5f9ae206 Mon Sep 17 00:00:00 2001
From: Sam Rose <hello@samwho.dev>
Date: Wed, 3 Jul 2024 16:39:30 +0100
Subject: [PATCH 2/4] Remove unused SENDGRID_API_KEY

---
 packages/server/src/environment.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/server/src/environment.ts b/packages/server/src/environment.ts
index c8203392e6..b643bd50b0 100644
--- a/packages/server/src/environment.ts
+++ b/packages/server/src/environment.ts
@@ -75,7 +75,6 @@ const environment = {
   AUTOMATION_MAX_ITERATIONS:
     parseIntSafe(process.env.AUTOMATION_MAX_ITERATIONS) ||
     DEFAULTS.AUTOMATION_MAX_ITERATIONS,
-  SENDGRID_API_KEY: process.env.SENDGRID_API_KEY,
   DYNAMO_ENDPOINT: process.env.DYNAMO_ENDPOINT,
   QUERY_THREAD_TIMEOUT: QUERY_THREAD_TIMEOUT,
   AUTOMATION_THREAD_TIMEOUT:

From 5fc5524693c8bfa38dfe6ddfadb63d895afbe334 Mon Sep 17 00:00:00 2001
From: Sam Rose <hello@samwho.dev>
Date: Wed, 3 Jul 2024 16:41:51 +0100
Subject: [PATCH 3/4] Delete unused SendGrid mock.

---
 packages/server/__mocks__/@sendgrid/mail.ts | 23 ---------------------
 1 file changed, 23 deletions(-)
 delete mode 100644 packages/server/__mocks__/@sendgrid/mail.ts

diff --git a/packages/server/__mocks__/@sendgrid/mail.ts b/packages/server/__mocks__/@sendgrid/mail.ts
deleted file mode 100644
index 8613ae4b16..0000000000
--- a/packages/server/__mocks__/@sendgrid/mail.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-module SendgridMock {
-  class Email {
-    constructor() {
-      // @ts-ignore
-      this.apiKey = null
-    }
-
-    setApiKey(apiKey: any) {
-      // @ts-ignore
-      this.apiKey = apiKey
-    }
-
-    async send(msg: any) {
-      if (msg.to === "invalid@example.com") {
-        throw "Invalid"
-      }
-      return msg
-    }
-  }
-
-  module.exports = new Email()
-}

From 3a74df0a4a9d1cfe337fbd70032351b35c29aeb6 Mon Sep 17 00:00:00 2001
From: Sam Rose <hello@samwho.dev>
Date: Wed, 3 Jul 2024 17:04:31 +0100
Subject: [PATCH 4/4] Fix tests.

---
 packages/server/src/automations/tests/openai.spec.ts | 12 +++---------
 .../server/src/tests/utilities/TestConfiguration.ts  |  2 +-
 2 files changed, 4 insertions(+), 10 deletions(-)

diff --git a/packages/server/src/automations/tests/openai.spec.ts b/packages/server/src/automations/tests/openai.spec.ts
index 44b410a0f1..6bebe16a49 100644
--- a/packages/server/src/automations/tests/openai.spec.ts
+++ b/packages/server/src/automations/tests/openai.spec.ts
@@ -43,9 +43,7 @@ describe("test the openai action", () => {
 
   it("should present the correct error message when the OPENAI_API_KEY variable isn't set", async () => {
     await config.withCoreEnv({ OPENAI_API_KEY: "" }, async () => {
-      let res = await runStep("OPENAI", {
-        prompt: OPENAI_PROMPT,
-      })
+      let res = await runStep("OPENAI", { prompt: OPENAI_PROMPT })
       expect(res.response).toEqual(
         "OpenAI API Key not configured - please add the OPENAI_API_KEY environment variable."
       )
@@ -54,17 +52,13 @@ describe("test the openai action", () => {
   })
 
   it("should be able to receive a response from ChatGPT given a prompt", async () => {
-    const res = await runStep("OPENAI", {
-      prompt: OPENAI_PROMPT,
-    })
+    const res = await runStep("OPENAI", { prompt: OPENAI_PROMPT })
     expect(res.response).toEqual("This is a test")
     expect(res.success).toBeTruthy()
   })
 
   it("should present the correct error message when a prompt is not provided", async () => {
-    const res = await runStep("OPENAI", {
-      prompt: null,
-    })
+    const res = await runStep("OPENAI", { prompt: null })
     expect(res.response).toEqual(
       "Budibase OpenAI Automation Failed: No prompt supplied"
     )
diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts
index 325d911f07..793bfa8c6a 100644
--- a/packages/server/src/tests/utilities/TestConfiguration.ts
+++ b/packages/server/src/tests/utilities/TestConfiguration.ts
@@ -290,7 +290,7 @@ export default class TestConfiguration {
    * that can be called to reset the environment variables to their original values.
    */
   setCoreEnv(newEnvVars: Partial<typeof coreEnv>): () => void {
-    const oldEnv = cloneDeep(env)
+    const oldEnv = cloneDeep(coreEnv)
 
     let key: keyof typeof newEnvVars
     for (key in newEnvVars) {