diff --git a/CHANGELOG.md b/CHANGELOG.md index e144193d7..768b4dcdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,22 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v0.75.0](https://github.com/isomerpages/isomercms-backend/compare/v0.74.0...v0.75.0) + +- feat: carry the name of the wrapped handler [`#1260`](https://github.com/isomerpages/isomercms-backend/pull/1260) +- fix: only release the lock after the handler is done [`#1259`](https://github.com/isomerpages/isomercms-backend/pull/1259) +- backport v0.74.0 [`#1258`](https://github.com/isomerpages/isomercms-backend/pull/1258) + #### [v0.74.0](https://github.com/isomerpages/isomercms-backend/compare/v0.73.0...v0.74.0) +> 1 April 2024 + - ISOM-824 feat: don't fetch redundant author info from db [`#1255`](https://github.com/isomerpages/isomercms-backend/pull/1255) - fix(navbar): allow ext links [`#1250`](https://github.com/isomerpages/isomercms-backend/pull/1250) - fix(lock): fix early release of lock [`#1256`](https://github.com/isomerpages/isomercms-backend/pull/1256) - ISOM-852 feat(logger): add context for logs [`#1234`](https://github.com/isomerpages/isomercms-backend/pull/1234) - backport v0.73.0 [`#1249`](https://github.com/isomerpages/isomercms-backend/pull/1249) +- chore: bump version to v0.74.0 [`687182e`](https://github.com/isomerpages/isomercms-backend/commit/687182e536d53a6a06fe3ac04ecec19b21154128) #### [v0.73.0](https://github.com/isomerpages/isomercms-backend/compare/v0.72.0...v0.73.0) diff --git a/package-lock.json b/package-lock.json index f0422b4d6..4896eb4e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "isomercms", - "version": "0.74.0", + "version": "0.75.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "isomercms", - "version": "0.74.0", + "version": "0.75.0", "hasInstallScript": true, "dependencies": { "@aws-sdk/client-amplify": "^3.521.0", diff --git a/package.json b/package.json index dffce1e85..522475441 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "isomercms", - "version": "0.74.0", + "version": "0.75.0", "private": true, "scripts": { "build": "tsc -p tsconfig.build.json", diff --git a/src/middleware/routeHandler.ts b/src/middleware/routeHandler.ts index e212cac34..da27016b9 100644 --- a/src/middleware/routeHandler.ts +++ b/src/middleware/routeHandler.ts @@ -76,49 +76,66 @@ type RouteWrapper< next: NextFunction ) => void -// Used when there are no write API calls to the repo on GitHub -export const attachReadRouteHandlerWrapper: RouteWrapper = ( - routeHandler -) => async (req, res, next) => { - Promise.resolve(routeHandler(req, res, next)).catch((err: Error) => { - next(err) - }) +const nameRouteHandlerWrapper = < + ResBody, + ReqBody, + ReqQuery, + Params = Record, + Locals extends Record = Record +>( + func: RequestHandler, + name: string +): RequestHandler => { + Object.defineProperty(func, "name", { value: name, writable: false }) + return func } +// Used when there are no write API calls to the repo on GitHub +export const attachReadRouteHandlerWrapper: RouteWrapper = (routeHandler) => + nameRouteHandlerWrapper(async (req, res, next) => { + Promise.resolve(routeHandler(req, res, next)).catch((err: Error) => { + next(err) + }) + }, routeHandler.name) + // Used when there are write API calls to the repo on GitHub export const attachWriteRouteHandlerWrapper: RouteWrapper<{ siteName: string -}> = (routeHandler) => async (req, res, next) => { - const { siteName } = req.params - const { growthbook } = req +}> = (routeHandler) => + nameRouteHandlerWrapper(async (req, res, next) => { + const { siteName } = req.params + const { growthbook } = req - let isGitAvailable = true + let isGitAvailable = true - // only check git file lock if the repo is ggs enabled - if (growthbook?.getFeatureValue(FEATURE_FLAGS.IS_GGS_ENABLED, false)) { - isGitAvailable = await handleGitFileLock(siteName, next) - } - - if (!isGitAvailable) return + // only check git file lock if the repo is ggs enabled + if (growthbook?.getFeatureValue(FEATURE_FLAGS.IS_GGS_ENABLED, false)) { + isGitAvailable = await handleGitFileLock(siteName, next) + } - try { - await lock(siteName) - } catch (err) { - next(err) - return - } + if (!isGitAvailable) return - Promise.resolve(routeHandler(req, res, next)).catch(async (err: Error) => { - await unlock(siteName) - next(err) - }) + try { + await lock(siteName) + } catch (err) { + next(err) + return + } - try { - await unlock(siteName) - } catch (err) { - next(err) - } -} + Promise.resolve(routeHandler(req, res, next)).then( + async () => { + try { + await unlock(siteName) + } catch (err) { + next(err) + } + }, + async (err) => { + await unlock(siteName) + next(err) + } + ) + }, routeHandler.name) export const attachRollbackRouteHandlerWrapper: RouteWrapper< { @@ -130,196 +147,197 @@ export const attachRollbackRouteHandlerWrapper: RouteWrapper< userWithSiteSessionData: UserWithSiteSessionData githubSessionData: GithubSessionData } -> = (routeHandler) => async (req, res, next: NextFunction) => { - const { userWithSiteSessionData } = res.locals - const { siteName } = req.params +> = (routeHandler) => + nameRouteHandlerWrapper(async (req, res, next: NextFunction) => { + const { userWithSiteSessionData } = res.locals + const { siteName } = req.params - const { accessToken } = userWithSiteSessionData - const { growthbook } = req + const { accessToken } = userWithSiteSessionData + const { growthbook } = req - if (!growthbook) { - next(new Error("GrowthBook instance is undefined.")) - return - } - - const isGitAvailable = await isGitFileAndIsGitAvail( - growthbook, - siteName, - next - ) - - if (!isGitAvailable) return - try { - await lock(siteName) - } catch (err) { - next(err) - return - } - - let originalStagingCommitSha: string - let originalStagingLiteCommitSha: string - - const shouldUseGitFileSystem = growthbook?.getFeatureValue( - FEATURE_FLAGS.IS_GGS_ENABLED, - false - ) - const shouldCheckStagingLite = growthbook?.getFeatureValue( - FEATURE_FLAGS.IS_BUILD_TIMES_REDUCTION_ENABLED, - false - ) - - if (shouldUseGitFileSystem && shouldCheckStagingLite) { - // ggs + quickie - const results = await ResultAsync.combine([ - gitFileSystemService.getLatestCommitOfBranch(siteName, STAGING_BRANCH), - gitFileSystemService.getLatestCommitOfBranch( - siteName, - STAGING_LITE_BRANCH - ), - ]) - - if (results.isErr()) { - await unlock(siteName) - next(results.error) - return - } - const [stagingResult, stagingLiteResult] = results.value - if (!stagingResult.sha || !stagingLiteResult.sha) { - await unlock(siteName) + if (!growthbook) { + next(new Error("GrowthBook instance is undefined.")) return } - originalStagingCommitSha = stagingResult.sha - originalStagingLiteCommitSha = stagingLiteResult.sha - // Unused for git file system, but to maintain existing structure - res.locals.githubSessionData = new GithubSessionData({ - currentCommitSha: "", - treeSha: "", - }) - } else if (shouldUseGitFileSystem && !shouldCheckStagingLite) { - // ggs alone - const result = await gitFileSystemService.getLatestCommitOfBranch( + const isGitAvailable = await isGitFileAndIsGitAvail( + growthbook, siteName, - STAGING_BRANCH + next ) - if (result.isErr()) { - await unlock(siteName) - next(result.error) + if (!isGitAvailable) return + try { + await lock(siteName) + } catch (err) { + next(err) return } - if (!result.value.sha) { - await unlock(siteName) - return - } + let originalStagingCommitSha: string + let originalStagingLiteCommitSha: string - originalStagingCommitSha = result.value.sha - // Unused for git file system, but to maintain existing structure - res.locals.githubSessionData = new GithubSessionData({ - currentCommitSha: "", - treeSha: "", - }) - } else { - // non-GGS flow - try { - const { - currentCommitSha: currentStgCommitSha, - treeSha: stgTreeSha, - } = await getCommitAndTreeSha(siteName, accessToken, STAGING_BRANCH) - - const githubSessionData = new GithubSessionData({ - currentCommitSha: currentStgCommitSha, - treeSha: stgTreeSha, - }) - res.locals.githubSessionData = githubSessionData + const shouldUseGitFileSystem = growthbook?.getFeatureValue( + FEATURE_FLAGS.IS_GGS_ENABLED, + false + ) + const shouldCheckStagingLite = growthbook?.getFeatureValue( + FEATURE_FLAGS.IS_BUILD_TIMES_REDUCTION_ENABLED, + false + ) - originalStagingCommitSha = currentStgCommitSha - } catch (err) { - await unlock(siteName) - logger.error( - `Failed to rollback repo ${siteName}: ${JSON.stringify(err)}` + if (shouldUseGitFileSystem && shouldCheckStagingLite) { + // ggs + quickie + const results = await ResultAsync.combine([ + gitFileSystemService.getLatestCommitOfBranch(siteName, STAGING_BRANCH), + gitFileSystemService.getLatestCommitOfBranch( + siteName, + STAGING_LITE_BRANCH + ), + ]) + + if (results.isErr()) { + await unlock(siteName) + next(results.error) + return + } + const [stagingResult, stagingLiteResult] = results.value + if (!stagingResult.sha || !stagingLiteResult.sha) { + await unlock(siteName) + return + } + + originalStagingCommitSha = stagingResult.sha + originalStagingLiteCommitSha = stagingLiteResult.sha + // Unused for git file system, but to maintain existing structure + res.locals.githubSessionData = new GithubSessionData({ + currentCommitSha: "", + treeSha: "", + }) + } else if (shouldUseGitFileSystem && !shouldCheckStagingLite) { + // ggs alone + const result = await gitFileSystemService.getLatestCommitOfBranch( + siteName, + STAGING_BRANCH ) - next(err) - return - } - } - Promise.resolve(routeHandler(req, res, next)).then( - async () => { - try { + if (result.isErr()) { + await unlock(siteName) + next(result.error) + return + } + + if (!result.value.sha) { await unlock(siteName) + return + } + + originalStagingCommitSha = result.value.sha + // Unused for git file system, but to maintain existing structure + res.locals.githubSessionData = new GithubSessionData({ + currentCommitSha: "", + treeSha: "", + }) + } else { + // non-GGS flow + try { + const { + currentCommitSha: currentStgCommitSha, + treeSha: stgTreeSha, + } = await getCommitAndTreeSha(siteName, accessToken, STAGING_BRANCH) + + const githubSessionData = new GithubSessionData({ + currentCommitSha: currentStgCommitSha, + treeSha: stgTreeSha, + }) + res.locals.githubSessionData = githubSessionData + + originalStagingCommitSha = currentStgCommitSha } catch (err) { + await unlock(siteName) + logger.error( + `Failed to rollback repo ${siteName}: ${JSON.stringify(err)}` + ) next(err) + return } - }, - async (err: Error) => { - try { - if (shouldUseGitFileSystem) { - await backOff( - () => - convertNeverThrowToPromise( - gitFileSystemService.rollback( - siteName, - originalStagingCommitSha, - STAGING_BRANCH - ) - ), - backoffOptions - ) + } - if (shouldCheckStagingLite) { - // for quickie sites + Promise.resolve(routeHandler(req, res, next)).then( + async () => { + try { + await unlock(siteName) + } catch (err) { + next(err) + } + }, + async (err: Error) => { + try { + if (shouldUseGitFileSystem) { await backOff( () => convertNeverThrowToPromise( gitFileSystemService.rollback( siteName, - originalStagingLiteCommitSha, - STAGING_LITE_BRANCH + originalStagingCommitSha, + STAGING_BRANCH ) ), backoffOptions ) - } - await backOff(() => { - let pushRes = gitFileSystemService.push( - siteName, - STAGING_BRANCH, - true - ) - if (originalStagingLiteCommitSha) { - pushRes = pushRes.andThen(() => - gitFileSystemService.push(siteName, STAGING_LITE_BRANCH, true) + if (shouldCheckStagingLite) { + // for quickie sites + await backOff( + () => + convertNeverThrowToPromise( + gitFileSystemService.rollback( + siteName, + originalStagingLiteCommitSha, + STAGING_LITE_BRANCH + ) + ), + backoffOptions ) } - return convertNeverThrowToPromise(pushRes) - }, backoffOptions) - } else { - // Github flow - await backOff( - () => - revertCommit( - originalStagingCommitSha, + await backOff(() => { + let pushRes = gitFileSystemService.push( siteName, - accessToken, - STAGING_BRANCH - ), - backoffOptions + STAGING_BRANCH, + true + ) + if (originalStagingLiteCommitSha) { + pushRes = pushRes.andThen(() => + gitFileSystemService.push(siteName, STAGING_LITE_BRANCH, true) + ) + } + + return convertNeverThrowToPromise(pushRes) + }, backoffOptions) + } else { + // Github flow + await backOff( + () => + revertCommit( + originalStagingCommitSha, + siteName, + accessToken, + STAGING_BRANCH + ), + backoffOptions + ) + } + } catch (retryErr) { + await unlock(siteName) + logger.error( + `Failed to rollback repo ${siteName}: ${JSON.stringify(retryErr)}` ) + next(retryErr) + return } - } catch (retryErr) { await unlock(siteName) - logger.error( - `Failed to rollback repo ${siteName}: ${JSON.stringify(retryErr)}` - ) - next(retryErr) - return + next(err) } - await unlock(siteName) - next(err) - } - ) -} + ) + }, routeHandler.name)