Files
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

438 lines
11 KiB
Markdown

# 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
```typescript
// 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)
```typescript
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/issues`);
const issues = await response.json(); // any
```
### After (Type-Safe)
```typescript
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:
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
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
- [x] GitHubClient service created with singleton pattern
- [x] Type-safe interfaces for all GitHub API responses
- [x] Rate limiting tracked and warnings emitted
- [x] Request caching with configurable TTL
- [x] Error handling with user-friendly messages
- [x] Integration with existing GitHubOAuthService
- [x] Clean public API via index.ts
- [x] EventDispatcher integration for React components
- [x] 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
- [Octokit REST API Documentation](https://octokit.github.io/rest.js/)
- [GitHub REST API v3](https://docs.github.com/en/rest)
- [EventDispatcher Pattern (Phase 0)](../../phase-0-foundation-stabilisation/TASK-011-react-event-pattern-guide/)
---
**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_