Compare commits
5
Commits
cd326a34ba
...
3d2c0487b7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d2c0487b7 | ||
|
|
cb89a1c6b9 | ||
|
|
5e27a5bc93 | ||
|
|
f52a3449c6 | ||
|
|
ca94b80beb |
@@ -6,17 +6,19 @@ Order matters — later steps assume earlier ones are done.
|
|||||||
|
|
||||||
## 1. Audit cleanup
|
## 1. Audit cleanup
|
||||||
|
|
||||||
- [ ] **Remove `eightpoints/guzzle-bundle` + `idci/graphql-client-bundle`.**
|
- [x] **Remove `eightpoints/guzzle-bundle` + `idci/graphql-client-bundle`.**
|
||||||
They exist only to build one near-static GitHub GraphQL query string
|
They exist only to build one near-static GitHub GraphQL query string
|
||||||
(`GitHubProvider.php:59-74`); the bundle's own HTTP transport is already
|
(`GitHubProvider.php:59-74`); the bundle's own HTTP transport is already
|
||||||
bypassed (comment at `GitHubProvider.php:76-77`). Replace
|
bypassed (comment at `GitHubProvider.php:76-77`).
|
||||||
|
- [ ] Replace
|
||||||
`$graphqlClient->buildQuery(...)->getGraphQLQuery()` with a plain
|
`$graphqlClient->buildQuery(...)->getGraphQLQuery()` with a plain
|
||||||
`sprintf`/heredoc query string. Delete the two packages from
|
`sprintf`/heredoc query string. Delete the two packages from
|
||||||
`composer.json`, `config/bundles.php`, and delete
|
`composer.json`, `config/bundles.php`, and delete
|
||||||
`config/packages/eight_points_guzzle.yaml` +
|
`config/packages/eight_points_guzzle.yaml` +
|
||||||
`config/packages/idci_graphql_client.yaml`. Run `composer update` and
|
`config/packages/idci_graphql_client.yaml`. Run `composer update` and
|
||||||
commit the regenerated `composer.lock`.
|
commit the regenerated `composer.lock`.
|
||||||
- [ ] **Dedupe `baseUrl` normalization.** `GitLabProvider.php` (lines 41,
|
|
||||||
|
- [x] **Dedupe `baseUrl` normalization.** `GitLabProvider.php` (lines 41,
|
||||||
53) and `GiteaProvider.php` (lines 42, 54) each call
|
53) and `GiteaProvider.php` (lines 42, 54) each call
|
||||||
`rtrim($this->baseUrl..., '/')` twice — once in `ping()`, once in
|
`rtrim($this->baseUrl..., '/')` twice — once in `ping()`, once in
|
||||||
`fetch()`. Compute it once as a `private readonly string $baseUrl` in
|
`fetch()`. Compute it once as a `private readonly string $baseUrl` in
|
||||||
@@ -35,16 +37,16 @@ Order matters — later steps assume earlier ones are done.
|
|||||||
|
|
||||||
## 3. `ContributionStore` (SQLite via native PDO)
|
## 3. `ContributionStore` (SQLite via native PDO)
|
||||||
|
|
||||||
- [ ] New `src/Service/ContributionStore.php`. PDO SQLite, DB file at
|
- [x] New `src/Service/ContributionStore.php`. PDO SQLite, DB file at
|
||||||
`%kernel.project_dir%/var/data/contributions.db` (configurable
|
`%kernel.project_dir%/var/data/contributions.db` (configurable
|
||||||
constructor arg), table created lazily:
|
constructor arg), table created lazily:
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE IF NOT EXISTS contributions (
|
CREATE TABLE IF NOT EXISTS contributions (
|
||||||
provider TEXT NOT NULL,
|
provider TEXT NOT NULL,
|
||||||
date TEXT NOT NULL,
|
date TEXT NOT NULL,
|
||||||
count INTEGER NOT NULL,
|
count INTEGER NOT NULL CHECK (count >= 0),
|
||||||
PRIMARY KEY (provider, date)
|
PRIMARY KEY (provider, date)
|
||||||
);
|
) WITHOUT ROWID, STRICT;
|
||||||
```
|
```
|
||||||
- [ ] `latestDate(string $provider): ?string` — `SELECT MAX(date) WHERE provider = ?`.
|
- [ ] `latestDate(string $provider): ?string` — `SELECT MAX(date) WHERE provider = ?`.
|
||||||
- [ ] `merge(string $provider, array $dateCounts): void` — upsert via
|
- [ ] `merge(string $provider, array $dateCounts): void` — upsert via
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.2",
|
"php": ">=8.2",
|
||||||
"eightpoints/guzzle-bundle": "^8.6",
|
|
||||||
"idci/graphql-client-bundle": "^2.0",
|
|
||||||
"monolog/monolog": "^3.10",
|
"monolog/monolog": "^3.10",
|
||||||
"symfony/cache": "7.4.*",
|
"symfony/cache": "7.4.*",
|
||||||
"symfony/console": "7.4.*",
|
"symfony/console": "7.4.*",
|
||||||
|
|||||||
Generated
+308
-1501
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
|||||||
eight_points_guzzle:
|
|
||||||
clients:
|
|
||||||
github_graphql:
|
|
||||||
base_url: 'https://api.github.com/graphql'
|
|
||||||
options:
|
|
||||||
headers:
|
|
||||||
Authorization: 'Bearer %env(GITHUB_TOKEN)%'
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
idci_graphql_client:
|
|
||||||
clients:
|
|
||||||
github:
|
|
||||||
http_client: 'eight_points_guzzle.client.github_graphql'
|
|
||||||
@@ -121,7 +121,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* }
|
* }
|
||||||
* @psalm-type ServicesConfig = array{
|
* @psalm-type ServicesConfig = array{
|
||||||
* _defaults?: DefaultsType,
|
* _defaults?: DefaultsType,
|
||||||
* _instanceof?: InstanceofType,
|
* _instanceof?: array<class-string, InstanceofType>,
|
||||||
* ...<string, DefinitionType|AliasType|PrototypeType|StackType|ArgumentsType|null>
|
* ...<string, DefinitionType|AliasType|PrototypeType|StackType|ArgumentsType|null>
|
||||||
* }
|
* }
|
||||||
* @psalm-type ExtensionType = array<string, mixed>
|
* @psalm-type ExtensionType = array<string, mixed>
|
||||||
@@ -857,6 +857,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
|||||||
* http_errors?: bool|Param,
|
* http_errors?: bool|Param,
|
||||||
* expect?: mixed,
|
* expect?: mixed,
|
||||||
* ssl_key?: mixed,
|
* ssl_key?: mixed,
|
||||||
|
* force_ip_resolve?: mixed, // Default: null
|
||||||
* stream?: bool|Param,
|
* stream?: bool|Param,
|
||||||
* synchronous?: bool|Param,
|
* synchronous?: bool|Param,
|
||||||
* read_timeout?: scalar|Param|null,
|
* read_timeout?: scalar|Param|null,
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ services:
|
|||||||
dockerfile: Dockerfile.dev
|
dockerfile: Dockerfile.dev
|
||||||
volumes:
|
volumes:
|
||||||
- .:/app
|
- .:/app
|
||||||
- /app/vendor # keeps vendor from the dev image, not your local dir
|
- ./vendor:/app/vendor
|
||||||
|
- /app/var/data
|
||||||
environment:
|
environment:
|
||||||
APP_ENV: dev
|
APP_ENV: dev
|
||||||
APP_DEBUG: "1"
|
APP_DEBUG: "1"
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
class ContributionStore {
|
||||||
|
private PDO $pdo;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly string $dbPath = "%kernel.project_dir%/var/data/contributions.db",
|
||||||
|
private readonly ?int $retentionDays = null,
|
||||||
|
){
|
||||||
|
if(!is_dir(\dirname($this->dbPath))){
|
||||||
|
mkdir(\dirname($this->dbPath), 0755, recursive: true);
|
||||||
|
}
|
||||||
|
$this->pdo = new PDO('sqlite:' . $this->dbPath);
|
||||||
|
$this->pdo->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
|
||||||
|
$this->prepareSchema();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function prepareSchema():void{
|
||||||
|
$this->pdo->exec('
|
||||||
|
CREATE TABLE IF NOT EXISTS contributions (
|
||||||
|
provider TEXT NOT NULL,
|
||||||
|
date TEXT NOT NULL,
|
||||||
|
count INTEGER NOT NULL CHECK (count >= 0),
|
||||||
|
PRIMARY KEY (provider, date)
|
||||||
|
) WITHOUT ROWID, STRICT
|
||||||
|
');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -25,7 +25,9 @@ final class GitLabProvider implements ProviderInterface
|
|||||||
private readonly string $token,
|
private readonly string $token,
|
||||||
private readonly LoggerInterface $logger,
|
private readonly LoggerInterface $logger,
|
||||||
private readonly string $baseUrl = '',
|
private readonly string $baseUrl = '',
|
||||||
) {}
|
) {
|
||||||
|
$this->baseUrl = rtrim($this->baseUrl !== '' ? $this->baseUrl : 'https://gitlab.com', '/');
|
||||||
|
}
|
||||||
|
|
||||||
public function getName(): string
|
public function getName(): string
|
||||||
{
|
{
|
||||||
@@ -39,9 +41,8 @@ final class GitLabProvider implements ProviderInterface
|
|||||||
|
|
||||||
public function ping(): void
|
public function ping(): void
|
||||||
{
|
{
|
||||||
$baseUrl = rtrim($this->baseUrl !== '' ? $this->baseUrl : 'https://gitlab.com', '/');
|
|
||||||
|
$this->client->request('GET', "$this->baseUrl/api/v4/user", [
|
||||||
$this->client->request('GET', "$baseUrl/api/v4/user", [
|
|
||||||
'headers' => ['PRIVATE-TOKEN' => $this->token],
|
'headers' => ['PRIVATE-TOKEN' => $this->token],
|
||||||
])->getContent();
|
])->getContent();
|
||||||
}
|
}
|
||||||
@@ -56,23 +57,22 @@ final class GitLabProvider implements ProviderInterface
|
|||||||
*/
|
*/
|
||||||
public function startFetch(): array
|
public function startFetch(): array
|
||||||
{
|
{
|
||||||
$baseUrl = rtrim($this->baseUrl !== '' ? $this->baseUrl : 'https://gitlab.com', '/');
|
|
||||||
|
$this->logger->debug('GitLabProvider: fetching contributions', ['user' => $this->username, 'url' => $this->baseUrl]);
|
||||||
|
|
||||||
$this->logger->debug('GitLabProvider: fetching contributions', ['user' => $this->username, 'url' => $baseUrl]);
|
$userResponse = $this->client->request('GET', "$this->baseUrl/api/v4/users", [
|
||||||
|
|
||||||
$userResponse = $this->client->request('GET', "$baseUrl/api/v4/users", [
|
|
||||||
'headers' => ['PRIVATE-TOKEN' => $this->token],
|
'headers' => ['PRIVATE-TOKEN' => $this->token],
|
||||||
'query' => ['username' => $this->username],
|
'query' => ['username' => $this->username],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$users = $userResponse->toArray();
|
$users = $userResponse->toArray();
|
||||||
if (empty($users)) {
|
if (empty($users)) {
|
||||||
throw new NotFoundHttpException("GitLab: user '{$this->username}' not found on $baseUrl");
|
throw new NotFoundHttpException("GitLab: user '{$this->username}' not found on $this->baseUrl");
|
||||||
}
|
}
|
||||||
$userId = $users[0]['id'];
|
$userId = $users[0]['id'];
|
||||||
$after = (new \DateTimeImmutable('-365 days'))->format('Y-m-d');
|
$after = (new \DateTimeImmutable('-365 days'))->format('Y-m-d');
|
||||||
|
|
||||||
$response = $this->client->request('GET', "$baseUrl/api/v4/users/$userId/events", [
|
$response = $this->client->request('GET', "$this->baseUrl/api/v4/users/$userId/events", [
|
||||||
'headers' => ['PRIVATE-TOKEN' => $this->token],
|
'headers' => ['PRIVATE-TOKEN' => $this->token],
|
||||||
'query' => [
|
'query' => [
|
||||||
'after' => $after,
|
'after' => $after,
|
||||||
@@ -81,7 +81,7 @@ final class GitLabProvider implements ProviderInterface
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return ['baseUrl' => $baseUrl, 'userId' => $userId, 'after' => $after, 'page' => 1, 'response' => $response];
|
return ['baseUrl' => $this->baseUrl, 'userId' => $userId, 'after' => $after, 'page' => 1, 'response' => $response];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ final class GiteaProvider implements ProviderInterface
|
|||||||
private readonly string $token,
|
private readonly string $token,
|
||||||
private readonly string $baseUrl,
|
private readonly string $baseUrl,
|
||||||
private readonly LoggerInterface $logger,
|
private readonly LoggerInterface $logger,
|
||||||
) {}
|
) {
|
||||||
|
$this->baseUrl = rtrim($this->baseUrl, '/');
|
||||||
|
}
|
||||||
|
|
||||||
public function getName(): string
|
public function getName(): string
|
||||||
{
|
{
|
||||||
@@ -40,20 +42,20 @@ final class GiteaProvider implements ProviderInterface
|
|||||||
|
|
||||||
public function ping(): void
|
public function ping(): void
|
||||||
{
|
{
|
||||||
$baseUrl = rtrim($this->baseUrl, '/');
|
|
||||||
|
|
||||||
$this->client->request('GET', "$baseUrl/api/v1/user", [
|
$this->client->request('GET', "$this->baseUrl/api/v1/user", [
|
||||||
'headers' => ['Authorization' => "token {$this->token}"],
|
'headers' => ['Authorization' => "token {$this->token}"],
|
||||||
])->getContent();
|
])->getContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function startFetch(): ResponseInterface
|
public function startFetch(): ResponseInterface
|
||||||
{
|
{
|
||||||
$baseUrl = rtrim($this->baseUrl, '/');
|
|
||||||
|
|
||||||
$this->logger->debug('GiteaProvider: fetching contributions', ['user' => $this->username, 'url' => $baseUrl]);
|
$this->logger->debug('GiteaProvider: fetching contributions', ['user' => $this->username, 'url' => $this->baseUrl]);
|
||||||
|
|
||||||
return $this->client->request('GET', "$baseUrl/api/v1/users/{$this->username}/heatmap", [
|
return $this->client->request('GET', "$this->baseUrl/api/v1/users/{$this->username}/heatmap", [
|
||||||
'headers' => ['Authorization' => "token {$this->token}"],
|
'headers' => ['Authorization' => "token {$this->token}"],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user