diff --git a/README.md b/README.md index 2426d5b..6595533 100644 --- a/README.md +++ b/README.md @@ -1,143 +1,168 @@ + +
+ Typing SVG +
-# GitHub Explorer -GitHub Explorer is a modern web dashboard and Node.js API helper for exploring GitHub user profiles, repositories, activities, languages, commits, and contributions. It provides a beautiful UI and a modular API for developers. ---- +

+ One Interface. All Repositories. Infinite Possibilities. +

+ -## Table of Contents -- [Features](#features) -- [Architecture](#architecture) -- [Installation](#installation) -- [Configuration](#configuration) -- [Usage](#usage) -- [Testing](#testing) -- [Contributing](#contributing) -- [FAQ](#faq) -- [License](#license) +

+ + License + + + Docs + +

+ +

+ A futuristic framework that connects GitHub, GitLab, and more — all in one place.
+ Seamlessly transform raw API calls into elegant, UI-ready visual data with TypeScript and full analytics. +

--- -## Features -- View GitHub user profile, avatar, bio, followers, following, and repository count -- Explore repository details: description, language, stars, forks, license, homepage -- List and view commits, commit stats, and file diffs -- Browse repository files and folders, view file content with syntax highlighting -- Visualize language usage, commit stats, and monthly contributions with interactive charts -- Copy file contents to clipboard -- Responsive design with Bootstrap and custom styles -- Modular API helper for Node.js scripts and automation +📘 Full Documentation: +🔗 [Read the full guide here](https://luisotavio13.github.io/github-framework-documentation/) --- -## Architecture +## 📚 INDEX -**Frontend:** -- `index.html`: Main dashboard UI -- `style.css`: Custom styles for dark mode and responsive layout -- Uses Bootstrap, FontAwesome, Chart.js, Highlight.js +1️⃣ [Installation](#1-installation) +2️⃣ [Features](#2-features) +3️⃣ [Basic Usage](#3-basic-usage) +4️⃣ [Detailed Docs](#4-detailed-docs) +5️⃣ [Advanced Examples](#5-advanced-examples) +6️⃣ [Troubleshooting](#6-troubleshooting) +7️⃣ [Contributing](#7-contributing) +8️⃣ [Changelog](#8-changelog) +9️⃣ [License](#9-license) -**Backend/API Helper:** -- `src/`: Main source code - - `core/GitHubAPIHelper.js`: Main class, orchestrates all services - - `services/`: Modular service classes for user, repo, activity, language, commit, and contribution data - - `utils/`: Helper functions and formatters +--- -**Examples & Tests:** -- `examples/`: Example usage scripts for Node.js -- `tests/`: Jest test files for all modules -- `docs/`: Documentation files +## 1️⃣ INSTALLATION + +```bash +npm install @luisotavio13/github-framework@1.0.0 +``` --- -## Installation -1. Clone the repository: - ```sh - git clone https://github.com/your-username/github-explorer.git - cd github-explorer - ``` -2. Install dependencies: - ```sh - npm install - ``` +## 2️⃣ FEATURES + +✨ Unified APIs for GitHub, GitLab & more +✨ Full user and repository data +✨ Dynamic sorting for repositories +✨ Language usage graphs +✨ Contribution history & commit stats +✨ Ready-to-render UI components +✨ Strong TypeScript typing --- -## Configuration -Edit `config.json` with your GitHub username and (optionally) a personal access token: +## 3️⃣ BASIC USAGE -```json -{ - "githubUsername": "octocat", - "githubToken": "your_github_token" -} +```javascript +import GitHubAPIHelper from '@luisotavio13/github-framework'; + +const config = { + username: 'your-username', + token: 'your-token' // optional +}; + +const apiHelper = new GitHubAPIHelper(config); + +await apiHelper.loadAllData(); + +console.log(apiHelper.userData); +console.log(apiHelper.reposData); +console.log(apiHelper.languagesData); + +const profile = apiHelper.renderProfile(); +const repos = apiHelper.renderRepos('stars'); +const charts = apiHelper.renderCharts(); ``` -Tokens are optional for public data but recommended for higher rate limits and private repositories. +--- + +## 4️⃣ DETAILED DOCS + +Documentation lives in the [`/docs`](./docs) folder: + +- 📘 [API Reference](./docs/API.md) +- 📘 [GitLab Integration](./docs/gitlab.md) +- 📘 [TypeScript Models](./docs/ts-github-models.md) +- 📘 [Advanced Usage Guide](./docs/USAGE.md) +- 📘 [Troubleshooting](./docs/TROUBLESHOOTING.md) +- 📘 [Contributing](./docs/CONTRIBUTING.md) +- 📘 [Changelog](./docs/CHANGELOG.md) +- 📘 [Legal & Compliance](./docs/LEGAL.md) --- -## Usage - -### Web Dashboard -1. Open `index.html` in your browser -2. Explore your profile, repositories, activities, languages, commits, and contributions visually - -### Node.js Example -See `examples/basic-usage.js`: -```js -import GitHubAPIHelper from '../src/index.js'; -const config = { githubUsername: 'octocat', githubToken: '' }; -const github = new GitHubAPIHelper(config); -await github.loadAllData(); -console.log(github.renderProfile()); -console.log(github.renderRepos('stars')); -console.log(github.renderActivities()); +## 5️⃣ ADVANCED EXAMPLES + +📈 **Contribution Monitoring:** + +```javascript +const monthlyContributions = apiHelper.contributionsData; ``` -### API Reference -See [`docs/API.md`](API.md) for full details on all classes and methods. +🧪 **Top Used Languages:** + +```javascript +const topLanguages = Object.entries(apiHelper.languagesData) + .sort((a, b) => b[1] - a[1]) + .slice(0, 3); +``` --- -## Testing +## 6️⃣ TROUBLESHOOTING -Run all tests with Jest: -```sh -npx jest -``` -Test files are located in the `tests/` folder and cover all main modules and services. +See [TROUBLESHOOTING.md](./docs/TROUBLESHOOTING.md) for help with: + +- 🔐 Authentication errors +- 🚫 API rate limit exceeded +- 🔒 Private repositories +- 🌐 Proxy configuration --- -## Contributing +## 7️⃣ CONTRIBUTING -Contributions are welcome! To contribute: -1. Fork the repository -2. Create a new branch -3. Make your changes -4. Submit a pull request +We welcome contributions! Please read [CONTRIBUTING.md](./docs/CONTRIBUTING.md) for: -Please write clear commit messages and add tests for new features. +- Git workflow +- Code standards +- Test automation +- Documentation guidelines --- -## FAQ +## 8️⃣ CHANGELOG -**Q: Do I need a GitHub token?** -A: No, but it is recommended for higher rate limits and private data. +Track version history in: +📜 [CHANGELOG.md](./docs/CHANGELOG.md) -**Q: Can I use this as a library in my own Node.js scripts?** -A: Yes! Import `GitHubAPIHelper` from `src/index.js` and use its methods. +--- -**Q: How do I customize the dashboard UI?** -A: Edit `style.css` and `index.html` as needed. The code is modular and easy to extend. +## 9️⃣ LICENSE -**Q: What browsers are supported?** -A: All modern browsers (Chrome, Firefox, Edge, Safari). +Licensed under the **GNU GPL v3.0**. +Read the [LICENSE](./LICENSE) file. +For API legal info, see: [LEGAL.md](./docs/LEGAL.md) --- -## License +## 📬 SUPPORT + +For issues, suggestions or bug reports: +📮 Open an issue or contact: ✉️ roberdoogarcia@gmail.com + -GPL diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 89ec0e2..3d7ab91 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -1,29 +1,120 @@ -# Contributing Guide +# 🤝 Contributing Guide -Thank you for considering contributing to GitHub Explorer! +Thank you for your interest in contributing to the **GitHub Framework**! +We welcome pull requests, suggestions, and improvements of all kinds. -## How to Contribute -1. Fork the repository -2. Create a new branch for your feature or fix -3. Make your changes with clear commit messages -4. Add or update tests as needed -5. Submit a pull request with a detailed description +--- -## Code Style -- Use ES6+ JavaScript syntax -- Follow existing code formatting and naming conventions -- Write JSDoc comments for all public functions and classes +## 🧭 Table of Contents -## Testing -- Add or update Jest tests in the `tests/` folder -- Run `npx jest` before submitting your PR +- [How to Contribute](#how-to-contribute) +- [Development Workflow](#development-workflow) +- [Code Style Guide](#code-style-guide) +- [Testing & Coverage](#testing--coverage) +- [Documentation Updates](#documentation-updates) +- [Security Policy](#security-policy) +- [Code of Conduct](#code-of-conduct) -## Issues -- If you find a bug or have a feature request, open an issue with details and steps to reproduce +--- -## Community -- Be respectful and constructive in discussions -- Help review and test other contributions +## ✅ How to Contribute -## License -GPL +1. **Fork** the repository. +2. Create a **new branch**: + ```bash + git checkout -b feature/my-feature + ``` +3. **Make your changes** and write tests if needed. +4. Run tests locally to verify. +5. **Commit** using clear, conventional messages: + ``` + feat: add support for GitHub reactions + fix: handle 404 errors on repo fetch + ``` +6. Push to your fork and **create a pull request**. + +--- + +## 🧪 Development Workflow + +```bash +# Install dependencies +npm install + +# Run in development mode +npm run dev + +# Build for production +npm run build + +# Run tests +npm run test +``` + +Please ensure that your PR passes **all tests and lint checks**. + +--- + +## 🎨 Code Style Guide + +- Language: **TypeScript** +- Format: Prettier + ESLint +- Naming: camelCase for variables, PascalCase for classes +- Folder structure follows `src/`, `adapters/`, and `types/` +- Comments are encouraged for complex logic + +Use: +```bash +npm run lint +npm run format +``` + +--- + +## 🧪 Testing & Coverage + +We use **Jest** for testing. + +- Place unit tests in `__tests__/` +- Test coverage goal: **80%+** +- Test new features and edge cases + +Run: +```bash +npm run test +``` + +--- + +## 📘 Documentation Updates + +If your contribution affects how the framework is used, update the relevant docs in: + +- [`README.md`](../README.md) +- [`/docs/USAGE.md`](./USAGE.md) +- [`/docs/API.md`](./API.md) + +Use clear, concise language and include code examples where useful. + +--- + +## 🔐 Security Policy + +If you discover a security vulnerability, **do not** open a public issue. +Instead, contact us directly: +✉️ `roberdoogarcia@gmail.com` + +--- + +## 🌐 Code of Conduct + +We follow the [Contributor Covenant](https://www.contributor-covenant.org/) Code of Conduct. + +- Be respectful and inclusive +- Assume good intent +- No harassment or abuse of any kind + +--- + +Thank you for making this project better! +Every contribution, large or small, helps move it forward 🚀 diff --git a/docs/LEGAL.md b/docs/LEGAL.md new file mode 100644 index 0000000..f410c98 --- /dev/null +++ b/docs/LEGAL.md @@ -0,0 +1,68 @@ +# 🛡️ Legal & API Compliance + +This document outlines the legal considerations and API compliance requirements related to the use of the GitHub Framework. + +--- + +## 📑 License + +This project is licensed under the **GNU General Public License v3.0 (GPL-3.0)**. +You are free to use, modify, and distribute the framework under the terms of this license. + +- Full license text: [LICENSE](../LICENSE) +- SPDX Identifier: `GPL-3.0-only` + +--- + +## 🔐 API Token Handling + +- Always store personal access tokens (PATs) in secure environments (e.g., `.env` files, vaults). +- Do **not** commit secrets to your version control system. +- The framework does **not** log or store tokens persistently. +- Using tokens improves rate limits and access to private data. + +--- + +## 🤝 API Terms of Use + +By using this framework, you agree to comply with the terms and usage policies of the respective APIs you integrate: + +### GitHub +- API Terms: [https://docs.github.com/en/site-policy/github-terms/github-terms-of-service](https://docs.github.com/en/site-policy/github-terms/github-terms-of-service) +- Rate limits: 5,000 req/hour with token; 60 req/hour without + +### GitLab +- API Terms: [https://about.gitlab.com/terms/](https://about.gitlab.com/terms/) +- Rate limits may vary by instance (GitLab.com or self-hosted) + +You are responsible for staying within usage boundaries and respecting rate limits. + +--- + +## 📡 Data Collection and Privacy + +- The framework does **not** transmit data to third-party servers. +- All API data is retrieved client-side or server-side based on your environment. +- The developer assumes responsibility for handling user data ethically and legally. + +If you build dashboards or store data persistently, ensure you comply with: + +- Local data protection laws (e.g., LGPD, GDPR) +- GitHub/GitLab data usage restrictions +- User consent if applicable + +--- + +## 🧪 External Dependencies + +This project may use open-source libraries under permissive licenses (MIT, Apache 2.0). +Ensure that usage and distribution comply with those licenses. All dependencies are listed in `package.json`. + +--- + +## ❗ Disclaimer + +This framework is provided **as-is** without warranty. +You are solely responsible for ensuring that your use of it is compliant with all relevant laws and API provider rules. + +For legal concerns, contact the project maintainer or consult a legal advisor. diff --git a/docs/USAGE.md b/docs/USAGE.md index 73f49f1..10169c7 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -1,32 +1,142 @@ -# Usage Guide +# ⚙️ Advanced Usage Guide -## Web Dashboard -- Open `index.html` in your browser -- Configure your GitHub username and token in `config.json` -- Explore profile, repositories, activities, languages, commits, and contributions visually +This guide provides practical examples and advanced techniques for using the `GitHubAPIHelper` class effectively in real-world applications. + +--- + +## 📥 Initialization + +```ts +import GitHubAPIHelper from '@luisotavio13/github-framework'; + +const apiHelper = new GitHubAPIHelper({ + username: 'your-username', + token: 'your-token' // optional +}); + +await apiHelper.loadAllData(); +``` + +The `loadAllData()` method fetches: + +- `userData` — profile and statistics +- `reposData` — all public (and private, if token is provided) repositories +- `languagesData` — aggregated language usage + +--- + +## 📌 Filtering Repositories + +You can filter and sort repositories by language, stars, forks, etc. + +```ts +const jsRepos = apiHelper.reposData.filter( + repo => repo.language === 'JavaScript' +); + +const topStarred = [...apiHelper.reposData].sort( + (a, b) => b.stars - a.stars +).slice(0, 5); +``` + +--- + +## 📈 Language Statistics + +```ts +const topLanguages = Object.entries(apiHelper.languagesData) + .sort((a, b) => b[1] - a[1]) + .slice(0, 3) + .map(([lang, count]) => `${lang}: ${count}`); +``` + +This is useful for UI dashboards, charts, and custom reports. + +--- + +## 📊 Rendering UI Components + +The framework includes built-in methods for rendering components as HTML or strings: + +```ts +const profileHTML = apiHelper.renderProfile(); +const reposHTML = apiHelper.renderRepos('stars'); // 'forks', 'name', etc. +const chartsHTML = apiHelper.renderCharts(); +``` + +These methods return raw HTML. You can inject them directly into your app or static site: -## Node.js Example -See `examples/basic-usage.js`: ```js -import GitHubAPIHelper from '../src/index.js'; -const config = { githubUsername: 'TylorSwift2', githubToken: '' }; -const github = new GitHubAPIHelper(config); -await github.loadAllData(); -console.log(github.renderProfile()); -console.log(github.renderRepos('stars')); -console.log(github.renderActivities()); +document.getElementById('profile').innerHTML = profileHTML; +``` + +--- + +## 📅 Contribution History + +```ts +const contributions = apiHelper.contributionsData; + +Object.entries(contributions).forEach(([month, count]) => { + console.log(`${month}: ${count} commits`); +}); +``` + +The object returned is keyed by `YYYY-MM` with number of commits. + +--- + +## 🧩 Using With Frameworks + +### React Example: + +```tsx +import { useEffect, useState } from 'react'; +import GitHubAPIHelper from '@luisotavio13/github-framework'; + +export function ProfileWidget() { + const [html, setHtml] = useState(''); + + useEffect(() => { + const api = new GitHubAPIHelper({ username: 'devuser' }); + api.loadAllData().then(() => { + setHtml(api.renderProfile()); + }); + }, []); + + return
; +} +``` + +--- + +## ⚠️ Error Handling + +Wrap async usage in `try/catch` to handle: + +```ts +try { + await apiHelper.loadAllData(); +} catch (err) { + console.error('Failed to load data:', err); +} ``` -## Testing -- Run `npm install` to install Jest -- Run `npx jest` to execute all tests in the `tests/` folder +--- + +## 🧪 Extending Integrations + +You can extend the framework to support more platforms (e.g., Bitbucket): + +- Create a new adapter in `src/adapters/bitbucketAdapter.ts` +- Implement the same methods used in `githubAdapter` and `gitlabAdapter` +- Register the adapter via factory pattern + +--- -## Customization -- Edit `style.css` for custom styles -- Extend service classes in `src/services/` for new features +## 💬 Have Questions? -## Troubleshooting -- Ensure your GitHub token is valid for private data or higher rate limits -- Check browser console or Node.js logs for error messages +Open an issue or [contact support](mailto:luisotavio@example.com). +Check `/docs/TROUBLESHOOTING.md` if something isn’t working as expected. -GPL +--- diff --git a/docs/gitlab.md b/docs/gitlab.md new file mode 100644 index 0000000..2babfd1 --- /dev/null +++ b/docs/gitlab.md @@ -0,0 +1,141 @@ +# 📚 GitLab Integration: Developer Documentation + +## ⚙️ Overview +This GitLab integration suite allows you to fetch and display detailed user and repository data using GitLab’s REST API. It’s organized into modular service classes that separate responsibilities for maintainability and clarity. + +--- + +## 🧩 Components + +### 1. `GitLabUserService` +**Purpose**: Retrieve GitLab user profile and contribution statistics. + +**Key Methods**: +- `loadUserData()` — Fetches user data and contribution stats. +- `renderProfile()` — Formats user profile info for display. + +**Returns**: +```json +{ + avatarUrl: "", + name: "", + bio: "", + followers: 0, + following: 0, + publicRepos: 0, + stats: {} +} +``` + +--- + +### 2. `GitLabRepoService` +**Purpose**: Manage and explore GitLab repositories. + +**Key Methods**: +- `loadReposData(sort, per_page)` — Retrieves repositories. +- `loadRepoFiles(repoId, path, ref)` — Lists files in a repo. +- `loadFileContent(repoId, filePath, ref)` — Gets raw file content. +- `renderRepos(sort)` — Formats repos for display. + +**Sort Options**: `stars`, `forks`, `updated` + +**Returns**: +```json +{ + id, name, description, language, + stars, forks, updatedAt, url +} +``` + +--- + +### 3. `GitLabActivityService` +**Purpose**: Track user activity (pushes, merges, creations). + +**Key Methods**: +- `loadActivities(per_page)` — Gets recent activity. +- `renderActivities()` — Formats it for UI. + +**Returns**: +```json +{ + text, icon, date +} +``` + +--- + +### 4. `GitLabCommitService` +**Purpose**: Access commit history and details. + +**Key Methods**: +- `loadCommitsData(reposData, per_page)` — Commit count per repo. +- `loadRepoCommits(repoId, page, per_page)` — Paginated commits. +- `loadCommitDetails(repoId, sha)` — Extended info. + +**Returns**: +```json +{ + sha, message, author, date, url, + stats: { additions, deletions, total }, + files: [] +} +``` + +--- + +### 5. `GitLabContributionService` +**Purpose**: Summarize push contributions by month. + +**Key Methods**: +- `loadContributionsData(year)` — Count pushes monthly. + +**Returns**: +```json +{ + Jan: 0, Feb: 0, ..., Dec: 0 +} +``` + +--- + +### 6. `GitLabLanguageService` +**Purpose**: Analyze language usage by repo size. + +**Key Methods**: +- `loadLanguagesData(reposData)` — Aggregates usage data. + +**Returns**: +```json +{ + JavaScript: 123456, + Python: 78901, + ... +} +``` + +--- + +## 🛠 Required Utilities + +From `../../utils/helpers.js`: +- `getAuthHeaders(token)` — Returns headers. +- `formatFileSize(bytes)` — Human-readable size. + +--- + +## 🚀 Getting Started + +```js +const config = { + username: 'yourUsername', + token: 'yourAccessToken', + baseUrl: 'https://gitlab.com/api/v4' +}; + +const userService = new GitLabUserService(config); +await userService.loadUserData(); +console.log(userService.renderProfile()); +``` +s \ No newline at end of file diff --git a/examples/example_04/index.js b/examples/example_04/index.js new file mode 100644 index 0000000..5580c3c --- /dev/null +++ b/examples/example_04/index.js @@ -0,0 +1,60 @@ +import { + GitLabUserService, + GitLabRepoService, + GitLabLanguageService, + GitLabContributionService, + GitLabActivityService, + GitLabCommitService +} from '../../src/index.js'; + +// Configuration for the GitLab API +const config = { + username: 'your_gitlab_username', + token: '', // Can be left empty if public access is sufficient + baseUrl: 'https://gitlab.com/api/v4' +}; + +async function buildGitLabDashboard() { + try { + // 1. Fetch basic user profile info (e.g., name, avatar, bio) + const userService = new GitLabUserService(config); + await userService.loadUserData(); + const profile = userService.renderProfile(); + console.log('User Profile:', profile); + + // 2. Fetch user repositories and sort by a specific criteria + const repoService = new GitLabRepoService(config); + await repoService.loadReposData('updated'); // Sort options: 'updated', 'stars', 'forks' + const repositories = repoService.renderRepos(); + console.log('Repositories:', repositories); + + // 3. Analyze programming languages used in those repositories + const languageService = new GitLabLanguageService(config); + await languageService.loadLanguagesData(repoService.reposData); + const languages = languageService.languagesData; + console.log('Languages Used:', languages); + + // 4. Retrieve recent activities like issues, merge requests, etc. + const activityService = new GitLabActivityService(config); + await activityService.loadActivities(); + const activities = activityService.renderActivities(); + console.log('Recent Activities:', activities); + + // 5. Fetch commit statistics for each repository + const commitService = new GitLabCommitService(config); + await commitService.loadCommitsData(repoService.reposData); + const commitsByRepo = commitService.commitsData; + console.log('Commits by Repository:', commitsByRepo); + + // 6. Retrieve user contributions grouped by month for the current year + const contributionService = new GitLabContributionService(config); + await contributionService.loadContributionsData(); // Defaults to the current year + const monthlyContributions = contributionService.contributionsData; + console.log('Monthly Contributions:', monthlyContributions); + + } catch (error) { + console.error('Error building GitLab dashboard:', error); + } +} + +buildGitLabDashboard(); diff --git a/package-lock.json b/package-lock.json index 65732e0..5b63eef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.1", "license": "GPL-3.0-only", "dependencies": { - "axios": "^1.6.7" + "axios": "^1.11.0" }, "devDependencies": { "@babel/core": "^7.28.0", @@ -3100,13 +3100,13 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", - "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, diff --git a/package.json b/package.json index b52673b..c0b603d 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "access": "public" }, "dependencies": { - "axios": "^1.6.7" + "axios": "^1.11.0" }, "devDependencies": { "@babel/core": "^7.28.0", diff --git a/src/core/BaseAPIHelper.js b/src/core/BaseAPIHelper.js new file mode 100644 index 0000000..1576131 --- /dev/null +++ b/src/core/BaseAPIHelper.js @@ -0,0 +1,65 @@ +/** + * Classe base para API Helpers + */ +export class BaseAPIHelper { + constructor(config) { + if (!config || !config.username) { + throw new Error('Nome de usuário é obrigatório'); + } + + this.config = { + username: config.username, + token: config.token || null, + baseUrl: config.baseUrl || null + }; + + // Serviços que serão implementados + this.userService = null; + this.repoService = null; + this.activityService = null; + this.languageService = null; + this.commitService = null; + this.contributionService = null; + + // Estado atual + this.currentRepo = null; + this.currentPath = ''; + this.currentCommit = null; + } + + async loadAllData() { + throw new Error('Método loadAllData() deve ser implementado'); + } + + // Getters + get userData() { return this.userService?.userData; } + get reposData() { return this.repoService?.reposData; } + get activitiesData() { return this.activityService?.activitiesData; } + get languagesData() { return this.languageService?.languagesData; } + get commitsData() { return this.commitService?.commitsData; } + get contributionsData() { return this.contributionService?.contributionsData; } + + // Métodos de renderização + renderProfile() { return this.userService?.renderProfile(); } + renderActivities() { return this.activityService?.renderActivities(); } + renderRepos(sort) { return this.repoService?.renderRepos(sort); } + + renderCharts() { + return { + languages: { + labels: Object.keys(this.languagesData || {}), + data: Object.values(this.languagesData || {}) + }, + commits: { + labels: Object.keys(this.commitsData || {}).slice(0, 10), + data: Object.values(this.commitsData || {}).slice(0, 10) + }, + contributions: { + labels: Object.keys(this.contributionsData || {}), + data: Object.values(this.contributionsData || {}) + } + }; + } +} + +export default BaseAPIHelper; \ No newline at end of file diff --git a/src/core/GitHubAPIHelper.js b/src/core/GitHubAPIHelper.js index 4c2a133..8ecd6f4 100644 --- a/src/core/GitHubAPIHelper.js +++ b/src/core/GitHubAPIHelper.js @@ -1,70 +1,32 @@ -/** - * @license - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -import UserService from '../services/UserService.js'; -import RepoService from '../services/RepoService.js'; -import ActivityService from '../services/ActivityService.js'; -import LanguageService from '../services/LanguageService.js'; -import CommitService from '../services/CommitService.js'; -import ContributionService from '../services/ContributionService.js'; -/** - * GitHubAPIHelper - Main facade class for GitHub API operations - * - * Provides a unified interface to access GitHub user data, repositories, - * activities, languages, commits and contributions through specialized services. - * Implements the Facade design pattern to simplify complex API interactions. - */ -class GitHubAPIHelper { - /** - * Constructs a new GitHubAPIHelper instance - * @param {Object} config Configuration object - * @param {string} config.githubUsername GitHub username (required) - * @param {string} [config.githubToken] GitHub personal access token (optional) - * @throws {Error} When githubUsername is not provided - */ +import BaseAPIHelper from './BaseAPIHelper.js'; +import GitHubUserService from '../services/github/UserService.js'; +import GitHubRepoService from '../services/github/RepoService.js'; +import GitHubActivityService from '../services/github/ActivityService.js'; +import GitHubLanguageService from '../services/github/LanguageService.js'; +import GitHubCommitService from '../services/github/CommitService.js'; +import GitHubContributionService from '../services/github/ContributionService.js'; + +class GitHubAPIHelper extends BaseAPIHelper { constructor(config) { - if (!config || !config.githubUsername) { - throw new Error('GitHub username is required'); - } - - this.config = { - githubUsername: config.githubUsername, - githubToken: config.githubToken || null + super({ + username: config.username, + token: config.token + }); + + // Mapeia para os campos esperados pelos serviços internos + const serviceConfig = { + githubUsername: config.username, + githubToken: config.token }; - // Initialize specialized services - this.userService = new UserService(this.config); - this.repoService = new RepoService(this.config); - this.activityService = new ActivityService(this.config); - this.languageService = new LanguageService(this.config); - this.commitService = new CommitService(this.config); - this.contributionService = new ContributionService(this.config); - - // Current state tracking - this.currentRepo = null; - this.currentPath = ''; - this.currentCommit = null; + this.userService = new GitHubUserService(serviceConfig); + this.repoService = new GitHubRepoService(serviceConfig); + this.activityService = new GitHubActivityService(serviceConfig); + this.languageService = new GitHubLanguageService(serviceConfig); + this.commitService = new GitHubCommitService(serviceConfig); + this.contributionService = new GitHubContributionService(serviceConfig); } - /** - * Loads all available GitHub data for the user - * @async - * @returns {Promise} True if all data loaded successfully - * @throws {Error} If any data loading fails - */ async loadAllData() { try { await this.userService.loadUserData(); @@ -75,140 +37,10 @@ class GitHubAPIHelper { await this.contributionService.loadContributionsData(); return true; } catch (err) { - console.error('Error loading data:', err); + console.error('Erro ao carregar dados do GitHub:', err); throw err; } } - - /* Data Accessors */ - - /** - * Gets user profile data - * @returns {Object} User profile information - */ - get userData() { return this.userService.userData; } - - /** - * Gets repositories data - * @returns {Array} List of repositories - */ - get reposData() { return this.repoService.reposData; } - - /** - * Gets user activities data - * @returns {Array} List of user activities - */ - get activitiesData() { return this.activityService.activitiesData; } - - /** - * Gets language statistics - * @returns {Object} Language usage data - */ - get languagesData() { return this.languageService.languagesData; } - - /** - * Gets commits data - * @returns {Object} Commits information - */ - get commitsData() { return this.commitService.commitsData; } - - /** - * Gets contributions data - * @returns {Object} Contribution statistics - */ - get contributionsData() { return this.contributionService.contributionsData; } - - /* Rendering Methods */ - - /** - * Renders user profile for display - * @returns {Object} Formatted profile data - */ - renderProfile() { return this.userService.renderProfile(); } - - /** - * Renders user activities for display - * @returns {Array} Formatted activities - */ - renderActivities() { return this.activityService.renderActivities(); } - - /** - * Renders repositories for display - * @param {string} [sort='updated'] Sorting method ('updated', 'stars', 'forks') - * @returns {Array} Formatted repositories - */ - renderRepos(sort) { return this.repoService.renderRepos(sort); } - - /** - * Prepares chart data for visualization - * @returns {Object} Chart-ready data including: - * - languages: {labels, data} - * - commits: {labels, data} - * - contributions: {labels, data} - */ - renderCharts() { - return { - languages: { - labels: Object.keys(this.languagesData), - data: Object.values(this.languagesData) - }, - commits: { - labels: Object.keys(this.commitsData).slice(0, 10), - data: Object.values(this.commitsData).slice(0, 10) - }, - contributions: { - labels: Object.keys(this.contributionsData), - data: Object.values(this.contributionsData) - } - }; - } - - /* Repository Operations */ - - /** - * Loads commits for a specific repository - * @async - * @param {string} repoName Repository name - * @param {number} [page=1] Page number - * @param {number} [per_page=10] Commits per page - * @returns {Promise>} List of commits - */ - async loadRepoCommits(repoName, page = 1, per_page = 10) { - return this.commitService.loadRepoCommits(repoName, page, per_page); - } - - /** - * Loads files for a repository path - * @async - * @param {string} repoName Repository name - * @param {string} [path=''] Path within repository - * @returns {Promise>} List of files/directories - */ - async loadRepoFiles(repoName, path = '') { - return this.repoService.loadRepoFiles(repoName, path); - } - - /** - * Loads content of a specific file - * @async - * @param {string} repoName Repository name - * @param {string} filePath File path - * @returns {Promise} File content - */ - async loadFileContent(repoName, filePath) { - return this.repoService.loadFileContent(repoName, filePath); - } - - /** - * Loads details for a specific commit - * @async - * @param {string} repoName Repository name - * @param {string} sha Commit SHA - * @returns {Promise} Detailed commit information - */ - async loadCommitDetails(repoName, sha) { - return this.commitService.loadCommitDetails(repoName, sha); - } } export default GitHubAPIHelper; \ No newline at end of file diff --git a/src/core/GitLabAPIHelper.js b/src/core/GitLabAPIHelper.js new file mode 100644 index 0000000..d3293a7 --- /dev/null +++ b/src/core/GitLabAPIHelper.js @@ -0,0 +1,42 @@ +// src/core/GitLabAPIHelper.js +import BaseAPIHelper from './BaseAPIHelper.js'; +import GitLabUserService from '../services/gitlab/GitLabUserService.js'; +import GitLabRepoService from '../services/gitlab/GitLabRepoService.js'; +import GitLabActivityService from '../services/gitlab/GitLabActivityService.js'; +import GitLabLanguageService from '../services/gitlab/GitLabLanguageService.js'; +import GitLabCommitService from '../services/gitlab/GitLabCommitService.js'; +import GitLabContributionService from '../services/gitlab/GitLabContributionService.js'; + +class GitLabAPIHelper extends BaseAPIHelper { + constructor(config) { + super({ + username: config.username, + token: config.token, + baseUrl: config.baseUrl || 'https://gitlab.com/api/v4' + }); + + // Inicializa serviços do GitLab + this.userService = new GitLabUserService(this.config); + this.repoService = new GitLabRepoService(this.config); + this.activityService = new GitLabActivityService(this.config); + this.languageService = new GitLabLanguageService(this.config); + this.commitService = new GitLabCommitService(this.config); + this.contributionService = new GitLabContributionService(this.config); + } + async loadAllData() { + try { + await this.userService.loadUserData(); + await this.repoService.loadReposData(); + await this.activityService.loadActivities(); + await this.languageService.loadLanguagesData(this.repoService.reposData); + await this.commitService.loadCommitsData(this.repoService.reposData); + await this.contributionService.loadContributionsData(); + return true; + } catch (err) { + console.error('Erro ao carregar dados do GitLab:', err); + throw err; + } + } +} + +export default GitLabAPIHelper; \ No newline at end of file diff --git a/src/index.d.ts b/src/index.d.ts index 33d026f..675e6d6 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -33,11 +33,11 @@ import GitHubAPIHelper from './core/GitHubAPIHelper'; * Individual services that handle specific GitHub data domains */ import UserService from './services/UserService'; // Handles user profile data -import RepoService from './services/RepoService'; // Manages repository data -import ActivityService from './services/ActivityService'; // Processes user activities -import LanguageService from './services/LanguageService'; // Analyzes language usage -import CommitService from './services/CommitService'; // Tracks commit statistics -import ContributionService from './services/ContributionService'; // Manages contribution data +import RepoService from './services/github/RepoService'; // Manages repository data +import ActivityService from './services/github/ActivityService'; // Processes user activities +import LanguageService from './services/github/LanguageService'; // Analyzes language usage +import CommitService from './services/github/CommitService'; // Tracks commit statistics +import ContributionService from './services/github/ContributionService'; // Manages contribution data /** * Utility Modules diff --git a/src/index.js b/src/index.js index 579eb7c..5c4e3de 100644 --- a/src/index.js +++ b/src/index.js @@ -13,40 +13,71 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + /** - * @file Main entry point for GitHub API Helper framework - * @module GitHubAPIHelper + * @file Main entry point for API Helper framework + * @module APIHelper * @description * Exports all framework components including: - * - Main GitHubAPIHelper class - * - Individual service classes + * - Main GitHub and GitLab API Helper classes + * - Individual service classes for both platforms * - Utility functions * * Provides both default and named exports for flexible importing. */ import GitHubAPIHelper from './core/GitHubAPIHelper.js'; +import GitLabAPIHelper from './core/GitLabAPIHelper.js'; /** - * Main GitHub API Helper class (default export) + * Main GitHub API Helper class (default export remains GitHub for backward compatibility) * @see GitHubAPIHelper */ export default GitHubAPIHelper; /** - * Individual service classes (named exports) - * @namespace Services + * Main GitLab API Helper class + * @see GitLabAPIHelper + */ +export { GitLabAPIHelper }; + +/** + * Individual GitHub service classes (named exports) + * @namespace GitHubServices + */ +export { default as GitHubUserService } from './services/github/UserService.js'; +export { default as GitHubRepoService } from './services/github/RepoService.js'; +export { default as GitHubActivityService } from './services/github/ActivityService.js'; +export { default as GitHubLanguageService } from './services/github/LanguageService.js'; +export { default as GitHubCommitService } from './services/github/CommitService.js'; +export { default as GitHubContributionService } from './services/github/ContributionService.js'; + +/** + * Individual GitLab service classes (named exports) + * @namespace GitLabServices */ -export { default as UserService } from './services/UserService.js'; // Handles user profile data -export { default as RepoService } from './services/RepoService.js'; // Manages repository operations -export { default as ActivityService } from './services/ActivityService.js'; // Processes user activities -export { default as LanguageService } from './services/LanguageService.js'; // Analyzes language statistics -export { default as CommitService } from './services/CommitService.js'; // Handles commit-related operations -export { default as ContributionService } from './services/ContributionService.js'; // Manages contribution data +export { default as GitLabUserService } from './services/gitlab/GitLabUserService.js'; +export { default as GitLabRepoService } from './services/gitlab/GitLabRepoService.js'; +export { default as GitLabActivityService } from './services/gitlab/GitLabActivityService.js'; +export { default as GitLabLanguageService } from './services/gitlab/GitLabLanguageService.js'; +export { default as GitLabCommitService } from './services/gitlab/GitLabCommitService.js'; +export { default as GitLabContributionService } from './services/gitlab/GitLabContributionService.js'; /** * Utility functions (wildcard export) * @namespace Utils * @see module:utils/helpers */ -export * from './utils/helpers.js'; \ No newline at end of file +export * from './utils/helpers.js'; + +/** + * Legacy service exports (maintains backward compatibility) + * @deprecated Use platform-specific services instead + * @namespace Services + */ +export { default as UserService } from './services/github/UserService.js'; +export { default as RepoService } from './services/github/RepoService.js'; +export { default as ActivityService } from './services/github/ActivityService.js'; +export { default as LanguageService } from './services/github/LanguageService.js'; +export { default as CommitService } from './services/github/CommitService.js'; +export { default as ContributionService } from './services/github/ContributionService.js'; \ No newline at end of file diff --git a/src/services/ActivityService.d.ts b/src/services/github/ActivityService.d.ts similarity index 100% rename from src/services/ActivityService.d.ts rename to src/services/github/ActivityService.d.ts diff --git a/src/services/ActivityService.js b/src/services/github/ActivityService.js similarity index 97% rename from src/services/ActivityService.js rename to src/services/github/ActivityService.js index 551a829..df26eb7 100644 --- a/src/services/ActivityService.js +++ b/src/services/github/ActivityService.js @@ -14,8 +14,8 @@ * along with this program. If not, see . */ import axios from 'axios'; -import { getAuthHeaders } from '../utils/helpers.js'; -import UserService from '../services/UserService.js'; +import { getAuthHeaders } from '../../utils/helpers.js'; +import UserService from '../UserService.js'; /** * Service class for handling GitHub user activities/events * diff --git a/src/services/CommitService.d.ts b/src/services/github/CommitService.d.ts similarity index 100% rename from src/services/CommitService.d.ts rename to src/services/github/CommitService.d.ts diff --git a/src/services/CommitService.js b/src/services/github/CommitService.js similarity index 97% rename from src/services/CommitService.js rename to src/services/github/CommitService.js index d32f7ce..5ccff82 100644 --- a/src/services/CommitService.js +++ b/src/services/github/CommitService.js @@ -14,8 +14,8 @@ * along with this program. If not, see . */ import axios from 'axios'; -import { getAuthHeaders } from '../utils/helpers.js'; -import UserService from '../services/UserService.js'; +import { getAuthHeaders } from '../../utils/helpers.js'; +import UserService from '../UserService.js'; /** * Service class for handling GitHub commit operations * diff --git a/src/services/ContributionService.d.ts b/src/services/github/ContributionService.d.ts similarity index 100% rename from src/services/ContributionService.d.ts rename to src/services/github/ContributionService.d.ts diff --git a/src/services/ContributionService.js b/src/services/github/ContributionService.js similarity index 97% rename from src/services/ContributionService.js rename to src/services/github/ContributionService.js index cf918c4..d7f2ee5 100644 --- a/src/services/ContributionService.js +++ b/src/services/github/ContributionService.js @@ -14,8 +14,8 @@ * along with this program. If not, see . */ import axios from 'axios'; -import { getAuthHeaders } from '../utils/helpers.js'; -import UserService from '../services/UserService.js'; +import { getAuthHeaders } from '../../utils/helpers.js'; +import UserService from '../UserService.js'; /** * Service class for handling GitHub contribution data * diff --git a/src/services/LanguageService.d.ts b/src/services/github/LanguageService.d.ts similarity index 96% rename from src/services/LanguageService.d.ts rename to src/services/github/LanguageService.d.ts index 6c194cd..b2ae0f2 100644 --- a/src/services/LanguageService.d.ts +++ b/src/services/github/LanguageService.d.ts @@ -21,7 +21,7 @@ * @class LanguageService */ -import { GitHubConfig, LanguageStats, Repository } from '../types'; +import { GitHubConfig, LanguageStats, Repository } from '../../types'; declare class LanguageService { /** diff --git a/src/services/LanguageService.js b/src/services/github/LanguageService.js similarity index 96% rename from src/services/LanguageService.js rename to src/services/github/LanguageService.js index e4019fb..9d64794 100644 --- a/src/services/LanguageService.js +++ b/src/services/github/LanguageService.js @@ -14,8 +14,8 @@ * along with this program. If not, see . */ import axios from 'axios'; -import { getAuthHeaders } from '../utils/helpers.js'; -import UserService from '../services/UserService.js'; +import { getAuthHeaders } from '../../utils/helpers.js'; +import UserService from '../UserService.js'; /** * Service class for handling GitHub repository language statistics * diff --git a/src/services/RepoService.d.ts b/src/services/github/RepoService.d.ts similarity index 100% rename from src/services/RepoService.d.ts rename to src/services/github/RepoService.d.ts diff --git a/src/services/RepoService.js b/src/services/github/RepoService.js similarity index 97% rename from src/services/RepoService.js rename to src/services/github/RepoService.js index 25fe687..52f2fe8 100644 --- a/src/services/RepoService.js +++ b/src/services/github/RepoService.js @@ -14,8 +14,8 @@ * along with this program. If not, see . */ import axios from 'axios'; -import { getAuthHeaders, formatFileSize } from '../utils/helpers.js'; -import UserService from '../services/UserService.js'; +import { getAuthHeaders, formatFileSize } from '../../utils/helpers.js'; +import UserService from '../UserService.js'; /** * Service class for handling GitHub repository operations * diff --git a/src/services/UserService.d.ts b/src/services/github/UserService.d.ts similarity index 100% rename from src/services/UserService.d.ts rename to src/services/github/UserService.d.ts diff --git a/src/services/UserService.js b/src/services/github/UserService.js similarity index 98% rename from src/services/UserService.js rename to src/services/github/UserService.js index 7092145..50b40e7 100644 --- a/src/services/UserService.js +++ b/src/services/github/UserService.js @@ -14,7 +14,7 @@ * along with this program. If not, see . */ import axios from 'axios'; -import { getAuthHeaders } from '../utils/helpers.js'; +import { getAuthHeaders } from '../../utils/helpers.js'; /** * Service class for handling GitHub user profile data diff --git a/src/services/gitlab/GitLabActivityService.js b/src/services/gitlab/GitLabActivityService.js new file mode 100644 index 0000000..242eb47 --- /dev/null +++ b/src/services/gitlab/GitLabActivityService.js @@ -0,0 +1,99 @@ +// Import Axios for HTTP requests +import axios from 'axios'; + +// Import helper function to generate authorization headers +import { getAuthHeaders } from '../../utils/helpers.js'; + +/** + * GitLabActivityService class + * + * This service is responsible for retrieving user activity events from the GitLab API + * and formatting them in a readable structure. + */ +export default class GitLabActivityService { + constructor(config) { + // Store configuration such as baseUrl, username, and token + this.config = config; + + // Initialize array to hold activities fetched from GitLab + this.activitiesData = []; + } + + /** + * Loads recent user activities from GitLab API + * @param {number} per_page - Number of activities to fetch (default is 10) + */ + async loadActivities(per_page = 10) { + try { + // Step 1: Retrieve user ID using provided username + const userResponse = await axios.get( + `${this.config.baseUrl}/users?username=${this.config.username}`, + { + headers: getAuthHeaders(this.config.token) + } + ); + + const userId = userResponse.data[0]?.id; + if (!userId) throw new Error('User not found'); + + // Step 2: Fetch activity events for the identified user + const response = await axios.get( + `${this.config.baseUrl}/users/${userId}/events`, + { + headers: getAuthHeaders(this.config.token), + params: { per_page } + } + ); + + // Store retrieved activity data + this.activitiesData = response.data; + + } catch (err) { + // In case of error, log the issue and clear activity data + console.error('Error loading activities:', err); + this.activitiesData = []; + } + } + + /** + * Renders the list of user activities in a formatted structure + * @returns {Array} Array of formatted activity descriptions + */ + renderActivities() { + return this.activitiesData.map(event => { + let text = ''; + let icon = ''; + + // Choose a descriptive label and icon based on activity type + switch (event.action_name) { + case 'pushed to': + text = `Pushed to ${event.project?.name || 'repository'}`; + icon = 'fa-code-commit'; + break; + case 'created': + text = `Created ${event.target_type} in ${event.project?.name || 'repository'}`; + icon = 'fa-plus-circle'; + break; + case 'opened': + if (event.target_type === 'MergeRequest') { + text = `Opened a merge request in ${event.project?.name || 'repository'}`; + icon = 'fa-code-pull-request'; + } else { + text = `Opened ${event.target_type} in ${event.project?.name || 'repository'}`; + icon = 'fa-exclamation-circle'; + } + break; + default: + text = `${event.action_name} in ${event.project?.name || 'repository'}`; + icon = 'fa-circle-notch'; + } + + // Return a structured object for UI rendering + return { + text, + icon, + date: new Date(event.created_at).toLocaleString() + }; + }); + } +} diff --git a/src/services/gitlab/GitLabCommitService.js b/src/services/gitlab/GitLabCommitService.js new file mode 100644 index 0000000..ca58508 --- /dev/null +++ b/src/services/gitlab/GitLabCommitService.js @@ -0,0 +1,120 @@ +// Import Axios for making HTTP requests +import axios from 'axios'; + +// Import custom helper to generate authorization headers +import { getAuthHeaders } from '../../utils/helpers.js'; + +/** + * GitLabCommitService + * + * This class handles retrieving commit-related data from GitLab repositories. + */ +export default class GitLabCommitService { + constructor(config) { + // Configuration includes username, token, base URL, etc. + this.config = config; + + // Object to hold commit counts per repository + this.commitsData = {}; + } + + /** + * Load the number of commits for each repository provided. + * @param {Array} reposData - Array of repository metadata + * @param {number} per_page - Number of commits to request per repository (default is 100) + */ + async loadCommitsData(reposData, per_page = 100) { + this.commitsData = {}; + + for (const repo of reposData) { + try { + // Fetch list of commits for the current repository + const response = await axios.get( + `${this.config.baseUrl}/projects/${repo.id}/repository/commits`, + { + headers: getAuthHeaders(this.config.token), + params: { per_page } + } + ); + + // Store the number of commits for this repository + this.commitsData[repo.name] = response.data.length; + + } catch (err) { + // If an error occurs, log it and default to zero commits + console.error(`Error loading commits for ${repo.name}:`, err); + this.commitsData[repo.name] = 0; + } + } + } + + /** + * Load a paginated list of commits from a specific repository. + * @param {number|string} repoId - ID of the repository + * @param {number} page - Page number for pagination (default is 1) + * @param {number} per_page - Number of commits per page (default is 10) + * @returns {Array} - Array of simplified commit objects + */ + async loadRepoCommits(repoId, page = 1, per_page = 10) { + try { + const response = await axios.get( + `${this.config.baseUrl}/projects/${repoId}/repository/commits`, + { + headers: getAuthHeaders(this.config.token), + params: { per_page, page } + } + ); + + // Transform each commit into a simplified object + return response.data.map(commit => ({ + sha: commit.id, + message: commit.title, + author: commit.author_name, + date: new Date(commit.created_at), + url: commit.web_url + })); + + } catch (err) { + // Wrap and rethrow with custom message + throw new Error('Error loading commits: ' + err.message); + } + } + + /** + * Load detailed information for a specific commit. + * @param {number|string} repoId - ID of the repository + * @param {string} sha - SHA identifier of the commit + * @returns {Object} - Detailed commit data including stats and changed files + */ + async loadCommitDetails(repoId, sha) { + try { + const response = await axios.get( + `${this.config.baseUrl}/projects/${repoId}/repository/commits/${sha}`, + { + headers: getAuthHeaders(this.config.token) + } + ); + + const commit = response.data; + + // Return structured details about the commit + return { + sha: commit.id, + message: commit.message, + author: commit.author_name, + date: new Date(commit.created_at), + url: commit.web_url, + stats: { + additions: commit.stats?.additions || 0, + deletions: commit.stats?.deletions || 0, + total: commit.stats?.total || 0 + }, + files: commit.stats?.files || [] + }; + + } catch (err) { + // Wrap and rethrow with custom message + throw new Error('Error loading commit details: ' + err.message); + } + } +} diff --git a/src/services/gitlab/GitLabContributionService.js b/src/services/gitlab/GitLabContributionService.js new file mode 100644 index 0000000..7ac8fe4 --- /dev/null +++ b/src/services/gitlab/GitLabContributionService.js @@ -0,0 +1,99 @@ +// Import Axios to handle HTTP requests +import axios from 'axios'; + +// Import helper to generate authorization headers using the user's token +import { getAuthHeaders } from '../../utils/helpers.js'; + +/** + * GitLabContributionService + * + * This class retrieves and summarizes GitLab user contributions (push events) + * grouped by month for a given year. + */ +export default class GitLabContributionService { + constructor(config) { + // Store configuration including baseUrl, username, and token + this.config = config; + + // Object to hold monthly contribution data + this.contributionsData = {}; + } + + /** + * Loads the user's GitLab contributions for a specified year. + * If no year is given, defaults to the current year. + * @param {number} year - Optional year for which contributions should be fetched + */ + async loadContributionsData(year) { + try { + const currentYear = year || new Date().getFullYear(); + const startDate = `${currentYear}-01-01`; + const endDate = `${currentYear}-12-31`; + + // Step 1: Fetch the GitLab user ID using the provided username + const userResponse = await axios.get( + `${this.config.baseUrl}/users?username=${this.config.username}`, + { + headers: getAuthHeaders(this.config.token) + } + ); + + const userId = userResponse.data[0]?.id; + if (!userId) throw new Error('User not found'); + + // Step 2: Fetch push events for the user within the year + const response = await axios.get( + `${this.config.baseUrl}/users/${userId}/events`, + { + headers: getAuthHeaders(this.config.token), + params: { + after: startDate, + before: endDate, + action: 'pushed', + per_page: 100 + } + } + ); + + // Initialize contribution counters per month + const contributions = { + 'Jan': 0, 'Feb': 0, 'Mar': 0, 'Apr': 0, 'May': 0, 'Jun': 0, + 'Jul': 0, 'Aug': 0, 'Sep': 0, 'Oct': 0, 'Nov': 0, 'Dec': 0 + }; + + // Tally push events based on their creation month + response.data.forEach(event => { + const date = new Date(event.created_at); + if (date.getFullYear() === currentYear) { + const month = date.getMonth(); + switch (month) { + case 0: contributions.Jan++; break; + case 1: contributions.Feb++; break; + case 2: contributions.Mar++; break; + case 3: contributions.Apr++; break; + case 4: contributions.May++; break; + case 5: contributions.Jun++; break; + case 6: contributions.Jul++; break; + case 7: contributions.Aug++; break; + case 8: contributions.Sep++; break; + case 9: contributions.Oct++; break; + case 10: contributions.Nov++; break; + case 11: contributions.Dec++; break; + } + } + }); + + // Store the final contribution data + this.contributionsData = contributions; + + } catch (err) { + console.error('Error loading contributions:', err); + + // Fallback data in case of error + this.contributionsData = { + 'Jan': 12, 'Feb': 19, 'Mar': 8, 'Apr': 15, 'May': 22, 'Jun': 30, + 'Jul': 18, 'Aug': 14, 'Sep': 25, 'Oct': 20, 'Nov': 17, 'Dec': 10 + }; + } + } +} diff --git a/src/services/gitlab/GitLabLanguageService.js b/src/services/gitlab/GitLabLanguageService.js new file mode 100644 index 0000000..59b3b52 --- /dev/null +++ b/src/services/gitlab/GitLabLanguageService.js @@ -0,0 +1,55 @@ +// Import Axios for making HTTP requests +import axios from 'axios'; + +// Import helper that adds authorization headers using the provided token +import { getAuthHeaders } from '../../utils/helpers.js'; + +/** + * GitLabLanguageService + * + * This class analyzes the programming language usage across multiple GitLab repositories. + * It fetches the language distribution for each repo and aggregates the data into + * approximate byte representation. + */ +export default class GitLabLanguageService { + constructor(config) { + // Configuration object (contains baseUrl, username, and token) + this.config = config; + + // Stores the total byte count per programming language + this.languagesData = {}; + } + + /** + * Loads and aggregates language usage data across repositories. + * @param {Array} reposData - Array containing repository metadata (must include `languages_url`) + */ + async loadLanguagesData(reposData) { + this.languagesData = {}; // Reset language data + + for (const repo of reposData) { + try { + // Fetch language usage percentages from the repo's languages URL + const response = await axios.get( + repo.languages_url, + { + headers: getAuthHeaders(this.config.token) + } + ); + + // Convert percentage usage to approximate byte count and aggregate totals + for (const [language, percentage] of Object.entries(response.data)) { + const repoSize = repo.size || 0; // Size in bytes + const bytes = (percentage * repoSize) / 100; + + // Sum the bytes for each language across all repos + this.languagesData[language] = + (this.languagesData[language] || 0) + bytes; + } + } catch (err) { + // Log error if language data fetch fails for a repo + console.error(`Error loading languages for ${repo.name}:`, err); + } + } + } +} diff --git a/src/services/gitlab/GitLabRepoService.js b/src/services/gitlab/GitLabRepoService.js new file mode 100644 index 0000000..77ebfe1 --- /dev/null +++ b/src/services/gitlab/GitLabRepoService.js @@ -0,0 +1,144 @@ +// Import Axios for handling HTTP requests +import axios from 'axios'; + +// Import helpers: one to generate authentication headers, and one to format file sizes +import { getAuthHeaders, formatFileSize } from '../../utils/helpers.js'; + +/** + * GitLabRepoService + * + * This class provides methods to retrieve and format information about + * GitLab repositories for a specific user. + */ +export default class GitLabRepoService { + constructor(config) { + // Configuration object containing baseUrl, username, and token + this.config = config; + + // Holds repository metadata + this.reposData = []; + } + + /** + * Load a list of repositories from the GitLab API for the configured user. + * Repositories are sorted according to the specified criteria. + * + * @param {string} sort - Sorting method: 'updated', 'stars', or 'forks' + * @param {number} per_page - Max number of repositories to fetch + */ + async loadReposData(sort = 'updated', per_page = 100) { + try { + const params = { + per_page, + order_by: sort === 'stars' ? 'stars_count' + : sort === 'forks' ? 'forks_count' + : 'last_activity_at', + sort: 'desc' // Descending order + }; + + const response = await axios.get( + `${this.config.baseUrl}/users/${this.config.username}/projects`, + { + headers: getAuthHeaders(this.config.token), + params + } + ); + + // Add a custom property for the language API endpoint + this.reposData = response.data.map(repo => ({ + ...repo, + languages_url: `${this.config.baseUrl}/projects/${repo.id}/languages` + })); + } catch (err) { + console.error('Error loading repositories:', err); + throw err; + } + } + + /** + * Load files within a specific repository and path. + * Useful for browsing folder contents. + * + * @param {number|string} repoId - Repository ID + * @param {string} path - Path inside the repository + * @param {string} ref - Branch or tag reference (default is 'main') + * @returns {Array} - List of file metadata + */ + async loadRepoFiles(repoId, path = '', ref = 'main') { + try { + const response = await axios.get( + `${this.config.baseUrl}/projects/${repoId}/repository/tree`, + { + headers: getAuthHeaders(this.config.token), + params: { path, ref } + } + ); + + // Format file/folder data for presentation + return response.data.map(item => ({ + name: item.name, + type: item.type, + path: item.path, + size: item.size ? formatFileSize(item.size) : '', // Optional file size + url: item.web_url + })); + } catch (err) { + throw new Error('Error loading files: ' + err.message); + } + } + + /** + * Load the raw content of a specific file in a repository. + * + * @param {number|string} repoId - Repository ID + * @param {string} filePath - Full path to the file inside the repo + * @param {string} ref - Branch or tag reference (default is 'main') + * @returns {string} - File contents as a string + */ + async loadFileContent(repoId, filePath, ref = 'main') { + try { + const response = await axios.get( + `${this.config.baseUrl}/projects/${repoId}/repository/files/${encodeURIComponent(filePath)}/raw`, + { + headers: getAuthHeaders(this.config.token), + params: { ref } + } + ); + return response.data; + } catch (err) { + throw new Error('Error loading file content: ' + err.message); + } + } + + /** + * Returns a formatted list of repositories with selected details. + * This can be used for displaying repositories in a UI. + * + * @param {string} sort - Sorting method: 'updated', 'stars', or 'forks' + * @returns {Array} - Array of formatted repository objects + */ + renderRepos(sort = 'updated') { + let repos = [...this.reposData]; + + // Sort repositories based on selected criteria + if (sort === 'stars') { + repos.sort((a, b) => b.star_count - a.star_count); + } else if (sort === 'forks') { + repos.sort((a, b) => b.forks_count - a.forks_count); + } else { + repos.sort((a, b) => new Date(b.last_activity_at) - new Date(a.last_activity_at)); + } + + // Return structured info for display + return repos.map(repo => ({ + id: repo.id, + name: repo.name, + description: repo.description || 'No description available', + language: repo.language || 'N/A', + stars: repo.star_count, + forks: repo.forks_count, + updatedAt: new Date(repo.last_activity_at).toLocaleDateString(), + url: repo.web_url + })); + } +} diff --git a/src/services/gitlab/GitLabUserService.js b/src/services/gitlab/GitLabUserService.js new file mode 100644 index 0000000..a7fe0eb --- /dev/null +++ b/src/services/gitlab/GitLabUserService.js @@ -0,0 +1,77 @@ +// Import Axios for handling HTTP requests +import axios from 'axios'; + +// Import helper that generates authentication headers from token +import { getAuthHeaders } from '../../utils/helpers.js'; + +/** + * GitLabUserService + * + * This class is responsible for retrieving a GitLab user's profile and statistics. + */ +export default class GitLabUserService { + constructor(config) { + // Configuration object including baseUrl, username, and token + this.config = config; + + // Object that will store user profile and statistics + this.userData = {}; + } + + /** + * Loads the user's profile and statistics from the GitLab API. + * This includes basic profile data and contribution statistics. + */ + async loadUserData() { + try { + // Step 1: Search for user by username (GitLab returns an array) + const response = await axios.get( + `${this.config.baseUrl}/users?username=${this.config.username}`, + { + headers: getAuthHeaders(this.config.token) + } + ); + + // If no user is found, throw an error + if (response.data.length === 0) { + throw new Error('User not found'); + } + + // Store the user profile information + this.userData = response.data[0]; + + // Step 2: Fetch extended contribution statistics for the user + const statsResponse = await axios.get( + `${this.config.baseUrl}/users/${this.userData.id}/statistics`, + { + headers: getAuthHeaders(this.config.token) + } + ); + + // Attach stats data to the userData object + this.userData.stats = statsResponse.data; + + } catch (err) { + console.error('Error loading GitLab user:', err); + throw err; + } + } + + /** + * Returns a formatted user profile object for display in UI or reports. + * Defaults to fallback values when data is missing. + * + * @returns {Object} - Formatted user profile data + */ + renderProfile() { + return { + avatarUrl: this.userData.avatar_url || '', + name: this.userData.name || this.config.username, + bio: this.userData.bio || 'No bio available', + followers: this.userData.followers || 0, + following: this.userData.following || 0, + publicRepos: this.userData.public_repos || 0, + stats: this.userData.stats || {} + }; + } +}