This is an automated email from the ASF dual-hosted git repository.

zqr10159 pushed a commit to branch 2.0.0
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/2.0.0 by this push:
     new fb89e34485 Enable source-only metrics inventory queries
fb89e34485 is described below

commit fb89e344856a12607b7805b79dd74b683b59b3d2
Author: Logic <[email protected]>
AuthorDate: Wed Jun 10 07:57:42 2026 +0800

    Enable source-only metrics inventory queries
---
 .../ingestion/otlp/metrics/otlp-metrics-page.tsx   | 15 +++---
 web-next/app/ingestion/otlp/metrics/page.test.tsx  | 57 ++++++++++++++++++++++
 2 files changed, 66 insertions(+), 6 deletions(-)

diff --git a/web-next/app/ingestion/otlp/metrics/otlp-metrics-page.tsx 
b/web-next/app/ingestion/otlp/metrics/otlp-metrics-page.tsx
index 431c375dcd..b4e2bc5f5c 100644
--- a/web-next/app/ingestion/otlp/metrics/otlp-metrics-page.tsx
+++ b/web-next/app/ingestion/otlp/metrics/otlp-metrics-page.tsx
@@ -2299,7 +2299,11 @@ export default function OtlpMetricsPage() {
                     getRowKey={row => row.rowKey}
                     selectedRowKey={selectedMetricSeries?.key}
                     onRowClick={row => {
-                      if (row.series) applySelectedMetricSeries(row.series);
+                      if (row.series) {
+                        applySelectedMetricSeries(row.series);
+                        return;
+                      }
+                      applyMetricInventoryQuery(row.title, null);
                     }}
                     getRowProps={row => ({
                       'data-otlp-metrics-series-row': 'selectable-series',
@@ -2319,24 +2323,23 @@ export default function OtlpMetricsPage() {
                       {
                         key: 'name',
                         header: t('otlp.metrics.series.context.metric-name'),
-                        render: row => row.series ? (
+                        render: row => (
                           <HzButton
                             type="button"
                             size="xs"
                             intent="ghost"
-                            
data-otlp-metrics-inventory-query-action={row.series.key}
+                            
data-otlp-metrics-inventory-query-action={row.series?.key || row.rowKey}
                             
data-otlp-metrics-inventory-query-action-owner="hertzbeat-ui-button"
+                            
data-otlp-metrics-inventory-query-source={row.series ? 'console-series' : 
row.inventorySource || 'source-inventory'}
                             
aria-label={t('otlp.metrics.inventory.query-action.aria', { metric: row.title 
})}
                             onClick={event => {
                               event.stopPropagation();
-                              applyMetricInventoryQuery(row.title, row.series);
+                              applyMetricInventoryQuery(row.title, row.series 
|| null);
                             }}
                             className="min-w-0 justify-start truncate 
font-mono"
                           >
                             {row.title}
                           </HzButton>
-                        ) : (
-                          <HzDataCellText variant="title" 
display="block">{row.title}</HzDataCellText>
                         )
                       },
                       {
diff --git a/web-next/app/ingestion/otlp/metrics/page.test.tsx 
b/web-next/app/ingestion/otlp/metrics/page.test.tsx
index 68df062b36..6d9be37537 100644
--- a/web-next/app/ingestion/otlp/metrics/page.test.tsx
+++ b/web-next/app/ingestion/otlp/metrics/page.test.tsx
@@ -506,6 +506,7 @@ beforeEach(() => {
   mockState.replace.mockReset();
   mockState.lastLoad = null;
   mockState.metricSeries = [];
+  (mockState.renderData as any).inventory = undefined;
   apiMessageGet.mockReset();
   loadOtlpMetricsConsole.mockReset();
   loadOtlpMetricsInventory.mockReset();
@@ -2603,6 +2604,62 @@ describe('otlp metrics page', () => {
     expect(params.get('template')).toBe('spring-boot');
   });
 
+  it('loads a source-only inventory metric into the route-backed query 
builder', async () => {
+    mockState.searchParams = new 
URLSearchParams('serviceName=checkout&serviceNamespace=payments&environment=prod&timeRange=last-1h');
+    mockState.metricSeries = [];
+    (mockState.renderData as any).inventory = {
+      source: 'promql-inventory',
+      context: {
+        entityId: 7,
+        entityName: 'Checkout API',
+        serviceName: 'checkout',
+        serviceNamespace: 'payments',
+        environment: 'prod'
+      },
+      items: [
+        {
+          metricName: 'source.only.latency',
+          family: 'latency',
+          timeSeriesCount: 4,
+          latestObservedAt: 2000,
+          labels: {
+            service_name: 'checkout',
+            http_route: '/checkout'
+          }
+        }
+      ]
+    };
+
+    const { default: OtlpMetricsPage } = await import('./page');
+    interactionContainer = document.createElement('div');
+    document.body.appendChild(interactionContainer);
+    interactionRoot = createRoot(interactionContainer);
+
+    await act(async () => {
+      interactionRoot?.render(<OtlpMetricsPage />);
+      await Promise.resolve();
+    });
+
+    const queryAction = 
interactionContainer.querySelector('[data-otlp-metrics-inventory-query-action="inventory-source.only.latency-0"]')
 as HTMLButtonElement | null;
+    expect(queryAction).not.toBeNull();
+    
expect(queryAction?.getAttribute('data-otlp-metrics-inventory-query-source')).toBe('promql-inventory');
+    expect(queryAction?.textContent).toContain('source.only.latency');
+
+    await act(async () => {
+      queryAction?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
+      await Promise.resolve();
+    });
+
+    const href = String(mockState.replace.mock.calls.at(-1)?.[0]);
+    const params = new URL(href, 'http://localhost').searchParams;
+    expect(params.get('query')).toBe('source.only.latency');
+    expect(params.get('series')).toBeNull();
+    expect(params.get('serviceName')).toBe('checkout');
+    expect(params.get('serviceNamespace')).toBe('payments');
+    expect(params.get('environment')).toBe('prod');
+    expect(params.get('timeRange')).toBe('last-1h');
+  });
+
   it('renders a route-backed table inspector for selected metric raw samples', 
async () => {
     mockState.searchParams = new 
URLSearchParams('inspector=table&query=http.server.duration');
     mockState.metricSeries = [


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to