diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 949ba8f..3cc397a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,16 +17,16 @@ on: default: 'false' type: choice options: - - 'true' - - 'false' + - 'true' + - 'false' ENV: required: true default: 'testing' type: choice options: - - testing - - staging - - production + - testing + - staging + - production env: BRANCH: ${{ github.event.inputs.BRANCH || github.ref }} @@ -44,7 +44,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v2.1.2 with: - node-version: "14.x" + node-version: '14.x' - name: Get yarn cache directory path id: yarn-cache-dir-path @@ -84,7 +84,7 @@ jobs: zip "netdata-datasource-${{ steps.build_environment.outputs.BUILD_VERSION }}.zip" netdata-datasource -r - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: netdata-datasource-${{ steps.build_environment.outputs.BUILD_VERSION }}.zip path: ./netdata-datasource-${{ steps.build_environment.outputs.BUILD_VERSION }}.zip diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 11bc30c..790273c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: Release on: push: tags: - - "v*.*.*" + - 'v*.*.*' jobs: release: @@ -17,7 +17,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v2.1.2 with: - node-version: "14.x" + node-version: '14.x' - name: Get yarn cache directory path id: yarn-cache-dir-path @@ -64,7 +64,7 @@ jobs: export GRAFANA_PLUGIN_TYPE=$(cat dist/plugin.json | jq -r .type) export GRAFANA_PLUGIN_ARTIFACT=${GRAFANA_PLUGIN_ID}-${GRAFANA_PLUGIN_VERSION}.zip export GRAFANA_PLUGIN_ARTIFACT_CHECKSUM=${GRAFANA_PLUGIN_ARTIFACT}.md5 - + echo "plugin-id=${GRAFANA_PLUGIN_ID}" >> $GITHUB_OUTPUT echo "plugin-version=${GRAFANA_PLUGIN_VERSION}" >> $GITHUB_OUTPUT echo "plugin-type=${GRAFANA_PLUGIN_TYPE}" >> $GITHUB_OUTPUT @@ -91,7 +91,7 @@ jobs: echo "checksum=$(cat './${{ steps.metadata.outputs.archive-checksum }}' | cut -d' ' -f1)" >> $GITHUB_OUTPUT - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: ${{ steps.metadata.outputs.archive }} path: ./${{ steps.metadata.outputs.archive }} diff --git a/.prettierrc.js b/.prettierrc.js index 263dbd3..b035698 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,3 +1,3 @@ module.exports = { - ...require("./node_modules/@grafana/toolkit/src/config/prettier.plugin.config.json"), + ...require('./node_modules/@grafana/toolkit/src/config/prettier.plugin.config.json'), }; diff --git a/LICENSE b/LICENSE index 8dada3e..f7144cd 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {yyyy} {name of copyright owner} + Copyright 2025 Netdata Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/package.json b/package.json index 2765158..18cf6cb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "netdatacloud-netdata-datasource", - "version": "2.0.0", + "version": "3.0.0", "description": "netdata datasource plugin", "scripts": { "build": "grafana-toolkit plugin:build", diff --git a/src/QueryEditor.tsx b/src/QueryEditor.tsx index 2fbd1a6..a8dd98e 100644 --- a/src/QueryEditor.tsx +++ b/src/QueryEditor.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Input, LegacyForms, Select } from '@grafana/ui'; import { QueryEditorProps, SelectableValue } from '@grafana/data'; import { DataSource } from './datasource'; @@ -9,16 +9,21 @@ import { useFetchRooms } from 'shared/hooks/useFetchRooms'; import { useFetchContexts } from 'shared/hooks/useFetchContexts'; import { useFetchNodes } from 'shared/hooks/useFetchNodes'; import { Aggreagations, GroupByList, Methods } from 'shared/constants'; -import { useFetchDimensions } from 'shared/hooks/useFetchDimensions'; import { Dropdown } from 'shared/types/dropdown.interface'; +import { getDimensions, getFilters, getGroupingByList, defaultFilter } from 'shared/utils/transformations'; import PubSub from 'pubsub-js'; type Props = QueryEditorProps; const { FormField } = LegacyForms; -const QueryEditor: React.FC = ({ datasource, query, onChange, onRunQuery }) => { +const QueryEditor: React.FC = ({ datasource, query, range, onChange, onRunQuery }) => { const { baseUrl } = datasource; + const from = range!.from.valueOf(); + const to = range!.to.valueOf(); + const after = Math.floor(from / 1000); + const before = Math.floor(to / 1000); + const [selectedSpace, setSelectedSpace] = React.useState(); const [selectedRoom, setSelectedRoom] = React.useState(); const [selectedFilter, setSelectedFilter] = React.useState(); @@ -42,7 +47,10 @@ const QueryEditor: React.FC = ({ datasource, query, onChange, onRunQuery const { rooms, fetchRooms } = useFetchRooms(baseUrl); const { nodes, fetchNodes } = useFetchNodes(baseUrl); const { contexts, fetchContexts } = useFetchContexts(baseUrl); - const { allDimensions, groupingByList, filters, units, fetchDimensions } = useFetchDimensions(baseUrl); + const [allDimensions, setAllDimensions] = useState([]); + const [units, setUnits] = useState(''); + const [filters, setFilters] = useState(defaultFilter); + const [groupingByList, setGroupingByList] = useState(GroupByList); const filterList = React.useMemo(() => Object.keys(filters).map((s) => ({ label: s, value: s })), [filters]); const nodeList = React.useMemo(() => nodes?.map((c: any) => ({ label: c.name, value: c.id })), [nodes]); @@ -50,8 +58,15 @@ const QueryEditor: React.FC = ({ datasource, query, onChange, onRunQuery const { spaceId, roomId, nodes: allNodes, dimensions, groupBy, contextId, filterBy, filterValue } = query; const mySubscriber = (msg: any, data: any) => { - setTotalNodes(data.data.nodes.length); - setTotalInstances(data.data.nodes.reduce((acc: number, node: any) => acc + node.chartIDs.length, 0)); + const { summary, view } = data?.data || {}; + const { nodes = [], instances = [], labels = [] } = summary || {}; + const { dimensions, units } = view || {}; + setFilters(getFilters(labels)); + setGroupingByList(getGroupingByList(labels)); + setAllDimensions(getDimensions(dimensions)); + setUnits(units); + setTotalNodes(nodes.length); + setTotalInstances(instances.length); }; const isGroupFunctionAvailable = React.useCallback(() => { @@ -88,9 +103,9 @@ const QueryEditor: React.FC = ({ datasource, query, onChange, onRunQuery const room = rooms.find((r) => r.value === roomId); setSelectedRoom({ label: room?.label, value: room?.value }); fetchNodes(spaceId || '', roomId); - fetchContexts(spaceId || '', roomId); + fetchContexts(spaceId || '', roomId, after, before); } - }, [roomId, rooms, fetchContexts, fetchNodes, spaceId]); + }, [roomId, rooms, fetchContexts, fetchNodes, spaceId, after, before]); React.useEffect(() => { // eslint-disable-line @@ -115,8 +130,6 @@ const QueryEditor: React.FC = ({ datasource, query, onChange, onRunQuery filteredNodes.push({ label: currentNode?.name, value: currentNode?.id }); }); } - - fetchDimensions({ spaceId, roomId, contextId, nodeIDs: filteredNodes.map((n: any) => n.value) }); } }, [contextId]); // eslint-disable-line @@ -171,7 +184,7 @@ const QueryEditor: React.FC = ({ datasource, query, onChange, onRunQuery setSelectedMethod(Methods[0]); setSelectedAggregations(Aggreagations[0]); - fetchContexts(selectedSpace?.value || '', v.value || ''); + fetchContexts(selectedSpace?.value || '', v.value || '', after, before); fetchNodes(selectedSpace?.value || '', v.value || ''); onChange({ ...query, spaceId: spaceId, roomId: v.value }); onRunQuery(); @@ -188,7 +201,6 @@ const QueryEditor: React.FC = ({ datasource, query, onChange, onRunQuery setSelectedMethod(Methods[0]); setSelectedAggregations(Aggreagations[0]); - fetchDimensions({ spaceId, roomId, contextId: v.value, nodeIDs: selectedNodes?.map((n: any) => n.value) || [] }); onChange({ ...query, contextId: v.value }); onRunQuery(); }; @@ -204,7 +216,6 @@ const QueryEditor: React.FC = ({ datasource, query, onChange, onRunQuery setSelectedMethod(Methods[0]); setSelectedAggregations(Aggreagations[0]); - fetchDimensions({ spaceId, roomId, contextId, nodeIDs: data }); setSelectedNodes(data); onChange({ ...query, spaceId, roomId, contextId, nodes: data } as MyQuery); onRunQuery(); @@ -225,13 +236,13 @@ const QueryEditor: React.FC = ({ datasource, query, onChange, onRunQuery const onFilterByChange = (v: SelectableValue) => { setSelectedFilter(v); + setSelectedFilterValue({}); if (v.value === 'No filter') { - setSelectedFilterValue({}); onChange({ ...query, filterBy: undefined, filterValue: undefined }); onRunQuery(); } else { - setFilterByValues(filters[v?.value || ''].map((v) => ({ label: v, value: v }))); + setFilterByValues(filters[v?.value || ''].map((v: string) => ({ label: v, value: v }))); } }; diff --git a/src/datasource.ts b/src/datasource.ts index e18611e..3dad058 100644 --- a/src/datasource.ts +++ b/src/datasource.ts @@ -61,16 +61,19 @@ export class DataSource extends DataSourceApi { const frame = new MutableDataFrame({ refId, fields: response.data.result.labels.map((id: string, i: number) => { - const node = response.data.nodes.find((n: any) => n.id === id); + const node = response.data.summary.nodes.find((n: any) => n.mg === id); return { - name: node?.name || id, + name: node?.nm || id, type: i === 0 ? FieldType.time : FieldType.number, }; }), }); + const valueIndex = response.data.result.point.value; + response.data.result.data.forEach((point: any) => { - frame.appendRow([...point]); + const [timestamp, ...rest] = point; + frame.appendRow([timestamp, ...rest.map((r: any[]) => r[valueIndex])]); }); return frame; diff --git a/src/shared/hooks/useFetchContexts.test.ts b/src/shared/hooks/useFetchContexts.test.ts index 50958bd..12c60bd 100644 --- a/src/shared/hooks/useFetchContexts.test.ts +++ b/src/shared/hooks/useFetchContexts.test.ts @@ -15,7 +15,7 @@ describe('useFetchContexts', () => { const { result, waitFor } = renderHook(() => hooks.useFetchContexts(baseUrl)); - await result.current.fetchContexts('spaceId', 'roomId'); + await result.current.fetchContexts('spaceId', 'roomId', -900, 0); await waitFor(() => result.current.contexts.length > 0); diff --git a/src/shared/hooks/useFetchContexts.ts b/src/shared/hooks/useFetchContexts.ts index 484c145..432a2d9 100644 --- a/src/shared/hooks/useFetchContexts.ts +++ b/src/shared/hooks/useFetchContexts.ts @@ -1,10 +1,28 @@ import { Dropdown } from './../types/dropdown.interface'; import React from 'react'; -import { Get } from 'shared/utils/request'; +import { Post } from 'shared/utils/request'; -export const getContexts = async (spaceId: string, roomId: string, baseUrl: string) => { - const response = await Get({ path: `/v2/spaces/${spaceId}/rooms/${roomId}/contexts`, baseUrl }); - return response?.data?.results as string[]; +export const getContexts = async (spaceId: string, roomId: string, after: number, before: number, baseUrl: string) => { + const response = await Post({ + path: `/v3/spaces/${spaceId}/rooms/${roomId}/contexts`, + baseUrl, + data: { + scope: { + contexts: ['*'], + nodes: [], + }, + selectors: { + contexts: [], + nodes: [], + }, + window: { + after, + before, + }, + }, + }); + const { contexts = {} } = response?.data || {}; + return Object.keys(contexts) as string[]; }; export const useFetchContexts = (baseUrl: string) => { @@ -12,11 +30,11 @@ export const useFetchContexts = (baseUrl: string) => { const [contexts, setContexts] = React.useState([]); const fetchContexts = React.useCallback( - async (spaceId: string, roomId: string) => { + async (spaceId: string, roomId: string, after: number, before: number) => { setIsError(false); try { - const result = await getContexts(spaceId, roomId, baseUrl); + const result = await getContexts(spaceId, roomId, after, before, baseUrl); setContexts(result.map((c) => ({ label: c, value: c }))); } catch (error) { setIsError(true); diff --git a/src/shared/hooks/useFetchDimensions.test.ts b/src/shared/hooks/useFetchDimensions.test.ts deleted file mode 100644 index 58a35f5..0000000 --- a/src/shared/hooks/useFetchDimensions.test.ts +++ /dev/null @@ -1,324 +0,0 @@ -import { renderHook } from '@testing-library/react-hooks'; -import * as hooks from './useFetchDimensions'; - -const baseUrl = '/base'; - -const dataMock: Promise = Promise.resolve({ - 'apps.cpu': { - chartLabels: { - _collect_module: ['_none_'], - _collect_plugin: ['apps.plugin'], - _instance_family: ['cpu'], - }, - chartType: 'stacked', - context: 'apps.cpu', - dimensions: { - agent_sd: { - id: 'agent_sd', - name: 'agent_sd', - }, - alertmanager: { - id: 'alertmanager', - name: 'alertmanager', - }, - 'apps.plugin': { - id: 'apps.plugin', - name: 'apps.plugin', - }, - bash: { - id: 'bash', - name: 'bash', - }, - 'cloud-accounts-service': { - id: 'cloud-accounts-service', - name: 'cloud-accounts-service', - }, - 'cloud-agent-data-ctrl-service': { - id: 'cloud-agent-data-ctrl-service', - name: 'cloud-agent-data-ctrl-service', - }, - 'cloud-agent-service': { - id: 'cloud-agent-service', - name: 'cloud-agent-service', - }, - 'cloud-custom-dashboard-service': { - id: 'cloud-custom-dashboard-service', - name: 'cloud-custom-dashboard-service', - }, - 'cloud-iam-agent-service': { - id: 'cloud-iam-agent-service', - name: 'cloud-iam-agent-service', - }, - 'cloud-iam-user-service': { - id: 'cloud-iam-user-service', - name: 'cloud-iam-user-service', - }, - 'cloud-node-mqtt-input-service': { - id: 'cloud-node-mqtt-input-service', - name: 'cloud-node-mqtt-input-service', - }, - 'cloud-node-mqtt-output-service': { - id: 'cloud-node-mqtt-output-service', - name: 'cloud-node-mqtt-output-service', - }, - 'cloud-nodes-service': { - id: 'cloud-nodes-service', - name: 'cloud-nodes-service', - }, - 'cloud-spaceroom-service': { - id: 'cloud-spaceroom-service', - name: 'cloud-spaceroom-service', - }, - containerd: { - id: 'containerd', - name: 'containerd', - }, - 'containerd-shim': { - id: 'containerd-shim', - name: 'containerd-shim', - }, - dockerd: { - id: 'dockerd', - name: 'dockerd', - }, - envoy: { - id: 'envoy', - name: 'envoy', - }, - 'go.d.plugin': { - id: 'go.d.plugin', - name: 'go.d.plugin', - }, - grafana: { - id: 'grafana', - name: 'grafana', - }, - gunicorn: { - id: 'gunicorn', - name: 'gunicorn', - }, - haproxy: { - id: 'haproxy', - name: 'haproxy', - }, - 'haproxy-ingress': { - id: 'haproxy-ingress', - name: 'haproxy-ingress', - }, - 'kube-proxy': { - id: 'kube-proxy', - name: 'kube-proxy', - }, - 'kube-state-metrics': { - id: 'kube-state-metrics', - name: 'kube-state-metrics', - }, - kubelet: { - id: 'kubelet', - name: 'kubelet', - }, - loki: { - id: 'loki', - name: 'loki', - }, - 'metrics-server': { - id: 'metrics-server', - name: 'metrics-server', - }, - mongod: { - id: 'mongod', - name: 'mongod', - }, - netdata: { - id: 'netdata', - name: 'netdata', - }, - nginx: { - id: 'nginx', - name: 'nginx', - }, - node_exporter: { - id: 'node_exporter', - name: 'node_exporter', - }, - other: { - id: 'other', - name: 'other', - }, - pomerium: { - id: 'pomerium', - name: 'pomerium', - }, - 'prometheus-adapter': { - id: 'prometheus-adapter', - name: 'prometheus-adapter', - }, - 'prometheus-pushgateway': { - id: 'prometheus-pushgateway', - name: 'prometheus-pushgateway', - }, - 'prometheus-server': { - id: 'prometheus-server', - name: 'prometheus-server', - }, - promtail: { - id: 'promtail', - name: 'promtail', - }, - python: { - id: 'python', - name: 'python', - }, - 'python.d.plugin': { - id: 'python.d.plugin', - name: 'python.d.plugin', - }, - redis: { - id: 'redis', - name: 'redis', - }, - sshd: { - id: 'sshd', - name: 'sshd', - }, - traefik: { - id: 'traefik', - name: 'traefik', - }, - vernemq: { - id: 'vernemq', - name: 'vernemq', - }, - }, - family: 'cpu', - firstEntry: 1662555061, - lastEntry: 1662969591, - module: '_none_', - nodeIDs: [ - '3d36c8ea-49dc-4191-9010-9a7862fe472d', - '05ce83ea-f62e-4839-868e-90bc81a7114a', - '113074c4-4575-4feb-8abd-7c077b9c5e96', - '2475156b-54a1-4d2f-99d6-7e5f816e5317', - 'ca1c7695-88e4-4124-b54f-804b297e6e9d', - '972c5e17-a341-4aa7-9598-31fd0aef8e26', - '113074c4-4575-4feb-8abd-7c077b9c5e96', - '74ffad89-08c2-42f4-baa3-c653d189b5dc', - 'ca1c7695-88e4-4124-b54f-804b297e6e9d', - 'ca1c7695-88e4-4124-b54f-804b297e6e9d', - '3bd6c73f-888e-4da5-bbe6-6ef0baf9d939', - '05ce83ea-f62e-4839-868e-90bc81a7114a', - '054b5fee-a743-473c-b0d2-8d394c43a333', - '3d36c8ea-49dc-4191-9010-9a7862fe472d', - '2f4360eb-5969-4603-b6d1-5bb22e2334e6', - '113074c4-4575-4feb-8abd-7c077b9c5e96', - '25fe7213-4ff2-44d3-8e81-07b5747b502b', - '2f4360eb-5969-4603-b6d1-5bb22e2334e6', - '2475156b-54a1-4d2f-99d6-7e5f816e5317', - '0fc6e555-1f75-49d9-bd35-70321f6dd2e6', - '3bd6c73f-888e-4da5-bbe6-6ef0baf9d939', - '9535c5e7-eafd-4889-8097-844e536628a6', - '972c5e17-a341-4aa7-9598-31fd0aef8e26', - '054b5fee-a743-473c-b0d2-8d394c43a333', - '25fe7213-4ff2-44d3-8e81-07b5747b502b', - '74ffad89-08c2-42f4-baa3-c653d189b5dc', - '3bd6c73f-888e-4da5-bbe6-6ef0baf9d939', - '9535c5e7-eafd-4889-8097-844e536628a6', - '2f4360eb-5969-4603-b6d1-5bb22e2334e6', - '9535c5e7-eafd-4889-8097-844e536628a6', - '972c5e17-a341-4aa7-9598-31fd0aef8e26', - '0fc6e555-1f75-49d9-bd35-70321f6dd2e6', - '05ce83ea-f62e-4839-868e-90bc81a7114a', - '25fe7213-4ff2-44d3-8e81-07b5747b502b', - '2475156b-54a1-4d2f-99d6-7e5f816e5317', - '3d36c8ea-49dc-4191-9010-9a7862fe472d', - '054b5fee-a743-473c-b0d2-8d394c43a333', - '0fc6e555-1f75-49d9-bd35-70321f6dd2e6', - '74ffad89-08c2-42f4-baa3-c653d189b5dc', - ], - plugin: 'apps.plugin', - priority: 20001, - title: 'Apps CPU Time (100% = 1 core)', - units: 'percentage', - }, -}); - -describe('useFetchDimensions', () => { - it('return correct data', async () => { - jest.spyOn(hooks, 'getDimensions').mockImplementation(() => dataMock); - - const { result, waitFor } = renderHook(() => hooks.useFetchDimensions(baseUrl)); - - await result.current.fetchDimensions({ - spaceId: 'spaceId', - roomId: 'roomId', - contextId: 'apps.cpu', - nodeIDs: ['nodeIDs'], - }); - - await waitFor(() => result.current.allDimensions.length > 0); - - expect(result.current.allDimensions).toBeDefined(); - expect(result.current.allDimensions).toEqual([ - { label: 'agent_sd', value: 'agent_sd' }, - { label: 'alertmanager', value: 'alertmanager' }, - { label: 'apps.plugin', value: 'apps.plugin' }, - { label: 'bash', value: 'bash' }, - { label: 'cloud-accounts-service', value: 'cloud-accounts-service' }, - { label: 'cloud-agent-data-ctrl-service', value: 'cloud-agent-data-ctrl-service' }, - { label: 'cloud-agent-service', value: 'cloud-agent-service' }, - { label: 'cloud-custom-dashboard-service', value: 'cloud-custom-dashboard-service' }, - { label: 'cloud-iam-agent-service', value: 'cloud-iam-agent-service' }, - { label: 'cloud-iam-user-service', value: 'cloud-iam-user-service' }, - { label: 'cloud-node-mqtt-input-service', value: 'cloud-node-mqtt-input-service' }, - { label: 'cloud-node-mqtt-output-service', value: 'cloud-node-mqtt-output-service' }, - { label: 'cloud-nodes-service', value: 'cloud-nodes-service' }, - { label: 'cloud-spaceroom-service', value: 'cloud-spaceroom-service' }, - { label: 'containerd', value: 'containerd' }, - { label: 'containerd-shim', value: 'containerd-shim' }, - { label: 'dockerd', value: 'dockerd' }, - { label: 'envoy', value: 'envoy' }, - { label: 'go.d.plugin', value: 'go.d.plugin' }, - { label: 'grafana', value: 'grafana' }, - { label: 'gunicorn', value: 'gunicorn' }, - { label: 'haproxy', value: 'haproxy' }, - { label: 'haproxy-ingress', value: 'haproxy-ingress' }, - { label: 'kube-proxy', value: 'kube-proxy' }, - { label: 'kube-state-metrics', value: 'kube-state-metrics' }, - { label: 'kubelet', value: 'kubelet' }, - { label: 'loki', value: 'loki' }, - { label: 'metrics-server', value: 'metrics-server' }, - { label: 'mongod', value: 'mongod' }, - { label: 'netdata', value: 'netdata' }, - { label: 'nginx', value: 'nginx' }, - { label: 'node_exporter', value: 'node_exporter' }, - { label: 'other', value: 'other' }, - { label: 'pomerium', value: 'pomerium' }, - { label: 'prometheus-adapter', value: 'prometheus-adapter' }, - { label: 'prometheus-pushgateway', value: 'prometheus-pushgateway' }, - { label: 'prometheus-server', value: 'prometheus-server' }, - { label: 'promtail', value: 'promtail' }, - { label: 'python', value: 'python' }, - { label: 'python.d.plugin', value: 'python.d.plugin' }, - { label: 'redis', value: 'redis' }, - { label: 'sshd', value: 'sshd' }, - { label: 'traefik', value: 'traefik' }, - { label: 'vernemq', value: 'vernemq' }, - ]); - - expect(result.current.filters).toEqual({ - 'No filter': [], - _collect_module: ['_none_'], - _collect_plugin: ['apps.plugin'], - _instance_family: ['cpu'], - }); - expect(result.current.groupingByList).toEqual([ - { label: 'dimension', value: 'dimension' }, - { label: 'node', value: 'node' }, - { label: 'instance', value: 'instance' }, - { label: '_collect_module', value: '_collect_module' }, - { label: '_collect_plugin', value: '_collect_plugin' }, - { label: '_instance_family', value: '_instance_family' }, - ]); - expect(result.current.units).toEqual('percentage'); - }); -}); diff --git a/src/shared/hooks/useFetchDimensions.ts b/src/shared/hooks/useFetchDimensions.ts deleted file mode 100644 index 0ed07e2..0000000 --- a/src/shared/hooks/useFetchDimensions.ts +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; -import { GroupByList } from 'shared/constants'; -import { Dropdown } from 'shared/types/dropdown.interface'; -import { Post } from 'shared/utils/request'; - -export const getDimensions = async ( - spaceId: string, - roomId: string, - contextId: string, - nodeIDs: string[], - baseUrl: string -) => { - const response = await Post({ - path: `/v2/spaces/${spaceId}/rooms/${roomId}/charts/${contextId}`, - baseUrl, - data: { - filter: { nodeIDs }, - }, - }); - return response?.data?.results; -}; - -export const useFetchDimensions = (baseUrl: string) => { - const [isError, setIsError] = React.useState(false); - const [allDimensions, setDimensions] = React.useState([]); - const [filters, setFilters] = React.useState<{ [key in string]: [] }>({}); - const [groupingByList, setGroupingByList] = React.useState(GroupByList); - const [units, setUnits] = React.useState(''); - - const fetchDimensions = async ({ spaceId, roomId, contextId, nodeIDs }: any) => { - setIsError(false); - try { - const result = await getDimensions(spaceId, roomId, contextId, nodeIDs, baseUrl); - setUnits(result?.[contextId].units); - setDimensions(Object.values(result?.[contextId]?.dimensions)?.map((c: any) => ({ label: c.name, value: c.id }))); - setFilters({ 'No filter': [], ...result?.[contextId]?.chartLabels }); - - const groupByData = [ - ...GroupByList, - ...Object.keys(result?.[contextId]?.chartLabels).map((g: any) => ({ label: g, value: g })), - ]; - - setGroupingByList(groupByData); - } catch (error) { - console.log('ERROR (useFetchDimensions): ', error); - setIsError(true); - } - }; - - return { - isError, - allDimensions, - filters, - groupingByList, - units, - fetchDimensions, - }; -}; diff --git a/src/shared/hooks/useFetchNodes.ts b/src/shared/hooks/useFetchNodes.ts index ac43d25..14dbc68 100644 --- a/src/shared/hooks/useFetchNodes.ts +++ b/src/shared/hooks/useFetchNodes.ts @@ -1,9 +1,28 @@ import React from 'react'; -import { Get } from 'shared/utils/request'; +import { Post } from 'shared/utils/request'; + +type Node = { + nd: string; + mg: string; + nm: string; + [key: string]: any; +}; + +const transformNodes = (nodes: Node[] = []) => + nodes.map(({ nd, mg, nm, ...rest }) => ({ id: nd || mg, name: nm, ...rest })); export const getNodes = async (spaceId: string, roomId: string, baseUrl: string) => { - const response = await Get({ path: `/v2/spaces/${spaceId}/rooms/${roomId}/nodes`, baseUrl }); - return response?.data; + const response = await Post({ + path: `/v3/spaces/${spaceId}/rooms/${roomId}/nodes`, + baseUrl, + data: { + scope: { + nodes: [], + }, + }, + }); + + return response?.data?.nodes; }; export const useFetchNodes = (baseUrl: string) => { @@ -27,7 +46,7 @@ export const useFetchNodes = (baseUrl: string) => { return { isError, - nodes, + nodes: transformNodes(nodes), fetchNodes, }; }; diff --git a/src/shared/hooks/useGetChartData.ts b/src/shared/hooks/useGetChartData.ts index 31fc867..775347a 100644 --- a/src/shared/hooks/useGetChartData.ts +++ b/src/shared/hooks/useGetChartData.ts @@ -32,48 +32,80 @@ export const useGetChartData = async ({ from, to, }: UseGetChartDataType) => { - let aggregations = []; + let metrics = []; switch (groupBy) { case 'node': - aggregations = [ - { method: 'sum', groupBy: ['chart', 'node'] }, - { method, groupBy: ['node'] }, + metrics = [ + { + group_by: ['chart', 'node'], + group_by_label: [], + aggregation: 'sum', + }, + { + group_by: ['node'], + group_by_label: [], + aggregation: method, + }, ]; break; case 'dimension': - aggregations = [{ method, groupBy: ['dimension'] }]; + metrics = [ + { + group_by: ['dimension'], + group_by_label: [], + aggregation: method, + }, + ]; break; case 'instance': - aggregations = [{ method: 'sum', groupBy: ['chart', 'node'] }]; + metrics = [ + { + group_by: ['chart', 'node'], + group_by_label: [], + aggregation: 'sum', + }, + ]; break; default: - aggregations = [ - { method: 'sum', groupBy: ['chart', `label=${groupBy}`] }, - { method: 'avg', groupBy: [`label=${groupBy}`] }, + metrics = [ + { + group_by: ['chart'], + group_by_label: groupBy, + aggregation: 'sum', + }, + { + group_by: [], + group_by_label: groupBy, + aggregation: 'avg', + }, ]; break; } return await Post({ - path: `/v2/spaces/${spaceId}/rooms/${roomId}/data`, + path: `/v3/spaces/${spaceId}/rooms/${roomId}/data`, baseUrl, data: { - filter: { - nodeIDs: nodes, - context: contextId, + format: 'json2', + options: ['jsonwrap', 'flip', 'ms'], + scope: { + contexts: [contextId], + nodes, dimensions, - ...(filterBy && filterValue ? { labels: { [filterBy]: [filterValue] } } : {}), }, - aggregations, - agent_options: ['jsonwrap', 'flip', 'ms'], - points: 335, - format: 'json', - group, - gtime: 0, - after: from, - before: to, - with_metadata: true, + selectors: { + contexts: ['*'], + nodes: ['*'], + instances: ['*'], + dimensions: ['*'], + labels: ['*'], + }, + aggregations: { + metrics, + time: { time_group: group, time_resampling: 0 }, + }, + window: { after: from, before: to, points: 269 }, }, }); }; diff --git a/src/shared/utils/transformations.ts b/src/shared/utils/transformations.ts new file mode 100644 index 0000000..503a396 --- /dev/null +++ b/src/shared/utils/transformations.ts @@ -0,0 +1,34 @@ +import { GroupByList } from 'shared/constants'; + +export const getDimensions = (dimensions: any) => { + const { ids, names } = dimensions; + if (!ids) { + return []; + } + + return ids.map((id: string, index: number) => ({ value: id, label: names[index] || id })); +}; + +export const defaultFilter = { 'No filter': [] }; + +export const getFilters = (labels: any[]) => { + if (!labels?.length) { + return defaultFilter; + } + + return { + ...defaultFilter, + ...labels.reduce((acc: any, label: any) => { + acc[label.id] = (label.vl || []).map((value: any) => value.id); + return acc; + }, {}), + }; +}; + +export const getGroupingByList = (labels: any[]) => { + if (!labels?.length) { + return GroupByList; + } + + return [...GroupByList, ...labels.map((label: any) => ({ value: label.id, label: label.id }))]; +};