From 14ca1ea7c8bca230f5baf86aa1f88fd7b2dec25e Mon Sep 17 00:00:00 2001 From: Lukas Giger Date: Tue, 10 Dec 2024 08:09:37 +0100 Subject: [PATCH] XIVY-14730 disable metadata of overwritten variables Make the metadata read only if the variable is found in the known variables of the required projects. --- integrations/standalone/package.json | 4 +- package-lock.json | 28 +++++----- packages/variable-editor/package.json | 6 +- .../variables/data/test-utils/test-utils.tsx | 25 +++++++-- .../variables/detail/DetailContent.test.ts | 56 +++++++++++++++++++ .../variables/detail/DetailContent.tsx | 31 ++++++++-- .../tests/integration/mock/import.spec.ts | 9 ++- 7 files changed, 130 insertions(+), 29 deletions(-) create mode 100644 packages/variable-editor/src/components/variables/detail/DetailContent.test.ts diff --git a/integrations/standalone/package.json b/integrations/standalone/package.json index 7e5d54c3..8e369640 100644 --- a/integrations/standalone/package.json +++ b/integrations/standalone/package.json @@ -5,8 +5,8 @@ "dependencies": { "@axonivy/variable-editor": "*", "@axonivy/variable-editor-protocol": "*", - "@axonivy/ui-components": "~12.0.1-next.390", - "@axonivy/ui-icons": "~12.0.1-next.390", + "@axonivy/ui-components": "~12.0.1-next.401", + "@axonivy/ui-icons": "~12.0.1-next.401", "react": "^18.3.1", "react-dom": "^18.3.1" }, diff --git a/package-lock.json b/package-lock.json index d7f57762..5c4e07ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,8 +33,8 @@ "name": "@axonivy/config-editor-standalone", "version": "12.0.1-next", "dependencies": { - "@axonivy/ui-components": "~12.0.1-next.390", - "@axonivy/ui-icons": "~12.0.1-next.390", + "@axonivy/ui-components": "~12.0.1-next.401", + "@axonivy/ui-icons": "~12.0.1-next.401", "@axonivy/variable-editor": "*", "@axonivy/variable-editor-protocol": "*", "react": "^18.3.1", @@ -78,18 +78,18 @@ "link": true }, "node_modules/@axonivy/jsonrpc": { - "version": "12.0.1-next.390", - "resolved": "https://npmjs-registry.ivyteam.ch/@axonivy/jsonrpc/-/jsonrpc-12.0.1-next.390.tgz", - "integrity": "sha512-c+pA8Gta+IRPdH6smx0wQQl8l50JPMn5lCtWThfbZMdQ5f9eY7J7AcTSTzPQE/HtWt+bwZ6ppnha+go3jSWSag==", + "version": "12.0.1-next.401", + "resolved": "https://npmjs-registry.ivyteam.ch/@axonivy/jsonrpc/-/jsonrpc-12.0.1-next.401.tgz", + "integrity": "sha512-/nSfXe3NZ58JqlcNQgSLQ8+NZXgFN90DHmj1x0y+aQ3sb9NfIGMB/+9khj4PhWPY62dAbvVrJKpw2epgojtvoQ==", "license": "(EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0)", "dependencies": { "vscode-jsonrpc": "^8.2.0" } }, "node_modules/@axonivy/ui-components": { - "version": "12.0.1-next.390", - "resolved": "https://npmjs-registry.ivyteam.ch/@axonivy/ui-components/-/ui-components-12.0.1-next.390.tgz", - "integrity": "sha512-AuoG8Drj3wiYN+oZkTaxSZmb/ZVWFUrNI/vxWmbtsDxBnWvVbvSdaMSmZPuanTySuojq4m57RB3SyJztzNaSeg==", + "version": "12.0.1-next.401", + "resolved": "https://npmjs-registry.ivyteam.ch/@axonivy/ui-components/-/ui-components-12.0.1-next.401.tgz", + "integrity": "sha512-24jQWI9QB+2PAvjtwwIFjTZZiTOOAoX1+vpeN1ka4w3Hw41jljtYy8upFmscWJ39v2/R2P4j5fbwJUJWVf6KhA==", "license": "(EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0)", "dependencies": { "@radix-ui/react-accordion": "1.2.1", @@ -119,9 +119,9 @@ } }, "node_modules/@axonivy/ui-icons": { - "version": "12.0.1-next.390", - "resolved": "https://npmjs-registry.ivyteam.ch/@axonivy/ui-icons/-/ui-icons-12.0.1-next.390.tgz", - "integrity": "sha512-32nSB27HM+l11iUfGh54mJbuK2K0b1H6/t62F3aRP8MDhhhnP+i95THjsULDBVQBwpa7XUp+qX/2XNxnEe/VSw==", + "version": "12.0.1-next.401", + "resolved": "https://npmjs-registry.ivyteam.ch/@axonivy/ui-icons/-/ui-icons-12.0.1-next.401.tgz", + "integrity": "sha512-ArZYAUrID0toxbE1rMWd68HPnfrsF8fm7DUwXr3jJH/8yUxYED9nzlvVK15HAC9SA2a/mBHX9Te9nStBU04cvw==", "license": "(EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0)" }, "node_modules/@axonivy/variable-editor": { @@ -15959,9 +15959,9 @@ "version": "12.0.1-next", "license": "Apache-2.0", "dependencies": { - "@axonivy/jsonrpc": "~12.0.1-next.390", - "@axonivy/ui-components": "~12.0.1-next.390", - "@axonivy/ui-icons": "~12.0.1-next.390", + "@axonivy/jsonrpc": "~12.0.1-next.401", + "@axonivy/ui-components": "~12.0.1-next.401", + "@axonivy/ui-icons": "~12.0.1-next.401", "@axonivy/variable-editor-protocol": "12.0.1-next", "@tanstack/react-query": "5.32.1", "@tanstack/react-query-devtools": "5.32.1", diff --git a/packages/variable-editor/package.json b/packages/variable-editor/package.json index 1512e35d..a2d7524d 100644 --- a/packages/variable-editor/package.json +++ b/packages/variable-editor/package.json @@ -17,9 +17,9 @@ "types": "lib/index.d.ts", "main": "lib/editor.js", "dependencies": { - "@axonivy/jsonrpc": "~12.0.1-next.390", - "@axonivy/ui-components": "~12.0.1-next.390", - "@axonivy/ui-icons": "~12.0.1-next.390", + "@axonivy/jsonrpc": "~12.0.1-next.401", + "@axonivy/ui-components": "~12.0.1-next.401", + "@axonivy/ui-icons": "~12.0.1-next.401", "@axonivy/variable-editor-protocol": "12.0.1-next", "@tanstack/react-query": "5.32.1", "@tanstack/react-query-devtools": "5.32.1", diff --git a/packages/variable-editor/src/components/variables/data/test-utils/test-utils.tsx b/packages/variable-editor/src/components/variables/data/test-utils/test-utils.tsx index b3ff92b8..ff25912a 100644 --- a/packages/variable-editor/src/components/variables/data/test-utils/test-utils.tsx +++ b/packages/variable-editor/src/components/variables/data/test-utils/test-utils.tsx @@ -1,27 +1,42 @@ +import type { Client, ValidationMessages, VariablesEditorDataContext } from '@axonivy/variable-editor-protocol'; import type { RenderHookOptions } from '@testing-library/react'; import { renderHook } from '@testing-library/react'; import { type ReactNode } from 'react'; import { AppProvider } from '../../../../context/AppContext'; -import type { ValidationMessages, VariablesEditorDataContext } from '@axonivy/variable-editor-protocol'; +import { ClientContextProvider } from '../../../../protocol/ClientContextProvider'; +import { QueryProvider } from '../../../../query/QueryProvider'; +import { initQueryClient } from '../../../../query/query-client'; +import type { TreePath } from '../../../../utils/tree/types'; +import type { Variable } from '../variable'; type ContextHelperProps = { + client?: Partial; appContext?: { + variables?: Array; + selectedVariable?: TreePath; validations?: ValidationMessages; }; }; const ContextHelper = (props: ContextHelperProps & { children: ReactNode }) => { + const client = (props.client ?? new EmptyClient()) as Client; const appContext = { context: {} as VariablesEditorDataContext, - variables: [], + variables: props.appContext?.variables ?? [], setVariables: () => {}, - selectedVariable: [], + selectedVariable: props.appContext?.selectedVariable ?? [], setSelectedVariable: () => {}, validations: props.appContext?.validations ?? [], detail: true, setDetail: () => {} }; - return {props.children}; + return ( + + + {props.children} + + + ); }; export const customRenderHook = ( @@ -33,3 +48,5 @@ export const customRenderHook = ( ...options }); }; + +class EmptyClient implements Partial {} diff --git a/packages/variable-editor/src/components/variables/detail/DetailContent.test.ts b/packages/variable-editor/src/components/variables/detail/DetailContent.test.ts new file mode 100644 index 00000000..1ab75fdd --- /dev/null +++ b/packages/variable-editor/src/components/variables/detail/DetailContent.test.ts @@ -0,0 +1,56 @@ +import type { Client, MetaRequestTypes, ProjectVarNode } from '@axonivy/variable-editor-protocol'; +import { waitFor } from '@testing-library/react'; +import { customRenderHook } from '../data/test-utils/test-utils'; +import type { Variable } from '../data/variable'; +import { useOverwrites } from './DetailContent'; + +describe('useOverwrites', () => { + describe('false', () => { + test('in known folder', () => { + const view = customRenderHook(() => useOverwrites(), { + wrapperProps: { client: new ClientMock(), appContext: { variables: variables, selectedVariable: [1, 0] } } + }); + expect(view.result.current).toBeFalsy(); + }); + + test('not in known folder', () => { + const view = customRenderHook(() => useOverwrites(), { + wrapperProps: { client: new ClientMock(), appContext: { variables: variables, selectedVariable: [0] } } + }); + expect(view.result.current).toBeFalsy(); + }); + }); + + describe('true', () => { + test('leaf', async () => { + const view = customRenderHook(() => useOverwrites(), { + wrapperProps: { client: new ClientMock(), appContext: { variables: variables, selectedVariable: [1, 1, 0] } } + }); + await waitFor(() => { + expect(view.result.current).toBeTruthy(); + }); + }); + + test('not leaf', async () => { + const view = customRenderHook(() => useOverwrites(), { + wrapperProps: { client: new ClientMock(), appContext: { variables: variables, selectedVariable: [1, 1] } } + }); + await waitFor(() => { + expect(view.result.current).toBeTruthy(); + }); + }); + }); +}); + +const variables = [ + { name: 'Variable' }, + { name: 'Amazon', children: [{ name: 'AmazonVariable' }, { name: 'Comprehend', children: [{ name: 'SecretKey' }] }] } +] as Array; + +class ClientMock implements Partial { + meta(): Promise { + return Promise.resolve({ + children: [{ name: 'Amazon', children: [{ name: 'Comprehend', children: [{ name: 'SecretKey' }, { name: 'AccessKey' }] }] }] + } as ProjectVarNode); + } +} diff --git a/packages/variable-editor/src/components/variables/detail/DetailContent.tsx b/packages/variable-editor/src/components/variables/detail/DetailContent.tsx index f2b224cb..65084121 100644 --- a/packages/variable-editor/src/components/variables/detail/DetailContent.tsx +++ b/packages/variable-editor/src/components/variables/detail/DetailContent.tsx @@ -1,16 +1,33 @@ -import { BasicField, Flex, BasicInput, PanelMessage, Textarea } from '@axonivy/ui-components'; +import { BasicField, BasicInput, Flex, PanelMessage, ReadonlyProvider, Textarea } from '@axonivy/ui-components'; +import { EMPTY_PROJECT_VAR_NODE, type ProjectVarNode } from '@axonivy/variable-editor-protocol'; +import { useMemo } from 'react'; import { useAppContext } from '../../../context/AppContext'; -import { getNode, updateNode, hasChildren as variableHasChildren } from '../../../utils/tree/tree-data'; +import { useMeta } from '../../../context/useMeta'; +import { getNode, getNodesOnPath, updateNode, hasChildren as variableHasChildren } from '../../../utils/tree/tree-data'; import { type VariableUpdates } from '../data/variable'; +import './DetailContent.css'; import { Metadata } from './Metadata'; import { Value } from './Value'; -import './DetailContent.css'; -import { useMemo } from 'react'; + +export const useOverwrites = () => { + const { context, variables, selectedVariable } = useAppContext(); + const variableNodes = getNodesOnPath(variables, selectedVariable); + let currentNode: ProjectVarNode | undefined = useMeta('meta/knownVariables', context, EMPTY_PROJECT_VAR_NODE).data; + for (const variableNode of variableNodes) { + currentNode = currentNode.children.find(child => child.name === variableNode?.name); + if (!currentNode) { + return false; + } + } + return true; +}; export const VariablesDetailContent = () => { const { variables, setVariables, selectedVariable } = useAppContext(); const variable = useMemo(() => getNode(variables, selectedVariable), [variables, selectedVariable]); + const overwrites = useOverwrites(); + if (!variable) { return ; } @@ -31,7 +48,11 @@ export const VariablesDetailContent = () => { onChange={event => handleVariableAttributeChange([{ key: 'description', value: event.target.value }])} /> - {!hasChildren && } + {!hasChildren && ( + + + + )} ); }; diff --git a/playwright/tests/integration/mock/import.spec.ts b/playwright/tests/integration/mock/import.spec.ts index c3726e59..2abe8562 100644 --- a/playwright/tests/integration/mock/import.spec.ts +++ b/playwright/tests/integration/mock/import.spec.ts @@ -1,4 +1,4 @@ -import { test } from '@playwright/test'; +import { expect, test } from '@playwright/test'; import { VariableEditor } from '../../pageobjects/VariableEditor'; test('importAndOverwrite', async ({ page }) => { @@ -44,3 +44,10 @@ test('importAndOverwriteWholeSubTree', async ({ page }) => { await tree.row(14).click(); await details.expectValues('AccessKey', '', 'Access key to access amazon comprehend', 'Default'); }); + +test('disabledMetadataOfOverwrittenVariable', async ({ page }) => { + const editor = await VariableEditor.openMock(page); + await editor.addVariable('SecretKey', 'Amazon.Comprehend'); + await editor.tree.row(13).click(); + await expect(editor.details.metaData.locator).toBeDisabled(); +});