diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ff5fca..f0c124d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 1.7.0 (IN PROGRESS) + +### Features / Enhancements + +- Updated useNestedObjects hook to display request errors and empty values (#158) + ## 1.6.0 (2024-10-29) ### Features / Enhancements diff --git a/package.json b/package.json index 375eb54..71de5ec 100644 --- a/package.json +++ b/package.json @@ -86,5 +86,5 @@ "test:e2e:docker": "docker compose --profile e2e up --exit-code-from test", "upgrade": "npm upgrade --save" }, - "version": "1.6.0" + "version": "1.7.0" } diff --git a/src/hooks/useNestedObjects.test.ts b/src/hooks/useNestedObjects.test.ts index 977979b..4c2bcb0 100644 --- a/src/hooks/useNestedObjects.test.ts +++ b/src/hooks/useNestedObjects.test.ts @@ -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'; @@ -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(); + }); }); diff --git a/src/hooks/useNestedObjects.ts b/src/hooks/useNestedObjects.ts index 7eae286..e9df365 100644 --- a/src/hooks/useNestedObjects.ts +++ b/src/hooks/useNestedObjects.ts @@ -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'; @@ -28,14 +29,30 @@ export const useNestedObjects = ({ >({}); const [nestedObjectsLoading, setNestedObjectsLoading] = useState>({}); + 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>) => { 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; } @@ -46,29 +63,26 @@ 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) => ({ @@ -76,7 +90,7 @@ export const useNestedObjects = ({ [objectKey]: false, })); }, - [datasourceRequest, objects, replaceVariables] + [datasourceRequest, notifyError, objects, replaceVariables] ); /**