Skip to content

Commit 052d471

Browse files
jdevalkclaude
andcommitted
Add reported and discovered datetime fields to release output
Add a nullable `reported` timestamp column to package_releases for author-declared release dates. Expose both `reported` and `discovered` (mapped from created_at) as ISO 8601 strings in the FAIR metadata response. Includes migration, model cast, factory update, and tests. Closes fairpm/fair-protocol#64 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Joost de Valk <joost@altha.nl>
1 parent 45de135 commit 052d471

File tree

5 files changed

+102
-0
lines changed

5 files changed

+102
-0
lines changed

app/Models/PackageRelease.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* @property-read array<string, mixed>|null $artifacts
2121
* @property-read string|null $signature
2222
* @property-read string|null $checksum
23+
* @property-read CarbonImmutable|null $reported
2324
* @property-read CarbonImmutable|null $created_at
2425
* @property-read Package|null $package
2526
*/
@@ -45,6 +46,7 @@ protected function casts(): array
4546
'suggests' => 'array',
4647
'provides' => 'array',
4748
'artifacts' => 'array',
49+
'reported' => 'immutable_datetime',
4850
'created_at' => 'immutable_datetime',
4951
];
5052
}

app/Values/Packages/FairMetadata.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ public static function fromPackage(Package $package): array
101101
->releases
102102
->map(fn($release) => [
103103
'version' => $release->version,
104+
'reported' => $release->reported?->toIso8601String(),
105+
'discovered' => $release->created_at?->toIso8601String(),
104106
'artifacts' => $release->artifacts,
105107
'provides' => $release->provides,
106108
'requires' => $release->requires,

database/factories/PackageReleaseFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public function definition(): array
2727
'provides' => [
2828
'some-feature' => $this->faker->semver(),
2929
],
30+
'reported' => $this->faker->dateTimeBetween('-1 year', 'now'),
3031
'artifacts' => [
3132
'package' => [
3233
[
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
use Illuminate\Database\Migrations\Migration;
5+
use Illuminate\Database\Schema\Blueprint;
6+
use Illuminate\Support\Facades\Schema;
7+
8+
return new class extends Migration
9+
{
10+
public function up(): void
11+
{
12+
Schema::table('package_releases', function (Blueprint $table) {
13+
$table->timestamp('reported')->nullable()->after('checksum');
14+
});
15+
}
16+
17+
public function down(): void
18+
{
19+
Schema::table('package_releases', function (Blueprint $table) {
20+
$table->dropColumn('reported');
21+
});
22+
}
23+
};
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
use App\Models\Package;
5+
6+
beforeEach(function () {
7+
Package::truncate();
8+
});
9+
10+
it('includes reported and discovered datetime fields in release output', function () {
11+
Package::factory()
12+
->withAuthors()
13+
->withReleases(1)
14+
->withMetas()
15+
->create([
16+
'did' => 'fake:datetime-test',
17+
'name' => 'Datetime Test Package',
18+
'slug' => 'datetime-test',
19+
'origin' => 'wp',
20+
'type' => 'wp-plugin',
21+
'license' => 'GPLv2',
22+
'raw_metadata' => [],
23+
]);
24+
25+
$response = $this->getJson('/packages/fake:datetime-test')
26+
->assertStatus(200);
27+
28+
$releases = $response->json('releases');
29+
expect($releases)->toBeArray()->not->toBeEmpty();
30+
31+
$release = $releases[0];
32+
expect($release)
33+
->toHaveKeys(['reported', 'discovered'])
34+
->and($release['reported'])->toBeString()
35+
->and($release['discovered'])->toBeString();
36+
});
37+
38+
it('returns null for reported when it is not set', function () {
39+
$package = Package::factory()
40+
->withAuthors()
41+
->withMetas()
42+
->create([
43+
'did' => 'fake:null-reported-test',
44+
'name' => 'Null Reported Test',
45+
'slug' => 'null-reported-test',
46+
'origin' => 'wp',
47+
'type' => 'wp-plugin',
48+
'license' => 'GPLv2',
49+
'raw_metadata' => [],
50+
]);
51+
52+
// Create a release without a reported value
53+
$package->releases()->create([
54+
'version' => '1.0.0',
55+
'download_url' => 'https://example.com/test.zip',
56+
'reported' => null,
57+
'artifacts' => [
58+
'package' => [['url' => 'https://example.com/test.zip', 'type' => 'zip']],
59+
],
60+
]);
61+
62+
$response = $this->getJson('/packages/fake:null-reported-test')
63+
->assertStatus(200);
64+
65+
$releases = $response->json('releases');
66+
expect($releases)->toBeArray()->not->toBeEmpty();
67+
68+
$release = $releases[0];
69+
expect($release)
70+
->toHaveKey('reported')
71+
->toHaveKey('discovered')
72+
->and($release['reported'])->toBeNull()
73+
->and($release['discovered'])->toBeString();
74+
});

0 commit comments

Comments
 (0)