diff --git a/frontend/resourceadm/types/NavigationBarPage.d.ts b/frontend/resourceadm/types/NavigationBarPage.d.ts
index 998f945b7fd..ed2d6cf82f5 100644
--- a/frontend/resourceadm/types/NavigationBarPage.d.ts
+++ b/frontend/resourceadm/types/NavigationBarPage.d.ts
@@ -1 +1,7 @@
-export type NavigationBarPage = 'about' | 'policy' | 'deploy' | 'migration' | 'accesslists';
+export type NavigationBarPage =
+ | 'about'
+ | 'policy'
+ | 'deploy'
+ | 'migration'
+ | 'accesslists'
+ | 'back';
diff --git a/frontend/resourceadm/utils/resourceUtils/index.ts b/frontend/resourceadm/utils/resourceUtils/index.ts
index 49f1c9dd78e..5ae0eac1dad 100644
--- a/frontend/resourceadm/utils/resourceUtils/index.ts
+++ b/frontend/resourceadm/utils/resourceUtils/index.ts
@@ -1,8 +1,6 @@
export {
mapLanguageKeyToLanguageText,
getMissingInputLanguageString,
- getIsActiveTab,
- createNavigationTab,
getResourceIdentifierErrorMessage,
deepCompare,
getAvailableEnvironments,
diff --git a/frontend/resourceadm/utils/resourceUtils/resourceUtils.test.tsx b/frontend/resourceadm/utils/resourceUtils/resourceUtils.test.tsx
index 99c6088e80b..266b8fa6d05 100644
--- a/frontend/resourceadm/utils/resourceUtils/resourceUtils.test.tsx
+++ b/frontend/resourceadm/utils/resourceUtils/resourceUtils.test.tsx
@@ -1,6 +1,4 @@
import {
- createNavigationTab,
- getIsActiveTab,
getMissingInputLanguageString,
mapLanguageKeyToLanguageText,
deepCompare,
@@ -9,9 +7,6 @@ import {
validateResource,
} from './';
import type { EnvId } from './resourceUtils';
-import type { LeftNavigationTab } from '../../components/LeftNavigationBar';
-import { TestFlaskIcon } from '@studio/icons';
-import React from 'react';
import type { Resource, SupportedLanguage } from 'app-shared/types/ResourceAdm';
describe('mapKeywordStringToKeywordTypeArray', () => {
@@ -107,42 +102,6 @@ describe('getMissingInputLanguageString', () => {
});
});
-describe('getIsActiveTab', () => {
- it('returns true when current page and tab id mathces', () => {
- const isActive = getIsActiveTab('about', 'about');
- expect(isActive).toBeTruthy();
- });
-
- it('returns false when current page and tab id does not match', () => {
- const isActive = getIsActiveTab('about', 'policy');
- expect(isActive).toBeFalsy();
- });
-});
-
-describe('createNavigationTab', () => {
- const mockOnClick = jest.fn();
-
- const mockTo: string = '/about';
-
- const mockTab: LeftNavigationTab = {
- icon:
,
- tabName: 'resourceadm.left_nav_bar_about',
- tabId: 'about',
- action: {
- type: 'link',
- onClick: mockOnClick,
- to: mockTo,
- },
- isActiveTab: true,
- };
-
- it('creates a new tab when the function is called', () => {
- const newTab = createNavigationTab(
, 'about', mockOnClick, 'about', mockTo);
-
- expect(newTab).toEqual(mockTab);
- });
-});
-
describe('deepCompare', () => {
it('should return true for equal objects', () => {
const obj1 = {
diff --git a/frontend/resourceadm/utils/resourceUtils/resourceUtils.ts b/frontend/resourceadm/utils/resourceUtils/resourceUtils.ts
index 19122d97839..bad205cb034 100644
--- a/frontend/resourceadm/utils/resourceUtils/resourceUtils.ts
+++ b/frontend/resourceadm/utils/resourceUtils/resourceUtils.ts
@@ -1,5 +1,4 @@
import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs';
-import type { LeftNavigationTab } from '../../components/LeftNavigationBar';
import type {
ResourceTypeOption,
ResourceStatusOption,
@@ -10,8 +9,6 @@ import type {
Resource,
ResourceFormError,
} from 'app-shared/types/ResourceAdm';
-import type { ReactNode } from 'react';
-import type { NavigationBarPage } from '../../types/NavigationBarPage';
import { isAppPrefix, isSePrefix } from '../stringUtils';
/**
@@ -154,50 +151,6 @@ export const mapKeywordStringToKeywordTypeArray = (keywrodString: string): Resou
.map((val) => ({ language: 'nb', word: val.trim() }));
};
-/**
- * Gets the status for if a tab is active or not based on the
- * current page and the tabs id.
- *
- * @param currentPage the currently selected tab
- * @param tabId the id of the tab to check
- *
- * @returns if the tab is active or not
- */
-export const getIsActiveTab = (currentPage: NavigationBarPage, tabId: string): boolean => {
- return currentPage === tabId;
-};
-
-/**
- * Creates a new navigation tab to be used in the LeftNavigationBar
- *
- * @param icon the icon to display
- * @param tabId the id of the tab
- * @param onClick function to be executed on click
- * @param currentPage the current selected page
- * @param to where to navigate to
- *
- * @returns a LeftNavigationTab
- */
-export const createNavigationTab = (
- icon: ReactNode,
- tabId: string,
- onClick: (tabId: string) => void,
- currentPage: NavigationBarPage,
- to: string,
-): LeftNavigationTab => {
- return {
- icon,
- tabName: `resourceadm.left_nav_bar_${tabId}`,
- tabId,
- action: {
- type: 'link',
- onClick,
- to,
- },
- isActiveTab: getIsActiveTab(currentPage, tabId),
- };
-};
-
export const getResourceIdentifierErrorMessage = (identifier: string, isConflict?: boolean) => {
const hasAppPrefix = isAppPrefix(identifier);
const hasSePrefix = isSePrefix(identifier);
From 270057e876341fbd365489386aea943af24b2aee Mon Sep 17 00:00:00 2001
From: andreastanderen <71079896+standeren@users.noreply.github.com>
Date: Fri, 6 Dec 2024 14:54:16 +0100
Subject: [PATCH 03/35] fix: allow appmetadata to be undefined when validating
name when data model is uploaded (#14236)
---
.../TopToolbar/utils/validationUtils.test.ts | 22 ++++++++++++++++++-
.../TopToolbar/utils/validationUtils.ts | 2 +-
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/TopToolbar/utils/validationUtils.test.ts b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/TopToolbar/utils/validationUtils.test.ts
index 658a8330d1b..ce2c8d2cb55 100644
--- a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/TopToolbar/utils/validationUtils.test.ts
+++ b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/TopToolbar/utils/validationUtils.test.ts
@@ -1,5 +1,5 @@
import { mockAppMetadata, mockDataTypeId } from '../../../../../test/applicationMetadataMock';
-import { extractDataTypeNamesFromAppMetadata } from './validationUtils';
+import { extractDataTypeNamesFromAppMetadata, findFileNameError } from './validationUtils';
describe('extractDataTypeNamesFromAppMetadata', () => {
it('should extract data type names when application metadata is provided', () => {
@@ -21,3 +21,23 @@ describe('extractDataTypeNamesFromAppMetadata', () => {
expect(dataTypeNames).toEqual([]);
});
});
+
+describe('findFileNameError', () => {
+ it('should validate name as invalid if name does not match regEx', () => {
+ const fileName = 'æ';
+ const validationResult = findFileNameError(fileName, mockAppMetadata);
+ expect(validationResult).toEqual('invalidFileName');
+ });
+
+ it('should validate name as invalid if name exists in appMetadata', () => {
+ const fileName = mockAppMetadata.dataTypes[0].id;
+ const validationResult = findFileNameError(fileName, mockAppMetadata);
+ expect(validationResult).toEqual('fileExists');
+ });
+
+ it('should validate name as valid if appMetadata is undefined', () => {
+ const fileName = 'fileName';
+ const validationResult = findFileNameError(fileName, undefined);
+ expect(validationResult).toEqual(null);
+ });
+});
diff --git a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/TopToolbar/utils/validationUtils.ts b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/TopToolbar/utils/validationUtils.ts
index d65112503a6..d47438c321d 100644
--- a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/TopToolbar/utils/validationUtils.ts
+++ b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/TopToolbar/utils/validationUtils.ts
@@ -46,7 +46,7 @@ const isNameFormatValid = (fileNameWithoutExtension: string): boolean => {
const doesFileExistInMetadata = (
appMetadata: ApplicationMetadata,
fileNameWithoutExtension: string,
-): boolean => appMetadata.dataTypes?.some((dataType) => dataType.id === fileNameWithoutExtension);
+): boolean => appMetadata?.dataTypes?.some((dataType) => dataType.id === fileNameWithoutExtension);
export const extractDataTypeNamesFromAppMetadata = (
appMetadata?: ApplicationMetadata,
From 6e309a897ed1f48bba342cc53775147b5233f028 Mon Sep 17 00:00:00 2001
From: Martin Gunnerud
Date: Fri, 6 Dec 2024 17:19:35 +0100
Subject: [PATCH 04/35] chore(resource-adm): performance improvements / minor
changes (#14156)
---
.../run-playwright-resourceadm-on-pr.yml | 2 +-
.../packages/shared/src/enums/ServerCodes.ts | 1 +
.../useAddAccessListMemberMutation.ts | 3 ++
.../useRemoveAccessListMemberMutation.ts | 3 ++
frontend/resourceadm/package.json | 3 +-
.../AboutResourcePage.test.tsx | 49 +++++++++++++++++--
.../AboutResourcePage/AboutResourcePage.tsx | 9 ++--
.../DeployResourcePage.test.tsx | 18 +++++++
.../DeployResourcePage/DeployResourcePage.tsx | 4 +-
.../pages/ResourcePage/ResourcePage.test.tsx | 18 +++----
.../pages/ResourcePage/ResourcePage.tsx | 34 ++++---------
.../utils/resourceUtils/resourceUtils.ts | 5 +-
yarn.lock | 1 -
13 files changed, 97 insertions(+), 53 deletions(-)
diff --git a/.github/workflows/run-playwright-resourceadm-on-pr.yml b/.github/workflows/run-playwright-resourceadm-on-pr.yml
index 734eca103f0..df66ed55ad6 100644
--- a/.github/workflows/run-playwright-resourceadm-on-pr.yml
+++ b/.github/workflows/run-playwright-resourceadm-on-pr.yml
@@ -61,4 +61,4 @@ jobs:
if: failure()
with:
name: playwright-resourceadm-screenshots
- path: frontend/testing/playwright/test-results
+ path: frontend/resourceadm/testing/playwright/test-results
diff --git a/frontend/packages/shared/src/enums/ServerCodes.ts b/frontend/packages/shared/src/enums/ServerCodes.ts
index c60679e9227..5356e5f29ca 100644
--- a/frontend/packages/shared/src/enums/ServerCodes.ts
+++ b/frontend/packages/shared/src/enums/ServerCodes.ts
@@ -1,4 +1,5 @@
export enum ServerCodes {
+ Ok = 200,
Unauthorized = 401,
Forbidden = 403,
Conflict = 409,
diff --git a/frontend/resourceadm/hooks/mutations/useAddAccessListMemberMutation.ts b/frontend/resourceadm/hooks/mutations/useAddAccessListMemberMutation.ts
index 80da7258ada..3cb4206fba9 100644
--- a/frontend/resourceadm/hooks/mutations/useAddAccessListMemberMutation.ts
+++ b/frontend/resourceadm/hooks/mutations/useAddAccessListMemberMutation.ts
@@ -26,6 +26,9 @@ export const useAddAccessListMemberMutation = (
queryClient.invalidateQueries({
queryKey: [QueryKey.AccessListMembers, env, listIdentifier],
});
+ queryClient.invalidateQueries({
+ queryKey: [QueryKey.AccessList, env, listIdentifier],
+ });
},
});
};
diff --git a/frontend/resourceadm/hooks/mutations/useRemoveAccessListMemberMutation.ts b/frontend/resourceadm/hooks/mutations/useRemoveAccessListMemberMutation.ts
index bc7e5a157e0..a97d2b65805 100644
--- a/frontend/resourceadm/hooks/mutations/useRemoveAccessListMemberMutation.ts
+++ b/frontend/resourceadm/hooks/mutations/useRemoveAccessListMemberMutation.ts
@@ -26,6 +26,9 @@ export const useRemoveAccessListMemberMutation = (
queryClient.invalidateQueries({
queryKey: [QueryKey.AccessListMembers, env, listIdentifier],
});
+ queryClient.invalidateQueries({
+ queryKey: [QueryKey.AccessList, env, listIdentifier],
+ });
},
});
};
diff --git a/frontend/resourceadm/package.json b/frontend/resourceadm/package.json
index 93c38da6d5c..96a3f74dbfb 100644
--- a/frontend/resourceadm/package.json
+++ b/frontend/resourceadm/package.json
@@ -10,8 +10,7 @@
],
"dependencies": {
"react": "18.3.1",
- "react-dom": "18.3.1",
- "react-router-dom": "6.27.0"
+ "react-dom": "18.3.1"
},
"devDependencies": {
"@svgr/webpack": "8.1.0",
diff --git a/frontend/resourceadm/pages/AboutResourcePage/AboutResourcePage.test.tsx b/frontend/resourceadm/pages/AboutResourcePage/AboutResourcePage.test.tsx
index 80cf1c8f550..034b3f87ece 100644
--- a/frontend/resourceadm/pages/AboutResourcePage/AboutResourcePage.test.tsx
+++ b/frontend/resourceadm/pages/AboutResourcePage/AboutResourcePage.test.tsx
@@ -78,7 +78,7 @@ describe('AboutResourcePage', () => {
const mockOnSaveResource = jest.fn();
const defaultProps: AboutResourcePageProps = {
- showAllErrors: false,
+ validationErrors: [],
resourceData: mockResource1,
onSaveResource: mockOnSaveResource,
id: mockId,
@@ -306,8 +306,47 @@ describe('AboutResourcePage', () => {
});
});
- it('displays errors for the required translation fields when showAllErrors are true', async () => {
- render();
+ it('displays errors for the required translation fields', async () => {
+ render(
+ ,
+ );
expect(
screen.getAllByText(textMock('resourceadm.about_resource_resource_type_error')),
@@ -345,7 +384,7 @@ describe('AboutResourcePage', () => {
render(
,
);
@@ -378,7 +417,7 @@ describe('AboutResourcePage', () => {
render(
,
);
diff --git a/frontend/resourceadm/pages/AboutResourcePage/AboutResourcePage.tsx b/frontend/resourceadm/pages/AboutResourcePage/AboutResourcePage.tsx
index b7e9b5338f7..bb262d01aca 100644
--- a/frontend/resourceadm/pages/AboutResourcePage/AboutResourcePage.tsx
+++ b/frontend/resourceadm/pages/AboutResourcePage/AboutResourcePage.tsx
@@ -10,6 +10,7 @@ import type {
ResourceContactPoint,
SupportedLanguage,
ResourceReference,
+ ResourceFormError,
} from 'app-shared/types/ResourceAdm';
import {
availableForTypeMap,
@@ -17,7 +18,6 @@ import {
mapKeywordStringToKeywordTypeArray,
mapKeywordsArrayToString,
resourceTypeMap,
- validateResource,
} from '../../utils/resourceUtils';
import { useTranslation } from 'react-i18next';
import {
@@ -32,8 +32,8 @@ import { ResourceReferenceFields } from '../../components/ResourceReferenceField
import { AccessListEnvLinks } from '../../components/AccessListEnvLinks';
export type AboutResourcePageProps = {
- showAllErrors: boolean;
resourceData: Resource;
+ validationErrors: ResourceFormError[];
onSaveResource: (r: Resource) => void;
id: string;
};
@@ -42,7 +42,6 @@ export type AboutResourcePageProps = {
* @component
* Page that displays information about a resource
*
- * @property {boolean}[showAllErrors] - Flag to decide if all errors should be shown or not
* @property {Resource}[resourceData] - The metadata for the resource
* @property {function}[onSaveResource] - Function to be handled when saving the resource
* @property {string}[id] - The id of the page
@@ -50,8 +49,8 @@ export type AboutResourcePageProps = {
* @returns {React.JSX.Element} - The rendered component
*/
export const AboutResourcePage = ({
- showAllErrors,
resourceData,
+ validationErrors,
onSaveResource,
id,
}: AboutResourcePageProps): React.JSX.Element => {
@@ -93,8 +92,6 @@ export const AboutResourcePage = ({
onSaveResource(res);
};
- const validationErrors = showAllErrors ? validateResource(resourceData, t) : [];
-
/**
* Displays the content on the page
*/
diff --git a/frontend/resourceadm/pages/DeployResourcePage/DeployResourcePage.test.tsx b/frontend/resourceadm/pages/DeployResourcePage/DeployResourcePage.test.tsx
index 3185b101a75..a9c5d50ec70 100644
--- a/frontend/resourceadm/pages/DeployResourcePage/DeployResourcePage.test.tsx
+++ b/frontend/resourceadm/pages/DeployResourcePage/DeployResourcePage.test.tsx
@@ -277,6 +277,24 @@ describe('DeployResourcePage', () => {
expect(prodButton).toBeDisabled();
});
+ it('disables the deploy buttons when there is no policy', async () => {
+ await resolveAndWaitForSpinnerToDisappear({
+ getValidatePolicy: () => Promise.resolve({ status: 404, errors: [] }),
+ });
+ const tt02 = textMock('resourceadm.deploy_test_env');
+ const prod = textMock('resourceadm.deploy_prod_env');
+
+ const tt02Button = screen.getByRole('button', {
+ name: textMock('resourceadm.deploy_card_publish', { env: tt02 }),
+ });
+ const prodButton = screen.getByRole('button', {
+ name: textMock('resourceadm.deploy_card_publish', { env: prod }),
+ });
+
+ expect(tt02Button).toBeDisabled();
+ expect(prodButton).toBeDisabled();
+ });
+
it('disables the deploy buttons when there is validate policy error', async () => {
await resolveAndWaitForSpinnerToDisappear({
getValidatePolicy: () => Promise.resolve(mockValidatePolicyData2),
diff --git a/frontend/resourceadm/pages/DeployResourcePage/DeployResourcePage.tsx b/frontend/resourceadm/pages/DeployResourcePage/DeployResourcePage.tsx
index b8af8828b24..a9cc14e029a 100644
--- a/frontend/resourceadm/pages/DeployResourcePage/DeployResourcePage.tsx
+++ b/frontend/resourceadm/pages/DeployResourcePage/DeployResourcePage.tsx
@@ -24,6 +24,7 @@ import { useTranslation, Trans } from 'react-i18next';
import { mergeQueryStatuses } from 'app-shared/utils/tanstackQueryUtils';
import { useUrlParams } from '../../hooks/useUrlParams';
import { getAvailableEnvironments } from '../../utils/resourceUtils';
+import { ServerCodes } from 'app-shared/enums/ServerCodes';
export type DeployResourcePageProps = {
navigateToPageWithError: (page: NavigationBarPage) => void;
@@ -177,7 +178,8 @@ export const DeployResourcePage = ({
* @returns a boolean for if it is possible
*/
const isDeployPossible = (envVersion: string): boolean => {
- const policyError = validatePolicyData === undefined || validatePolicyData.status === 400;
+ const policyError =
+ validatePolicyData === undefined || validatePolicyData.status !== ServerCodes.Ok;
const canDeploy =
validateResourceData.status === 200 &&
!policyError &&
diff --git a/frontend/resourceadm/pages/ResourcePage/ResourcePage.test.tsx b/frontend/resourceadm/pages/ResourcePage/ResourcePage.test.tsx
index 0ac1858a711..306efcc6677 100644
--- a/frontend/resourceadm/pages/ResourcePage/ResourcePage.test.tsx
+++ b/frontend/resourceadm/pages/ResourcePage/ResourcePage.test.tsx
@@ -25,6 +25,11 @@ const mockResource1: Resource = {
{ reference: '1', referenceType: 'ServiceCode', referenceSource: 'Altinn2' },
{ reference: '2', referenceType: 'ServiceEditionCode', referenceSource: 'Altinn2' },
],
+ delegable: false,
+ resourceType: 'GenericAccessResource',
+ status: 'Completed',
+ contactPoints: [{ category: 'test', contactPage: '', email: '', telephone: '' }],
+ availableForType: ['Company'],
};
const mockResource2: Resource = {
@@ -57,11 +62,6 @@ describe('ResourcePage', () => {
expect(queriesMock.getValidatePolicy).toHaveBeenCalledTimes(1);
});
- it('fetches validate resource on mount', () => {
- renderResourcePage();
- expect(queriesMock.getValidateResource).toHaveBeenCalledTimes(1);
- });
-
it('fetches resource on mount', () => {
renderResourcePage();
expect(queriesMock.getResource).toHaveBeenCalledTimes(1);
@@ -183,14 +183,8 @@ describe('ResourcePage', () => {
const getResource = jest
.fn()
.mockImplementation(() => Promise.resolve(mockResource1));
- const getValidateResource = jest.fn().mockImplementation(() =>
- Promise.resolve({
- status: 200,
- errors: {},
- }),
- );
- renderResourcePage({ getResource, getValidateResource });
+ renderResourcePage({ getResource });
await waitForElementToBeRemoved(() =>
screen.queryByTitle(textMock('resourceadm.about_resource_spinner')),
);
diff --git a/frontend/resourceadm/pages/ResourcePage/ResourcePage.tsx b/frontend/resourceadm/pages/ResourcePage/ResourcePage.tsx
index 722aa856835..ec780461b0f 100644
--- a/frontend/resourceadm/pages/ResourcePage/ResourcePage.tsx
+++ b/frontend/resourceadm/pages/ResourcePage/ResourcePage.tsx
@@ -5,11 +5,7 @@ import classes from './ResourcePage.module.css';
import { PolicyEditorPage } from '../PolicyEditorPage';
import { getResourceDashboardURL, getResourcePageURL } from '../../utils/urlUtils';
import { DeployResourcePage } from '../DeployResourcePage';
-import {
- useSinlgeResourceQuery,
- useValidatePolicyQuery,
- useValidateResourceQuery,
-} from '../../hooks/queries';
+import { useSinlgeResourceQuery, useValidatePolicyQuery } from '../../hooks/queries';
import { AboutResourcePage } from '../AboutResourcePage';
import { NavigationModal } from '../../components/NavigationModal';
import { Spinner } from '@digdir/designsystemet-react';
@@ -24,7 +20,7 @@ import {
MigrationIcon,
UploadIcon,
} from '@studio/icons';
-import { deepCompare, getAltinn2Reference } from '../../utils/resourceUtils';
+import { deepCompare, getAltinn2Reference, validateResource } from '../../utils/resourceUtils';
import type { EnvId } from '../../utils/resourceUtils';
import { ResourceAccessLists } from '../../components/ResourceAccessLists';
import { AccessListDetail } from '../../components/AccessListDetails';
@@ -64,14 +60,11 @@ export const ResourcePage = (): React.JSX.Element => {
// Get metadata for policy
const { refetch: refetchValidatePolicy } = useValidatePolicyQuery(org, app, resourceId);
- // Get metadata for resource
- const { refetch: refetchValidateResource } = useValidateResourceQuery(org, app, resourceId);
-
- const {
- data: loadedResourceData,
- refetch: refetchResource,
- isPending: resourcePending,
- } = useSinlgeResourceQuery(org, app, resourceId);
+ const { data: loadedResourceData, isPending: resourcePending } = useSinlgeResourceQuery(
+ org,
+ app,
+ resourceId,
+ );
const { data: accessList } = useGetAccessListQuery(org, accessListId, env);
@@ -97,15 +90,9 @@ export const ResourcePage = (): React.JSX.Element => {
*/
const navigateToPage = async (page: NavigationBarPage) => {
if (currentPage !== page) {
- await editResource(resourceData);
- await refetchResource();
-
// Validate Resource and display errors + modal
if (currentPage === 'about') {
- const data = await refetchValidateResource();
- const validationStatus = data?.data?.status ?? null;
-
- if (validationStatus === 200) {
+ if (validationErrors.length === 0) {
setShowResourceErrors(false);
handleNavigation(page);
} else {
@@ -157,8 +144,6 @@ export const ResourcePage = (): React.JSX.Element => {
*/
const navigateToPageWithError = async (page: NavigationBarPage) => {
if (page === 'about') {
- await refetchResource();
- await refetchValidateResource();
setShowResourceErrors(true);
}
if (page === 'policy') {
@@ -171,6 +156,7 @@ export const ResourcePage = (): React.JSX.Element => {
handleNavigation(nextPage);
};
+ const validationErrors = validateResource(resourceData, t);
const altinn2References = getAltinn2Reference(resourceData);
/**
* Decide if the migration page should be accessible or not
@@ -259,8 +245,8 @@ export const ResourcePage = (): React.JSX.Element => {
{currentPage === aboutPageId && (
diff --git a/frontend/resourceadm/utils/resourceUtils/resourceUtils.ts b/frontend/resourceadm/utils/resourceUtils/resourceUtils.ts
index bad205cb034..467f56cefd9 100644
--- a/frontend/resourceadm/utils/resourceUtils/resourceUtils.ts
+++ b/frontend/resourceadm/utils/resourceUtils/resourceUtils.ts
@@ -199,11 +199,14 @@ export const deepCompare = (original: any, changed: any) => {
};
export const validateResource = (
- resourceData: Resource,
+ resourceData: Resource | undefined,
t: (key: string, params?: KeyValuePairs
) => string,
): ResourceFormError[] => {
const errors: ResourceFormError[] = [];
+ if (!resourceData) {
+ return [];
+ }
// validate resourceType
if (!Object.keys(resourceTypeMap).includes(resourceData.resourceType)) {
errors.push({
diff --git a/yarn.lock b/yarn.lock
index 8be1b93681d..75f1ea0b44b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -16139,7 +16139,6 @@ __metadata:
jest: "npm:29.7.0"
react: "npm:18.3.1"
react-dom: "npm:18.3.1"
- react-router-dom: "npm:6.27.0"
typescript: "npm:5.7.2"
webpack: "npm:5.96.1"
webpack-dev-server: "npm:5.1.0"
From a5cb81b936ec4c95ca0d2f7df5e306e29448970b Mon Sep 17 00:00:00 2001
From: Lars <74791975+lassopicasso@users.noreply.github.com>
Date: Sun, 8 Dec 2024 23:20:39 +0100
Subject: [PATCH 05/35] style: remove help texts in other settings (#14196)
Co-authored-by: JamalAlabdullah <90609090+JamalAlabdullah@users.noreply.github.com>
---
.../components/config/FormComponentConfig.tsx | 2 -
.../config/editModal/EditBooleanValue.tsx | 8 ++--
.../packages/ux-editor/src/hooks/index.ts | 1 +
.../useComponentPropertyHelpText.test.ts | 37 +++++++++++++++++++
.../src/hooks/useComponentPropertyHelpText.ts | 16 ++++++++
5 files changed, 57 insertions(+), 7 deletions(-)
create mode 100644 frontend/packages/ux-editor/src/hooks/useComponentPropertyHelpText.test.ts
create mode 100644 frontend/packages/ux-editor/src/hooks/useComponentPropertyHelpText.ts
diff --git a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx
index 61cca8fc1c3..5ab3cf2935c 100644
--- a/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx
+++ b/frontend/packages/ux-editor/src/components/config/FormComponentConfig.tsx
@@ -127,7 +127,6 @@ export const FormComponentConfig = ({
propertyKey={propertyKey}
defaultValue={properties[propertyKey].default}
key={propertyKey}
- helpText={properties[propertyKey]?.description}
/>
);
})}
@@ -137,7 +136,6 @@ export const FormComponentConfig = ({
<>
{
diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditBooleanValue.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditBooleanValue.tsx
index 4cae14575a3..1a259f1edff 100644
--- a/frontend/packages/ux-editor/src/components/config/editModal/EditBooleanValue.tsx
+++ b/frontend/packages/ux-editor/src/components/config/editModal/EditBooleanValue.tsx
@@ -1,13 +1,11 @@
import React from 'react';
import { Switch } from '@digdir/designsystemet-react';
import type { IGenericEditComponent } from '../componentConfig';
-import { useText } from '../../../hooks';
+import { useText, useComponentPropertyLabel, useComponentPropertyHelpText } from '../../../hooks';
import { FormField } from '../../FormField';
-import { useComponentPropertyLabel } from '../../../hooks/useComponentPropertyLabel';
export interface EditBooleanValueProps extends IGenericEditComponent {
propertyKey: string;
- helpText?: string;
defaultValue?: boolean;
}
@@ -15,11 +13,11 @@ export const EditBooleanValue = ({
component,
handleComponentChange,
propertyKey,
- helpText,
defaultValue,
}: EditBooleanValueProps) => {
const t = useText();
const componentPropertyLabel = useComponentPropertyLabel();
+ const componentPropertyHelpText = useComponentPropertyHelpText();
const handleChange = () => {
handleComponentChange({
@@ -44,7 +42,7 @@ export const EditBooleanValue = ({
helpText={
isValueExpression(component[propertyKey])
? t('ux_editor.component_properties.config_is_expression_message')
- : helpText
+ : componentPropertyHelpText(propertyKey)
}
renderField={({ fieldProps }) => {
return (
diff --git a/frontend/packages/ux-editor/src/hooks/index.ts b/frontend/packages/ux-editor/src/hooks/index.ts
index 10e5eda8b0f..62a95c9457a 100644
--- a/frontend/packages/ux-editor/src/hooks/index.ts
+++ b/frontend/packages/ux-editor/src/hooks/index.ts
@@ -12,3 +12,4 @@ export { useValidateComponent } from './useValidateComponent';
export { useComponentPropertyLabel } from './useComponentPropertyLabel';
export { useAppContext } from './useAppContext';
export { useGetLayoutSetByName } from './useGetLayoutSetByName';
+export { useComponentPropertyHelpText } from './useComponentPropertyHelpText';
diff --git a/frontend/packages/ux-editor/src/hooks/useComponentPropertyHelpText.test.ts b/frontend/packages/ux-editor/src/hooks/useComponentPropertyHelpText.test.ts
new file mode 100644
index 00000000000..4f1bab81cb9
--- /dev/null
+++ b/frontend/packages/ux-editor/src/hooks/useComponentPropertyHelpText.test.ts
@@ -0,0 +1,37 @@
+import { renderHook } from '@testing-library/react';
+import { useComponentPropertyHelpText } from './useComponentPropertyHelpText';
+import { textMock } from '@studio/testing/mocks/i18nMock';
+import type { KeyValuePairs } from 'app-shared/types/KeyValuePairs';
+
+const somePropertyName = 'undefinedKey';
+
+const customTextMockToHandleUndefined = (
+ keys: string | string[],
+ variables?: KeyValuePairs,
+) => {
+ const key = Array.isArray(keys) ? keys[0] : keys;
+ if (key === `ux_editor.component_properties_help_text.${somePropertyName}`) return key;
+ return variables
+ ? '[mockedText(' + key + ', ' + JSON.stringify(variables) + ')]'
+ : '[mockedText(' + key + ')]';
+};
+
+jest.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: customTextMockToHandleUndefined,
+ }),
+}));
+
+describe('useComponentPropertyHelpText', () => {
+ it('Returns a function that returns the help text', () => {
+ const result = renderHook(() => useComponentPropertyHelpText()).result.current;
+ const propertyHelpText = result('test');
+ expect(propertyHelpText).toEqual(textMock('ux_editor.component_properties_help_text.test'));
+ });
+
+ it('Returns a function that returns undefined if there was no text key for the help text', () => {
+ const result = renderHook(() => useComponentPropertyHelpText()).result.current;
+ const propertyHelpText = result(somePropertyName);
+ expect(propertyHelpText).toEqual(undefined);
+ });
+});
diff --git a/frontend/packages/ux-editor/src/hooks/useComponentPropertyHelpText.ts b/frontend/packages/ux-editor/src/hooks/useComponentPropertyHelpText.ts
new file mode 100644
index 00000000000..519771883dd
--- /dev/null
+++ b/frontend/packages/ux-editor/src/hooks/useComponentPropertyHelpText.ts
@@ -0,0 +1,16 @@
+import { useTranslation } from 'react-i18next';
+import { useCallback } from 'react';
+
+export const useComponentPropertyHelpText = () => {
+ const { t } = useTranslation();
+
+ return useCallback(
+ (propertyKey: string): string | undefined => {
+ const translationKey: string = `ux_editor.component_properties_help_text.${propertyKey}`;
+ const translation = t(translationKey);
+
+ return translation !== translationKey ? translation : undefined;
+ },
+ [t],
+ );
+};
From 9aa4f75900c44da1263fd32a758a16f90e31ac55 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 9 Dec 2024 08:44:05 +0100
Subject: [PATCH 06/35] chore(deps): update dependency
eslint-plugin-testing-library to v7 (#14246)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
package.json | 2 +-
yarn.lock | 152 ++++++++++++++++++++++-----------------------------
2 files changed, 65 insertions(+), 89 deletions(-)
diff --git a/package.json b/package.json
index 053724c3368..77a046e4d66 100644
--- a/package.json
+++ b/package.json
@@ -45,7 +45,7 @@
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-react": "7.37.2",
"eslint-plugin-react-hooks": "4.6.2",
- "eslint-plugin-testing-library": "6.5.0",
+ "eslint-plugin-testing-library": "7.1.0",
"glob": "11.0.0",
"husky": "9.1.7",
"jest": "29.7.0",
diff --git a/yarn.lock b/yarn.lock
index 75f1ea0b44b..34568b94fde 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5272,13 +5272,6 @@ __metadata:
languageName: node
linkType: hard
-"@types/semver@npm:^7.3.12":
- version: 7.3.13
- resolution: "@types/semver@npm:7.3.13"
- checksum: 10/0064efd7a0515a539062b71630c72ca2b058501b957326c285cdff82f42c1716d9f9f831332ccf719d5ee8cc3ef24f9ff62122d7a7140c73959a240b49b0f62d
- languageName: node
- linkType: hard
-
"@types/semver@npm:^7.3.4":
version: 7.5.8
resolution: "@types/semver@npm:7.5.8"
@@ -5452,16 +5445,6 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/scope-manager@npm:5.62.0":
- version: 5.62.0
- resolution: "@typescript-eslint/scope-manager@npm:5.62.0"
- dependencies:
- "@typescript-eslint/types": "npm:5.62.0"
- "@typescript-eslint/visitor-keys": "npm:5.62.0"
- checksum: 10/e827770baa202223bc0387e2fd24f630690809e460435b7dc9af336c77322290a770d62bd5284260fa881c86074d6a9fd6c97b07382520b115f6786b8ed499da
- languageName: node
- linkType: hard
-
"@typescript-eslint/scope-manager@npm:7.18.0":
version: 7.18.0
resolution: "@typescript-eslint/scope-manager@npm:7.18.0"
@@ -5482,6 +5465,16 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/scope-manager@npm:8.17.0, @typescript-eslint/scope-manager@npm:^8.15.0":
+ version: 8.17.0
+ resolution: "@typescript-eslint/scope-manager@npm:8.17.0"
+ dependencies:
+ "@typescript-eslint/types": "npm:8.17.0"
+ "@typescript-eslint/visitor-keys": "npm:8.17.0"
+ checksum: 10/fa934d9fd88070833c57a3e79c0f933d0b68884c00293a1d571889b882e5c9680ccfdc5c77a7160d5a4b8b46657f93db2468a4726a517fce4d3bc764b66f1995
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/type-utils@npm:7.18.0":
version: 7.18.0
resolution: "@typescript-eslint/type-utils@npm:7.18.0"
@@ -5499,13 +5492,6 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/types@npm:5.62.0":
- version: 5.62.0
- resolution: "@typescript-eslint/types@npm:5.62.0"
- checksum: 10/24e8443177be84823242d6729d56af2c4b47bfc664dd411a1d730506abf2150d6c31bdefbbc6d97c8f91043e3a50e0c698239dcb145b79bb6b0c34469aaf6c45
- languageName: node
- linkType: hard
-
"@typescript-eslint/types@npm:7.18.0":
version: 7.18.0
resolution: "@typescript-eslint/types@npm:7.18.0"
@@ -5520,21 +5506,10 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/typescript-estree@npm:5.62.0":
- version: 5.62.0
- resolution: "@typescript-eslint/typescript-estree@npm:5.62.0"
- dependencies:
- "@typescript-eslint/types": "npm:5.62.0"
- "@typescript-eslint/visitor-keys": "npm:5.62.0"
- debug: "npm:^4.3.4"
- globby: "npm:^11.1.0"
- is-glob: "npm:^4.0.3"
- semver: "npm:^7.3.7"
- tsutils: "npm:^3.21.0"
- peerDependenciesMeta:
- typescript:
- optional: true
- checksum: 10/06c975eb5f44b43bd19fadc2e1023c50cf87038fe4c0dd989d4331c67b3ff509b17fa60a3251896668ab4d7322bdc56162a9926971218d2e1a1874d2bef9a52e
+"@typescript-eslint/types@npm:8.17.0":
+ version: 8.17.0
+ resolution: "@typescript-eslint/types@npm:8.17.0"
+ checksum: 10/46baf69ab30dd814a390590b94ca64c407ac725cb0143590ddcaf72fa43c940cec180539752ce4af26ac7e0ae2f5f921cfd0d07b088ca680f8a28800d4d33a5f
languageName: node
linkType: hard
@@ -5576,6 +5551,25 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/typescript-estree@npm:8.17.0":
+ version: 8.17.0
+ resolution: "@typescript-eslint/typescript-estree@npm:8.17.0"
+ dependencies:
+ "@typescript-eslint/types": "npm:8.17.0"
+ "@typescript-eslint/visitor-keys": "npm:8.17.0"
+ debug: "npm:^4.3.4"
+ fast-glob: "npm:^3.3.2"
+ is-glob: "npm:^4.0.3"
+ minimatch: "npm:^9.0.4"
+ semver: "npm:^7.6.0"
+ ts-api-utils: "npm:^1.3.0"
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ checksum: 10/8a1f8be767b82e75d41eedda7fdb5135787ceaab480671b6d9891b5f92ee3a13f19ad6f48d5abf5e4f2afc4dd3317c621c1935505ef098f22b67be2f9d01ab7b
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/utils@npm:7.18.0":
version: 7.18.0
resolution: "@typescript-eslint/utils@npm:7.18.0"
@@ -5590,21 +5584,20 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/utils@npm:^5.62.0":
- version: 5.62.0
- resolution: "@typescript-eslint/utils@npm:5.62.0"
+"@typescript-eslint/utils@npm:^8.15.0":
+ version: 8.17.0
+ resolution: "@typescript-eslint/utils@npm:8.17.0"
dependencies:
- "@eslint-community/eslint-utils": "npm:^4.2.0"
- "@types/json-schema": "npm:^7.0.9"
- "@types/semver": "npm:^7.3.12"
- "@typescript-eslint/scope-manager": "npm:5.62.0"
- "@typescript-eslint/types": "npm:5.62.0"
- "@typescript-eslint/typescript-estree": "npm:5.62.0"
- eslint-scope: "npm:^5.1.1"
- semver: "npm:^7.3.7"
+ "@eslint-community/eslint-utils": "npm:^4.4.0"
+ "@typescript-eslint/scope-manager": "npm:8.17.0"
+ "@typescript-eslint/types": "npm:8.17.0"
+ "@typescript-eslint/typescript-estree": "npm:8.17.0"
peerDependencies:
- eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
- checksum: 10/15ef13e43998a082b15f85db979f8d3ceb1f9ce4467b8016c267b1738d5e7cdb12aa90faf4b4e6dd6486c236cf9d33c463200465cf25ff997dbc0f12358550a1
+ eslint: ^8.57.0 || ^9.0.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ checksum: 10/e82934468bece55ccf633be9f3fe6cae26791fa6488b5af08ea22566f6b32e1296917e46cb1fe39bba7717ebdf0dca49935112760c4439a11af36b3b7925917a
languageName: node
linkType: hard
@@ -5625,16 +5618,6 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/visitor-keys@npm:5.62.0":
- version: 5.62.0
- resolution: "@typescript-eslint/visitor-keys@npm:5.62.0"
- dependencies:
- "@typescript-eslint/types": "npm:5.62.0"
- eslint-visitor-keys: "npm:^3.3.0"
- checksum: 10/dc613ab7569df9bbe0b2ca677635eb91839dfb2ca2c6fa47870a5da4f160db0b436f7ec0764362e756d4164e9445d49d5eb1ff0b87f4c058946ae9d8c92eb388
- languageName: node
- linkType: hard
-
"@typescript-eslint/visitor-keys@npm:7.18.0":
version: 7.18.0
resolution: "@typescript-eslint/visitor-keys@npm:7.18.0"
@@ -5655,6 +5638,16 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/visitor-keys@npm:8.17.0":
+ version: 8.17.0
+ resolution: "@typescript-eslint/visitor-keys@npm:8.17.0"
+ dependencies:
+ "@typescript-eslint/types": "npm:8.17.0"
+ eslint-visitor-keys: "npm:^4.2.0"
+ checksum: 10/e7a3c3b9430ecefb8e720f735f8a94f87901f055c75dc8eec60052dfdf90cc28dd33f03c11cd8244551dc988bf98d1db9bd09ef8fd3c51236912cab3680b9c6b
+ languageName: node
+ linkType: hard
+
"@ungap/structured-clone@npm:^1.2.0":
version: 1.2.0
resolution: "@ungap/structured-clone@npm:1.2.0"
@@ -6195,7 +6188,7 @@ __metadata:
eslint-plugin-jsx-a11y: "npm:6.10.2"
eslint-plugin-react: "npm:7.37.2"
eslint-plugin-react-hooks: "npm:4.6.2"
- eslint-plugin-testing-library: "npm:6.5.0"
+ eslint-plugin-testing-library: "npm:7.1.0"
glob: "npm:11.0.0"
husky: "npm:9.1.7"
i18next: "npm:23.16.8"
@@ -9723,18 +9716,19 @@ __metadata:
languageName: node
linkType: hard
-"eslint-plugin-testing-library@npm:6.5.0":
- version: 6.5.0
- resolution: "eslint-plugin-testing-library@npm:6.5.0"
+"eslint-plugin-testing-library@npm:7.1.0":
+ version: 7.1.0
+ resolution: "eslint-plugin-testing-library@npm:7.1.0"
dependencies:
- "@typescript-eslint/utils": "npm:^5.62.0"
+ "@typescript-eslint/scope-manager": "npm:^8.15.0"
+ "@typescript-eslint/utils": "npm:^8.15.0"
peerDependencies:
- eslint: ^7.5.0 || ^8.0.0 || ^9.0.0
- checksum: 10/5ce5f71aed5dc39315f7fa2e987c7e1ffc78f7d35164c2d8769ad29000558828dc13c04bfef289c28faf57749ce7720e5ab17869780b743bc1d8cd59a5052a43
+ eslint: ^8.57.0 || ^9.0.0
+ checksum: 10/c250daad343514e107efcd7011cbb41e0e4ccd8c16cef1a1ba3612ad28970d431f81109bb38b6c2c3d6cb4217587aeb6c60f89d0faae6931065ef858dd2ba641
languageName: node
linkType: hard
-"eslint-scope@npm:5.1.1, eslint-scope@npm:^5.1.1":
+"eslint-scope@npm:5.1.1":
version: 5.1.1
resolution: "eslint-scope@npm:5.1.1"
dependencies:
@@ -17835,13 +17829,6 @@ __metadata:
languageName: node
linkType: hard
-"tslib@npm:^1.8.1":
- version: 1.14.1
- resolution: "tslib@npm:1.14.1"
- checksum: 10/7dbf34e6f55c6492637adb81b555af5e3b4f9cc6b998fb440dac82d3b42bdc91560a35a5fb75e20e24a076c651438234da6743d139e4feabf0783f3cdfe1dddb
- languageName: node
- linkType: hard
-
"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0":
version: 2.5.0
resolution: "tslib@npm:2.5.0"
@@ -17856,17 +17843,6 @@ __metadata:
languageName: node
linkType: hard
-"tsutils@npm:^3.21.0":
- version: 3.21.0
- resolution: "tsutils@npm:3.21.0"
- dependencies:
- tslib: "npm:^1.8.1"
- peerDependencies:
- typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
- checksum: 10/ea036bec1dd024e309939ffd49fda7a351c0e87a1b8eb049570dd119d447250e2c56e0e6c00554e8205760e7417793fdebff752a46e573fbe07d4f375502a5b2
- languageName: node
- linkType: hard
-
"tunnel-agent@npm:^0.6.0":
version: 0.6.0
resolution: "tunnel-agent@npm:0.6.0"
From 586089ab5885e5d526dbeb55796a0288fffa91b5 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 9 Dec 2024 08:48:30 +0100
Subject: [PATCH 07/35] chore(deps): update alpine docker tag to v3.21.0
(#14243)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
backend/Migrations.Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/backend/Migrations.Dockerfile b/backend/Migrations.Dockerfile
index 09569daacc0..03dff76c05b 100644
--- a/backend/Migrations.Dockerfile
+++ b/backend/Migrations.Dockerfile
@@ -13,7 +13,7 @@ ENV OidcLoginSettings__ClientSecret=dummyRequired
RUN dotnet ef migrations script --project src/Designer/Designer.csproj -o /app/migrations.sql
-FROM alpine:3.20.3 AS final
+FROM alpine:3.21.0 AS final
COPY --from=build /app/migrations.sql migrations.sql
RUN apk --no-cache add postgresql-client
From 5565df8930296f9118e944a5d8dfd2fb38e14da7 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 9 Dec 2024 08:51:13 +0100
Subject: [PATCH 08/35] chore(deps): update dependency @types/node to v22
(#14244)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.../testing/playwright/package.json | 2 +-
frontend/testing/playwright/package.json | 2 +-
yarn.lock | 20 ++-----------------
3 files changed, 4 insertions(+), 20 deletions(-)
diff --git a/frontend/resourceadm/testing/playwright/package.json b/frontend/resourceadm/testing/playwright/package.json
index e8e73299949..a5ca9ad4215 100644
--- a/frontend/resourceadm/testing/playwright/package.json
+++ b/frontend/resourceadm/testing/playwright/package.json
@@ -9,7 +9,7 @@
"devDependencies": {
"@playwright/test": "^1.41.1",
"@types/dotenv": "^8.2.0",
- "@types/node": "^20.10.5",
+ "@types/node": "^22.0.0",
"ts-node": "^10.9.2"
},
"scripts": {
diff --git a/frontend/testing/playwright/package.json b/frontend/testing/playwright/package.json
index 27627af7599..595ec32cb9d 100644
--- a/frontend/testing/playwright/package.json
+++ b/frontend/testing/playwright/package.json
@@ -9,7 +9,7 @@
"devDependencies": {
"@playwright/test": "^1.41.1",
"@types/dotenv": "^8.2.0",
- "@types/node": "^20.10.5",
+ "@types/node": "^22.0.0",
"ts-node": "^10.9.2"
},
"scripts": {
diff --git a/yarn.lock b/yarn.lock
index 34568b94fde..795f1dc4025 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5166,15 +5166,6 @@ __metadata:
languageName: node
linkType: hard
-"@types/node@npm:^20.10.5":
- version: 20.17.9
- resolution: "@types/node@npm:20.17.9"
- dependencies:
- undici-types: "npm:~6.19.2"
- checksum: 10/11604a47adf383892394a59a339136b2746a71addf4a3b13f661d23b6e81e8a4f3b35e69dbcffc94698368e5ab5ec056a43a86c87eff00b1b8ea8cfcbfe641df
- languageName: node
- linkType: hard
-
"@types/parse-json@npm:^4.0.0":
version: 4.0.0
resolution: "@types/parse-json@npm:4.0.0"
@@ -14657,7 +14648,7 @@ __metadata:
dependencies:
"@playwright/test": "npm:^1.41.1"
"@types/dotenv": "npm:^8.2.0"
- "@types/node": "npm:^20.10.5"
+ "@types/node": "npm:^22.0.0"
dotenv: "npm:^16.3.1"
ts-node: "npm:^10.9.2"
languageName: unknown
@@ -14669,7 +14660,7 @@ __metadata:
dependencies:
"@playwright/test": "npm:^1.41.1"
"@types/dotenv": "npm:^8.2.0"
- "@types/node": "npm:^20.10.5"
+ "@types/node": "npm:^22.0.0"
dotenv: "npm:^16.3.1"
ts-node: "npm:^10.9.2"
languageName: unknown
@@ -18100,13 +18091,6 @@ __metadata:
languageName: node
linkType: hard
-"undici-types@npm:~6.19.2":
- version: 6.19.8
- resolution: "undici-types@npm:6.19.8"
- checksum: 10/cf0b48ed4fc99baf56584afa91aaffa5010c268b8842f62e02f752df209e3dea138b372a60a963b3b2576ed932f32329ce7ddb9cb5f27a6c83040d8cd74b7a70
- languageName: node
- linkType: hard
-
"undici-types@npm:~6.20.0":
version: 6.20.0
resolution: "undici-types@npm:6.20.0"
From f93a1ab479bb669c7bad462e5b066d5b258d12fe Mon Sep 17 00:00:00 2001
From: Martin Gunnerud
Date: Mon, 9 Dec 2024 09:01:05 +0100
Subject: [PATCH 09/35] feat(resource-adm): add better error messages for
migrate delegations (#14167)
---
.../Controllers/ResourceAdminController.cs | 7 +++
.../packages/shared/src/enums/ServerCodes.ts | 1 +
.../MigrationPanel/MigrationPanel.test.tsx | 49 ++++++++++++++++++-
.../MigrationPanel/MigrationPanel.tsx | 39 +++++++--------
frontend/resourceadm/language/src/nb.json | 3 ++
.../resourceadm/utils/resourceUtils/index.ts | 1 +
.../resourceUtils/resourceUtils.test.tsx | 46 ++++++++++++++++-
.../utils/resourceUtils/resourceUtils.ts | 45 +++++++++++++++++
8 files changed, 166 insertions(+), 25 deletions(-)
diff --git a/backend/src/Designer/Controllers/ResourceAdminController.cs b/backend/src/Designer/Controllers/ResourceAdminController.cs
index 29cca0e6408..4d86405ae04 100644
--- a/backend/src/Designer/Controllers/ResourceAdminController.cs
+++ b/backend/src/Designer/Controllers/ResourceAdminController.cs
@@ -336,6 +336,13 @@ public async Task ImportResource(string org, string serviceCode, i
[Route("designer/api/{org}/resources/altinn2/delegationcount/{serviceCode}/{serviceEdition}/{env}")]
public async Task GetDelegationCount(string org, string serviceCode, int serviceEdition, string env)
{
+ List allResources = await _resourceRegistry.GetResourceList(env.ToLower(), true);
+ bool serviceExists = allResources.Any(x => x.Identifier.Equals($"se_{serviceCode}_{serviceEdition}"));
+ if (!serviceExists)
+ {
+ return new NotFoundResult();
+ }
+
ServiceResource resource = await _resourceRegistry.GetServiceResourceFromService(serviceCode, serviceEdition, env.ToLower());
if (!IsServiceOwner(resource, org))
{
diff --git a/frontend/packages/shared/src/enums/ServerCodes.ts b/frontend/packages/shared/src/enums/ServerCodes.ts
index 5356e5f29ca..297f1986a42 100644
--- a/frontend/packages/shared/src/enums/ServerCodes.ts
+++ b/frontend/packages/shared/src/enums/ServerCodes.ts
@@ -6,4 +6,5 @@ export enum ServerCodes {
NotFound = 404,
PreconditionFailed = 412,
TooLargeContent = 413,
+ InternalServerError = 500,
}
diff --git a/frontend/resourceadm/components/MigrationPanel/MigrationPanel.test.tsx b/frontend/resourceadm/components/MigrationPanel/MigrationPanel.test.tsx
index 168cc8b8988..6381dcd46b1 100644
--- a/frontend/resourceadm/components/MigrationPanel/MigrationPanel.test.tsx
+++ b/frontend/resourceadm/components/MigrationPanel/MigrationPanel.test.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { render, screen, waitFor } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { textMock } from '@studio/testing/mocks/i18nMock';
import { MemoryRouter } from 'react-router-dom';
@@ -47,7 +47,52 @@ describe('MigrationPanel', () => {
}),
},
);
- await waitFor(() => screen.findByText(textMock('resourceadm.migration_not_needed')));
+ expect(
+ await screen.findByText(textMock('resourceadm.migration_not_needed')),
+ ).toBeInTheDocument();
+ });
+
+ it('should show message if link service does not exist in given environment', async () => {
+ renderMigrationPanel(
+ {},
+ {
+ getAltinn2DelegationsCount: jest.fn().mockImplementation(() => {
+ return Promise.reject({ response: { status: ServerCodes.NotFound } });
+ }),
+ },
+ );
+ expect(
+ await screen.findByText(textMock('resourceadm.migration_service_not_found')),
+ ).toBeInTheDocument();
+ });
+
+ it('should show message if link service cannot be migrated in given environment', async () => {
+ renderMigrationPanel(
+ {},
+ {
+ getAltinn2DelegationsCount: jest.fn().mockImplementation(() => {
+ return Promise.reject({ response: { status: ServerCodes.Forbidden } });
+ }),
+ },
+ );
+ expect(
+ await screen.findByText(textMock('resourceadm.migration_cannot_migrate_in_env')),
+ ).toBeInTheDocument();
+ });
+
+ it('should show message if get delegation count fails in given environment', async () => {
+ renderMigrationPanel(
+ {},
+ {
+ getAltinn2DelegationsCount: jest.fn().mockImplementation(() => {
+ return Promise.reject({ response: { status: ServerCodes.InternalServerError } });
+ }),
+ },
+ );
+
+ expect(
+ await screen.findByText(textMock('resourceadm.migration_technical_error')),
+ ).toBeInTheDocument();
});
it('should show error when user starts migrate delegations if user has no permission to migrate', async () => {
diff --git a/frontend/resourceadm/components/MigrationPanel/MigrationPanel.tsx b/frontend/resourceadm/components/MigrationPanel/MigrationPanel.tsx
index d4074ee54e8..82f66e2fb6c 100644
--- a/frontend/resourceadm/components/MigrationPanel/MigrationPanel.tsx
+++ b/frontend/resourceadm/components/MigrationPanel/MigrationPanel.tsx
@@ -4,12 +4,10 @@ import { Alert, Checkbox, Heading } from '@digdir/designsystemet-react';
import { useTranslation } from 'react-i18next';
import { StudioButton, StudioModal } from '@studio/components';
import classes from './MigrationPanel.module.css';
-import type { Environment } from '../../utils/resourceUtils';
+import { getMigrationErrorMessage, type Environment } from '../../utils/resourceUtils';
import { useGetAltinn2DelegationsCount } from '../../hooks/queries/useGetAltinn2DelegationCount';
import { useMigrateDelegationsMutation } from '../../hooks/mutations/useMigrateDelegationsMutation';
import { useUrlParams } from '../../hooks/useUrlParams';
-import type { ResourceError } from 'app-shared/types/ResourceAdm';
-import { ServerCodes } from 'app-shared/enums/ServerCodes';
export interface MigrationPanelProps {
serviceCode: string;
@@ -35,12 +33,11 @@ export const MigrationPanel = ({
const { mutate: migrateDelegations, isPending: isSettingMigrateDelegations } =
useMigrateDelegationsMutation(org, env.id);
- const { data: numberOfA2Delegations, isFetching: isLoadingDelegationCount } =
- useGetAltinn2DelegationsCount(org, serviceCode, serviceEdition, env.id);
-
- const isErrorForbidden = (error: Error) => {
- return (error as ResourceError)?.response?.status === ServerCodes.Forbidden;
- };
+ const {
+ data: numberOfA2Delegations,
+ isFetching: isLoadingDelegationCount,
+ error: loadDelegationCountError,
+ } = useGetAltinn2DelegationsCount(org, serviceCode, serviceEdition, env.id);
const postMigrateDelegations = (): void => {
setMigrateDelegationsError(null);
@@ -67,6 +64,13 @@ export const MigrationPanel = ({
setServiceExpiredWarningModalRef.current?.close();
};
+ const errorMessage = getMigrationErrorMessage(
+ loadDelegationCountError,
+ migrateDelegationsError,
+ isPublishedInEnv,
+ );
+ const isMigrateButtonDisabled = !!errorMessage || isSettingMigrateDelegations;
+
return (
<>
{t('resourceadm.migration_altinn3_delegations')} N/A
- {!isPublishedInEnv && (
-
- {t('resourceadm.migration_not_published')}
-
- )}
{isPublishedInEnv && numberOfA2Delegations?.numberOfDelegations === 0 && (
{t('resourceadm.migration_not_needed')}
)}
- {migrateDelegationsError && (
-
- {isErrorForbidden(migrateDelegationsError)
- ? t('resourceadm.migration_no_migration_access')
- : t('resourceadm.migration_post_migration_failed')}
-
+ {errorMessage && (
+ {t(errorMessage.errorMessage)}
)}