Skip to main content
Glama
gitlab.ts7.61 kB
import { Gitlab } from '@gitbeaker/rest'; import { GitLabActivity } from '../types/index.js'; export class GitLabIntegration { private client: InstanceType<typeof Gitlab> | null = null; private userId: number | null = null; public debugInfo: any = null; // For debugging API responses async initialize(token: string, gitlabUrl: string = 'https://gitlab.com'): Promise<void> { this.client = new Gitlab({ token, host: gitlabUrl, }); // Get current user ID const user = await this.client.Users.showCurrentUser(); this.userId = user.id; } async getActivityForDate(dateStr: string): Promise<GitLabActivity> { if (!this.client || !this.userId) { throw new Error('GitLab client not initialized'); } // Validate YYYY-MM-DD format if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) { throw new Error('Date must be in YYYY-MM-DD format (e.g., "2025-12-01")'); } // Parse date to create Date object for activity const [year, month, day] = dateStr.split('-').map(Number); const date = new Date(year, month - 1, day); const activity: GitLabActivity = { date, commits: [], mergeRequests: [], issues: [], }; try { // Calculate week before date for API 'after' parameter const weekBeforeDate = new Date(year, month - 1, day); weekBeforeDate.setDate(weekBeforeDate.getDate() - 7); const wy = weekBeforeDate.getFullYear(); const wm = String(weekBeforeDate.getMonth() + 1).padStart(2, '0'); const wd = String(weekBeforeDate.getDate()).padStart(2, '0'); const afterDate = `${wy}-${wm}-${wd}`; // Fetch all pages of events let page = 1; const perPage = 100; // Max per page let hasMorePages = true; while (hasMorePages) { const events = await this.client.Events.all({ after: afterDate, page, perPage, }); // Store complete RAW API response if (page === 1) { this.debugInfo = { dateRange: { after: afterDate }, targetDay: dateStr, eventsReturned: events.length, rawApiResponse: events // ALL events, complete raw response }; } // If we get less than perPage, this is the last page if (events.length < perPage) { hasMorePages = false; } // Process events for (const event of events) { // Only process events by the current user if (event.author_id !== this.userId) continue; // Filter by date - extract YYYY-MM-DD from event timestamp const eventDateStr = (event.created_at as string).split('T')[0]; if (eventDateStr !== dateStr) continue; // Get project name (cached) const projectName = event.project_id && typeof event.project_id === 'number' ? await this.getProjectName(event.project_id) : 'Unknown'; // Process based on action type this.processEvent(event, projectName, activity); } page++; } } catch (error) { console.error('Error fetching GitLab activity:', error); } return activity; } private processEvent(event: any, projectName: string, activity: GitLabActivity): void { switch (event.action_name) { case 'pushed to': case 'pushed new': if (event.push_data && typeof event.push_data === 'object') { const pushData = event.push_data as any; activity.commits.push({ message: String(pushData.commit_title || 'Commit'), project: projectName, branch: String(pushData.ref || 'unknown'), }); } break; case 'opened': if (event.target_type === 'MergeRequest') { activity.mergeRequests.push({ action: 'created', title: String(event.target_title || 'MR'), project: projectName, id: event.target_iid, }); } else if (event.target_type === 'Issue') { activity.issues.push({ action: 'opened', title: String(event.target_title || 'Issue'), project: projectName, id: event.target_iid, }); } break; case 'commented on': // For comments, check the note.noteable_type instead of target_type // target_type can be "Note" or "DiffNote" which doesn't tell us what was commented on if (event.note && typeof event.note === 'object') { const noteableType = (event.note as any).noteable_type; const noteableIid = (event.note as any).noteable_iid; if (noteableType === 'MergeRequest') { activity.mergeRequests.push({ action: 'commented', title: String(event.target_title || 'MR'), project: projectName, id: noteableIid || event.target_iid, }); } else if (noteableType === 'Issue') { activity.issues.push({ action: 'commented', title: String(event.target_title || 'Issue'), project: projectName, id: noteableIid || event.target_iid, }); } } else { // Fallback to old logic if note object is not available if (event.target_type === 'MergeRequest') { activity.mergeRequests.push({ action: 'commented', title: String(event.target_title || 'MR'), project: projectName, id: event.target_iid, }); } else if (event.target_type === 'Issue') { activity.issues.push({ action: 'commented', title: String(event.target_title || 'Issue'), project: projectName, id: event.target_iid, }); } } break; case 'accepted': case 'approved': if (event.target_type === 'MergeRequest') { activity.mergeRequests.push({ action: 'approved', title: String(event.target_title || 'MR'), project: projectName, id: event.target_iid, }); } break; case 'closed': if (event.target_type === 'MergeRequest') { activity.mergeRequests.push({ action: 'closed', title: String(event.target_title || 'MR'), project: projectName, id: event.target_iid, }); } else if (event.target_type === 'Issue') { activity.issues.push({ action: 'closed', title: String(event.target_title || 'Issue'), project: projectName, id: event.target_iid, }); } break; case 'merged': if (event.target_type === 'MergeRequest') { activity.mergeRequests.push({ action: 'merged', title: String(event.target_title || 'MR'), project: projectName, id: event.target_iid, }); } break; } } private projectNameCache = new Map<number, string>(); private async getProjectName(projectId: number): Promise<string> { if (this.projectNameCache.has(projectId)) { return this.projectNameCache.get(projectId)!; } try { const project = await this.client!.Projects.show(projectId); this.projectNameCache.set(projectId, project.name); return project.name; } catch { return `Project ${projectId}`; } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sharadmathuratthepsi/timesheet-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server