diff --git a/src/features/applicationMetadata/ApplicationMetadataProvider.tsx b/src/features/applicationMetadata/ApplicationMetadataProvider.tsx
index c5dae60c42..743f9baeb1 100644
--- a/src/features/applicationMetadata/ApplicationMetadataProvider.tsx
+++ b/src/features/applicationMetadata/ApplicationMetadataProvider.tsx
@@ -7,11 +7,10 @@ import type { UseQueryOptions } from '@tanstack/react-query';
import { delayedContext } from 'src/core/contexts/delayedContext';
import { createQueryContext } from 'src/core/contexts/queryContext';
import { onEntryValuesThatHaveState } from 'src/features/applicationMetadata/appMetadataUtils';
-import { MINIMUM_APPLICATION_VERSION } from 'src/features/applicationMetadata/minVersion';
import { VersionErrorOrChildren } from 'src/features/applicationMetadata/VersionErrorOrChildren';
import { useNavigationParam } from 'src/hooks/navigation';
import { fetchApplicationMetadata } from 'src/queries/queries';
-import { isAtLeastVersion } from 'src/utils/versionCompare';
+import { isMinimumApplicationVersion } from 'src/utils/versioning/versions';
import type { ApplicationMetadata, IncomingApplicationMetadata } from 'src/features/applicationMetadata/types';
// Also used for prefetching @see appPrefetcher.ts
@@ -24,12 +23,7 @@ export function getApplicationMetadataQueryDef(instanceGuid: string | undefined)
return {
...data,
- isValidVersion:
- !!data.altinnNugetVersion &&
- isAtLeastVersion({
- actualVersion: data.altinnNugetVersion,
- minimumVersion: MINIMUM_APPLICATION_VERSION.build,
- }),
+ isValidVersion: isMinimumApplicationVersion(data.altinnNugetVersion),
onEntry,
isStatelessApp: isStatelessApp(!!instanceGuid, onEntry.show),
logoOptions: data.logo,
diff --git a/src/features/applicationMetadata/VersionErrorOrChildren.tsx b/src/features/applicationMetadata/VersionErrorOrChildren.tsx
index 72ffd316c6..01b187b231 100644
--- a/src/features/applicationMetadata/VersionErrorOrChildren.tsx
+++ b/src/features/applicationMetadata/VersionErrorOrChildren.tsx
@@ -2,9 +2,9 @@ import React from 'react';
import type { PropsWithChildren } from 'react';
import { useApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider';
-import { MINIMUM_APPLICATION_VERSION } from 'src/features/applicationMetadata/minVersion';
import { InstantiationErrorPage } from 'src/features/instantiate/containers/InstantiationErrorPage';
import { Lang } from 'src/features/language/Lang';
+import { MINIMUM_APPLICATION_VERSION_NAME } from 'src/utils/versioning/versions';
export function VersionErrorOrChildren({ children }: PropsWithChildren) {
const { isValidVersion } = useApplicationMetadata();
@@ -21,7 +21,7 @@ export function VersionErrorOrChildren({ children }: PropsWithChildren) {
>
}
diff --git a/src/features/applicationMetadata/minVersion.ts b/src/features/applicationMetadata/minVersion.ts
deleted file mode 100644
index d36f6c123d..0000000000
--- a/src/features/applicationMetadata/minVersion.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export const MINIMUM_APPLICATION_VERSION = {
- build: '8.0.0.108',
- name: 'v8.0.0',
-};
diff --git a/src/features/attachments/AttachmentsStorePlugin.tsx b/src/features/attachments/AttachmentsStorePlugin.tsx
index 0646e19a77..dfd0c927e5 100644
--- a/src/features/attachments/AttachmentsStorePlugin.tsx
+++ b/src/features/attachments/AttachmentsStorePlugin.tsx
@@ -11,7 +11,7 @@ import { ContextNotProvided } from 'src/core/contexts/context';
import { useApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider';
import { isAttachmentUploaded, isDataPostError } from 'src/features/attachments/index';
import { sortAttachmentsByName } from 'src/features/attachments/sortAttachments';
-import { appSupportsNewAttachmentAPI, attachmentSelector } from 'src/features/attachments/tools';
+import { attachmentSelector } from 'src/features/attachments/tools';
import { FileScanResults } from 'src/features/attachments/types';
import { FD } from 'src/features/formData/FormDataWrite';
import { dataModelPairsToObject } from 'src/features/formData/types';
@@ -28,6 +28,7 @@ import { useWaitForState } from 'src/hooks/useWaitForState';
import { nodesProduce } from 'src/utils/layout/NodesContext';
import { NodeDataPlugin } from 'src/utils/layout/plugins/NodeDataPlugin';
import { splitDashedKey } from 'src/utils/splitDashedKey';
+import { appSupportsNewAttachmentAPI } from 'src/utils/versioning/versions';
import type {
DataPostResponse,
IAttachment,
@@ -296,7 +297,7 @@ export class AttachmentsStorePlugin extends NodeDataPlugin (state: NodesContext) => {
}
return emptyArray;
};
-
-export function appSupportsNewAttachmentAPI({ altinnNugetVersion }: ApplicationMetadata) {
- return !altinnNugetVersion || isAtLeastVersion({ actualVersion: altinnNugetVersion, minimumVersion: '8.5.0.153' });
-}
diff --git a/src/features/instance/useProcessNext.tsx b/src/features/instance/useProcessNext.tsx
index 13f9a0e315..e8f3a40c71 100644
--- a/src/features/instance/useProcessNext.tsx
+++ b/src/features/instance/useProcessNext.tsx
@@ -12,13 +12,14 @@ import { useOptimisticallyUpdateProcess, useProcessQuery } from 'src/features/in
import { Lang } from 'src/features/language/Lang';
import { useCurrentLanguage } from 'src/features/language/LanguageProvider';
import { useUpdateInitialValidations } from 'src/features/validation/backendValidation/backendValidationQuery';
-import { appSupportsIncrementalValidationFeatures } from 'src/features/validation/backendValidation/backendValidationUtils';
import { useOnFormSubmitValidation } from 'src/features/validation/callbacks/onFormSubmitValidation';
import { Validation } from 'src/features/validation/validationContext';
import { TaskKeys, useNavigateToTask } from 'src/hooks/useNavigatePage';
import { doProcessNext } from 'src/queries/queries';
-import { isAtLeastVersion } from 'src/utils/versionCompare';
-import type { ApplicationMetadata } from 'src/features/applicationMetadata/types';
+import {
+ appSupportsIncrementalValidationFeatures,
+ appSupportsUnlockingOnProcessNextFailure,
+} from 'src/utils/versioning/versions';
import type { BackendValidationIssue } from 'src/features/validation';
import type { IActionType, IProcess, ProblemDetails } from 'src/types/shared';
import type { HttpClientError } from 'src/utils/network/sharedNetworking';
@@ -44,12 +45,15 @@ export function useProcessNext({ action }: ProcessNextProps = {}) {
const updateInitialValidations = useUpdateInitialValidations();
const setShowAllBackendErrors = Validation.useSetShowAllBackendErrors();
const onSubmitFormValidation = useOnFormSubmitValidation();
- const applicationMetadata = useApplicationMetadata();
const queryClient = useQueryClient();
const displayError = useDisplayError();
const hasPendingScans = useHasPendingScans();
const optimisticallyUpdateProcess = useOptimisticallyUpdateProcess();
+ const altinnNugetVersion = useApplicationMetadata().altinnNugetVersion;
+ const isUnlockingOnProcessNextSupported = appSupportsUnlockingOnProcessNextFailure(altinnNugetVersion);
+ const isIncrementalValidationSupported = appSupportsIncrementalValidationFeatures(altinnNugetVersion);
+
return useMutation({
scope: { id: 'process/next' },
mutationKey: getProcessNextMutationKey(action),
@@ -76,7 +80,7 @@ export function useProcessNext({ action }: ProcessNextProps = {}) {
} else if (
error.response?.status === 500 &&
error.response?.data?.['detail'] === 'Pdf generation failed' &&
- appSupportsUnlockingOnProcessNextFailure(applicationMetadata)
+ isUnlockingOnProcessNextSupported
) {
// If process next fails due to the PDF generator failing, don't show unknown error if the app unlocks data elements
toast(, { type: 'error', autoClose: false });
@@ -100,7 +104,7 @@ export function useProcessNext({ action }: ProcessNextProps = {}) {
navigateToTask(task);
} else if (validationIssues) {
// Set initial validation to validation issues from process/next and make all errors visible
- updateInitialValidations(validationIssues, !appSupportsIncrementalValidationFeatures(applicationMetadata));
+ updateInitialValidations(validationIssues, !isIncrementalValidationSupported);
const hasValidationErrors = await onSubmitFormValidation(true);
if (!hasValidationErrors) {
@@ -111,7 +115,7 @@ export function useProcessNext({ action }: ProcessNextProps = {}) {
onError: async (error: HttpClientError) => {
window.logError('Process next failed:\n', error);
- if (!appSupportsUnlockingOnProcessNextFailure(applicationMetadata)) {
+ if (!isUnlockingOnProcessNextSupported) {
displayError(error);
return;
}
@@ -132,10 +136,6 @@ export function useProcessNext({ action }: ProcessNextProps = {}) {
});
}
-function appSupportsUnlockingOnProcessNextFailure({ altinnNugetVersion }: ApplicationMetadata) {
- return !altinnNugetVersion || isAtLeastVersion({ actualVersion: altinnNugetVersion, minimumVersion: '8.1.0.115' });
-}
-
export function getTargetTaskFromProcess(processData: IProcess | undefined) {
if (!processData) {
return undefined;
diff --git a/src/features/language/LanguageProvider.tsx b/src/features/language/LanguageProvider.tsx
index 42f465cfeb..bf1542e664 100644
--- a/src/features/language/LanguageProvider.tsx
+++ b/src/features/language/LanguageProvider.tsx
@@ -7,7 +7,7 @@ import { useGetAppLanguageQuery } from 'src/features/language/textResources/useG
import { useProfileQuery } from 'src/features/profile/ProfileProvider';
import { useIsAllowAnonymous } from 'src/features/stateless/getAllowAnonymous';
import { useLocalStorageState } from 'src/hooks/useLocalStorageState';
-import { isAtLeastVersion } from 'src/utils/versionCompare';
+import { appSupportsFetchAppLanguagesInAnonymous } from 'src/utils/versioning/versions';
interface LanguageCtx {
current: string;
@@ -91,15 +91,9 @@ export const SetShouldFetchAppLanguages = () => {
// We make the same assumption as in ProfileProvider that the user is logged in when the app does not allow anonymous.
const userIsAuthenticated = useIsAllowAnonymous(false);
const { altinnNugetVersion } = useApplicationMetadata();
- const appSupportsFetchAppLanguagesInAnonymous =
- altinnNugetVersion &&
- isAtLeastVersion({
- actualVersion: altinnNugetVersion,
- minimumVersion: '8.5.6.180',
- });
const setShouldFetchAppLanguages = useCtx().setShouldFetchAppLanguages;
- const shouldFetchAppLanguages = appSupportsFetchAppLanguagesInAnonymous || userIsAuthenticated;
+ const shouldFetchAppLanguages = appSupportsFetchAppLanguagesInAnonymous(altinnNugetVersion) || userIsAuthenticated;
useEffect(() => {
setShouldFetchAppLanguages(shouldFetchAppLanguages);
}, [shouldFetchAppLanguages, setShouldFetchAppLanguages]);
diff --git a/src/features/validation/backendValidation/backendValidationQuery.ts b/src/features/validation/backendValidation/backendValidationQuery.ts
index 15ec877d49..8422457600 100644
--- a/src/features/validation/backendValidation/backendValidationQuery.ts
+++ b/src/features/validation/backendValidation/backendValidationQuery.ts
@@ -11,9 +11,9 @@ import { useCurrentDataModelGuid } from 'src/features/datamodel/useBindingSchema
import { useLaxInstanceId } from 'src/features/instance/InstanceContext';
import { useProcessQuery } from 'src/features/instance/useProcessQuery';
import { useCurrentLanguage } from 'src/features/language/LanguageProvider';
-import { appSupportsIncrementalValidationFeatures } from 'src/features/validation/backendValidation/backendValidationUtils';
import { useAsRef } from 'src/hooks/useAsRef';
import { fetchBackendValidationsForDataElement } from 'src/queries/queries';
+import { appSupportsIncrementalValidationFeatures } from 'src/utils/versioning/versions';
import type { fetchBackendValidations } from 'src/queries/queries';
/**
@@ -132,7 +132,9 @@ export function useBackendValidationQuery(
) {
const queryKey = useBackendValidationQueryKey();
const { fetchBackendValidations, fetchBackendValidationsForDataElement } = useAppQueries();
- const hasIncrementalValidationFeatures = appSupportsIncrementalValidationFeatures(useApplicationMetadata());
+ const hasIncrementalValidationFeatures = appSupportsIncrementalValidationFeatures(
+ useApplicationMetadata().altinnNugetVersion,
+ );
const currentDataElementID = useCurrentDataModelGuid();
const instanceId = useLaxInstanceId();
const currentLanguage = useAsRef(useCurrentLanguage()).current;
diff --git a/src/features/validation/backendValidation/backendValidationUtils.ts b/src/features/validation/backendValidation/backendValidationUtils.ts
index abd7d69a0b..8ac950e244 100644
--- a/src/features/validation/backendValidation/backendValidationUtils.ts
+++ b/src/features/validation/backendValidation/backendValidationUtils.ts
@@ -7,8 +7,6 @@ import { BackendValidationSeverity, BuiltInValidationIssueSources, ValidationMas
import { validationTexts } from 'src/features/validation/backendValidation/validationTexts';
import { useIsPdf } from 'src/hooks/useIsPdf';
import { TaskKeys } from 'src/hooks/useNavigatePage';
-import { isAtLeastVersion } from 'src/utils/versionCompare';
-import type { ApplicationMetadata } from 'src/features/applicationMetadata/types';
import type { TextReference } from 'src/features/language/useLanguage';
import type {
BackendFieldValidatorGroups,
@@ -179,20 +177,3 @@ export function mapValidatorGroupsToDataModelValidations(
return backendValidations;
}
-
-/**
- * TODO(Subform): Make sure we reference the correct version here, and in applicationMetadataMock
- *
- * Prior to app-lib version 8.5.0 there was no way of identifying validation messages that were not run incrementally (ITaskValidator),
- * this led to an edge case where if an ITaskValidator returned a validation message with a field, we could not
- * distinguish this from a regular custom backend validation which does runs incrementally. The problem is that we block
- * submit when we have custom backend validation errors until they are fixed, but since ITaskValidator is not run
- * incrementally it would never get fixed until the user refreshed the page. This issue was somewhat mitigated
- * by the old dataElement validation API which did not run ITaskValidators.
- *
- * Therefore, if this function returns false, this means that the app does not make this distinction, but
- * has the old API available, so this needs to be used for backwards compatibility.
- */
-export function appSupportsIncrementalValidationFeatures({ altinnNugetVersion }: ApplicationMetadata) {
- return !altinnNugetVersion || isAtLeastVersion({ actualVersion: altinnNugetVersion, minimumVersion: '8.5.0.141' });
-}
diff --git a/src/layout/PDFPreviewButton/PDFPreviewButtonComponent.tsx b/src/layout/PDFPreviewButton/PDFPreviewButtonComponent.tsx
index 5e684a1a11..dbab3539a3 100644
--- a/src/layout/PDFPreviewButton/PDFPreviewButtonComponent.tsx
+++ b/src/layout/PDFPreviewButton/PDFPreviewButtonComponent.tsx
@@ -7,22 +7,18 @@ import { useApplicationMetadata } from 'src/features/applicationMetadata/Applica
import { useStrictInstanceId } from 'src/features/instance/InstanceContext';
import { NodesInternal } from 'src/utils/layout/NodesContext';
import { useItemWhenType } from 'src/utils/layout/useNodeItem';
-import { isAtLeastVersion } from 'src/utils/versionCompare';
+import { appSupportsPdfPreviewButton, FEATURE_VERSION_MAP } from 'src/utils/versioning/versions';
import type { NodeValidationProps } from 'src/layout/layout';
export function PDFPreviewButtonRenderLayoutValidator({ intermediateItem }: NodeValidationProps<'PDFPreviewButton'>) {
const instanceId = useStrictInstanceId();
const addError = NodesInternal.useAddError();
const applicationMetadata = useApplicationMetadata();
- const minimumBackendVersion = '8.5.0.157';
- const backendVersionOK = isAtLeastVersion({
- actualVersion: applicationMetadata.altinnNugetVersion ?? '',
- minimumVersion: minimumBackendVersion,
- });
+ const isPdfPreviewButtonSupported = appSupportsPdfPreviewButton(applicationMetadata.altinnNugetVersion);
useEffect(() => {
- if (!backendVersionOK) {
- const error = `Need to be on at least backend version: ${minimumBackendVersion} to user this component`;
+ if (!isPdfPreviewButtonSupported) {
+ const error = `Need to be on at least backend version: ${FEATURE_VERSION_MAP.PDF_PREVIEW_BUTTON} to use this component`;
addError(error, intermediateItem.id, 'node');
window.logErrorOnce(`Validation error for '${intermediateItem.id}': ${error}`);
}
@@ -32,7 +28,7 @@ export function PDFPreviewButtonRenderLayoutValidator({ intermediateItem }: Node
addError(error, intermediateItem.id, 'node');
window.logErrorOnce(`Validation error for '${intermediateItem.id}': ${error}`);
}
- }, [addError, backendVersionOK, instanceId, intermediateItem.id]);
+ }, [addError, isPdfPreviewButtonSupported, instanceId, intermediateItem.id]);
return null;
}
diff --git a/src/utils/versionCompare.test.ts b/src/utils/versioning/versionCompare.test.ts
similarity index 94%
rename from src/utils/versionCompare.test.ts
rename to src/utils/versioning/versionCompare.test.ts
index f1368faf2e..6c597c650e 100644
--- a/src/utils/versionCompare.test.ts
+++ b/src/utils/versioning/versionCompare.test.ts
@@ -1,4 +1,4 @@
-import { isAtLeastVersion } from 'src/utils/versionCompare';
+import { isAtLeastVersion } from 'src/utils/versioning/versionCompare';
describe('versionCompare', () => {
interface TestCase {
diff --git a/src/utils/versionCompare.ts b/src/utils/versioning/versionCompare.ts
similarity index 100%
rename from src/utils/versionCompare.ts
rename to src/utils/versioning/versionCompare.ts
diff --git a/src/utils/versioning/versions.ts b/src/utils/versioning/versions.ts
new file mode 100644
index 0000000000..071ac2ad54
--- /dev/null
+++ b/src/utils/versioning/versions.ts
@@ -0,0 +1,64 @@
+import { isAtLeastVersion } from 'src/utils/versioning/versionCompare';
+
+export const MINIMUM_APPLICATION_VERSION_NAME = 'v8.0.0';
+export const FEATURE_VERSION_MAP = {
+ MINIMUM_APPLICATION_VERSION: '8.0.0.108',
+ UNLOCKING_ON_PROCESS_NEXT_FAILURE: '8.1.0.115',
+ INCREMENTAL_VALIDATION: '8.5.0.141',
+ NEW_ATTACHMENTS_API: '8.5.0.153',
+ PDF_PREVIEW_BUTTON: '8.5.0.157',
+ APP_LANGUAGES_IN_ANONYMOUS: '8.5.6.180',
+} as const;
+
+type AppFeature = keyof typeof FEATURE_VERSION_MAP;
+
+function isFeatureSupported({
+ feature,
+ currentNugetVersion,
+}: {
+ feature: AppFeature;
+ currentNugetVersion: string | undefined;
+}) {
+ if (!currentNugetVersion) {
+ return false;
+ }
+
+ return isAtLeastVersion({ actualVersion: currentNugetVersion, minimumVersion: FEATURE_VERSION_MAP[feature] });
+}
+
+export function isMinimumApplicationVersion(currentNugetVersion: string | undefined) {
+ return isFeatureSupported({ feature: 'MINIMUM_APPLICATION_VERSION', currentNugetVersion });
+}
+
+export function appSupportsPdfPreviewButton(currentNugetVersion: string | undefined) {
+ return isFeatureSupported({ feature: 'PDF_PREVIEW_BUTTON', currentNugetVersion });
+}
+
+export function appSupportsFetchAppLanguagesInAnonymous(currentNugetVersion: string | undefined) {
+ return isFeatureSupported({ feature: 'APP_LANGUAGES_IN_ANONYMOUS', currentNugetVersion });
+}
+
+export function appSupportsUnlockingOnProcessNextFailure(currentNugetVersion: string | undefined) {
+ return isFeatureSupported({ feature: 'UNLOCKING_ON_PROCESS_NEXT_FAILURE', currentNugetVersion });
+}
+
+export function appSupportsNewAttachmentAPI(currentNugetVersion: string | undefined) {
+ return isFeatureSupported({ feature: 'NEW_ATTACHMENTS_API', currentNugetVersion });
+}
+
+/**
+ * TODO(Subform): Make sure we reference the correct version here, and in applicationMetadataMock
+ *
+ * Prior to app-lib version 8.5.0 there was no way of identifying validation messages that were not run incrementally (ITaskValidator),
+ * this led to an edge case where if an ITaskValidator returned a validation message with a field, we could not
+ * distinguish this from a regular custom backend validation which does runs incrementally. The problem is that we block
+ * submit when we have custom backend validation errors until they are fixed, but since ITaskValidator is not run
+ * incrementally it would never get fixed until the user refreshed the page. This issue was somewhat mitigated
+ * by the old dataElement validation API which did not run ITaskValidators.
+ *
+ * Therefore, if this function returns false, this means that the app does not make this distinction, but
+ * has the old API available, so this needs to be used for backwards compatibility.
+ */
+export function appSupportsIncrementalValidationFeatures(currentNugetVersion: string | undefined) {
+ return isFeatureSupported({ feature: 'INCREMENTAL_VALIDATION', currentNugetVersion });
+}