-
Notifications
You must be signed in to change notification settings - Fork 43
feat(java-agent): implement core release comparison engine (Phase 2) #504
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Pittu-Sharma
wants to merge
14
commits into
open-telemetry:main
Choose a base branch
from
Pittu-Sharma:feat/java-release-comparison-phase2
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,143
−22
Open
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
2c52c4b
feat(java-agent): implement core release comparison engine (Phase 2)
Pittu-Sharma f3911d5
feat(java-agent): add defensive comparison checks and fix row contras…
Pittu-Sharma fc695a1
chore: auto-format repository to fix Prettier CI check failures
Pittu-Sharma 3805f59
Merge branch 'main' into feat/java-release-comparison-phase2
Pittu-Sharma f00e472
Merge branch 'main' into feat/java-release-comparison-phase2
Pittu-Sharma 98e0105
fix(java-agent): resolve release comparison review comments from copi…
Pittu-Sharma 35b143a
Merge branch 'main' into feat/java-release-comparison-phase2
Pittu-Sharma 74549ab
Merge branch 'main' into feat/java-release-comparison-phase2
Pittu-Sharma ae917cb
Merge branch 'main' into feat/java-release-comparison-phase2
Pittu-Sharma 2f2dd10
Merge branch 'main' into feat/java-release-comparison-phase2
Pittu-Sharma 2e729ee
Merge branch 'main' into feat/java-release-comparison-phase2
Pittu-Sharma 90d138a
Merge branch 'main' into feat/java-release-comparison-phase2
Pittu-Sharma 6953ddc
Merge branch 'main' into feat/java-release-comparison-phase2
Pittu-Sharma 6b55730
Merge branch 'main' into feat/java-release-comparison-phase2
Pittu-Sharma File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
157 changes: 157 additions & 0 deletions
157
...lorer/src/features/java-agent/components/release-comparison/instrumentation-diff-card.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| /* | ||
| * 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 } from "react"; | ||
| import { ChevronDown, ChevronUp, Plus, Minus, RefreshCcw } from "lucide-react"; | ||
| import type { InstrumentationDiff } from "../../utils/release-diff"; | ||
| import { DiffResultsSection } from "../telemetry-comparison/diff-results-section"; | ||
| import { GlowBadge } from "@/components/ui/glow-badge"; | ||
|
|
||
| const STATUS_CONFIG: Record< | ||
| InstrumentationDiff["status"], | ||
| { className: string; icon: React.ReactNode } | ||
| > = { | ||
| added: { | ||
| className: "border-green-400/30 bg-green-400/10 text-green-400", | ||
| icon: <Plus className="h-4 w-4" />, | ||
| }, | ||
| removed: { | ||
| className: "border-red-400/30 bg-red-400/10 text-red-400", | ||
| icon: <Minus className="h-4 w-4" />, | ||
| }, | ||
| changed: { | ||
| className: "border-blue-400/30 bg-blue-400/10 text-blue-400", | ||
| icon: <RefreshCcw className="h-4 w-4" />, | ||
| }, | ||
| unchanged: { | ||
| className: "border-border/50 bg-muted/20 text-muted-foreground", | ||
| icon: <div className="h-2 w-2 rounded-full bg-current" />, | ||
| }, | ||
| }; | ||
|
|
||
| interface InstrumentationDiffCardProps { | ||
| diff: InstrumentationDiff; | ||
| } | ||
|
|
||
| export function InstrumentationDiffCard({ diff }: InstrumentationDiffCardProps) { | ||
| const [isExpanded, setIsExpanded] = useState(false); | ||
|
|
||
| const changedMetricsCount = diff.telemetryDiff.metrics.filter( | ||
| (m) => m.status !== "unchanged" | ||
| ).length; | ||
| const changedSpansCount = diff.telemetryDiff.spans.filter((s) => s.status !== "unchanged").length; | ||
| const configChangesCount = | ||
| (diff.configDiff?.added.length || 0) + | ||
| (diff.configDiff?.removed.length || 0) + | ||
| (diff.configDiff?.changed.length || 0); | ||
|
|
||
| const statusInfo = STATUS_CONFIG[diff.status]; | ||
|
|
||
| return ( | ||
| <div className="border-border/30 bg-card/20 overflow-hidden rounded-xl border transition-all duration-200"> | ||
| <button | ||
| onClick={() => setIsExpanded(!isExpanded)} | ||
| aria-expanded={isExpanded} | ||
| className="flex w-full items-center justify-between p-4 text-left transition-colors hover:bg-white/5" | ||
| > | ||
| <div className="flex items-center gap-4"> | ||
| <div | ||
| className={`flex h-8 w-8 items-center justify-center rounded-lg border ${statusInfo.className}`} | ||
| > | ||
| {statusInfo.icon} | ||
| </div> | ||
| <div> | ||
| <h3 className="text-sm font-semibold">{diff.displayName}</h3> | ||
| <p className="text-muted-foreground text-xs">{diff.id}</p> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="flex items-center gap-6"> | ||
| <div className="flex items-center gap-2"> | ||
| {changedMetricsCount > 0 && ( | ||
| <GlowBadge variant="success"> | ||
| {changedMetricsCount} Metric{changedMetricsCount > 1 ? "s" : ""} | ||
| </GlowBadge> | ||
| )} | ||
| {changedSpansCount > 0 && ( | ||
| <GlowBadge variant="info"> | ||
| {changedSpansCount} Span{changedSpansCount > 1 ? "s" : ""} | ||
| </GlowBadge> | ||
| )} | ||
| {configChangesCount > 0 && ( | ||
| <GlowBadge variant="warning"> | ||
| {configChangesCount} Config{configChangesCount > 1 ? "s" : ""} | ||
| </GlowBadge> | ||
| )} | ||
| </div> | ||
| {isExpanded ? ( | ||
| <ChevronUp className="text-muted-foreground h-5 w-5" /> | ||
| ) : ( | ||
| <ChevronDown className="text-muted-foreground h-5 w-5" /> | ||
| )} | ||
| </div> | ||
| </button> | ||
|
|
||
| {isExpanded && ( | ||
| <div className="border-border/30 animate-in fade-in slide-in-from-top-2 space-y-8 border-t p-6 duration-200"> | ||
| {configChangesCount > 0 && ( | ||
| <div className="space-y-4"> | ||
| <div className="flex items-center justify-center gap-4"> | ||
| <div className="to-border h-px w-16 bg-gradient-to-r from-transparent" /> | ||
| <span className="text-muted-foreground text-sm font-medium tracking-wider uppercase"> | ||
| Configuration Changes | ||
| </span> | ||
| <div className="to-border h-px w-16 bg-gradient-to-l from-transparent" /> | ||
| </div> | ||
|
|
||
| <div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3"> | ||
| {diff.configDiff?.added.map((name) => ( | ||
| <div | ||
| key={name} | ||
| className="flex items-center gap-2 rounded-lg border border-green-400/20 bg-green-400/5 p-3" | ||
| > | ||
| <Plus className="h-4 w-4 text-green-400" /> | ||
| <code className="text-foreground/90 font-mono text-xs">{name}</code> | ||
| </div> | ||
| ))} | ||
| {diff.configDiff?.changed.map((name) => ( | ||
| <div | ||
| key={name} | ||
| className="flex items-center gap-2 rounded-lg border border-blue-400/20 bg-blue-400/5 p-3" | ||
| > | ||
| <RefreshCcw className="h-4 w-4 text-blue-400" /> | ||
| <code className="text-foreground/90 font-mono text-xs">{name}</code> | ||
| </div> | ||
| ))} | ||
| {diff.configDiff?.removed.map((name) => ( | ||
| <div | ||
| key={name} | ||
| className="flex items-center gap-2 rounded-lg border border-red-400/20 bg-red-400/5 p-3" | ||
| > | ||
| <Minus className="h-4 w-4 text-red-400" /> | ||
| <code className="text-foreground/90 font-mono text-xs">{name}</code> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| </div> | ||
| )} | ||
|
|
||
| <DiffResultsSection diffResult={diff.telemetryDiff} /> | ||
| </div> | ||
| )} | ||
| </div> | ||
| ); | ||
| } |
91 changes: 91 additions & 0 deletions
91
...plorer/src/features/java-agent/components/release-comparison/release-version-selector.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| /* | ||
| * 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 { Info } from "lucide-react"; | ||
| import type { VersionInfo } from "@/types/javaagent"; | ||
|
|
||
| interface ReleaseVersionSelectorProps { | ||
| versions: VersionInfo[]; | ||
| fromVersion: string; | ||
| toVersion: string; | ||
| onFromVersionChange: (version: string) => void; | ||
| onToVersionChange: (version: string) => void; | ||
| } | ||
|
|
||
| export function ReleaseVersionSelector({ | ||
| versions, | ||
| fromVersion, | ||
| toVersion, | ||
| onFromVersionChange, | ||
| onToVersionChange, | ||
| }: ReleaseVersionSelectorProps) { | ||
| return ( | ||
| <div className="mx-auto max-w-4xl"> | ||
| <div className="border-border/30 bg-card/40 flex flex-col gap-6 rounded-xl border p-6 shadow-sm backdrop-blur-sm"> | ||
| <div className="bg-secondary/10 border-secondary/20 flex w-fit items-center gap-2 rounded-lg border px-3 py-2"> | ||
| <Info className="text-secondary h-4 w-4" aria-hidden="true" /> | ||
| <span className="text-foreground/90 text-xs font-medium"> | ||
| Select versions to compare Java Agent changes | ||
| </span> | ||
| </div> | ||
|
|
||
| <div className="grid grid-cols-1 gap-6 md:grid-cols-2"> | ||
| <div className="space-y-3"> | ||
| <label | ||
| htmlFor="from-version-select" | ||
| className="bg-muted/50 text-foreground/70 block w-fit rounded-md px-3 py-1.5 text-[10px] font-bold tracking-widest uppercase" | ||
| > | ||
| From Version | ||
| </label> | ||
| <select | ||
| id="from-version-select" | ||
| value={fromVersion} | ||
| onChange={(e) => onFromVersionChange(e.target.value)} | ||
| className="border-primary/20 bg-primary/5 text-foreground hover:border-primary/40 hover:bg-primary/10 focus:ring-primary/50 focus:border-primary/50 w-full cursor-pointer rounded-lg border-2 px-4 py-2.5 text-sm font-medium shadow-sm transition-all duration-200 hover:shadow-md focus:ring-2 focus:outline-none" | ||
| > | ||
| {versions.map((v) => ( | ||
| <option key={v.version} value={v.version}> | ||
| {v.version} {v.is_latest ? "(latest)" : ""} | ||
| </option> | ||
| ))} | ||
| </select> | ||
| </div> | ||
|
|
||
| <div className="space-y-3"> | ||
| <label | ||
| htmlFor="to-version-select" | ||
| className="bg-muted/50 text-foreground/70 block w-fit rounded-md px-3 py-1.5 text-[10px] font-bold tracking-widest uppercase" | ||
| > | ||
| To Version | ||
| </label> | ||
| <select | ||
| id="to-version-select" | ||
| value={toVersion} | ||
| onChange={(e) => onToVersionChange(e.target.value)} | ||
| className="border-primary/20 bg-primary/5 text-foreground hover:border-primary/40 hover:bg-primary/10 focus:ring-primary/50 focus:border-primary/50 w-full cursor-pointer rounded-lg border-2 px-4 py-2.5 text-sm font-medium shadow-sm transition-all duration-200 hover:shadow-md focus:ring-2 focus:outline-none" | ||
| > | ||
| {versions.map((v) => ( | ||
| <option key={v.version} value={v.version}> | ||
| {v.version} {v.is_latest ? "(latest)" : ""} | ||
| </option> | ||
| ))} | ||
| </select> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
101 changes: 101 additions & 0 deletions
101
ecosystem-explorer/src/features/java-agent/hooks/use-release-comparison.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| /* | ||
| * 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"; | ||
| import * as javaagentData from "@/lib/api/javaagent-data"; | ||
| import { compareReleases, type ReleaseDiff } from "../utils/release-diff"; | ||
|
|
||
| /** | ||
| * Custom hook to fetch instrumentation data for two Java Agent versions and compute the difference. | ||
| * | ||
| * @param fromVersion The base version for comparison | ||
| * @param toVersion The target version for comparison | ||
| * @returns An object containing the diff results, loading state, and any error encountered | ||
| */ | ||
| export function useReleaseComparison( | ||
| fromVersion: string, | ||
| toVersion: string, | ||
| validVersions: string[] = [] | ||
| ) { | ||
| const [diff, setDiff] = useState<ReleaseDiff | null>(null); | ||
| const [loading, setLoading] = useState(false); | ||
| const [error, setError] = useState<Error | null>(null); | ||
|
|
||
| useEffect(() => { | ||
| let cancelled = false; | ||
|
|
||
| async function loadComparison() { | ||
| if (!fromVersion || !toVersion) { | ||
| setDiff(null); | ||
| setLoading(false); | ||
| setError(null); | ||
| return; | ||
| } | ||
| if (fromVersion === toVersion) { | ||
| setDiff(null); | ||
| setLoading(false); | ||
| setError(null); | ||
| return; | ||
| } | ||
|
|
||
| if (validVersions.length > 0) { | ||
| const fromIndex = validVersions.indexOf(fromVersion); | ||
| const toIndex = validVersions.indexOf(toVersion); | ||
| if (fromIndex === -1 || toIndex === -1 || fromIndex <= toIndex) { | ||
| setDiff(null); | ||
| setLoading(false); | ||
| setError(null); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| setLoading(true); | ||
| setError(null); | ||
| setDiff(null); // Clear previous diff to avoid showing stale data | ||
|
|
||
| try { | ||
| const [fromData, toData] = await Promise.all([ | ||
| javaagentData.loadAllInstrumentations(fromVersion), | ||
| javaagentData.loadAllInstrumentations(toVersion), | ||
| ]); | ||
|
|
||
| if (cancelled) return; | ||
|
|
||
| if (!fromData || !toData) { | ||
| throw new Error("Failed to load instrumentation data for one or both versions."); | ||
| } | ||
|
|
||
| const result = compareReleases(fromVersion, toVersion, fromData, toData); | ||
| setDiff(result); | ||
| setLoading(false); | ||
| } catch (err) { | ||
| if (!cancelled) { | ||
| setDiff(null); | ||
| setError(err instanceof Error ? err : new Error(String(err))); | ||
| setLoading(false); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| loadComparison(); | ||
|
|
||
| return () => { | ||
| cancelled = true; | ||
| }; | ||
| }, [fromVersion, toVersion, validVersions]); | ||
|
|
||
| return { diff, loading, error }; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.