Merged in fix/SW-2401-share-cache-in-prod (pull request #1815)
Fix/SW-2401 share cache in prod * fix: reuse cache between prod and pre-prod * tests: add tests for generating cachePrefix * tests: remove unnecessary reset of process.env * tests: add tests for generateCacheKey * fix: make sure that we don't get invalid cacheKeys * fix: make sure that we don't get invalid cacheKeys Approved-by: Linus Flood
This commit is contained in:
@@ -1,14 +1,12 @@
|
|||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
import { generateCacheKey } from "./generateCacheKey"
|
|
||||||
|
|
||||||
export function getCacheEndpoint(key: string) {
|
export function getCacheEndpoint(key: string) {
|
||||||
if (!env.REDIS_API_HOST) {
|
if (!env.REDIS_API_HOST) {
|
||||||
throw new Error("REDIS_API_HOST is not set")
|
throw new Error("REDIS_API_HOST is not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = new URL(`/api/cache`, env.REDIS_API_HOST)
|
const url = new URL(`/api/cache`, env.REDIS_API_HOST)
|
||||||
url.searchParams.set("key", encodeURIComponent(generateCacheKey(key)))
|
url.searchParams.set("key", encodeURIComponent(key))
|
||||||
|
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
import { env } from "@/env/server"
|
|
||||||
|
|
||||||
export function generateCacheKey(key: string | string[]): string {
|
|
||||||
const prefix = getPrefix()
|
|
||||||
key = Array.isArray(key) ? key.join("_") : key
|
|
||||||
|
|
||||||
return `${prefix ? `${prefix}:` : ""}${key}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPrefix(): string {
|
|
||||||
if (process.env.NODE_ENV === "development") {
|
|
||||||
const devPrefix = process.env.USER || process.env.USERNAME || "dev"
|
|
||||||
return `${devPrefix}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const branch = env.BRANCH.trim()
|
|
||||||
const gitSha = env.GIT_SHA?.trim().substring(0, 7)
|
|
||||||
|
|
||||||
return `${branch}:${gitSha}`
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { describe, expect, it } from "@jest/globals"
|
||||||
|
|
||||||
|
import { getBranchPrefix } from "./getBranchPrefix"
|
||||||
|
|
||||||
|
describe("getBranchPrefix", () => {
|
||||||
|
it("should return empty string for production branches", () => {
|
||||||
|
expect(getBranchPrefix("production")).toBe("")
|
||||||
|
expect(getBranchPrefix("prod")).toBe("")
|
||||||
|
expect(getBranchPrefix("release")).toBe("")
|
||||||
|
expect(getBranchPrefix("release-v1")).toBe("")
|
||||||
|
expect(getBranchPrefix("release-v1.2")).toBe("")
|
||||||
|
expect(getBranchPrefix("release-v1.2.3")).toBe("")
|
||||||
|
expect(getBranchPrefix("release-v1.2.3-rc1")).toBe("")
|
||||||
|
expect(getBranchPrefix("release-v1.2-beta")).toBe("")
|
||||||
|
expect(getBranchPrefix("release-v1-preview")).toBe("")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return branch name for non-production branches", () => {
|
||||||
|
expect(getBranchPrefix("feature/hello")).toBe("feature/hello")
|
||||||
|
expect(getBranchPrefix("fix/stuff")).toBe("fix/stuff")
|
||||||
|
expect(getBranchPrefix("releasee")).toBe("releasee")
|
||||||
|
expect(getBranchPrefix("release-vA")).toBe("release-vA")
|
||||||
|
expect(getBranchPrefix("release-v1.A")).toBe("release-v1.A")
|
||||||
|
expect(getBranchPrefix("release-v1.2.A")).toBe("release-v1.2.A")
|
||||||
|
expect(getBranchPrefix("release-v1.2.A-rc1")).toBe("release-v1.2.A-rc1")
|
||||||
|
expect(getBranchPrefix("release-v1.A-beta")).toBe("release-v1.A-beta")
|
||||||
|
expect(getBranchPrefix("release-vA-preview")).toBe("release-vA-preview")
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* This will match release branches
|
||||||
|
* @example
|
||||||
|
* release-v1.2.3
|
||||||
|
* release-v1.2
|
||||||
|
* release-v1
|
||||||
|
* release-v1.2.3-alpha
|
||||||
|
* release-v1.2-beta
|
||||||
|
* release-v1-preview
|
||||||
|
*/
|
||||||
|
const releaseRegex = /^release-v\d+(?:\.\d+){0,2}(?:-\w+)?$/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the branch is a production branch reuse the same prefix so that we can reuse the cache between pre-prod and prod
|
||||||
|
* @param branch
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getBranchPrefix = (branch: string) => {
|
||||||
|
const isProdBranch =
|
||||||
|
branch === "production" ||
|
||||||
|
branch === "prod" ||
|
||||||
|
branch === "release" ||
|
||||||
|
releaseRegex.test(branch)
|
||||||
|
|
||||||
|
return isProdBranch ? "" : branch
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import { describe, expect, it, jest } from "@jest/globals"
|
||||||
|
|
||||||
|
jest.doMock("@/env/server", () => ({
|
||||||
|
env: {
|
||||||
|
NODE_ENV: "test",
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const { env } = require("@/env/server")
|
||||||
|
const { getPrefix } = require("./getPrefix")
|
||||||
|
|
||||||
|
const mockedEnv = env as { BRANCH: string; GIT_SHA: string }
|
||||||
|
|
||||||
|
describe("getPrefix", () => {
|
||||||
|
const OLD_ENV = process.env
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetModules()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.env = OLD_ENV
|
||||||
|
})
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
"prod",
|
||||||
|
"production",
|
||||||
|
"release",
|
||||||
|
"release-v1",
|
||||||
|
"release-v2",
|
||||||
|
"release-v2-alpha",
|
||||||
|
"release-v2.1-alpha",
|
||||||
|
"release-v2.1.2-alpha",
|
||||||
|
])(
|
||||||
|
"should return gitsha for production branches when name is '%s'",
|
||||||
|
(branchName: string) => {
|
||||||
|
mockedEnv.BRANCH = branchName
|
||||||
|
mockedEnv.GIT_SHA = "gitsha"
|
||||||
|
|
||||||
|
const result = getPrefix()
|
||||||
|
|
||||||
|
expect(result).toBe("gitsha")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
"fix/stuff",
|
||||||
|
"feat/my-feature",
|
||||||
|
"feature/my-feature",
|
||||||
|
"releasee",
|
||||||
|
"release-vA",
|
||||||
|
"FEAT",
|
||||||
|
])(
|
||||||
|
"should return branch name and gitsha for non-production branches when name is '%s'",
|
||||||
|
(branchName: string) => {
|
||||||
|
mockedEnv.BRANCH = branchName
|
||||||
|
mockedEnv.GIT_SHA = "gitsha"
|
||||||
|
|
||||||
|
const result = getPrefix()
|
||||||
|
|
||||||
|
expect(result).toBe(`${mockedEnv.BRANCH}:${mockedEnv.GIT_SHA}`)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
it("should throw if BRANCH and/or GIT_SHA is not set", () => {
|
||||||
|
mockedEnv.BRANCH = ""
|
||||||
|
mockedEnv.GIT_SHA = ""
|
||||||
|
|
||||||
|
expect(getPrefix).toThrow(
|
||||||
|
"Unable to getPrefix, BRANCH and GIT_SHA must be set"
|
||||||
|
)
|
||||||
|
|
||||||
|
mockedEnv.BRANCH = "hasBranch"
|
||||||
|
mockedEnv.GIT_SHA = ""
|
||||||
|
|
||||||
|
expect(getPrefix).toThrow("Unable to getPrefix, GIT_SHA must be set")
|
||||||
|
|
||||||
|
mockedEnv.BRANCH = ""
|
||||||
|
mockedEnv.GIT_SHA = "hasGitSha"
|
||||||
|
|
||||||
|
expect(getPrefix).toThrow("Unable to getPrefix, BRANCH must be set")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return dev or local user if running locally", () => {
|
||||||
|
;(process.env.NODE_ENV as any) = "development"
|
||||||
|
|
||||||
|
process.env.USER = "test_user"
|
||||||
|
process.env.USERNAME = "test_username"
|
||||||
|
|
||||||
|
mockedEnv.BRANCH = ""
|
||||||
|
mockedEnv.GIT_SHA = ""
|
||||||
|
|
||||||
|
expect(getPrefix()).toBe("test_user")
|
||||||
|
|
||||||
|
process.env.USER = ""
|
||||||
|
|
||||||
|
expect(getPrefix()).toBe("test_username")
|
||||||
|
|
||||||
|
process.env.USERNAME = ""
|
||||||
|
expect(getPrefix()).toBe("dev")
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
|
import { getBranchPrefix } from "./getBranchPrefix"
|
||||||
|
|
||||||
|
export function getPrefix(): string {
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
const devPrefix = process.env.USER || process.env.USERNAME || "dev"
|
||||||
|
return `${devPrefix}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const branch = env.BRANCH.trim()
|
||||||
|
const gitSha = env.GIT_SHA?.trim().substring(0, 7)
|
||||||
|
|
||||||
|
if (!branch && !gitSha) {
|
||||||
|
throw new Error("Unable to getPrefix, BRANCH and GIT_SHA must be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!branch) {
|
||||||
|
throw new Error("Unable to getPrefix, BRANCH must be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gitSha) {
|
||||||
|
throw new Error("Unable to getPrefix, GIT_SHA must be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefixTokens = [getBranchPrefix(branch), gitSha].filter(Boolean)
|
||||||
|
|
||||||
|
return prefixTokens.join(":")
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { describe, expect, it, jest } from "@jest/globals"
|
||||||
|
|
||||||
|
jest.mock("./getPrefix", () => ({
|
||||||
|
getPrefix: jest.fn(() => "gitsha"),
|
||||||
|
}))
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||||
|
const indexModule: typeof import(".") = require(".")
|
||||||
|
|
||||||
|
const { generateCacheKey } = indexModule
|
||||||
|
|
||||||
|
describe("generateCacheKey", () => {
|
||||||
|
const OLD_ENV = process.env
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetModules()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.env = OLD_ENV
|
||||||
|
})
|
||||||
|
|
||||||
|
it("generates cachekey with prefix and key using string", () => {
|
||||||
|
expect(generateCacheKey("key1")).toBe("gitsha:key1")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("generates cachekey with prefix and key using array", () => {
|
||||||
|
expect(generateCacheKey(["key1"])).toBe("gitsha:key1")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("generates cachekey with prefix and keys", () => {
|
||||||
|
const actual = generateCacheKey(["key1", "key2"])
|
||||||
|
expect(actual).toBe("gitsha:key1_key2")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error if no keys are provided", () => {
|
||||||
|
expect(() => generateCacheKey([])).toThrow("No keys provided")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error if only invalid keys are provided", () => {
|
||||||
|
expect(() => generateCacheKey(["", undefined, null] as string[])).toThrow(
|
||||||
|
"No keys provided"
|
||||||
|
)
|
||||||
|
expect(() => generateCacheKey("")).toThrow("No keys provided")
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { getPrefix } from "./getPrefix"
|
||||||
|
|
||||||
|
export function generateCacheKey(key: string | string[]): string {
|
||||||
|
const keyArray = (Array.isArray(key) ? key : [key]).filter(Boolean)
|
||||||
|
|
||||||
|
if (keyArray.length === 0) {
|
||||||
|
throw new Error("No keys provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefix = getPrefix()
|
||||||
|
|
||||||
|
const keyTokens = [prefix, keyArray.join("_")].filter(Boolean).join(":")
|
||||||
|
|
||||||
|
return keyTokens
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user