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
30 changes: 30 additions & 0 deletions webapp/packages/core-resource/src/Resource/Resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -107,6 +108,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<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
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