Skip to content

Conversation

@ulrikandersen
Copy link
Contributor

Summary

  • Prevent the right drawer and toggle button from flashing during SSR/hydration
  • Add mounted state check so components only render after the client has mounted and session storage state has been properly read

Move repository-to-project mapping logic into a dedicated ProjectMapper
class. The mapper handles filtering, sorting, and all mapping transformations
with generic type support for provider-specific repository types.

This prepares the codebase for supporting multiple project source providers
by decoupling the mapping logic from the data source implementation.
- Add Microsoft Entra ID authentication via next-auth
- Implement AzureDevOpsClient for REST API interactions
- Add AzureDevOpsProjectDataSource and AzureDevOpsRepositoryDataSource
- Create unified IBlobProvider interface for file content fetching
- Support binary image files in blob API
- Configure via PROJECT_SOURCE_PROVIDER env var (github or azure-devops)
Add comprehensive tests for ProjectMapper, Azure DevOps client, blob
providers, and repository data sources. Fix AzureDevOpsError prototype
chain to ensure instanceof checks work correctly.
Add provider configuration section explaining how to use either GitHub
or Azure DevOps as the project source provider.
Remove duplicated mapping code by having AzureDevOpsProjectDataSource
use the shared ProjectMapper class with Azure DevOps-specific URL builders.

- Update AzureDevOpsRepositoryWithRefs to extend RepositoryWithRefs
- Create azureDevOpsURLBuilders for Azure DevOps URL generation
- Reduce AzureDevOpsProjectDataSource from 270 to 57 lines
Microsoft Entra ID access tokens are JWTs that can exceed 2000
characters, which doesn't fit in VARCHAR(255).
Copilot AI review requested due to automatic review settings December 8, 2025 13:33
@github-actions
Copy link

github-actions bot commented Dec 8, 2025

⚠️ It looks like .env.example has changed. Remember to update the Setting Environment Variables article accordingly.

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 pull request addresses two main objectives: fixing UI flashing during SSR/hydration and adding Azure DevOps support as an alternative to GitHub. The changes introduce a provider-based architecture that allows switching between GitHub and Azure DevOps through environment configuration while sharing common logic through a new ProjectMapper abstraction.

  • Prevents flash of right drawer and toggle button during SSR by deferring rendering until client mount
  • Refactors project mapping logic into a provider-agnostic ProjectMapper class with URL builder abstraction
  • Adds full Azure DevOps support including authentication (Microsoft Entra ID), repository access, and blob retrieval

Reviewed changes

