From db5af3d0a434eb05d0a83fac63b13da77a883503 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 9 Jan 2024 12:49:45 +0100 Subject: [PATCH 1/9] CB-4010 connection form removed reshadow --- .../src/ConnectionForm/Options/Options.m.css | 4 ++++ .../src/ConnectionForm/Options/Options.tsx | 18 +++++++----------- 2 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.m.css diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.m.css b/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.m.css new file mode 100644 index 0000000000..a9c02f1af7 --- /dev/null +++ b/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.m.css @@ -0,0 +1,4 @@ +.form { + flex: 1; + overflow: auto; +} diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.tsx b/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.tsx index a1718c6a0a..1b8e5f4fbc 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.tsx +++ b/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.tsx @@ -7,7 +7,6 @@ */ import { observer } from 'mobx-react-lite'; import { useCallback, useRef } from 'react'; -import styled, { css } from 'reshadow'; import { AUTH_PROVIDER_LOCAL_ID, EAdminPermission } from '@cloudbeaver/core-authentication'; import { @@ -24,11 +23,13 @@ import { ObjectPropertyInfoForm, Radio, RadioGroup, + s, Textarea, useAdministrationSettings, useFormValidator, usePermission, useResource, + useS, useTranslate, } from '@cloudbeaver/core-blocks'; import { DatabaseAuthModelsResource, DBDriverResource, isLocalConnection } from '@cloudbeaver/core-connections'; @@ -43,19 +44,13 @@ import { ProjectSelect } from '@cloudbeaver/plugin-projects'; import { ConnectionFormService } from '../ConnectionFormService'; import type { IConnectionFormProps } from '../IConnectionFormProps'; import { ConnectionOptionsTabService } from './ConnectionOptionsTabService'; +import styles from './Options.m.css'; import { ParametersForm } from './ParametersForm'; import { ProviderPropertiesForm } from './ProviderPropertiesForm'; import { useOptions } from './useOptions'; const PROFILE_AUTH_MODEL_ID = 'profile'; -const styles = css` - Form { - flex: 1; - overflow: auto; - } -`; - interface IDriverConfiguration { name: string; value: DriverConfigurationType; @@ -81,6 +76,7 @@ export const Options: TabContainerPanelComponent = observe const formRef = useRef(null); const translate = useTranslate(); const { info, config, availableDrivers, submittingTask: submittingHandlers, disabled } = state; + const style = useS(styles); //@TODO it's here until the profile implementation in the CloudBeaver const readonly = state.readonly || info?.authModel === PROFILE_AUTH_MODEL_ID; @@ -167,8 +163,8 @@ export const Options: TabContainerPanelComponent = observe properties = info.authProperties; } - return styled(styles)( -
+ return ( + @@ -354,6 +350,6 @@ export const Options: TabContainerPanelComponent = observe )} -
, + ); }); From 74a7cde7d773523da4d78b9e8dde9dfb306a5916 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 9 Jan 2024 19:39:37 +0100 Subject: [PATCH 2/9] CB-4010 feat: is equal resources --- .../src/ResourcesHooks/useResource.ts | 7 ++++- .../core-resource/src/Resource/Resource.ts | 31 ++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts b/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts index 7fce2ba3fe..c777cb9bf9 100644 --- a/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts +++ b/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts @@ -161,7 +161,12 @@ export function useResource< propertiesRef.includes = includes; } - if (key === null || propertiesRef.key === null || !propertiesRef.resource.isIntersect(key, propertiesRef.key)) { + if ( + key === null || + propertiesRef.key === null || + !propertiesRef.resource.isIntersect(key, propertiesRef.key) || + !propertiesRef.resource.isEqual(key, propertiesRef.key) + ) { propertiesRef.key = key; } }); diff --git a/webapp/packages/core-resource/src/Resource/Resource.ts b/webapp/packages/core-resource/src/Resource/Resource.ts index eb405efcfb..2ae3608597 100644 --- a/webapp/packages/core-resource/src/Resource/Resource.ts +++ b/webapp/packages/core-resource/src/Resource/Resource.ts @@ -8,7 +8,7 @@ import { makeObservable, observable, toJS } from 'mobx'; import { Dependency } from '@cloudbeaver/core-di'; -import { isContainsException, isPrimitive, MetadataMap } from '@cloudbeaver/core-utils'; +import { isArraysEqual, isContainsException, isPrimitive, MetadataMap } from '@cloudbeaver/core-utils'; import { CachedResourceParamKey } from './CachedResource'; import type { ICachedResourceMetadata } from './ICachedResourceMetadata'; @@ -107,6 +107,35 @@ export abstract class Resource< return ResourceKeyUtils.isIntersect(key, nextKey, this.isKeyEqual); } + /** + * Checks + * @param param - Resource key + * @param second - Resource key + * @returns {boolean} Returns true if key can is the same by all key-values + */ + isEqual(param: ResourceKey, second: ResourceKey): boolean { + if (param === second) { + return true; + } + + if (isResourceAlias(param) && isResourceAlias(second)) { + param = this.aliases.transformToAlias(param); + second = this.aliases.transformToAlias(second); + + return param.isEqual(second); + } + + if (isResourceAlias(param) || isResourceAlias(second)) { + return false; + } + + if (isResourceKeyList(param) && isResourceKeyList(second)) { + return isArraysEqual(param, second); + } + + return false; + } + /** * Can be overridden to provide equality check for complicated keys */ From ea5e28df66f70fde54d248d261eeed24bd9e8583 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 9 Jan 2024 19:40:19 +0100 Subject: [PATCH 3/9] CB-4010 :feat isArraysEqual performance optimization --- .../packages/core-utils/src/isArraysEqual.ts | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/webapp/packages/core-utils/src/isArraysEqual.ts b/webapp/packages/core-utils/src/isArraysEqual.ts index 0f9ebf892b..e0e8077e8d 100644 --- a/webapp/packages/core-utils/src/isArraysEqual.ts +++ b/webapp/packages/core-utils/src/isArraysEqual.ts @@ -13,11 +13,29 @@ export function isArraysEqual(first: T[], second: T[], isEqual: (a: T, b: T) return false; } - return !first.some((b, index) => { - if (order) { - return !isEqual(second[index], b); + const map = new Map(); + + for (let i = 0; i < first.length; i++) { + const currentFirst = first[i]; + const currentSecond = second[i]; + + if (order && !isEqual(currentFirst, currentSecond)) { + return false; } - return !second.some(a => isEqual(a, b)); - }); + map.set(currentFirst, (map.get(currentFirst) ?? 0) + 1); + } + + for (let i = 0; i < second.length; i++) { + const currentSecond = second[i]; + const mapValue = map.get(currentSecond); + + if (mapValue === undefined || mapValue === 0) { + return false; + } + + map.set(currentSecond, Number(map.get(currentSecond)) - 1); + } + + return true; } From 41bd73ff598a982f06295ae633d50dec820ca287 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 9 Jan 2024 19:40:45 +0100 Subject: [PATCH 4/9] CB-4010 connections options cleanup --- .../plugin-connections/src/ConnectionForm/Options/Options.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.tsx b/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.tsx index 1b8e5f4fbc..a0045d4326 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.tsx +++ b/webapp/packages/plugin-connections/src/ConnectionForm/Options/Options.tsx @@ -134,7 +134,7 @@ export const Options: TabContainerPanelComponent = observe const { data: applicableAuthModels } = useResource( Options, DatabaseAuthModelsResource, - getComputed(() => (driver?.applicableAuthModels ? resourceKeyList(driver.applicableAuthModels) : CachedResourceListEmptyKey)), + driver?.applicableAuthModels ? resourceKeyList(driver.applicableAuthModels) : CachedResourceListEmptyKey, ); const { data: authModel } = useResource( From e4aaca7f20dd6bd2a6e7f31dd5abd20d165e5ba4 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Tue, 9 Jan 2024 20:07:46 +0100 Subject: [PATCH 5/9] CB-4010 fix: isArraysEquals works with multiple objects and multiple type of data (primitive and not) in one array --- .../core-utils/src/isArraysEqual.test.ts | 8 ++++++++ webapp/packages/core-utils/src/isArraysEqual.ts | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/webapp/packages/core-utils/src/isArraysEqual.test.ts b/webapp/packages/core-utils/src/isArraysEqual.test.ts index b4550e49bd..ecbef7e28e 100644 --- a/webapp/packages/core-utils/src/isArraysEqual.test.ts +++ b/webapp/packages/core-utils/src/isArraysEqual.test.ts @@ -55,4 +55,12 @@ describe('Is array equals', () => { test('should use isEqual argument if passed and "order" argument is "true"', () => { expect(isArraysEqual([{ a: 1 }], [{ a: 1 }], (a, b) => a.a === b.a, true)).toBe(true); }); + + test('should pass with array of objects (length > 1)', () => { + expect(isArraysEqual([{ b: 3 }, { a: 1 }], [{ a: 1 }, { b: 3 }])).toBe(true); + }); + + test('should pass with primitive and non primitive in array', () => { + expect(isArraysEqual([1, 1, { a: 1 }, 2], [2, { a: 1 }, 1, 1])).toBe(true); + }); }); diff --git a/webapp/packages/core-utils/src/isArraysEqual.ts b/webapp/packages/core-utils/src/isArraysEqual.ts index e0e8077e8d..221a5b8bb3 100644 --- a/webapp/packages/core-utils/src/isArraysEqual.ts +++ b/webapp/packages/core-utils/src/isArraysEqual.ts @@ -5,6 +5,7 @@ * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ +import { isPrimitive } from './isPrimitive'; const isObjectEqual = (a: T, b: T) => a === b; @@ -26,8 +27,24 @@ export function isArraysEqual(first: T[], second: T[], isEqual: (a: T, b: T) map.set(currentFirst, (map.get(currentFirst) ?? 0) + 1); } + if (order) { + return true; + } + for (let i = 0; i < second.length; i++) { const currentSecond = second[i]; + const isPrimitiveValue = isPrimitive(currentSecond); + + if (!isPrimitiveValue) { + for (let j = 0; j < first.length; j++) { + if (isEqual(first[j], currentSecond)) { + map.set(currentSecond, Number(map.get(currentSecond)) - 1); + break; + } + } + continue; + } + const mapValue = map.get(currentSecond); if (mapValue === undefined || mapValue === 0) { From 2eab75a1f19e50a7a396d9e980bc391e580575b0 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Wed, 10 Jan 2024 12:34:10 +0100 Subject: [PATCH 6/9] CB-4010 fix: resource isEqual can compare not only primitives --- .../src/ResourcesHooks/useResource.ts | 7 +----- .../core-resource/src/Resource/Resource.ts | 7 +++--- .../src/Resource/ResourceKeyUtils.ts | 24 +++++++++++++++++++ 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts b/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts index a20ad7dc1e..5455990f3b 100644 --- a/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts +++ b/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts @@ -161,12 +161,7 @@ export function useResource< propertiesRef.includes = includes; } - if ( - key === null || - propertiesRef.key === null || - !propertiesRef.resource.isIntersect(key, propertiesRef.key) || - !propertiesRef.resource.isEqual(key, propertiesRef.key) - ) { + if (key === null || propertiesRef.key === null || !propertiesRef.resource.isEqual(key, propertiesRef.key)) { propertiesRef.key = key; } }); diff --git a/webapp/packages/core-resource/src/Resource/Resource.ts b/webapp/packages/core-resource/src/Resource/Resource.ts index 17a06be2c7..3eea6a18a1 100644 --- a/webapp/packages/core-resource/src/Resource/Resource.ts +++ b/webapp/packages/core-resource/src/Resource/Resource.ts @@ -44,6 +44,7 @@ export abstract class Resource< super(); this.isKeyEqual = this.isKeyEqual.bind(this); this.isIntersect = this.isIntersect.bind(this); + this.isEqual = this.isEqual.bind(this); this.logger = new ResourceLogger(this.getName()); this.aliases = new ResourceAliases(this.logger, this.validateKey.bind(this)); @@ -122,7 +123,7 @@ export abstract class Resource< param = this.aliases.transformToAlias(param); second = this.aliases.transformToAlias(second); - return param.isEqual(second); + return param.isEqual(second) && this.isEqual(param.target, second.target); } if (isResourceAlias(param) || isResourceAlias(second)) { @@ -130,10 +131,10 @@ export abstract class Resource< } if (isResourceKeyList(param) && isResourceKeyList(second)) { - return isArraysEqual(param, second); + return ResourceKeyUtils.isEqual(param, second, this.isKeyEqual); } - return false; + return ResourceKeyUtils.isEqual(param, second, this.isKeyEqual); } /** diff --git a/webapp/packages/core-resource/src/Resource/ResourceKeyUtils.ts b/webapp/packages/core-resource/src/Resource/ResourceKeyUtils.ts index 4a7f0eb131..860d8240f1 100644 --- a/webapp/packages/core-resource/src/Resource/ResourceKeyUtils.ts +++ b/webapp/packages/core-resource/src/Resource/ResourceKeyUtils.ts @@ -37,6 +37,11 @@ export interface ResourceKeyUtils { second: TKey | ResourceKeyList, isEqual?: (keyA: TKey, keyB: TKey) => boolean, ) => boolean; + isEqual: ( + first: TKey | ResourceKeyList, + second: TKey | ResourceKeyList, + isEqualFn?: (keyA: TKey, keyB: TKey) => boolean, + ) => boolean; join: (...keys: Array>) => ResourceKeyList; toArray: (key: TKey | ResourceKeyList) => TKey[]; toList: (key: TKey | ResourceKeyList) => ResourceKeyList; @@ -125,6 +130,25 @@ export const ResourceKeyUtils: ResourceKeyUtils = { return isEqual(param, key); }, + isEqual( + param: TKey | ResourceKeyList, + key: TKey | ResourceKeyList, + isEqualFn = (keyA: TKey, keyB: TKey) => keyA === keyB, + ): boolean { + if (param === key) { + return true; + } + + if (isResourceKeyList(param) && isResourceKeyList(key)) { + return param.length === key.length && param.every((keyA, index) => isEqualFn(keyA, key[index])); + } + + if (isResourceKeyList(key) || isResourceKeyList(param)) { + return false; + } + + return isEqualFn(param, key); + }, join(...keys: Array>): ResourceKeyList { const list: TKey[] = []; From be9dface0a25757cb16f192109c5cd460453f4db Mon Sep 17 00:00:00 2001 From: Aleksei Potsetsuev Date: Wed, 10 Jan 2024 20:00:27 +0800 Subject: [PATCH 7/9] CB-4010 chore: add isEqual to resource list class --- .../packages/core-blocks/src/ResourcesHooks/useResource.ts | 2 +- webapp/packages/core-resource/src/Resource/Resource.ts | 2 +- .../packages/core-resource/src/Resource/ResourceKeyList.ts | 5 +++++ .../packages/core-resource/src/Resource/ResourceKeyUtils.ts | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts b/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts index 5455990f3b..43f4f455ad 100644 --- a/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts +++ b/webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts @@ -209,7 +209,7 @@ export function useResource< key = toJS(key); if (this.useRef[0] !== null && propertiesRef.resource.useTracker.hasUseId(this.useRef[1])) { - if (key !== null && propertiesRef.resource.isIntersect(key, this.useRef[0])) { + if (key !== null && propertiesRef.resource.isEqual(key, this.useRef[0])) { return; } diff --git a/webapp/packages/core-resource/src/Resource/Resource.ts b/webapp/packages/core-resource/src/Resource/Resource.ts index 3eea6a18a1..bba380c512 100644 --- a/webapp/packages/core-resource/src/Resource/Resource.ts +++ b/webapp/packages/core-resource/src/Resource/Resource.ts @@ -131,7 +131,7 @@ export abstract class Resource< } if (isResourceKeyList(param) && isResourceKeyList(second)) { - return ResourceKeyUtils.isEqual(param, second, this.isKeyEqual); + return param.isEqual(second, this.isKeyEqual); } return ResourceKeyUtils.isEqual(param, second, this.isKeyEqual); diff --git a/webapp/packages/core-resource/src/Resource/ResourceKeyList.ts b/webapp/packages/core-resource/src/Resource/ResourceKeyList.ts index 2831517a04..578cc067e8 100644 --- a/webapp/packages/core-resource/src/Resource/ResourceKeyList.ts +++ b/webapp/packages/core-resource/src/Resource/ResourceKeyList.ts @@ -5,12 +5,17 @@ * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ +import { isArraysEqual } from '@cloudbeaver/core-utils'; export class ResourceKeyList extends Array { static get [Symbol.species]() { return Array; } + isEqual(key: ResourceKeyList, isEqual?: (a: TKey, b: TKey) => boolean): boolean { + return isArraysEqual(this, key, isEqual, true); + } + includes(key: TKey, fromIndex?: number): boolean; includes(key: TKey | ResourceKeyList): boolean; includes(key: TKey | ResourceKeyList, isEqual: (keyA: TKey, keyB: TKey) => boolean): boolean; diff --git a/webapp/packages/core-resource/src/Resource/ResourceKeyUtils.ts b/webapp/packages/core-resource/src/Resource/ResourceKeyUtils.ts index 860d8240f1..284e3b041e 100644 --- a/webapp/packages/core-resource/src/Resource/ResourceKeyUtils.ts +++ b/webapp/packages/core-resource/src/Resource/ResourceKeyUtils.ts @@ -140,7 +140,7 @@ export const ResourceKeyUtils: ResourceKeyUtils = { } if (isResourceKeyList(param) && isResourceKeyList(key)) { - return param.length === key.length && param.every((keyA, index) => isEqualFn(keyA, key[index])); + return param.isEqual(key, isEqualFn); } if (isResourceKeyList(key) || isResourceKeyList(param)) { From 0a5d4b1110ac03cc127947bcc5a9d87792981bc7 Mon Sep 17 00:00:00 2001 From: "s.teleshev" Date: Wed, 10 Jan 2024 14:00:36 +0100 Subject: [PATCH 8/9] CB-4010 add to isArraysEqual edge cases + tests --- .../core-utils/src/isArraysEqual.test.ts | 18 ++++++++++++++---- .../packages/core-utils/src/isArraysEqual.ts | 6 +++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/webapp/packages/core-utils/src/isArraysEqual.test.ts b/webapp/packages/core-utils/src/isArraysEqual.test.ts index 5b1c944e69..cd2b78e3a2 100644 --- a/webapp/packages/core-utils/src/isArraysEqual.test.ts +++ b/webapp/packages/core-utils/src/isArraysEqual.test.ts @@ -56,11 +56,21 @@ describe('Is array equals', () => { expect(isArraysEqual([{ a: 1 }], [{ a: 1 }], (a, b) => a.a === b.a, true)).toBe(true); }); - test('should pass with array of objects (length > 1)', () => { - expect(isArraysEqual([{ b: 3 }, { a: 1 }], [{ a: 1 }, { b: 3 }])).toBe(true); + test('should not pass with no equal fn and array of objects (length > 1)', () => { + expect(isArraysEqual([{ b: 3 }, { a: 1 }], [{ a: 1 }, { b: 3 }])).toBe(false); }); - test('should pass with primitive and non primitive in array', () => { - expect(isArraysEqual([1, 1, { a: 1 }, 2], [2, { a: 1 }, 1, 1])).toBe(true); + test('should not pass with no equal fn and primitive and non primitive in array', () => { + expect(isArraysEqual([1, 1, { a: 1 }, 2], [2, { a: 1 }, 1, 1])).toBe(false); + }); + + test('should pass with equal fn and array of objects (length > 1)', () => { + const isEqual = jest.fn((a: { a: number }, b: { a: number }) => a.a === b.a); + expect(isArraysEqual([{ a: 3 }, { a: 1 }], [{ a: 1 }, { a: 3 }], isEqual)).toBe(true); + }); + + test('should pass with equal fn and primitive and non primitive in array', () => { + const isEqual = jest.fn((a: { a: number }, b: { a: number }) => a.a === b.a); + expect(isArraysEqual([1, { a: 3 }, { a: 1 }] as any, [1, { a: 1 }, { a: 3 }] as any, isEqual)).toBe(true); }); }); diff --git a/webapp/packages/core-utils/src/isArraysEqual.ts b/webapp/packages/core-utils/src/isArraysEqual.ts index db4c84e1ac..975f8f5fdf 100644 --- a/webapp/packages/core-utils/src/isArraysEqual.ts +++ b/webapp/packages/core-utils/src/isArraysEqual.ts @@ -41,13 +41,17 @@ export function isArraysEqual(first: T[], second: T[], isEqual: (a: T, b: T) map.set(currentSecond, Number(map.get(currentSecond)) - 1); break; } + + if (j === first.length - 1) { + return false; + } } continue; } const mapValue = map.get(currentSecond); - if (mapValue === undefined || mapValue === 0) { + if (mapValue === undefined || mapValue <= 0) { return false; } From b5829a2d8aa967212e84406a820cc1c9fdd66285 Mon Sep 17 00:00:00 2001 From: Aleksei Potsetsuev Date: Wed, 10 Jan 2024 23:11:23 +0800 Subject: [PATCH 9/9] CB-4010 chore: remove unused import --- webapp/packages/core-resource/src/Resource/Resource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/packages/core-resource/src/Resource/Resource.ts b/webapp/packages/core-resource/src/Resource/Resource.ts index bba380c512..0e451dac95 100644 --- a/webapp/packages/core-resource/src/Resource/Resource.ts +++ b/webapp/packages/core-resource/src/Resource/Resource.ts @@ -8,7 +8,7 @@ import { makeObservable, observable, toJS } from 'mobx'; import { Dependency } from '@cloudbeaver/core-di'; -import { isArraysEqual, isContainsException, isPrimitive, MetadataMap } from '@cloudbeaver/core-utils'; +import { isContainsException, isPrimitive, MetadataMap } from '@cloudbeaver/core-utils'; import { CachedResourceParamKey } from './CachedResource'; import type { ICachedResourceMetadata } from './ICachedResourceMetadata';