Files
OpenNoodl/dev-docs/tasks/phase-3-editor-ux-overhaul/TASK-002B-github-advanced-integration/GIT-004A-COMPLETE.md
Richard Osborne ddcb9cd02e feat: Phase 5 BYOB foundation + Phase 3 GitHub integration
Phase 5 - BYOB Backend (TASK-007A/B):
- LocalSQL Adapter with full CloudStore API compatibility
- QueryBuilder translates Parse-style queries to SQL
- SchemaManager with PostgreSQL/Supabase export
- LocalBackendServer with REST endpoints
- BackendManager with IPC handlers for Electron
- In-memory fallback when better-sqlite3 unavailable

Phase 3 - GitHub Panel (GIT-004):
- Issues tab with list/detail views
- Pull Requests tab with list/detail views
- GitHub API client with OAuth support
- Repository info hook integration

Phase 3 - Editor UX Bugfixes (TASK-013):
- Legacy runtime detection banners
- Read-only enforcement for legacy projects
- Code editor modal close improvements
- Property panel stuck state fix
- Blockly node deletion and UI polish

Phase 11 - Cloud Functions Planning:
- Architecture documentation for workflow automation
- Execution history storage schema design
- Canvas overlay concept for debugging

Docs: Updated LEARNINGS.md and COMMON-ISSUES.md
2026-01-15 17:37:15 +01:00

11 KiB

GIT-004A: GitHub Client Foundation - COMPLETE

Status: Complete
Completed: January 14, 2026
Implementation Time: ~2 hours


Overview

Built a comprehensive GitHub REST API client layer on top of the existing OAuth authentication, providing type-safe access to GitHub's API with built-in rate limiting, caching, and error handling.


What Was Implemented

TypeScript Type Definitions

File: packages/noodl-editor/src/editor/src/services/github/GitHubTypes.ts

Comprehensive interfaces for all GitHub API data structures:

  • Core Types:

    • GitHubIssue - Issue data with labels, assignees, milestones
    • GitHubPullRequest - PR data with merge status and checks
    • GitHubRepository - Repository information with permissions
    • GitHubUser - User/author information
    • GitHubOrganization - Organization data
    • GitHubLabel - Issue/PR labels
    • GitHubMilestone - Project milestones
    • GitHubComment - Issue/PR comments
    • GitHubCommit - Commit information
    • GitHubCheckRun - CI/CD check runs
    • GitHubReview - PR review data
  • Utility Types:

    • GitHubRateLimit - Rate limit tracking
    • GitHubApiResponse<T> - Wrapper with rate limit info
    • GitHubIssueFilters - Query filters for issues/PRs
    • CreateIssueOptions - Issue creation parameters
    • UpdateIssueOptions - Issue update parameters
    • GitHubApiError - Error response structure

GitHub API Client

File: packages/noodl-editor/src/editor/src/services/github/GitHubClient.ts

Singleton service extending EventDispatcher with:

Authentication Integration:

  • Automatically initializes when user authenticates
  • Listens for auth state changes via EventDispatcher
  • Re-initializes Octokit when token refreshes
  • Clears cache on disconnection

Rate Limiting:

  • Tracks rate limit from response headers
  • Emits rate-limit-warning when approaching limit (10% remaining)
  • Emits rate-limit-updated on every API call
  • Provides getTimeUntilRateLimitReset() utility
  • User-friendly error messages when rate limited

Caching:

  • LRU cache with configurable TTL (default 30 seconds)
  • Max 100 cached entries
  • Cache invalidation on mutations (create/update/delete)
  • Pattern-based cache clearing
  • Per-method TTL customization (e.g., 5 minutes for labels)

Error Handling:

  • HTTP status code mapping to user-friendly messages
  • 401: "Please reconnect your GitHub account"
  • 403: Rate limit or permissions error
  • 404: Resource not found
  • 422: Validation errors with details
  • Proper error propagation with context

API Methods Implemented:

Repository Methods:

  • getRepository(owner, repo) - Get repo info
  • listRepositories(options) - List user repos

