Skip to content

Commit

Permalink
XIVY-15648 import when manually adding variable
Browse files Browse the repository at this point in the history
When manually adding a variable that is present in a required project, the known variable is imported instead.
  • Loading branch information
ivy-lgi committed Dec 18, 2024
1 parent 5d44d16 commit fac18b6
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 59 deletions.
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
import { BasicField, BasicInput, Flex, PanelMessage, ReadonlyProvider, Textarea, useReadonly } from '@axonivy/ui-components';
import { EMPTY_KNOWN_VARIABLES, type KnownVariables } from '@axonivy/variable-editor-protocol';
import { EMPTY_KNOWN_VARIABLES } from '@axonivy/variable-editor-protocol';
import { useMemo } from 'react';
import { useAppContext } from '../../../context/AppContext';
import { useMeta } from '../../../context/useMeta';
import { getNode, getNodesOnPath, updateNode, hasChildren as variableHasChildren } from '../../../utils/tree/tree-data';
import { type VariableUpdates } from '../data/variable';
import { findKnownVariable } from '../dialog/known-variables';
import './DetailContent.css';
import { Metadata } from './Metadata';
import { Value } from './Value';

export const useOverwrites = () => {
const { context, variables, selectedVariable } = useAppContext();
let currentNode: KnownVariables | undefined = useMeta('meta/knownVariables', context, EMPTY_KNOWN_VARIABLES).data;
if (currentNode === undefined || currentNode.children.length === 0) {
const knownVariables = useMeta('meta/knownVariables', context, EMPTY_KNOWN_VARIABLES).data;
if (knownVariables.children.length === 0) {
return false;
}
const variableNodes = getNodesOnPath(variables, selectedVariable);
for (const variableNode of variableNodes) {
currentNode = currentNode.children.find(child => child.name === variableNode?.name);
if (!currentNode) {
return false;
}
}
return true;
const key = getNodesOnPath(variables, selectedVariable).map(node => (node ? node.name : ''));
return findKnownVariable(knownVariables, ...key) !== undefined;
};

export const VariablesDetailContent = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,24 @@ import {
selectRow
} from '@axonivy/ui-components';
import { IvyIcons } from '@axonivy/ui-icons';
import { EMPTY_KNOWN_VARIABLES } from '@axonivy/variable-editor-protocol';
import { type Table } from '@tanstack/react-table';
import { useMemo, useState } from 'react';
import { useAppContext } from '../../../context/AppContext';
import { useMeta } from '../../../context/useMeta';
import { keyOfFirstSelectedNonLeafRow, keysOfAllNonLeafRows, newNodeName, subRowNamesOfRow, toRowId } from '../../../utils/tree/tree';
import { addNode } from '../../../utils/tree/tree-data';
import { validateName, validateNamespace } from '../data/validation-utils';
import { createVariable, type Variable } from '../data/variable';
import './AddDialog.css';
import { addKnownVariable, findKnownVariable } from './known-variables';

type AddVariableDialogProps = {
table: Table<Variable>;
};

export const AddVariableDialog = ({ table }: AddVariableDialogProps) => {
const { variables, setVariables, setSelectedVariable } = useAppContext();
const { context, variables, setVariables, setSelectedVariable } = useAppContext();

const [name, setName] = useState('');
const [namespace, setNamespace] = useState('');
Expand All @@ -44,13 +47,23 @@ export const AddVariableDialog = ({ table }: AddVariableDialogProps) => {

const namespaceOptions = () => keysOfAllNonLeafRows(table).map(key => ({ value: key }));

const addVariable = () =>
const knownVariables = useMeta('meta/knownVariables', context, EMPTY_KNOWN_VARIABLES).data;

const addVariable = () => {
const namespaceKey = namespace ? namespace.split('.') : [];
const overwrittenVariable = findKnownVariable(knownVariables, ...namespaceKey, name);
setVariables(old => {
const addNodeReturnValue = addNode(name, namespace, old, createVariable);
let addNodeReturnValue;
if (overwrittenVariable) {
addNodeReturnValue = addKnownVariable(old, overwrittenVariable);
} else {
addNodeReturnValue = addNode(name, namespace, old, createVariable);
}
selectRow(table, toRowId(addNodeReturnValue.newNodePath));
setSelectedVariable(addNodeReturnValue.newNodePath);
return addNodeReturnValue.newData;
});
};

const allInputsValid = () => !nameValidationMessage && !namespaceValidationMessage;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import { type Table } from '@tanstack/react-table';
import { useState } from 'react';
import { useAppContext } from '../../../context/AppContext';
import { toRowId } from '../../../utils/tree/tree';
import { addNode } from '../../../utils/tree/tree-data';
import type { AddNodeReturnType } from '../../../utils/tree/types';
import { isMetadata, type Metadata } from '../data/metadata';
import { createVariable, type Variable } from '../data/variable';
import { type Variable } from '../data/variable';
import { VariableBrowser } from './VariableBrowser';
import { addKnownVariable } from './known-variables';

type OverwriteProps = {
table: Table<Variable>;
Expand All @@ -23,7 +21,7 @@ export const OverwriteDialog = ({ table }: OverwriteProps) => {
return;
}
setVariables(old => {
const addNodeReturnValue = addVariable(old, node);
const addNodeReturnValue = addKnownVariable(old, node);
selectRow(table, toRowId(addNodeReturnValue.newNodePath));
setSelectedVariable(addNodeReturnValue.newNodePath);
return addNodeReturnValue.newData;
Expand Down Expand Up @@ -51,28 +49,3 @@ export const OverwriteDialog = ({ table }: OverwriteProps) => {
</Dialog>
);
};

const addVariable = (variables: Array<Variable>, node: KnownVariables): AddNodeReturnType<Variable> => {
let metadata: Metadata = { type: '' };
const nodeMetaData = node.metaData;
if (isMetadata(nodeMetaData)) {
metadata = nodeMetaData;
}
let returnValue = addNode(node.name, node.namespace, variables, name => {
if (name === node.name) {
return {
name,
value: node.value,
children: [],
description: node.description,
metadata
};
}
return createVariable(name);
});
const newNodePath = returnValue.newNodePath;
for (const child of node.children) {
returnValue = addVariable(returnValue.newData, child);
}
return { newData: returnValue.newData, newNodePath };
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { EMPTY_KNOWN_VARIABLES, type KnownVariables } from '@axonivy/variable-editor-protocol';
import { toNodes } from './known-variables';
import { EMPTY_KNOWN_VARIABLES, type KnownVariables, type MetaData } from '@axonivy/variable-editor-protocol';
import type { Variable } from '../data/variable';
import { addKnownVariable, findKnownVariable, toNodes } from './known-variables';

const knownVariables: KnownVariables = {
namespace: '',
Expand Down Expand Up @@ -37,6 +38,22 @@ const knownVariables: KnownVariables = {
metaData: { type: 'string' },
description: 'Access key to access amazon comprehend',
children: []
},
{
namespace: 'Amazon.Comprehend',
name: 'Enum',
value: 'two',
metaData: { type: 'enum', values: ['one', 'two', 'three'] } as MetaData,
description: '',
children: []
},
{
namespace: 'Amazon.Comprehend',
name: 'File',
value: '',
metaData: { type: 'file', extension: 'json' } as MetaData,
description: '',
children: []
}
]
}
Expand All @@ -55,7 +72,161 @@ test('toNodes', () => {
expect(root.children).toHaveLength(1);
const node = root.children[0];
expect(node).toMatchObject({ value: 'Comprehend', icon: 'folder-open', info: 'Amazon comprehend connector settings' });
expect(node.children).toHaveLength(2);
expect(node.children).toHaveLength(4);
expect(node.children[0]).toMatchObject({ value: 'SecretKey', icon: 'password', info: 'Secret key to access amazon comprehend' });
expect(node.children[1]).toMatchObject({ value: 'AccessKey', icon: 'quote', info: 'Access key to access amazon comprehend' });
expect(node.children[2]).toMatchObject({ value: 'Enum', icon: 'list', info: '' });
expect(node.children[3]).toMatchObject({ value: 'File', icon: 'note', info: '' });
});

describe('findKnownVariable', () => {
test('known variables is empty', () => {
const knownVariables = { children: [] as Array<KnownVariables> } as KnownVariables;
expect(findKnownVariable(knownVariables, 'some', 'key')).toBeUndefined();
});

test('find folder', () => {
expect(findKnownVariable(knownVariables, 'Amazon', 'Comprehend')).toEqual(knownVariables.children[0].children[0]);
});

test('find leaf', () => {
expect(findKnownVariable(knownVariables, 'Amazon', 'Comprehend', 'AccessKey')).toEqual(
knownVariables.children[0].children[0].children[1]
);
});

test('variable does not exist', () => {
expect(findKnownVariable(knownVariables, 'notFound')).toBeUndefined();
});
});

describe('addKnownVariable', () => {
test('leaf', () => {
const variables = [{ name: 'Variable' }] as Array<Variable>;
const originalVariables = structuredClone(variables);
const addNodeReturnValue = addKnownVariable(variables, knownVariables.children[0].children[0].children[0]);
const newData = addNodeReturnValue.newData;
const newNodePath = addNodeReturnValue.newNodePath;
expect(variables).toEqual(originalVariables);
expect(newData).not.toBe(variables);
expect(newNodePath).toEqual([1, 0, 0]);
expect(newData).toEqual([
{ name: 'Variable' },
{
name: 'Amazon',
value: '',
description: '',
metadata: { type: '' },
children: [
{
name: 'Comprehend',
value: '',
description: '',
metadata: { type: '' },
children: [
{
name: 'SecretKey',
value: '<YOUR_SECRET_KEY>',
description: 'Secret key to access amazon comprehend',
metadata: { type: 'password' },
children: []
}
]
}
]
}
]);
});

test('folder', () => {
const variables = [{ name: 'Variable' }] as Array<Variable>;
const originalVariables = structuredClone(variables);
const addNodeReturnValue = addKnownVariable(variables, knownVariables.children[0].children[0]);
const newData = addNodeReturnValue.newData;
const newNodePath = addNodeReturnValue.newNodePath;
expect(variables).toEqual(originalVariables);
expect(newData).not.toBe(variables);
expect(newNodePath).toEqual([1, 0]);
expect(newData).toEqual([
{ name: 'Variable' },
{
name: 'Amazon',
value: '',
description: '',
metadata: { type: '' },
children: [
{
name: 'Comprehend',
value: '',
description: 'Amazon comprehend connector settings',
metadata: { type: '' },
children: [
{
name: 'SecretKey',
value: '<YOUR_SECRET_KEY>',
description: 'Secret key to access amazon comprehend',
metadata: { type: 'password' },
children: []
},
{
name: 'AccessKey',
value: '<YOUR_ACCESS_KEY>',
description: 'Access key to access amazon comprehend',
metadata: { type: '' },
children: []
},
{
name: 'Enum',
value: 'two',
description: '',
metadata: { type: 'enum', values: ['one', 'two', 'three'] },
children: []
},
{
name: 'File',
value: '',
description: '',
metadata: { type: 'file', extension: 'json' },
children: []
}
]
}
]
}
]);
});

test('parent already exists', () => {
const variables = [
{ name: 'Variable' },
{ name: 'Amazon', children: [{ name: 'Comprehend', children: [] as Array<Variable> }] }
] as Array<Variable>;
const originalVariables = structuredClone(variables);
const addNodeReturnValue = addKnownVariable(variables, knownVariables.children[0].children[0].children[0]);
const newData = addNodeReturnValue.newData;
const newNodePath = addNodeReturnValue.newNodePath;
expect(variables).toEqual(originalVariables);
expect(newData).not.toBe(variables);
expect(newNodePath).toEqual([1, 0, 0]);
expect(newData).toEqual([
{ name: 'Variable' },
{
name: 'Amazon',
children: [
{
name: 'Comprehend',
children: [
{
name: 'SecretKey',
value: '<YOUR_SECRET_KEY>',
description: 'Secret key to access amazon comprehend',
metadata: { type: 'password' },
children: []
}
]
}
]
}
]);
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { BrowserNode } from '@axonivy/ui-components';
import type { KnownVariables } from '@axonivy/variable-editor-protocol';
import { addNode } from '../../../utils/tree/tree-data';
import { isMetadata, type Metadata } from '../data/metadata';
import { createVariable, type Variable } from '../data/variable';
import { nodeIcon } from '../data/variable-utils';

export const toNodes = (root?: KnownVariables): Array<BrowserNode> => {
Expand All @@ -21,3 +24,39 @@ const toNode = (node: KnownVariables): BrowserNode => {
children
};
};

export const findKnownVariable = (node: KnownVariables, ...key: Array<string>) => {
let currentNode: KnownVariables | undefined = node;
for (const part of key) {
currentNode = currentNode.children.find(child => child.name === part);
if (!currentNode) {
return;
}
}
return currentNode;
};

export const addKnownVariable = (variables: Array<Variable>, node: KnownVariables) => {
let metadata: Metadata = { type: '' };
const nodeMetaData = node.metaData;
if (isMetadata(nodeMetaData)) {
metadata = nodeMetaData;
}
let returnValue = addNode(node.name, node.namespace, variables, name => {
if (name === node.name) {
return {
name,
value: node.value,
children: [],
description: node.description,
metadata
};
}
return createVariable(name);
});
const newNodePath = returnValue.newNodePath;
for (const child of node.children) {
returnValue = addKnownVariable(returnValue.newData, child);
}
return { newData: returnValue.newData, newNodePath };
};
Loading

0 comments on commit fac18b6

Please sign in to comment.