App version mismatch handling between frontend and backend (#13368)
https://github.com/user-attachments/assets/d153f177-4d70-4ec6-8693-15413e550938
This commit is contained in:
+46
@@ -6,6 +6,8 @@ import {
|
||||
} from '@envelop/core';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { GraphQLError, Kind, OperationDefinitionNode, print } from 'graphql';
|
||||
import semver from 'semver';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { GraphQLContext } from 'src/engine/api/graphql/graphql-config/interfaces/graphql-context.interface';
|
||||
|
||||
@@ -18,6 +20,7 @@ import {
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { MetricsService } from 'src/engine/core-modules/metrics/metrics.service';
|
||||
import { MetricsKeys } from 'src/engine/core-modules/metrics/types/metrics-keys.type';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import {
|
||||
graphQLErrorCodesToFilter,
|
||||
shouldCaptureException,
|
||||
@@ -26,6 +29,9 @@ import {
|
||||
const DEFAULT_EVENT_ID_KEY = 'exceptionEventId';
|
||||
const SCHEMA_VERSION_HEADER = 'x-schema-version';
|
||||
const SCHEMA_MISMATCH_ERROR = 'Schema version mismatch.';
|
||||
const APP_VERSION_HEADER = 'x-app-version';
|
||||
const APP_VERSION_MISMATCH_ERROR = 'App version mismatch.';
|
||||
const APP_VERSION_MISMATCH_CODE = 'APP_VERSION_MISMATCH';
|
||||
|
||||
type GraphQLErrorHandlerHookOptions = {
|
||||
metricsService: MetricsService;
|
||||
@@ -34,6 +40,8 @@ type GraphQLErrorHandlerHookOptions = {
|
||||
* The exception handler service to use.
|
||||
*/
|
||||
exceptionHandlerService: ExceptionHandlerService;
|
||||
|
||||
twentyConfigService: TwentyConfigService;
|
||||
/**
|
||||
* The key of the event id in the error's extension. `null` to disable.
|
||||
* @default exceptionEventId
|
||||
@@ -230,17 +238,55 @@ export const useGraphQLErrorHandlerHook = <
|
||||
const headers = context.req.headers;
|
||||
const currentMetadataVersion = context.req.workspaceMetadataVersion;
|
||||
const requestMetadataVersion = headers[SCHEMA_VERSION_HEADER];
|
||||
const backendAppVersion =
|
||||
options.twentyConfigService.get('APP_VERSION');
|
||||
const appVersionHeaderValue = headers[APP_VERSION_HEADER];
|
||||
const frontEndAppVersion =
|
||||
appVersionHeaderValue && Array.isArray(appVersionHeaderValue)
|
||||
? appVersionHeaderValue[0]
|
||||
: appVersionHeaderValue;
|
||||
|
||||
if (
|
||||
requestMetadataVersion &&
|
||||
requestMetadataVersion !== `${currentMetadataVersion}`
|
||||
) {
|
||||
options.metricsService.incrementCounter({
|
||||
key: MetricsKeys.SchemaVersionMismatch,
|
||||
});
|
||||
throw new GraphQLError(SCHEMA_MISMATCH_ERROR, {
|
||||
extensions: {
|
||||
userFriendlyMessage: t`Your workspace has been updated with a new data model. Please refresh the page.`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!frontEndAppVersion ||
|
||||
!backendAppVersion ||
|
||||
!semver.valid(frontEndAppVersion) ||
|
||||
!semver.valid(backendAppVersion)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const frontEndMajor = semver.parse(frontEndAppVersion)?.major;
|
||||
const backendMajor = semver.parse(backendAppVersion)?.major;
|
||||
|
||||
if (
|
||||
isDefined(frontEndMajor) &&
|
||||
isDefined(backendMajor) &&
|
||||
frontEndMajor < backendMajor
|
||||
) {
|
||||
options.metricsService.incrementCounter({
|
||||
key: MetricsKeys.AppVersionMismatch,
|
||||
});
|
||||
throw new GraphQLError(APP_VERSION_MISMATCH_ERROR, {
|
||||
extensions: {
|
||||
code: APP_VERSION_MISMATCH_CODE,
|
||||
userFriendlyMessage: t`Your app version is out of date. Please refresh the page to continue.`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user