Issue Methods:

  • listIssues(owner, repo, filters) - List issues with filtering
  • getIssue(owner, repo, issue_number) - Get single issue
  • createIssue(owner, repo, options) - Create new issue
  • updateIssue(owner, repo, issue_number, options) - Update issue
  • listIssueComments(owner, repo, issue_number) - Get comments
  • createIssueComment(owner, repo, issue_number, body) - Add comment

Pull Request Methods:

  • listPullRequests(owner, repo, filters) - List PRs
  • getPullRequest(owner, repo, pull_number) - Get single PR
  • listPullRequestCommits(owner, repo, pull_number) - List PR commits

Label Methods:

  • listLabels(owner, repo) - List repo labels

Utility Methods:

  • getRateLimit() - Get current rate limit status
  • clearCache() - Clear all cached data
  • isReady() - Check if authenticated and ready
  • getTimeUntilRateLimitReset() - Time until limit resets

Public API Exports

File: packages/noodl-editor/src/editor/src/services/github/index.ts

Clean barrel export with:

  • GitHubOAuthService - OAuth authentication
  • GitHubClient - API client
  • All TypeScript interfaces and types
  • JSDoc examples for usage

Technical Architecture

Service Layer Structure

packages/noodl-editor/src/editor/src/services/
├── GitHubOAuthService.ts          # OAuth (existing)
└── github/
    ├── GitHubClient.ts            # API client (new)
    ├── GitHubTypes.ts             # Type definitions (new)
    └── index.ts                   # Public exports (new)

Integration Pattern

// GitHubClient listens to GitHubOAuthService
GitHubOAuthService.instance.on('auth-state-changed', (event) => {
  if (event.authenticated) {
    // Initialize Octokit with token
    GitHubClient.instance.initializeOctokit();
  }
});

// Usage in components
const client = GitHubClient.instance;
const { data: issues, rateLimit } = await client.listIssues('owner', 'repo', {
  state: 'open',
  labels: ['bug', 'enhancement'],
  sort: 'updated',
  direction: 'desc'
});

Cache Strategy

  • Read operations: Check cache first, API on miss
  • Write operations: Invalidate related caches
  • TTL defaults:
    • Issues/PRs: 30 seconds
    • Repository info: 1 minute
    • Labels: 5 minutes

Rate Limit Management

GitHub API limits:

  • Authenticated users: 5,000 requests/hour
  • Strategy: Track remaining, warn at 10%, cache aggressively

Type Safety Improvements

Before (Manual Typing)

const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/issues`);
const issues = await response.json(); // any

After (Type-Safe)

const { data: issues } = await client.listIssues(owner, repo); // GitHubIssue[]
issues.forEach((issue) => {
  console.log(issue.title); // TypeScript knows all properties
});

Event-Based Architecture

GitHubClient emits events for UI updates:

client.on('rate-limit-warning', ({ rateLimit }) => {
  toast.warning(`API rate limit low: ${rateLimit.remaining} requests remaining`);
});

client.on('rate-limit-updated', ({ rateLimit }) => {
  updateStatusBar(`GitHub API: ${rateLimit.remaining}/${rateLimit.limit}`);
});

API Filter Compatibility

GitHub API has different filter parameters for issues vs PRs. The client handles these differences:

Issues:

  • Supports milestone parameter (string or number)
  • Supports labels array (converted to comma-separated string)

Pull Requests:

  • No milestone filter
  • Different sort options (popularity, long-running vs comments)
  • Client maps sort: 'comments' to 'created' for PRs

Files Created

  1. packages/noodl-editor/src/editor/src/services/github/GitHubTypes.ts (298 lines)
  2. packages/noodl-editor/src/editor/src/services/github/GitHubClient.ts (668 lines)
  3. packages/noodl-editor/src/editor/src/services/github/index.ts (46 lines)

Total: 1,012 lines of production code


Dependencies

Already Installed

  • @octokit/rest@^20.1.2 - GitHub REST API client

No New Dependencies Required

All existing dependencies were sufficient.


Testing Plan

Manual Testing Checklist

  • Client initializes when authenticated
  • API calls work (list repos, issues, PRs)
  • Rate limit tracking updates correctly
  • Cache hit/miss behavior
  • Error handling (404, 403, 422)
  • Token refresh doesn't break active client
  • Disconnect clears cache and resets client

Future Unit Tests

describe('GitHubClient', () => {
  describe('caching', () => {
    it('returns cached data within TTL', async () => {
      // Mock API call
      // Call twice, verify API called once
    });

    it('invalidates cache on update', async () => {
      // Mock list issues
      // Update issue
      // Verify list cache cleared
    });
  });

  describe('rate limiting', () => {
    it('emits warning at threshold', async () => {
      // Mock response with low rate limit
      // Verify event emitted
    });
  });
});

Usage Examples

Basic Issue Listing

import { GitHubClient } from '@noodl-editor/services/github';

const client = GitHubClient.instance;

// Simple list
const { data: issues } = await client.listIssues('owner', 'repo');

// Filtered list
const { data: openBugs } = await client.listIssues('owner', 'repo', {
  state: 'open',
  labels: ['bug'],
  sort: 'updated',
  direction: 'desc',
  per_page: 25
});

Create Issue with Error Handling

try {
  const { data: newIssue } = await client.createIssue('owner', 'repo', {
    title: 'Bug: Component not rendering',
    body: 'Steps to reproduce:\n1. ...',
    labels: ['bug', 'priority-high'],
    assignees: ['username']
  });

  console.log(`Created issue #${newIssue.number}`);
} catch (error) {
  // User-friendly error message
  console.error(error.message); // "Invalid request: Title is required"
}

