mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-03-08 01:53:30 +01:00
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
438 lines
11 KiB
Markdown
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_
|