Skip to content

Codeflow history API endpoint#5295

Open
adamzip wants to merge 19 commits intodotnet:mainfrom
adamzip:codeflow-history-controller
Open

Codeflow history API endpoint#5295
adamzip wants to merge 19 commits intodotnet:mainfrom
adamzip:codeflow-history-controller

Conversation

@adamzip
Copy link
Contributor

@adamzip adamzip commented Sep 23, 2025

No description provided.

@adamzip adamzip changed the title Codeflow history controller Codeflow history API endpoint Sep 23, 2025
@adamzip adamzip force-pushed the codeflow-history-controller branch from 375cfae to 6189c3d Compare November 16, 2025 15:17
@adamzip adamzip force-pushed the codeflow-history-controller branch from 590bba8 to cbe3261 Compare December 18, 2025 18:38
[JsonProperty("description")]
public string Description { get; }

[JsonProperty("sourceRepoFlowSha")]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to "codeflowSourceSha" ?

@adamzip adamzip marked this pull request as ready for review January 22, 2026 16:26
Copilot AI review requested due to automatic review settings January 22, 2026 16:26
@adamzip
Copy link
Contributor Author

adamzip commented Jan 22, 2026

Marking this as ready to get the PR summary from copilot

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new API endpoint for retrieving codeflow history between repositories and the VMR (Virtual Mono Repository), including a visualization component in the BarViz UI. The implementation adds Redis-based caching for commit history and integrates GitHub API calls to fetch blame and commit data for tracking forward flows and backflows.

Changes:

  • Adds /api/subscriptions/{id}/codeflowhistory endpoint to fetch and visualize code flow between repositories
  • Implements CodeflowHistoryManager with Redis caching for commit history and codeflow metadata
  • Extends DarcLib with new methods for fetching commits and determining last incoming flows via GitHub GraphQL and REST APIs

Reviewed changes

Copilot reviewed 17 out of 20 changed files in this pull request and generated 35 comments.

Show a summary per file
File Description
PullRequestUpdater.cs Adds placeholder TODO comments and unused Kusto.Ingest import for future codeflow synchronization
CodeflowHistoryManager.cs New service implementing Redis-based caching and GitHub API integration for codeflow history tracking
CodeflowHistoryResult.cs API response model for codeflow history data
SubscriptionsController.cs (v2018_07_16) Adds base implementation of GetCodeflowHistory endpoint with logic to fetch and format commit graphs
SubscriptionsController.cs (v2019_01_16) Adds commented-out endpoint stub for this API version
SubscriptionsController.cs (v2020_02_20) Adds public endpoint definition delegating to base implementation
SubscriptionDetailDialog.razor Replaces subscription details table with codeflow history graph visualization
CodeflowHistoryGraph.razor New SVG-based visualization component for rendering commit history with flow arrows
PcsStartup.cs Registers CodeflowHistoryManager as scoped service
Subscriptions.cs (Generated Client) Adds generated client method for calling the new API endpoint
CodeflowHistory.cs (Generated Models) Generated client models for API responses
IRemote.cs, IRemoteGitRepo.cs Extends interfaces with methods for fetching commits and determining last flows
Remote.cs Wrapper methods delegating to RemoteGitClient implementations
GitHubClient.cs Implements commit fetching, blame API calls, and codeflow detection via source manifests and version details
AzureDevOpsClient.cs Stub implementations throwing NotImplementedException
GitRepoFactory.cs, RemoteFactory.cs (multiple) Updates to pass IVersionDetailsParser dependency to GitHubClient
appsettings.Development.json Formatting-only change to comment alignment

