Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cb 4010 auth methods dont update when switching drivers #2286

Merged
merged 12 commits into from
Jan 11, 2024
Merged
4 changes: 2 additions & 2 deletions webapp/packages/core-blocks/src/ResourcesHooks/useResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ 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.isEqual(key, propertiesRef.key)) {
propertiesRef.key = key;
}
});
Expand Down Expand Up @@ -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;
}

Expand Down
32 changes: 31 additions & 1 deletion webapp/packages/core-resource/src/Resource/Resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Check warning on line 11 in webapp/packages/core-resource/src/Resource/Resource.ts

View check run for this annotation

Jenkins-CI-integration / CheckStyle TypeScript Report

webapp/packages/core-resource/src/Resource/Resource.ts#L11

isArraysEqual is defined but never used. (@typescript-eslint/no-unused-vars)

import { CachedResourceParamKey } from './CachedResource';
import type { ICachedResourceMetadata } from './ICachedResourceMetadata';
Expand Down Expand Up @@ -44,6 +44,7 @@
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));
Expand Down Expand Up @@ -107,6 +108,35 @@
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<TKey>, second: ResourceKey<TKey>): 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) && this.isEqual(param.target, second.target);
}

if (isResourceAlias(param) || isResourceAlias(second)) {
return false;
}

if (isResourceKeyList(param) && isResourceKeyList(second)) {
return param.isEqual(second, this.isKeyEqual);
}

return ResourceKeyUtils.isEqual(param, second, this.isKeyEqual);
}

/**
* Can be overridden to provide equality check for complicated keys
*/
Expand Down
5 changes: 5 additions & 0 deletions webapp/packages/core-resource/src/Resource/ResourceKeyList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<TKey> extends Array<TKey> {
static get [Symbol.species]() {
return Array;
}

isEqual(key: ResourceKeyList<TKey>, isEqual?: (a: TKey, b: TKey) => boolean): boolean {
return isArraysEqual(this, key, isEqual, true);
}

includes(key: TKey, fromIndex?: number): boolean;
includes(key: TKey | ResourceKeyList<TKey>): boolean;
includes(key: TKey | ResourceKeyList<TKey>, isEqual: (keyA: TKey, keyB: TKey) => boolean): boolean;
Expand Down
24 changes: 24 additions & 0 deletions webapp/packages/core-resource/src/Resource/ResourceKeyUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ export interface ResourceKeyUtils {
second: TKey | ResourceKeyList<TKey>,
isEqual?: (keyA: TKey, keyB: TKey) => boolean,
) => boolean;
isEqual: <TKey>(
first: TKey | ResourceKeyList<TKey>,
second: TKey | ResourceKeyList<TKey>,
isEqualFn?: (keyA: TKey, keyB: TKey) => boolean,
) => boolean;
join: <TKey>(...keys: Array<TKey | ResourceKeyList<TKey>>) => ResourceKeyList<TKey>;
toArray: <TKey>(key: TKey | ResourceKeyList<TKey>) => TKey[];
toList: <TKey>(key: TKey | ResourceKeyList<TKey>) => ResourceKeyList<TKey>;
Expand Down Expand Up @@ -125,6 +130,25 @@ export const ResourceKeyUtils: ResourceKeyUtils = {

return isEqual(param, key);
},
isEqual<TKey>(
param: TKey | ResourceKeyList<TKey>,
key: TKey | ResourceKeyList<TKey>,
isEqualFn = (keyA: TKey, keyB: TKey) => keyA === keyB,
): boolean {
if (param === key) {
return true;
}

if (isResourceKeyList(param) && isResourceKeyList(key)) {
return param.isEqual(key, isEqualFn);
}

if (isResourceKeyList(key) || isResourceKeyList(param)) {
return false;
}

return isEqualFn(param, key);
},
join<TKey>(...keys: Array<TKey | ResourceKeyList<TKey>>): ResourceKeyList<TKey> {
const list: TKey[] = [];

Expand Down
18 changes: 18 additions & 0 deletions webapp/packages/core-utils/src/isArraysEqual.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,22 @@ 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 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 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);
});
});
49 changes: 44 additions & 5 deletions webapp/packages/core-utils/src/isArraysEqual.ts
Copy link
Contributor Author

@sergeyteleshev sergeyteleshev Jan 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe optimization here is has to be, because it is basic utility, which is gonna be used for big calculations for sure. It is already used in resources and I'm sure somewhere there are a lot of fields comes from backend side

Your call guys 👾

Original file line number Diff line number Diff line change
Expand Up @@ -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 = <T>(a: T, b: T) => a === b;

Expand All @@ -13,11 +14,49 @@ export function isArraysEqual<T>(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<T, number>();

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);
}

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;
}

if (j === first.length - 1) {
return false;
}
}
continue;
}

const mapValue = map.get(currentSecond);

if (mapValue === undefined || mapValue <= 0) {
return false;
}

map.set(currentSecond, Number(map.get(currentSecond)) - 1);
}

return true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.form {
flex: 1;
overflow: auto;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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';
Expand All @@ -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;
Expand All @@ -81,6 +76,7 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
const formRef = useRef<HTMLFormElement>(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;
Expand Down Expand Up @@ -138,7 +134,7 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
const { data: applicableAuthModels } = useResource(
Options,
DatabaseAuthModelsResource,
getComputed(() => (driver?.applicableAuthModels ? resourceKeyList(driver.applicableAuthModels) : CachedResourceListEmptyKey)),
driver?.applicableAuthModels ? resourceKeyList(driver.applicableAuthModels) : CachedResourceListEmptyKey,
);

const { data: authModel } = useResource(
Expand Down Expand Up @@ -167,8 +163,8 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
properties = info.authProperties;
}

return styled(styles)(
<Form ref={formRef} disabled={driverMap.isLoading()} onChange={handleFormChange}>
return (
<Form ref={formRef} className={s(style, { form: true })} disabled={driverMap.isLoading()} onChange={handleFormChange}>
<ColoredContainer wrap overflow parent gap>
<Container medium gap>
<Group form gap>
Expand Down Expand Up @@ -354,6 +350,6 @@ export const Options: TabContainerPanelComponent<IConnectionFormProps> = observe
)}
</Container>
</ColoredContainer>
</Form>,
</Form>
);
});
Loading