Monitor Rate Limit

client.on('rate-limit-updated', ({ rateLimit }) => {
  const percent = (rateLimit.remaining / rateLimit.limit) * 100;
  console.log(`GitHub API: ${percent.toFixed(1)}% remaining`);
});

client.on('rate-limit-warning', ({ rateLimit }) => {
  const resetTime = new Date(rateLimit.reset * 1000);
  alert(`GitHub rate limit low! Resets at ${resetTime.toLocaleTimeString()}`);
});

Success Criteria

  • GitHubClient service created with singleton pattern
  • Type-safe interfaces for all GitHub API responses
  • Rate limiting tracked and warnings emitted
  • Request caching with configurable TTL
  • Error handling with user-friendly messages
  • Integration with existing GitHubOAuthService
  • Clean public API via index.ts
  • EventDispatcher integration for React components
  • No new dependencies required

Next Steps

GIT-004B: Issues Panel (Read & Display)

Now that the API client foundation is in place, we can build UI components:

  1. Create GitHubPanel sidebar component
  2. Issues list with filtering UI
  3. Issue detail view with markdown rendering
  4. Search/filter functionality

Blocked Tasks Unblocked

  • GIT-004B (Issues Panel - Read)
  • GIT-004C (Pull Requests Panel)
  • GIT-004D (Create & Update Issues)
  • GIT-004E (Component Linking - depends on 004D)
  • GIT-004F (Dashboard Widgets)

Lessons Learned

  1. Octokit Type Compatibility:

    • GitHub API parameters have subtle differences between endpoints
    • Need to map/transform filters for issues vs PRs
    • Milestone can be string OR number depending on endpoint
  2. EventDispatcher Pattern:

    • Using Phase 0 best practices (.on() with context)
    • Clean integration for React components via useEventListener
  3. Cache Strategy:

    • 30-second default TTL balances freshness and API usage
    • Pattern-based invalidation prevents stale data
    • LRU eviction prevents memory growth
  4. Rate Limit UX:

    • Warning at 10% threshold gives users time to adjust
    • Time-until-reset calculation helps users plan
    • User-friendly error messages reduce frustration

Documentation Added

  • Comprehensive JSDoc comments on all public methods
  • Type definitions with property descriptions
  • Usage examples in index.ts
  • Architecture diagrams in this doc

References


Task Status: COMPLETE
Ready for: GIT-004B (Issues Panel UI)
Estimated time for 004B: 10-14 hours
Next session: Create sidebar panel component structure


Completed: January 14, 2026 22:11 UTC+1