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

Update useNestedObjects hook to display request errors and empty values #158

Merged
merged 2 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions src/hooks/useNestedObjects.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { toDataFrame } from '@grafana/data';
import { getAppEvents } from '@grafana/runtime';
import { act, renderHook } from '@testing-library/react';
import { useDatasourceRequest } from '@volkovlabs/components';

Expand Down Expand Up @@ -104,4 +105,132 @@ describe('useNestedObjects', () => {

expect(result.current.getValuesForColumn(column.objectId)).toBeUndefined();
});

it('Should notify error with error message if request invalid', async () => {
const object = createNestedObjectConfig({
id: '123',
editor: createNestedObjectEditorConfig({
id: 'myId',
}),
});

const column = createColumnConfig({
field: {
name: 'comments',
source: '',
},
type: CellType.NESTED_OBJECTS,
objectId: object.id,
});

const data = [
{
[column.field.name]: [1, 2],
},
];

/**
* Mock objects result
*/
datasourceRequestMock.mockRejectedValue(new Error('response error'));

const { result } = renderHook(() =>
useNestedObjects({
replaceVariables,
objects: [object],
})
);

await act(async () => result.current.onLoad(column, data));

expect(getAppEvents().publish).toHaveBeenCalledWith({
payload: ['Error', 'response error'],
type: 'alert-error',
});
});

it('Should notify error with "Unknown Error" text if request invalid', async () => {
const object = createNestedObjectConfig({
id: '123',
editor: createNestedObjectEditorConfig({
id: 'myId',
}),
});

const column = createColumnConfig({
field: {
name: 'comments',
source: '',
},
type: CellType.NESTED_OBJECTS,
objectId: object.id,
});

const data = [
{
[column.field.name]: [1, 2],
},
];

/**
* Mock objects result
*/
datasourceRequestMock.mockRejectedValue('response error');

const { result } = renderHook(() =>
useNestedObjects({
replaceVariables,
objects: [object],
})
);

await act(async () => result.current.onLoad(column, data));

expect(getAppEvents().publish).toHaveBeenCalledWith({
payload: ['Error', 'Unknown Error'],
type: 'alert-error',
});
});

it('Should work if Ids is empty', async () => {
const object = createNestedObjectConfig({
id: '123',
editor: createNestedObjectEditorConfig({
id: 'myId',
}),
});

const column = createColumnConfig({
field: {
name: 'comments',
source: '',
},
type: CellType.NESTED_OBJECTS,
objectId: object.id,
});

const data = [
{
[column.field.name]: [],
},
];

/**
* Mock objects result
*/
datasourceRequestMock.mockRejectedValue('response error');

const { result } = renderHook(() =>
useNestedObjects({
replaceVariables,
objects: [object],
})
);

expect(result.current.getValuesForColumn(column.objectId)).toBeUndefined();

await act(async () => result.current.onLoad(column, data));

expect(result.current.getValuesForColumn(column.objectId)).toBeUndefined();
});
});
66 changes: 40 additions & 26 deletions src/hooks/useNestedObjects.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { InterpolateFunction } from '@grafana/data';
import { AlertErrorPayload, AppEvents, InterpolateFunction } from '@grafana/data';
import { getAppEvents } from '@grafana/runtime';
import { useDatasourceRequest } from '@volkovlabs/components';
import { useCallback, useMemo, useState } from 'react';

Expand Down Expand Up @@ -28,14 +29,30 @@ export const useNestedObjects = ({
>({});
const [nestedObjectsLoading, setNestedObjectsLoading] = useState<Record<string, boolean>>({});

const appEvents = getAppEvents();

const notifyError = useCallback(
(payload: AlertErrorPayload) => appEvents.publish({ type: AppEvents.alertError.name, payload }),
[appEvents]
);

/**
* Load
*/
const onLoad = useCallback(
async (column: ColumnConfig, tableData: Array<Record<string, unknown>>) => {
const object = objects.find((object) => object.id === column.objectId);
const ids = tableData.reduce((acc, row) => {
const value = row[column.field.name];

if (!object) {
if (Array.isArray(value)) {
return acc.concat(...value);
}

return acc.concat(value as never);
}, []);

if (!object || !ids.length) {
return;
}

Expand All @@ -46,37 +63,34 @@ export const useNestedObjects = ({
[objectKey]: true,
}));

const result = await datasourceRequest({
query: object.get.payload,
datasource: object.get.datasource,
payload: {
rows: tableData,
ids: tableData.reduce((acc, row) => {
const value = row[column.field.name];

if (Array.isArray(value)) {
return acc.concat(...value);
}

return acc.concat(value as never);
}, []),
},
replaceVariables,
});

if (result.data && result.data[0]) {
setNestedObjectsData((value) => ({
...value,
[objectKey]: prepareFrameForNestedObject(object, result.data[0]),
}));
try {
const result = await datasourceRequest({
query: object.get.payload,
datasource: object.get.datasource,
payload: {
rows: tableData,
ids: ids,
},
replaceVariables,
});

if (result.data && result.data[0]) {
setNestedObjectsData((value) => ({
...value,
[objectKey]: prepareFrameForNestedObject(object, result.data[0]),
}));
}
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : 'Unknown Error';
notifyError(['Error', errorMessage]);
}

setNestedObjectsLoading((current) => ({
...current,
[objectKey]: false,
}));
},
[datasourceRequest, objects, replaceVariables]
[datasourceRequest, notifyError, objects, replaceVariables]
);

/**
Expand Down
Loading