From ac54736174ddcf90075a108292bc77dfdfae4dbf Mon Sep 17 00:00:00 2001 From: ansita20 Date: Sat, 16 May 2026 13:51:48 +0530 Subject: [PATCH 1/6] feat(search): add header search button and overlay (minimal) --- .../src/components/layout/header.tsx | 73 ++++--- .../src/components/ui/search-overlay.tsx | 179 ++++++++++++++++++ .../src/hooks/useDebouncedValue.ts | 20 ++ ecosystem-explorer/src/lib/search.ts | 94 +++++++++ 4 files changed, 339 insertions(+), 27 deletions(-) create mode 100644 ecosystem-explorer/src/components/ui/search-overlay.tsx create mode 100644 ecosystem-explorer/src/hooks/useDebouncedValue.ts create mode 100644 ecosystem-explorer/src/lib/search.ts diff --git a/ecosystem-explorer/src/components/layout/header.tsx b/ecosystem-explorer/src/components/layout/header.tsx index d86b1a83..01519323 100644 --- a/ecosystem-explorer/src/components/layout/header.tsx +++ b/ecosystem-explorer/src/components/layout/header.tsx @@ -13,38 +13,57 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { useState } from "react"; import { Link } from "react-router-dom"; +import { Search } from "lucide-react"; import { OtelLogo } from "@/components/icons/otel-logo"; +import { SearchOverlay } from "@/components/ui/search-overlay"; export function Header() { + const [isSearchOpen, setIsSearchOpen] = useState(false); + return ( -
-
- - - OTel Explorer - - -
-
+
+ + +
+ + + {isSearchOpen && ( + setIsSearchOpen(false)} /> + )} + ); } diff --git a/ecosystem-explorer/src/components/ui/search-overlay.tsx b/ecosystem-explorer/src/components/ui/search-overlay.tsx new file mode 100644 index 00000000..e1e7b129 --- /dev/null +++ b/ecosystem-explorer/src/components/ui/search-overlay.tsx @@ -0,0 +1,179 @@ +import { useState, useEffect, useRef } from "react"; +import { useNavigate } from "react-router-dom"; +import { useDebouncedValue } from "@/hooks/useDebouncedValue"; +import { search as performSearch } from "@/lib/search"; +import { X, Search, ChevronRight } from "lucide-react"; + +const RECENT_SEARCHES_KEY = "otel_recent_searches"; + +interface SearchOverlayProps { + onClose: () => void; + onSelect?: (query: string) => void; +} + +export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) { + const navigate = useNavigate(); + const [query, setQuery] = useState(""); + const [recentSearches, setRecentSearches] = useState([]); + const debouncedQuery = useDebouncedValue(query, 250); + const inputRef = useRef(null); + + // Load recent searches from localStorage on mount + useEffect(() => { + const stored = localStorage.getItem(RECENT_SEARCHES_KEY); + if (stored) { + try { + setRecentSearches(JSON.parse(stored)); + } catch { + setRecentSearches([]); + } + } + }, []); + + // Focus input on mount + useEffect(() => { + if (inputRef.current) { + inputRef.current.focus(); + } + }, []); + + // Handle search selection + const handleSelect = (q: string, path?: string) => { + if (q.trim()) { + // Add to recent searches + const updated = [q, ...recentSearches.filter((x) => x !== q)].slice(0, 6); + setRecentSearches(updated); + localStorage.setItem(RECENT_SEARCHES_KEY, JSON.stringify(updated)); + } + + if (path) { + navigate(path); + } + + onSelect?.(q); + setQuery(""); + onClose(); + }; + + const handleClearRecent = () => { + setRecentSearches([]); + localStorage.removeItem(RECENT_SEARCHES_KEY); + }; + + // Get search results if query is debounced + const searchResults = performSearch(debouncedQuery); + + // Show recent searches if empty query, otherwise show search results + const showRecent = !query.trim(); + + return ( + <> + {/* Backdrop */} + - {isSearchOpen && ( - setIsSearchOpen(false)} /> - )} + {isSearchOpen && setIsSearchOpen(false)} />} ); } diff --git a/ecosystem-explorer/src/components/ui/search-overlay.tsx b/ecosystem-explorer/src/components/ui/search-overlay.tsx index e1e7b129..aef4323f 100644 --- a/ecosystem-explorer/src/components/ui/search-overlay.tsx +++ b/ecosystem-explorer/src/components/ui/search-overlay.tsx @@ -1,3 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { useState, useEffect, useRef } from "react"; import { useNavigate } from "react-router-dom"; import { useDebouncedValue } from "@/hooks/useDebouncedValue"; @@ -14,21 +30,20 @@ interface SearchOverlayProps { export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) { const navigate = useNavigate(); const [query, setQuery] = useState(""); - const [recentSearches, setRecentSearches] = useState([]); - const debouncedQuery = useDebouncedValue(query, 250); - const inputRef = useRef(null); - - // Load recent searches from localStorage on mount - useEffect(() => { + const [recentSearches, setRecentSearches] = useState(() => { const stored = localStorage.getItem(RECENT_SEARCHES_KEY); - if (stored) { - try { - setRecentSearches(JSON.parse(stored)); - } catch { - setRecentSearches([]); - } + if (!stored) { + return []; } - }, []); + + try { + return JSON.parse(stored) as string[]; + } catch { + return []; + } + }); + const debouncedQuery = useDebouncedValue(query, 250); + const inputRef = useRef(null); // Focus input on mount useEffect(() => { @@ -45,11 +60,11 @@ export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) { setRecentSearches(updated); localStorage.setItem(RECENT_SEARCHES_KEY, JSON.stringify(updated)); } - + if (path) { navigate(path); } - + onSelect?.(q); setQuery(""); onClose(); @@ -62,7 +77,7 @@ export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) { // Get search results if query is debounced const searchResults = performSearch(debouncedQuery); - + // Show recent searches if empty query, otherwise show search results const showRecent = !query.trim(); @@ -76,9 +91,9 @@ export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) { /> {/* Overlay panel */} -
+
{/* Search input */} -
+
{query && ( @@ -111,21 +126,19 @@ export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) {
{searchResults.length > 0 && !showRecent ? ( <> -
- Results -
+
Results
    {searchResults.map((result) => (
  • ))} @@ -133,7 +146,7 @@ export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) { ) : recentSearches.length > 0 && showRecent ? ( <> -
    +
    Recent Searches
      @@ -148,9 +161,9 @@ export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) { handleSelect(searchQuery); } }} - className="w-full text-left rounded px-3 py-2 text-sm hover:bg-accent text-foreground transition-colors" + className="hover:bg-accent text-foreground w-full rounded px-3 py-2 text-left text-sm transition-colors" > - + {searchQuery} @@ -158,17 +171,17 @@ export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) {
    ) : query.trim() ? ( -
    +
    No results for "{debouncedQuery}"
    ) : ( -
    +
    Start typing to search
    )} diff --git a/ecosystem-explorer/src/hooks/useDebouncedValue.test.ts b/ecosystem-explorer/src/hooks/useDebouncedValue.test.ts new file mode 100644 index 00000000..60c09d00 --- /dev/null +++ b/ecosystem-explorer/src/hooks/useDebouncedValue.test.ts @@ -0,0 +1,90 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { renderHook, act } from "@testing-library/react"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { useDebouncedValue } from "./useDebouncedValue"; + +describe("useDebouncedValue", () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("returns the initial value immediately", () => { + const { result } = renderHook(() => useDebouncedValue("initial", 250)); + expect(result.current).toBe("initial"); + }); + + it("debounces value updates", async () => { + const { result, rerender } = renderHook(({ value }) => useDebouncedValue(value, 100), { + initialProps: { value: "a" }, + }); + + expect(result.current).toBe("a"); + + act(() => { + rerender({ value: "ab" }); + }); + + // Before delay, should still be 'a' + act(() => { + vi.advanceTimersByTime(50); + }); + expect(result.current).toBe("a"); + + // After delay, should be 'ab' + act(() => { + vi.advanceTimersByTime(50); + }); + expect(result.current).toBe("ab"); + }); + + it("cancels pending debounce on unmount", () => { + const { rerender, unmount } = renderHook(({ value }) => useDebouncedValue(value, 100), { + initialProps: { value: "a" }, + }); + + act(() => { + rerender({ value: "abc" }); + }); + + unmount(); + + act(() => { + vi.advanceTimersByTime(200); + }); + // Nothing to assert really, but confirming no errors/state updates after unmount + }); + + it("respects custom delay", () => { + const { result, rerender } = renderHook(({ value, delay }) => useDebouncedValue(value, delay), { + initialProps: { value: "a", delay: 50 }, + }); + + act(() => { + rerender({ value: "longer", delay: 50 }); + }); + + act(() => { + vi.advanceTimersByTime(50); + }); + + expect(result.current).toBe("longer"); + }); +}); diff --git a/ecosystem-explorer/src/hooks/useDebouncedValue.ts b/ecosystem-explorer/src/hooks/useDebouncedValue.ts index 03a2882d..20f235d5 100644 --- a/ecosystem-explorer/src/hooks/useDebouncedValue.ts +++ b/ecosystem-explorer/src/hooks/useDebouncedValue.ts @@ -1,3 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { useState, useEffect } from "react"; /** @@ -12,8 +28,14 @@ export function useDebouncedValue(value: T, delay = 250): T { const [debounced, setDebounced] = useState(value); useEffect(() => { - const id = setTimeout(() => setDebounced(value), delay); - return () => clearTimeout(id); + let mounted = true; + const id = setTimeout(() => { + if (mounted) setDebounced(value); + }, delay); + return () => { + mounted = false; + clearTimeout(id); + }; }, [value, delay]); return debounced; diff --git a/ecosystem-explorer/src/lib/search.ts b/ecosystem-explorer/src/lib/search.ts index 34b6a017..f94e196d 100644 --- a/ecosystem-explorer/src/lib/search.ts +++ b/ecosystem-explorer/src/lib/search.ts @@ -18,52 +18,52 @@ export interface SearchResult { title: string; description: string; path: string; - type: 'page' | 'section'; + type: "page" | "section"; } // Define searchable content in the app const searchableContent: SearchResult[] = [ { - title: 'Java Agent', - description: 'Explore OpenTelemetry Java auto-instrumentation', - path: '/java-agent', - type: 'page', + title: "Java Agent", + description: "Explore OpenTelemetry Java auto-instrumentation", + path: "/java-agent", + type: "page", }, { - title: 'Java Instrumentations', - description: 'Browse supported Java libraries and instrumentations', - path: '/java-agent/instrumentations', - type: 'section', + title: "Java Instrumentations", + description: "Browse supported Java libraries and instrumentations", + path: "/java-agent/instrumentations", + type: "section", }, { - title: 'Java Configurations', - description: 'Configure OpenTelemetry Java Agent behavior', - path: '/java-agent/configurations', - type: 'section', + title: "Java Configurations", + description: "Configure OpenTelemetry Java Agent behavior", + path: "/java-agent/configurations", + type: "section", }, { - title: 'Java Release Comparison', - description: 'Compare features across Java Agent releases', - path: '/java-agent/release-comparison', - type: 'section', + title: "Java Release Comparison", + description: "Compare features across Java Agent releases", + path: "/java-agent/release-comparison", + type: "section", }, { - title: 'Collector', - description: 'Explore OpenTelemetry Collector components', - path: '/collector', - type: 'page', + title: "Collector", + description: "Explore OpenTelemetry Collector components", + path: "/collector", + type: "page", }, { - title: 'Configuration Builder', - description: 'Build custom OpenTelemetry configurations', - path: '/java-agent/config-builder', - type: 'section', + title: "Configuration Builder", + description: "Build custom OpenTelemetry configurations", + path: "/java-agent/config-builder", + type: "section", }, { - title: 'About', - description: 'Learn about OpenTelemetry Ecosystem Explorer', - path: '/about', - type: 'page', + title: "About", + description: "Learn about OpenTelemetry Ecosystem Explorer", + path: "/about", + type: "page", }, ]; @@ -76,7 +76,7 @@ export function search(query: string): SearchResult[] { if (!query.trim()) return []; const lowerQuery = query.toLowerCase(); - + return searchableContent .filter((item) => { const matchesTitle = item.title.toLowerCase().includes(lowerQuery); From 9131d12f566c216403796b0e54296af3ac5611e0 Mon Sep 17 00:00:00 2001 From: ansita20 Date: Mon, 18 May 2026 22:01:38 +0530 Subject: [PATCH 3/6] Refine search results and navigation --- .../data/javaagent/global-configurations.json | 738 +++++++++--------- .../schemas/collector-component.schema.json | 55 +- .../javaagent-instrumentation.schema.json | 109 ++- .../src/components/ui/search-overlay.test.tsx | 78 ++ .../src/components/ui/search-overlay.tsx | 87 ++- .../src/features/collector/collector-page.tsx | 13 +- .../java-instrumentation-list-page.test.tsx | 12 + .../java-instrumentation-list-page.tsx | 14 +- .../src/lib/api/collector-data.ts | 4 +- .../src/lib/api/javaagent-data.test.ts | 36 + .../src/lib/api/javaagent-data.ts | 4 +- ecosystem-explorer/src/lib/search.test.ts | 100 +++ ecosystem-explorer/src/lib/search.ts | 246 +++++- 13 files changed, 1029 insertions(+), 467 deletions(-) create mode 100644 ecosystem-explorer/src/components/ui/search-overlay.test.tsx create mode 100644 ecosystem-explorer/src/lib/search.test.ts diff --git a/ecosystem-explorer/public/data/javaagent/global-configurations.json b/ecosystem-explorer/public/data/javaagent/global-configurations.json index 88ea3eb3..63a69571 100644 --- a/ecosystem-explorer/public/data/javaagent/global-configurations.json +++ b/ecosystem-explorer/public/data/javaagent/global-configurations.json @@ -23,9 +23,9 @@ "name": "otel.instrumentation.aws-lambda.flush-timeout", "type": "int", "instrumentations": [ + "aws-lambda-core-1.0", "aws-lambda-events-3.11", - "aws-lambda-events-2.2", - "aws-lambda-core-1.0" + "aws-lambda-events-2.2" ] }, { @@ -85,62 +85,62 @@ "name": "otel.instrumentation.common.db-statement-sanitizer.enabled", "type": "boolean", "instrumentations": [ - "cassandra-4.4", + "mongo-4.0", + "jedis-4.0", "couchbase-2.6", - "clickhouse-client-v2-0.8", - "vertx-sql-client-4.0", - "vertx-redis-client-4.0", - "r2dbc-1.0", - "jedis-3.0", "mongo-async-3.3", "vertx-sql-client-5.0", - "mongo-3.7", "couchbase-2.0", "geode-1.4", - "jedis-4.0", - "cassandra-3.0", - "jdbc", - "mongo-4.0", + "mongo-3.1", + "jedis-3.0", "jedis-1.4", - "cassandra-4.0", + "mongo-3.7", + "lettuce-5.1", + "cassandra-4.4", "lettuce-5.0", - "clickhouse-client-v1-0.5", "influxdb-2.4", - "lettuce-5.1", - "mongo-3.1" + "vertx-redis-client-4.0", + "clickhouse-client-v2-0.8", + "jdbc", + "r2dbc-1.0", + "cassandra-3.0", + "clickhouse-client-v1-0.5", + "cassandra-4.0", + "vertx-sql-client-4.0" ] }, { - "declarative_name": "java.common.db.query_sanitization.enabled", "default": true, "description": "Enables query sanitization for database queries.", "name": "otel.instrumentation.common.db.query-sanitization.enabled", "type": "boolean", "instrumentations": [ - "cassandra-4.4", + "mongo-4.0", + "jedis-4.0", "couchbase-2.6", - "clickhouse-client-v2-0.8", - "vertx-sql-client-4.0", - "vertx-redis-client-4.0", - "r2dbc-1.0", - "jedis-3.0", "mongo-async-3.3", "vertx-sql-client-5.0", - "mongo-3.7", "couchbase-2.0", "geode-1.4", - "jedis-4.0", - "cassandra-3.0", - "jdbc", - "mongo-4.0", + "mongo-3.1", + "jedis-3.0", "jedis-1.4", - "cassandra-4.0", + "mongo-3.7", + "lettuce-5.1", + "cassandra-4.4", "lettuce-5.0", - "clickhouse-client-v1-0.5", "influxdb-2.4", - "lettuce-5.1", - "mongo-3.1" - ] + "vertx-redis-client-4.0", + "clickhouse-client-v2-0.8", + "jdbc", + "r2dbc-1.0", + "cassandra-3.0", + "clickhouse-client-v1-0.5", + "cassandra-4.0", + "vertx-sql-client-4.0" + ], + "declarative_name": "java.common.db.query_sanitization.enabled" }, { "default": false, @@ -175,39 +175,39 @@ "name": "otel.instrumentation.common.experimental.controller-telemetry.enabled", "type": "boolean", "instrumentations": [ - "jfinal-3.2", + "jaxws-metro-2.2", + "jaxrs-1.0", "jaxrs-3.0-resteasy-6.0", - "jaxws-2.0", - "spring-webmvc-6.0", - "jaxrs-2.0-resteasy-3.0", + "struts-7.0", + "grails-3.0", "play-mvc-2.4", - "vaadin-14.2", - "ratpack-1.7", + "jaxws-jws-api-1.1", + "jaxws-cxf-3.0", + "ratpack-1.4", + "jfinal-3.2", + "jaxws-2.0-axis2-1.6", "jaxrs-2.0-jersey-2.0", "struts-2.3", - "finatra-2.9", - "jaxrs-3.0-jersey-3.0", + "jaxrs-2.0-annotations", + "spring-webmvc-3.1", "tapestry-5.4", + "jsf-mojarra-3.0", + "vaadin-14.2", "jsf-mojarra-1.2", - "jaxrs-2.0-cxf-3.2", - "jaxrs-1.0", - "jsf-myfaces-1.2", "spring-ws-2.0", + "spring-webmvc-6.0", + "ratpack-1.7", "play-mvc-2.6", - "struts-7.0", - "jaxrs-2.0-annotations", "jaxrs-3.0-annotations", - "jaxws-jws-api-1.1", + "jsf-myfaces-1.2", + "jaxrs-2.0-cxf-3.2", + "jsf-myfaces-3.0", + "jaxrs-3.0-jersey-3.0", + "jaxws-2.0", "jaxrs-2.0-resteasy-3.1", "spring-webflux-5.0", - "jaxws-metro-2.2", - "spring-webmvc-3.1", - "ratpack-1.4", - "jaxws-cxf-3.0", - "jaxws-2.0-axis2-1.6", - "jsf-myfaces-3.0", - "jsf-mojarra-3.0", - "grails-3.0" + "jaxrs-2.0-resteasy-3.0", + "finatra-2.9" ], "declarative_name": "java.common.controller_telemetry/development.enabled" }, @@ -217,10 +217,10 @@ "name": "otel.instrumentation.common.experimental.view-telemetry.enabled", "type": "boolean", "instrumentations": [ - "spring-webmvc-6.0", - "dropwizard-views-0.7", "jsp-2.3", - "spring-webmvc-3.1" + "spring-webmvc-3.1", + "dropwizard-views-0.7", + "spring-webmvc-6.0" ], "declarative_name": "java.common.view_telemetry/development.enabled" }, @@ -230,10 +230,10 @@ "name": "otel.instrumentation.common.logging.span-id", "type": "string", "instrumentations": [ - "log4j-mdc-1.2", - "logback-mdc-1.0", "log4j-context-data-2.7", - "log4j-context-data-2.17" + "logback-mdc-1.0", + "log4j-context-data-2.17", + "log4j-mdc-1.2" ] }, { @@ -242,10 +242,10 @@ "name": "otel.instrumentation.common.logging.trace-flags", "type": "string", "instrumentations": [ - "log4j-mdc-1.2", - "logback-mdc-1.0", "log4j-context-data-2.7", - "log4j-context-data-2.17" + "logback-mdc-1.0", + "log4j-context-data-2.17", + "log4j-mdc-1.2" ] }, { @@ -254,10 +254,10 @@ "name": "otel.instrumentation.common.logging.trace-id", "type": "string", "instrumentations": [ - "log4j-mdc-1.2", - "logback-mdc-1.0", "log4j-context-data-2.7", - "log4j-context-data-2.17" + "logback-mdc-1.0", + "log4j-context-data-2.17", + "log4j-mdc-1.2" ] }, { @@ -266,10 +266,10 @@ "name": "otel.instrumentation.common.mdc.resource-attributes", "type": "list", "instrumentations": [ - "log4j-mdc-1.2", - "logback-mdc-1.0", "log4j-context-data-2.7", - "log4j-context-data-2.17" + "logback-mdc-1.0", + "log4j-context-data-2.17", + "log4j-mdc-1.2" ] }, { @@ -296,47 +296,47 @@ "name": "otel.instrumentation.common.peer-service-mapping", "type": "map", "instrumentations": [ - "jetty-httpclient-12.0", - "async-http-client-1.9", - "apache-dubbo-2.7", - "jetty-httpclient-9.2", - "okhttp-2.2", - "reactor-netty-1.0", - "play-ws-1.0", - "vertx-redis-client-4.0", - "r2dbc-1.0", - "play-ws-2.1", - "jedis-3.0", - "pekko-http-1.0", - "apache-httpclient-2.0", - "async-http-client-1.8", - "ktor-3.0", - "java-http-client", - "vertx-http-client-3.0", "http-url-connection", - "apache-httpasyncclient-4.1", - "vertx-http-client-4.0", - "ratpack-1.7", - "netty-4.1", - "vertx-http-client-5.0", - "armeria-1.3", - "jdbc", "apache-httpclient-5.0", - "ktor-2.0", - "async-http-client-2.0", - "netty-3.8", - "akka-http-10.0", "google-http-client-1.19", - "play-ws-2.0", "netty-4.0", + "java-http-client", + "netty-4.1", + "jodd-http-4.2", + "akka-http-10.0", + "vertx-http-client-3.0", + "ktor-3.0", + "jedis-3.0", "jedis-1.4", + "netty-3.8", + "apache-httpclient-2.0", + "jetty-httpclient-12.0", + "vertx-http-client-4.0", + "armeria-1.3", "lettuce-5.0", - "apache-httpclient-4.0", - "jodd-http-4.2", - "lettuce-4.0", + "pekko-http-1.0", + "reactor-netty-1.0", + "vertx-redis-client-4.0", + "jdbc", "java-http-server", + "async-http-client-2.0", + "r2dbc-1.0", + "vertx-http-client-5.0", + "play-ws-1.0", + "jetty-httpclient-9.2", + "lettuce-4.0", + "okhttp-3.0", + "ratpack-1.7", + "apache-httpclient-4.0", + "async-http-client-1.9", + "async-http-client-1.8", + "apache-dubbo-2.7", + "apache-httpasyncclient-4.1", + "play-ws-2.0", + "okhttp-2.2", + "play-ws-2.1", "kubernetes-client-7.0", - "okhttp-3.0" + "ktor-2.0" ] }, { @@ -367,21 +367,21 @@ "name": "otel.instrumentation.elasticsearch.capture-search-query", "type": "boolean", "instrumentations": [ - "elasticsearch-rest-7.0", "elasticsearch-rest-6.4", + "elasticsearch-rest-7.0", "elasticsearch-rest-5.0" ] }, { "declarative_name": "java.elasticsearch.experimental_span_attributes/development", "default": false, - "description": "Enable the capture of `elasticsearch.action`, `elasticsearch.id`, `elasticsearch.request`, `elasticsearch.request.indices`, `elasticsearch.request.write.type`, `elasticsearch.request.write.version`, `elasticsearch.response.status`, `elasticsearch.shard.replication.failed`, `elasticsearch.shard.replication.successful`, `elasticsearch.shard.replication.total`, `elasticsearch.type`, and `elasticsearch.version` experimental span attributes.", + "description": "Enable the capture of `elasticsearch.action`, `elasticsearch.id`, `elasticsearch.request`, `elasticsearch.request.indices`, `elasticsearch.request.search.types`, `elasticsearch.request.write.type`, `elasticsearch.request.write.version`, `elasticsearch.response.status`, `elasticsearch.shard.broadcast.failed`, `elasticsearch.shard.broadcast.successful`, `elasticsearch.shard.broadcast.total`, `elasticsearch.shard.replication.failed`, `elasticsearch.shard.replication.successful`, `elasticsearch.shard.replication.total`, `elasticsearch.type`, and `elasticsearch.version` experimental span attributes.", "name": "otel.instrumentation.elasticsearch.experimental-span-attributes", "type": "boolean", "instrumentations": [ - "elasticsearch-transport-6.0", "elasticsearch-transport-5.3", - "elasticsearch-transport-5.0" + "elasticsearch-transport-5.0", + "elasticsearch-transport-6.0" ] }, { @@ -583,8 +583,8 @@ "instrumentations": [ "hibernate-procedure-call-4.3", "hibernate-6.0", - "hibernate-4.0", - "hibernate-3.3" + "hibernate-3.3", + "hibernate-4.0" ] }, { @@ -594,38 +594,38 @@ "name": "otel.instrumentation.http.client.capture-request-headers", "type": "list", "instrumentations": [ - "jetty-httpclient-12.0", - "async-http-client-1.9", - "jetty-httpclient-9.2", - "okhttp-2.2", - "reactor-netty-1.0", - "play-ws-1.0", - "play-ws-2.1", - "pekko-http-1.0", - "apache-httpclient-2.0", - "async-http-client-1.8", - "ktor-3.0", + "http-url-connection", + "apache-httpclient-5.0", + "google-http-client-1.19", + "netty-4.0", "java-http-client", + "netty-4.1", + "jodd-http-4.2", + "akka-http-10.0", "vertx-http-client-3.0", - "http-url-connection", - "apache-httpasyncclient-4.1", + "ktor-3.0", + "netty-3.8", + "apache-httpclient-2.0", + "jetty-httpclient-12.0", "vertx-http-client-4.0", - "ratpack-1.7", - "netty-4.1", - "vertx-http-client-5.0", "armeria-1.3", - "apache-httpclient-5.0", - "ktor-2.0", + "pekko-http-1.0", + "reactor-netty-1.0", "async-http-client-2.0", - "netty-3.8", - "akka-http-10.0", - "google-http-client-1.19", - "play-ws-2.0", - "netty-4.0", + "vertx-http-client-5.0", + "play-ws-1.0", + "jetty-httpclient-9.2", + "okhttp-3.0", + "ratpack-1.7", "apache-httpclient-4.0", - "jodd-http-4.2", + "async-http-client-1.9", + "async-http-client-1.8", + "apache-httpasyncclient-4.1", + "play-ws-2.0", + "okhttp-2.2", + "play-ws-2.1", "kubernetes-client-7.0", - "okhttp-3.0" + "ktor-2.0" ] }, { @@ -635,38 +635,38 @@ "name": "otel.instrumentation.http.client.capture-response-headers", "type": "list", "instrumentations": [ - "jetty-httpclient-12.0", - "async-http-client-1.9", - "jetty-httpclient-9.2", - "okhttp-2.2", - "reactor-netty-1.0", - "play-ws-1.0", - "play-ws-2.1", - "pekko-http-1.0", - "apache-httpclient-2.0", - "async-http-client-1.8", - "ktor-3.0", + "http-url-connection", + "apache-httpclient-5.0", + "google-http-client-1.19", + "netty-4.0", "java-http-client", + "netty-4.1", + "jodd-http-4.2", + "akka-http-10.0", "vertx-http-client-3.0", - "http-url-connection", - "apache-httpasyncclient-4.1", + "ktor-3.0", + "netty-3.8", + "apache-httpclient-2.0", + "jetty-httpclient-12.0", "vertx-http-client-4.0", - "ratpack-1.7", - "netty-4.1", - "vertx-http-client-5.0", "armeria-1.3", - "apache-httpclient-5.0", - "ktor-2.0", + "pekko-http-1.0", + "reactor-netty-1.0", "async-http-client-2.0", - "netty-3.8", - "akka-http-10.0", - "google-http-client-1.19", - "play-ws-2.0", - "netty-4.0", + "vertx-http-client-5.0", + "play-ws-1.0", + "jetty-httpclient-9.2", + "okhttp-3.0", + "ratpack-1.7", "apache-httpclient-4.0", - "jodd-http-4.2", + "async-http-client-1.9", + "async-http-client-1.8", + "apache-httpasyncclient-4.1", + "play-ws-2.0", + "okhttp-2.2", + "play-ws-2.1", "kubernetes-client-7.0", - "okhttp-3.0" + "ktor-2.0" ] }, { @@ -676,39 +676,39 @@ "name": "otel.instrumentation.http.client.emit-experimental-telemetry", "type": "boolean", "instrumentations": [ - "jetty-httpclient-12.0", - "async-http-client-1.9", - "jetty-httpclient-9.2", - "okhttp-2.2", - "reactor-netty-1.0", - "play-ws-1.0", - "play-ws-2.1", - "pekko-http-1.0", - "apache-httpclient-2.0", - "async-http-client-1.8", - "ktor-3.0", + "http-url-connection", + "apache-httpclient-5.0", + "google-http-client-1.19", + "netty-4.0", "java-http-client", + "netty-4.1", + "jodd-http-4.2", + "akka-http-10.0", "vertx-http-client-3.0", - "http-url-connection", - "apache-httpasyncclient-4.1", + "ktor-3.0", + "netty-3.8", + "apache-httpclient-2.0", + "jetty-httpclient-12.0", "vertx-http-client-4.0", - "ratpack-1.7", - "netty-4.1", - "vertx-http-client-5.0", "armeria-1.3", - "apache-httpclient-5.0", - "ktor-2.0", + "pekko-http-1.0", + "reactor-netty-1.0", "async-http-client-2.0", - "netty-3.8", - "akka-http-10.0", - "google-http-client-1.19", - "play-ws-2.0", - "netty-4.0", "spring-web-6.0", + "vertx-http-client-5.0", + "play-ws-1.0", + "jetty-httpclient-9.2", + "okhttp-3.0", + "ratpack-1.7", "apache-httpclient-4.0", - "jodd-http-4.2", + "async-http-client-1.9", + "async-http-client-1.8", + "apache-httpasyncclient-4.1", + "play-ws-2.0", + "okhttp-2.2", + "play-ws-2.1", "kubernetes-client-7.0", - "okhttp-3.0" + "ktor-2.0" ] }, { @@ -718,38 +718,38 @@ "name": "otel.instrumentation.http.client.experimental.redact-query-parameters", "type": "boolean", "instrumentations": [ - "jetty-httpclient-12.0", - "async-http-client-1.9", - "jetty-httpclient-9.2", - "okhttp-2.2", - "reactor-netty-1.0", - "play-ws-1.0", - "play-ws-2.1", - "pekko-http-1.0", - "apache-httpclient-2.0", - "async-http-client-1.8", - "ktor-3.0", + "http-url-connection", + "apache-httpclient-5.0", + "google-http-client-1.19", + "netty-4.0", "java-http-client", + "netty-4.1", + "jodd-http-4.2", + "akka-http-10.0", "vertx-http-client-3.0", - "http-url-connection", - "apache-httpasyncclient-4.1", + "ktor-3.0", + "netty-3.8", + "apache-httpclient-2.0", + "jetty-httpclient-12.0", "vertx-http-client-4.0", - "ratpack-1.7", - "netty-4.1", - "vertx-http-client-5.0", "armeria-1.3", - "apache-httpclient-5.0", - "ktor-2.0", + "pekko-http-1.0", + "reactor-netty-1.0", "async-http-client-2.0", - "netty-3.8", - "akka-http-10.0", - "google-http-client-1.19", - "play-ws-2.0", - "netty-4.0", + "vertx-http-client-5.0", + "play-ws-1.0", + "jetty-httpclient-9.2", + "okhttp-3.0", + "ratpack-1.7", "apache-httpclient-4.0", - "jodd-http-4.2", + "async-http-client-1.9", + "async-http-client-1.8", + "apache-httpasyncclient-4.1", + "play-ws-2.0", + "okhttp-2.2", + "play-ws-2.1", "kubernetes-client-7.0", - "okhttp-3.0" + "ktor-2.0" ] }, { @@ -759,57 +759,57 @@ "name": "otel.instrumentation.http.known-methods", "type": "list", "instrumentations": [ - "jetty-httpclient-12.0", + "http-url-connection", + "apache-httpclient-5.0", + "google-http-client-1.19", + "netty-4.0", + "java-http-client", + "elasticsearch-rest-6.4", + "netty-4.1", + "jodd-http-4.2", + "akka-http-10.0", + "vertx-http-client-3.0", + "ktor-3.0", "elasticsearch-rest-7.0", - "async-http-client-1.9", - "jetty-httpclient-9.2", - "okhttp-2.2", + "liberty-20.0", "tomcat-10.0", - "reactor-netty-1.0", - "helidon-4.3", - "play-ws-1.0", - "play-ws-2.1", - "pekko-http-1.0", + "netty-3.8", "apache-httpclient-2.0", "jetty-8.0", - "async-http-client-1.8", - "ktor-3.0", - "java-http-client", - "vertx-http-client-3.0", - "http-url-connection", - "aws-lambda-events-2.2", - "apache-httpasyncclient-4.1", + "jetty-httpclient-12.0", "vertx-http-client-4.0", - "ratpack-1.7", - "netty-4.1", - "restlet-1.1", - "vertx-http-client-5.0", "armeria-1.3", - "liberty-20.0", - "apache-httpclient-5.0", - "ktor-2.0", - "elasticsearch-rest-6.4", + "pekko-http-1.0", + "reactor-netty-1.0", + "undertow-1.4", + "java-http-server", "async-http-client-2.0", - "netty-3.8", - "liberty-dispatcher-20.0", - "akka-http-10.0", - "google-http-client-1.19", - "play-ws-2.0", - "netty-4.0", - "elasticsearch-transport-5.0", "grizzly-2.3", - "jetty-11.0", + "vertx-http-client-5.0", + "jetty-12.0", + "play-ws-1.0", + "elasticsearch-transport-5.0", + "helidon-4.3", + "jetty-httpclient-9.2", + "tomcat-7.0", + "okhttp-3.0", + "ratpack-1.7", + "restlet-1.1", "apache-httpclient-4.0", - "jodd-http-4.2", + "async-http-client-1.9", "elasticsearch-rest-5.0", - "java-http-server", - "kubernetes-client-7.0", - "activej-http-6.0", + "async-http-client-1.8", "restlet-2.0", - "okhttp-3.0", - "undertow-1.4", - "jetty-12.0", - "tomcat-7.0" + "apache-httpasyncclient-4.1", + "play-ws-2.0", + "okhttp-2.2", + "jetty-11.0", + "liberty-dispatcher-20.0", + "play-ws-2.1", + "kubernetes-client-7.0", + "aws-lambda-events-2.2", + "ktor-2.0", + "activej-http-6.0" ] }, { @@ -818,29 +818,29 @@ "name": "otel.instrumentation.http.server.capture-request-headers", "type": "list", "instrumentations": [ - "tomcat-10.0", - "helidon-4.3", - "pekko-http-1.0", - "jetty-8.0", - "ktor-3.0", - "ratpack-1.7", + "netty-4.0", "netty-4.1", - "restlet-1.1", - "armeria-1.3", + "akka-http-10.0", + "ktor-3.0", "liberty-20.0", - "ktor-2.0", + "tomcat-10.0", "netty-3.8", - "liberty-dispatcher-20.0", - "akka-http-10.0", - "netty-4.0", - "grizzly-2.3", - "jetty-11.0", - "java-http-server", - "activej-http-6.0", - "restlet-2.0", + "jetty-8.0", + "armeria-1.3", + "pekko-http-1.0", "undertow-1.4", + "java-http-server", + "grizzly-2.3", "jetty-12.0", - "tomcat-7.0" + "helidon-4.3", + "tomcat-7.0", + "ratpack-1.7", + "restlet-1.1", + "restlet-2.0", + "jetty-11.0", + "liberty-dispatcher-20.0", + "ktor-2.0", + "activej-http-6.0" ], "declarative_name": "general.http.server.request_captured_headers" }, @@ -850,61 +850,61 @@ "name": "otel.instrumentation.http.server.capture-response-headers", "type": "list", "instrumentations": [ - "tomcat-10.0", - "helidon-4.3", - "pekko-http-1.0", - "jetty-8.0", - "ktor-3.0", - "ratpack-1.7", + "netty-4.0", "netty-4.1", - "restlet-1.1", - "armeria-1.3", + "akka-http-10.0", + "ktor-3.0", "liberty-20.0", - "ktor-2.0", + "tomcat-10.0", "netty-3.8", - "liberty-dispatcher-20.0", - "akka-http-10.0", - "netty-4.0", - "grizzly-2.3", - "jetty-11.0", - "java-http-server", - "activej-http-6.0", - "restlet-2.0", + "jetty-8.0", + "armeria-1.3", + "pekko-http-1.0", "undertow-1.4", + "java-http-server", + "grizzly-2.3", "jetty-12.0", - "tomcat-7.0" + "helidon-4.3", + "tomcat-7.0", + "ratpack-1.7", + "restlet-1.1", + "restlet-2.0", + "jetty-11.0", + "liberty-dispatcher-20.0", + "ktor-2.0", + "activej-http-6.0" ], "declarative_name": "general.http.server.response_captured_headers" }, { "default": false, - "description": "Enable the capture of experimental HTTP server telemetry. Adds the `http.request.body.size` and `http.response.body.size` attributes to spans, and records `http.server.request.body.size` and `http.server.response.body.size` metrics.", + "description": "Enable the capture of experimental HTTP server telemetry. Adds the `http.request.body.size` and `http.response.body.size` attributes to spans, and records `http.server.request.size` and `http.server.response.size` metrics.", "name": "otel.instrumentation.http.server.emit-experimental-telemetry", "type": "boolean", "instrumentations": [ - "tomcat-10.0", - "helidon-4.3", - "pekko-http-1.0", - "jetty-8.0", - "ktor-3.0", - "ratpack-1.7", + "netty-4.0", "netty-4.1", - "restlet-1.1", - "armeria-1.3", + "akka-http-10.0", + "ktor-3.0", "liberty-20.0", - "ktor-2.0", + "tomcat-10.0", "netty-3.8", - "liberty-dispatcher-20.0", - "akka-http-10.0", - "netty-4.0", - "grizzly-2.3", - "jetty-11.0", - "java-http-server", - "activej-http-6.0", - "restlet-2.0", + "jetty-8.0", + "armeria-1.3", + "pekko-http-1.0", "undertow-1.4", + "java-http-server", + "grizzly-2.3", "jetty-12.0", - "tomcat-7.0" + "helidon-4.3", + "tomcat-7.0", + "ratpack-1.7", + "restlet-1.1", + "restlet-2.0", + "jetty-11.0", + "liberty-dispatcher-20.0", + "ktor-2.0", + "activej-http-6.0" ], "declarative_name": "java.common.http.server.emit_experimental_telemetry/development" }, @@ -935,13 +935,13 @@ "type": "boolean", "instrumentations": [ "jaxrs-3.0-resteasy-6.0", - "jaxrs-2.0-resteasy-3.0", "jaxrs-2.0-jersey-2.0", - "jaxrs-3.0-jersey-3.0", - "jaxrs-2.0-cxf-3.2", "jaxrs-2.0-annotations", "jaxrs-3.0-annotations", - "jaxrs-2.0-resteasy-3.1" + "jaxrs-2.0-cxf-3.2", + "jaxrs-3.0-jersey-3.0", + "jaxrs-2.0-resteasy-3.1", + "jaxrs-2.0-resteasy-3.0" ] }, { @@ -1042,18 +1042,18 @@ ] }, { + "declarative_name": "java.kafka.experimental_span_attributes/development", "default": false, - "description": "Enables the capture of the experimental consumer attribute `kafka.record.queue_time_ms`.", + "description": "Enables the capture of the experimental consumer attributes `kafka.record.queue_time_ms` and `messaging.kafka.bootstrap.servers`.", "name": "otel.instrumentation.kafka.experimental-span-attributes", "type": "boolean", "instrumentations": [ - "kafka-streams-0.11", - "spring-kafka-2.7", - "reactor-kafka-1.0", "kafka-clients-0.11", - "vertx-kafka-client-3.6" - ], - "declarative_name": "java.kafka.experimental_span_attributes/development" + "vertx-kafka-client-3.6", + "spring-kafka-2.7", + "kafka-streams-0.11", + "reactor-kafka-1.0" + ] }, { "declarative_name": "java.kafka.producer_propagation.enabled", @@ -1294,28 +1294,28 @@ { "declarative_name": "java.common.messaging.capture_headers/development", "default": "", - "description": "Allows configuring headers to capture as span attributes.", + "description": "Enables capturing messaging headers as span attributes. Provide a comma-separated list of header names to capture.", "name": "otel.instrumentation.messaging.experimental.capture-headers", "type": "list", "instrumentations": [ - "aws-sdk-2.2", - "kafka-streams-0.11", - "jms-1.1", - "spring-kafka-2.7", - "pulsar-2.8", - "reactor-kafka-1.0", - "spring-rabbit-1.0", - "kafka-clients-0.11", "jms-3.0", - "spring-integration-4.1", + "spring-jms-2.0", + "spring-rabbit-1.0", + "aws-sdk-2.2", "nats-2.17", - "rocketmq-client-5.0", + "kafka-clients-0.11", + "spring-jms-6.0", + "pulsar-2.8", "aws-sdk-1.11", "vertx-kafka-client-3.6", + "rocketmq-client-5.0", + "spring-kafka-2.7", + "jms-1.1", "rocketmq-client-4.8", - "spring-jms-2.0", + "kafka-streams-0.11", "spring-pulsar-1.0", - "spring-jms-6.0" + "spring-integration-4.1", + "reactor-kafka-1.0" ] }, { @@ -1325,20 +1325,20 @@ "name": "otel.instrumentation.messaging.experimental.receive-telemetry.enabled", "type": "boolean", "instrumentations": [ + "jms-3.0", + "spring-jms-2.0", "aws-sdk-2.2", - "kafka-streams-0.11", - "jms-1.1", - "spring-kafka-2.7", - "pulsar-2.8", - "reactor-kafka-1.0", "kafka-clients-0.11", - "jms-3.0", - "rocketmq-client-5.0", + "spring-jms-6.0", + "pulsar-2.8", "aws-sdk-1.11", "vertx-kafka-client-3.6", - "spring-jms-2.0", + "rocketmq-client-5.0", + "spring-kafka-2.7", + "jms-1.1", + "kafka-streams-0.11", "spring-pulsar-1.0", - "spring-jms-6.0" + "reactor-kafka-1.0" ] }, { @@ -1374,10 +1374,10 @@ "name": "otel.instrumentation.mongo.query-sanitization.enabled", "type": "boolean", "instrumentations": [ - "mongo-async-3.3", - "mongo-3.7", "mongo-4.0", - "mongo-3.1" + "mongo-async-3.3", + "mongo-3.1", + "mongo-3.7" ] }, { @@ -1386,10 +1386,10 @@ "name": "otel.instrumentation.mongo.statement-sanitizer.enabled", "type": "boolean", "instrumentations": [ - "mongo-async-3.3", - "mongo-3.7", "mongo-4.0", - "mongo-3.1" + "mongo-async-3.3", + "mongo-3.1", + "mongo-3.7" ] }, { @@ -1398,8 +1398,8 @@ "name": "otel.instrumentation.netty.connection-telemetry.enabled", "type": "boolean", "instrumentations": [ - "netty-4.1", - "netty-4.0" + "netty-4.0", + "netty-4.1" ] }, { @@ -1408,8 +1408,8 @@ "name": "otel.instrumentation.netty.ssl-telemetry.enabled", "type": "boolean", "instrumentations": [ - "netty-4.1", - "netty-4.0" + "netty-4.0", + "netty-4.1" ] }, { @@ -1554,9 +1554,9 @@ "name": "otel.instrumentation.rxjava.experimental-span-attributes", "type": "boolean", "instrumentations": [ + "rxjava-2.0", "rxjava-3.0", - "rxjava-3.1.1", - "rxjava-2.0" + "rxjava-3.1.1" ] }, { @@ -1565,9 +1565,9 @@ "name": "otel.instrumentation.servlet.add-trace-id-request-attribute", "type": "boolean", "instrumentations": [ + "servlet-2.2", "servlet-3.0", - "servlet-5.0", - "servlet-2.2" + "servlet-5.0" ] }, { @@ -1576,10 +1576,10 @@ "name": "otel.instrumentation.servlet.capture-request-parameters", "type": "list", "instrumentations": [ + "servlet-2.2", "tomcat-10.0", "servlet-3.0", - "servlet-5.0", - "servlet-2.2" + "servlet-5.0" ] }, { @@ -1588,12 +1588,12 @@ "name": "otel.instrumentation.servlet.experimental-span-attributes", "type": "boolean", "instrumentations": [ - "tomcat-10.0", - "servlet-3.0", - "servlet-5.0", "servlet-2.2", "liberty-20.0", - "tomcat-7.0" + "tomcat-10.0", + "servlet-3.0", + "tomcat-7.0", + "servlet-5.0" ], "declarative_name": "java.servlet.experimental_span_attributes/development" }, @@ -1603,11 +1603,11 @@ "name": "otel.instrumentation.servlet.experimental.capture-request-parameters", "type": "list", "instrumentations": [ - "servlet-3.0", - "servlet-5.0", "servlet-2.2", "liberty-20.0", - "tomcat-7.0" + "servlet-3.0", + "tomcat-7.0", + "servlet-5.0" ], "declarative_name": "java.servlet.capture_request_parameters/development" }, @@ -1617,9 +1617,9 @@ "name": "otel.instrumentation.servlet.experimental.trace-id-request-attribute.enabled", "type": "boolean", "instrumentations": [ + "servlet-2.2", "servlet-3.0", - "servlet-5.0", - "servlet-2.2" + "servlet-5.0" ] }, { @@ -1655,8 +1655,8 @@ "name": "otel.instrumentation.spring-cloud-gateway.experimental-span-attributes", "type": "boolean", "instrumentations": [ - "spring-cloud-gateway-2.0", - "spring-cloud-gateway-webmvc-4.3" + "spring-cloud-gateway-webmvc-4.3", + "spring-cloud-gateway-2.0" ] }, { @@ -1710,8 +1710,8 @@ "name": "otel.instrumentation.spring-webmvc.experimental-span-attributes", "type": "boolean", "instrumentations": [ - "spring-webmvc-6.0", - "spring-webmvc-3.1" + "spring-webmvc-3.1", + "spring-webmvc-6.0" ] }, { @@ -1738,8 +1738,8 @@ "name": "otel.instrumentation.xxl-job.experimental-span-attributes", "type": "boolean", "instrumentations": [ - "xxl-job-2.1.2", "xxl-job-1.9.2", + "xxl-job-2.1.2", "xxl-job-2.3.0" ] }, @@ -1749,8 +1749,8 @@ "name": "otel.semconv-stability.opt-in", "type": "list", "instrumentations": [ - "apache-dbcp-2.0", "alibaba-druid-1.0", + "apache-dbcp-2.0", "tomcat-jdbc" ] } diff --git a/ecosystem-explorer/public/schemas/collector-component.schema.json b/ecosystem-explorer/public/schemas/collector-component.schema.json index 5eb204c2..21f58b94 100644 --- a/ecosystem-explorer/public/schemas/collector-component.schema.json +++ b/ecosystem-explorer/public/schemas/collector-component.schema.json @@ -16,7 +16,13 @@ }, "type": { "description": "The functional type of the component.", - "enum": ["connector", "exporter", "extension", "processor", "receiver"], + "enum": [ + "connector", + "exporter", + "extension", + "processor", + "receiver" + ], "type": "string" }, "distribution": { @@ -25,11 +31,17 @@ }, "display_name": { "description": "Human-readable name of the component.", - "type": ["null", "string"] + "type": [ + "null", + "string" + ] }, "description": { "description": "Brief description of the component's functionality.", - "type": ["null", "string"] + "type": [ + "null", + "string" + ] }, "repository": { "description": "Link to the component's source code or documentation.", @@ -84,7 +96,14 @@ } } }, - "propertyOrder": ["alpha", "beta", "stable", "deprecated", "unmaintained", "development"] + "propertyOrder": [ + "alpha", + "beta", + "stable", + "deprecated", + "unmaintained", + "development" + ] }, "distributions": { "type": "array", @@ -108,11 +127,23 @@ } } }, - "propertyOrder": ["active", "emeritus"] + "propertyOrder": [ + "active", + "emeritus" + ] } }, - "propertyOrder": ["class", "stability", "distributions", "codeowners"], - "required": ["class", "distributions", "stability"] + "propertyOrder": [ + "class", + "stability", + "distributions", + "codeowners" + ], + "required": [ + "class", + "distributions", + "stability" + ] } }, "propertyOrder": [ @@ -126,6 +157,12 @@ "repository", "status" ], - "required": ["distribution", "ecosystem", "id", "name", "type"], + "required": [ + "distribution", + "ecosystem", + "id", + "name", + "type" + ], "$schema": "https://json-schema.org/draft-07/schema#" -} +} \ No newline at end of file diff --git a/ecosystem-explorer/public/schemas/javaagent-instrumentation.schema.json b/ecosystem-explorer/public/schemas/javaagent-instrumentation.schema.json index 4ac7fa86..91e4dbde 100644 --- a/ecosystem-explorer/public/schemas/javaagent-instrumentation.schema.json +++ b/ecosystem-explorer/public/schemas/javaagent-instrumentation.schema.json @@ -57,8 +57,13 @@ "type": "string" } }, - "propertyOrder": ["name", "schema_url"], - "required": ["name"] + "propertyOrder": [ + "name", + "schema_url" + ], + "required": [ + "name" + ] }, "has_javaagent": { "description": "Indicates if this instrumentation is compatible with the OpenTelemetry Java Agent.", @@ -96,12 +101,23 @@ }, "type": { "description": "The expected data type of the configuration value.", - "enum": ["boolean", "double", "int", "list", "map", "string"], + "enum": [ + "boolean", + "double", + "int", + "list", + "map", + "string" + ], "type": "string" }, "default": { "description": "The default value if not specified.", - "type": ["string", "number", "boolean"] + "type": [ + "string", + "number", + "boolean" + ] }, "example": { "description": "Example values for this configuration option.", @@ -111,8 +127,20 @@ } } }, - "propertyOrder": ["name", "declarative_name", "description", "type", "default", "example"], - "required": ["default", "description", "name", "type"] + "propertyOrder": [ + "name", + "declarative_name", + "description", + "type", + "default", + "example" + ], + "required": [ + "default", + "description", + "name", + "type" + ] } }, "telemetry": { @@ -143,7 +171,12 @@ }, "instrument": { "description": "The instrument type (e.g., counter, gauge).", - "enum": ["counter", "gauge", "histogram", "updowncounter"], + "enum": [ + "counter", + "gauge", + "histogram", + "updowncounter" + ], "type": "string" }, "data_type": { @@ -189,8 +222,14 @@ "type": "string" } }, - "propertyOrder": ["name", "type"], - "required": ["name", "type"] + "propertyOrder": [ + "name", + "type" + ], + "required": [ + "name", + "type" + ] } } }, @@ -202,7 +241,13 @@ "unit", "attributes" ], - "required": ["data_type", "description", "instrument", "name", "unit"] + "required": [ + "data_type", + "description", + "instrument", + "name", + "unit" + ] } }, "spans": { @@ -214,7 +259,13 @@ "properties": { "span_kind": { "description": "The span kind (e.g., CLIENT, SERVER).", - "enum": ["CLIENT", "CONSUMER", "INTERNAL", "PRODUCER", "SERVER"], + "enum": [ + "CLIENT", + "CONSUMER", + "INTERNAL", + "PRODUCER", + "SERVER" + ], "type": "string" }, "attributes": { @@ -243,18 +294,35 @@ "type": "string" } }, - "propertyOrder": ["name", "type"], - "required": ["name", "type"] + "propertyOrder": [ + "name", + "type" + ], + "required": [ + "name", + "type" + ] } } }, - "propertyOrder": ["span_kind", "attributes"], - "required": ["span_kind"] + "propertyOrder": [ + "span_kind", + "attributes" + ], + "required": [ + "span_kind" + ] } } }, - "propertyOrder": ["when", "metrics", "spans"], - "required": ["when"] + "propertyOrder": [ + "when", + "metrics", + "spans" + ], + "required": [ + "when" + ] } }, "_is_custom": { @@ -280,6 +348,9 @@ "telemetry", "_is_custom" ], - "required": ["name", "scope"], + "required": [ + "name", + "scope" + ], "$schema": "https://json-schema.org/draft-07/schema#" -} +} \ No newline at end of file diff --git a/ecosystem-explorer/src/components/ui/search-overlay.test.tsx b/ecosystem-explorer/src/components/ui/search-overlay.test.tsx new file mode 100644 index 00000000..e339a851 --- /dev/null +++ b/ecosystem-explorer/src/components/ui/search-overlay.test.tsx @@ -0,0 +1,78 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { MemoryRouter, Route, Routes } from "react-router-dom"; +import { describe, expect, it, vi, beforeEach } from "vitest"; +import { SearchOverlay } from "./search-overlay"; + +vi.mock("@/hooks/useDebouncedValue", () => ({ + useDebouncedValue: (value: string) => value, +})); + +vi.mock("@/lib/search", async () => { + const actual = await vi.importActual("@/lib/search"); + + return { + ...actual, + search: vi.fn(), + }; +}); + +import { search as performSearch } from "@/lib/search"; + +describe("SearchOverlay", () => { + beforeEach(() => { + localStorage.clear(); + vi.mocked(performSearch).mockReset(); + }); + + it("navigates when a Java Agent search result is clicked", async () => { + const user = userEvent.setup(); + + vi.mocked(performSearch).mockResolvedValue([ + { + title: "Kafka Client", + description: "Messaging instrumentation for Kafka", + path: "/java-agent/instrumentation/1.2.3/kafka-client", + type: "item", + }, + ]); + + render( + + + } /> + Instrumentation detail page
    } + /> + + + ); + + await user.type(screen.getByRole("textbox", { name: "Search" }), "kafka"); + + await waitFor(() => expect(screen.getByRole("link", { name: /kafka client/i })).toBeInTheDocument()); + + await user.click(screen.getByRole("link", { name: /kafka client/i })); + + await waitFor(() => { + expect(screen.getByText("Instrumentation detail page")).toBeInTheDocument(); + }); + }); +}); \ No newline at end of file diff --git a/ecosystem-explorer/src/components/ui/search-overlay.tsx b/ecosystem-explorer/src/components/ui/search-overlay.tsx index aef4323f..e918bf51 100644 --- a/ecosystem-explorer/src/components/ui/search-overlay.tsx +++ b/ecosystem-explorer/src/components/ui/search-overlay.tsx @@ -15,10 +15,11 @@ */ import { useState, useEffect, useRef } from "react"; -import { useNavigate } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import { useDebouncedValue } from "@/hooks/useDebouncedValue"; import { search as performSearch } from "@/lib/search"; import { X, Search, ChevronRight } from "lucide-react"; +import type { SearchResult } from "@/lib/search"; const RECENT_SEARCHES_KEY = "otel_recent_searches"; @@ -30,6 +31,8 @@ interface SearchOverlayProps { export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) { const navigate = useNavigate(); const [query, setQuery] = useState(""); + const [searchResults, setSearchResults] = useState([]); + const [isSearching, setIsSearching] = useState(false); const [recentSearches, setRecentSearches] = useState(() => { const stored = localStorage.getItem(RECENT_SEARCHES_KEY); if (!stored) { @@ -52,14 +55,50 @@ export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) { } }, []); + const recordSearch = (searchQuery: string) => { + if (!searchQuery.trim()) { + return; + } + + const updated = [searchQuery, ...recentSearches.filter((x) => x !== searchQuery)].slice(0, 6); + setRecentSearches(updated); + localStorage.setItem(RECENT_SEARCHES_KEY, JSON.stringify(updated)); + }; + + useEffect(() => { + let cancelled = false; + const trimmedQuery = debouncedQuery.trim(); + + if (!trimmedQuery) { + setSearchResults([]); + setIsSearching(false); + return; + } + + setIsSearching(true); + + void performSearch(trimmedQuery) + .then((results) => { + if (!cancelled) { + setSearchResults(results); + setIsSearching(false); + } + }) + .catch(() => { + if (!cancelled) { + setSearchResults([]); + setIsSearching(false); + } + }); + + return () => { + cancelled = true; + }; + }, [debouncedQuery]); + // Handle search selection const handleSelect = (q: string, path?: string) => { - if (q.trim()) { - // Add to recent searches - const updated = [q, ...recentSearches.filter((x) => x !== q)].slice(0, 6); - setRecentSearches(updated); - localStorage.setItem(RECENT_SEARCHES_KEY, JSON.stringify(updated)); - } + recordSearch(q); if (path) { navigate(path); @@ -75,8 +114,14 @@ export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) { localStorage.removeItem(RECENT_SEARCHES_KEY); }; - // Get search results if query is debounced - const searchResults = performSearch(debouncedQuery); + const handleRecentSearch = async (searchQuery: string) => { + const results = await performSearch(searchQuery); + if (results.length > 0) { + handleSelect(searchQuery, results[0].path); + } else { + handleSelect(searchQuery); + } + }; // Show recent searches if empty query, otherwise show search results const showRecent = !query.trim(); @@ -98,7 +143,7 @@ export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) { setQuery(e.target.value)} onKeyDown={(e) => { @@ -124,22 +169,27 @@ export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) { {/* Suggestions or recent searches */}
    - {searchResults.length > 0 && !showRecent ? ( + {isSearching && !showRecent ? ( +
    + Searching... +
    + ) : searchResults.length > 0 && !showRecent ? ( <>
    Results
      {searchResults.map((result) => (
    • - + +
    • ))}
    @@ -154,12 +204,7 @@ export function SearchOverlay({ onClose, onSelect }: SearchOverlayProps) {