Copilot reviewed 37 out of 37 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/features/sidebar/view/internal/tertiary/RightContainer.tsx Adds mounted state check to prevent SSR flash
src/features/sidebar/view/SecondarySplitHeader.tsx Adds mounted state check to prevent button flash
src/features/projects/domain/ProjectMapper.ts New abstraction for mapping repositories to projects with provider-specific URL builders
src/features/projects/domain/IAzureDevOpsRepositoryDataSource.ts Type definitions for Azure DevOps repository structure
src/features/projects/data/GitHubProjectDataSource.ts Refactored to use ProjectMapper with GitHub-specific URL builders
src/features/projects/data/AzureDevOpsRepositoryDataSource.ts Fetches and maps Azure DevOps repositories
src/features/projects/data/AzureDevOpsProjectDataSource.ts Azure DevOps project data source using ProjectMapper
src/common/azure-devops/AzureDevOpsClient.ts HTTP client for Azure DevOps REST API with auth error handling
src/common/azure-devops/OAuthTokenRefreshingAzureDevOpsClient.ts Wrapper client that auto-refreshes OAuth tokens on auth errors
src/common/azure-devops/AzureDevOpsError.ts Custom error type for Azure DevOps API failures
src/common/blob/GitHubBlobProvider.ts Blob provider implementation for GitHub
src/common/blob/AzureDevOpsBlobProvider.ts Blob provider implementation for Azure DevOps
src/features/auth/data/AzureDevOpsOAuthTokenRefresher.ts OAuth token refresher using Microsoft Entra ID endpoints
src/features/auth/domain/log-in/LogInHandler.ts Updated to handle both GitHub and Microsoft Entra ID providers
src/composition.ts Provider-based composition with conditional initialization
src/app/auth/signin/page.tsx Refactored sign-in page to support multiple providers
src/app/auth/signin/SignInButton.tsx New client component for provider-specific sign-in buttons
src/app/api/hooks/github/route.ts Added null check for GitHub webhook handler
src/app/api/diff/[owner]/[repository]/[...path]/route.ts Added null check for diff calculator
src/app/api/blob/[owner]/[repository]/[...path]/route.ts Refactored to use provider-agnostic blob provider
create-tables.sql Expanded token columns from VARCHAR(255) to TEXT for longer tokens
test/projects/ProjectMapper.test.ts Comprehensive tests for ProjectMapper logic
test/projects/testUtils.ts Shared test utilities for encryption and encoding
test/projects/GitHubProjectDataSource.test.ts Simplified tests now that ProjectMapper handles mapping logic
test/projects/AzureDevOpsRepositoryDataSource.test.ts Tests for Azure DevOps repository data source
test/projects/AzureDevOpsProjectDataSource.test.ts Tests for Azure DevOps project data source
test/common/blob/GitHubBlobProvider.test.ts Tests for GitHub blob provider
test/common/blob/AzureDevOpsBlobProvider.test.ts Tests for Azure DevOps blob provider
test/common/azure-devops/*.test.ts Comprehensive tests for Azure DevOps client components
README.md Updated documentation to mention Azure DevOps support
.env.example Added Azure DevOps configuration variables

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

return `https://github.com/${repository.owner}/${repository.name}/tree/${ref.name}`
},
getSpecEditUrl(repository: RepositoryWithRefs, ref: RepositoryRef, fileName: string): string {
return `https://github.com/${repository.owner}/${repository.name}/edit/${ref.name}/${encodeURIComponent(fileName)}`
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

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

The editURL uses simple encodeURIComponent on the entire filename, which is incorrect for paths with slashes. It should use per-segment encoding like getDiffUrl does. For example, a file like "apis/users.yml" would be incorrectly encoded as "apis%2Fusers.yml" instead of "apis/users.yml".

Consider changing line 27 to:

const encodedPath = fileName.split('/').map(segment => encodeURIComponent(segment)).join('/')
return `https://github.com/${repository.owner}/${repository.name}/edit/${ref.name}/${encodedPath}`

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +29
getSpecEditUrl(repository: AzureDevOpsRepositoryWithRefs, ref: RepositoryRef, fileName: string): string {
return `${repository.webUrl}?path=/${fileName}&version=GB${ref.name}&_a=contents`
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

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

The getSpecEditUrl doesn't encode the filename path, which could cause issues for files with special characters or spaces. Consider applying URL encoding per path segment similar to the GitHub implementation:

const encodedPath = fileName.split('/').map(segment => encodeURIComponent(segment)).join('/')
return `${repository.webUrl}?path=/${encodedPath}&version=GB${ref.name}&_a=contents`

Copilot uses AI. Check for mistakes.
async getFileContent(repositoryId: string, path: string, version: string): Promise<string | ArrayBuffer | null> {
try {
const oauthToken = await this.oauthTokenDataSource.getOAuthToken()
const url = `https://dev.azure.com/${this.organization}/_apis/git/repositories/${repositoryId}/items?path=${encodeURIComponent(path)}&versionDescriptor.version=${encodeURIComponent(version)}&api-version=${this.apiVersion}`
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

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

The path parameter in the URL is not properly encoded. This could cause issues for file paths containing special characters or spaces. Use encodeURIComponent for the path value:

const url = `https://dev.azure.com/${this.organization}/_apis/git/repositories/${repositoryId}/items?path=${encodeURIComponent(path)}&versionDescriptor.version=${encodeURIComponent(version)}&api-version=${this.apiVersion}`

Copilot uses AI. Check for mistakes.
Comment on lines +286 to +288
export function getBlobURL(owner: string, repository: string, path: string, ref: string): string {
return `/api/blob/${owner}/${repository}/${path}?ref=${ref}`
}
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

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

The getBlobURL function doesn't apply per-segment encoding to the path parameter, which is inconsistent with how paths are encoded elsewhere (e.g., in getDiffUrl). Files with slashes in subdirectories won't be correctly encoded. Consider updating to:

export function getBlobURL(owner: string, repository: string, path: string, ref: string): string {
  const encodedPath = path.split('/').map(segment => encodeURIComponent(segment)).join('/')
  return `/api/blob/${owner}/${repository}/${encodedPath}?ref=${ref}`
}

This matches the pattern established in the codebase memory.

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.

2 participants