ConfigurationOptions options) : ICodeflowHistoryManager
{
private readonly IRemoteFactory _remoteFactory = remoteFactory;
private readonly IConnectionMultiplexer _connection = ConnectionMultiplexer.Connect(options);
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IConnectionMultiplexer is created in the constructor but is never disposed. This class should implement IDisposable or IAsyncDisposable to properly dispose of the Redis connection, or the connection should be managed as a singleton with a different lifetime strategy.

Copilot uses AI. Check for mistakes.
Comment on lines +67 to +70
if (commitValues.Any(val => !val.HasValue))
{
await ClearCodeflowCacheAsync(subscriptionId);
throw new InvalidOperationException($"Corrupted commit data encountered for subscription `{subscriptionId}`.");
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache is cleared when corrupted commit data is encountered, but the exception thrown immediately after means the cache clear operation may not persist if there's a transaction or if the method caller doesn't handle this properly. Consider logging this condition and whether the cache clear should happen before or after throwing.

Copilot uses AI. Check for mistakes.
using Maestro.Data.Models;
using Microsoft.DotNet.DarcLib;
using Microsoft.DotNet.DarcLib.Models.VirtualMonoRepo;
using Pipelines.Sockets.Unofficial.Arenas;
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unused import Pipelines.Sockets.Unofficial.Arenas should be removed as it's not used anywhere in this file.

Suggested change
using Pipelines.Sockets.Unofficial.Arenas;

Copilot uses AI. Check for mistakes.
.Where(sub =>
sub.SourceRepository == subscription.TargetRepository
&& sub.TargetRepository == subscription.SourceRepository)
.FirstOrDefaultAsync(sub => sub.SourceEnabled == true);
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression 'A == true' can be simplified to 'A'.

Copilot uses AI. Check for mistakes.
{
var subscription = await _context.Subscriptions
.Include(sub => sub.LastAppliedBuild)
.FirstOrDefaultAsync(sub => sub.Id == id && sub.SourceEnabled == true);
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression 'A == true' can be simplified to 'A'.

Copilot uses AI. Check for mistakes.
new("b3n5n7", "Grace", "Remove unused code", null),
};

private List<CodeflowGraphCommit> _vmrCommits = new List<CodeflowGraphCommit>
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field '_vmrCommits' can be 'readonly'.

Suggested change
private List<CodeflowGraphCommit> _vmrCommits = new List<CodeflowGraphCommit>
private readonly List<CodeflowGraphCommit> _vmrCommits = new List<CodeflowGraphCommit>

Copilot uses AI. Check for mistakes.
return result;
}

private List<CodeflowGraphCommit> _repoCommits = new List<CodeflowGraphCommit>
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field '_repoCommits' can be 'readonly'.

Suggested change
private List<CodeflowGraphCommit> _repoCommits = new List<CodeflowGraphCommit>
private readonly List<CodeflowGraphCommit> _repoCommits = new List<CodeflowGraphCommit>

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +63
@((MarkupString)$"<text x=\"0\" y=\"-10\" fill=\"#000\">{_leftColumnName}</text>")
<line x1="60" y1="0" x2="60" y2="@((_leftColumn.Count - 1) * (BOX_HEIGHT + BOX_Y_MARGIN))" stroke="#bbb" stroke-width="1.5" />
@for (int i = 0; i < _leftColumn.Count; i++)
{
if (!_leftColumn[i].Contains("hidden"))
{
<use href="#commitBox" x="0" y="@(i * (BOX_HEIGHT + BOX_Y_MARGIN))" />
@((MarkupString)$"<text x=\"{BOX_WIDTH / 2}\" y=\"{i * (BOX_HEIGHT + BOX_Y_MARGIN) + 32}\" text-anchor=\"middle\" fill=\"#000\">{_leftColumn[i]}</text>")
;
}
else
{

<use href="#whiteBox" x="-20" y="@(i * (BOX_HEIGHT + BOX_Y_MARGIN))" />
var firstLine = _leftColumn[i].Split('(')[0];
var secondLine = "(" + _leftColumn[i].Split('(')[1];
@((MarkupString)$"<text x=\"{BOX_WIDTH / 2}\" y=\"{i * (BOX_HEIGHT + BOX_Y_MARGIN) + 22}\" text-anchor=\"middle\" fill=\"#000\">{firstLine}</text>")
;
@((MarkupString)$"<text x=\"{BOX_WIDTH / 2}\" y=\"{i * (BOX_HEIGHT + BOX_Y_MARGIN) + 42}\" text-anchor=\"middle\" fill=\"#000\">{secondLine}</text>")
;
}
}
</g>

<!-- RIGHT COLUMN -->
<g class="column right" transform="translate(@(COL_SPACE), 50)">
@((MarkupString)$"<text x=\"0\" y=\"-10\" fill=\"#000\">{_rightColumnName}</text>")
<line x1="60" y1="0" x2="60" y2="@((_rightColumn.Count - 1) * (BOX_HEIGHT + BOX_Y_MARGIN))" stroke="#bbb" stroke-width="1.5" />
@for (int i = 0; i < _rightColumn.Count; i++)
{
if (!_rightColumn[i].Contains("hidden"))
{
<use href="#commitBox" x="0" y="@(i * (BOX_HEIGHT + BOX_Y_MARGIN))" />
@((MarkupString)$"<text x=\"{BOX_WIDTH / 2}\" y=\"{i * (BOX_HEIGHT + BOX_Y_MARGIN) + 32}\" text-anchor=\"middle\" fill=\"#000\">{_rightColumn[i]}</text>")
;
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This component uses MarkupString to inject _leftColumnName, _rightColumnName, and commit IDs directly into SVG <text> elements without HTML encoding, while those values are populated from the CodeflowHistory API (e.g., RepoName, which ultimately comes from subscription configuration). An attacker who can control subscription data (such as SourceDirectory or TargetBranch) could supply values containing HTML like </text><script>...</script> and achieve stored XSS against any user viewing this graph. Replace MarkupString usage with standard Blazor text rendering or explicitly HTML-encode/sanitize all dynamic values before constructing markup so that user-controlled data cannot break out of the intended <text> context.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments