aspirecloud/app/Values/Packages/PackageData.php
Benni Mack 3c9eded65a fix: Ensure security and sections are optional
Ensure that TYPO3's repo which only
adds the properties as optional,
can be imported as well.

Signed-off-by: Benni Mack <benni@typo3.org>
2026-03-21 17:48:12 +01:00

244 lines
7.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Values\Packages;
use App\Enums\Origin;
use App\Enums\PackageType;
use App\Models\WpOrg\Plugin;
use App\Models\WpOrg\Theme;
use App\Utils\Regex;
use App\Values\DTO;
use Bag\Attributes\Transforms;
use App\Services\Packages\PackageDIDService;
readonly class PackageData extends DTO
{
/**
* @param array<string, mixed> $raw_metadata
* @param array<array<string, string>> $authors
* @param array<array<string, string>> $security
* @param array<array<string, mixed>> $releases
* @param array<string> $tags
* @param array<string, mixed> $sections
*/
public function __construct(
public string $did,
public string $name,
public string $slug,
public string $description,
public string $download_url,
public string $version,
public string $license,
public string $type,
public string $origin,
public array $raw_metadata = [],
public array $authors = [],
public array $security = [],
public array $releases = [],
public array $tags = [],
public array $sections = [],
) {}
/**
* Transforms FairMetadata to a package data array.
*
* @param FairMetadata $fairMetadata
* @return array<string, mixed>
*/
#[Transforms(FairMetadata::class)]
public static function fromFairMetadata(FairMetadata $fairMetadata): array
{
$releases = $fairMetadata->releases;
$release = end($releases);
$version = $release['version'];
$downloadUrl = $release['artifacts']['package'][0]['url'];
$authors = array_map(
fn($author) => ['name' => $author['name'], 'url' => $author['url'] ?? null],
$fairMetadata->authors,
);
$security = is_array($fairMetadata->security)
? array_map(
fn($item) => array_filter($item, fn($value) => $value !== null),
$fairMetadata->security,
)
: [];
$tags = $fairMetadata->raw_metadata['keywords'] ?? [];
$ret = [
'did' => $fairMetadata->id,
'type' => $fairMetadata->type,
'origin' => Origin::FAIR->value,
'slug' => $fairMetadata->slug,
'name' => $fairMetadata->name,
'description' => $fairMetadata->description,
'download_url' => $downloadUrl,
'version' => $version,
'license' => $fairMetadata->license,
'raw_metadata' => $fairMetadata->raw_metadata,
'authors' => $authors,
'security' => $security,
'releases' => $releases,
'tags' => $tags,
];
if (is_array($fairMetadata->sections)) {
$ret['sections'] = $fairMetadata->sections;
}
return $ret;
}
/**
* @param Plugin $plugin
* @return array<string, mixed>
*/
#[Transforms(Plugin::class)]
public static function fromPlugin(Plugin $plugin): array
{
if ($matches = Regex::match('/^<a href="([^"]+)">([^<]+)<\/a>$/', $plugin->author)) {
$authorUrl = $matches[1];
$authorName = $matches[2];
} else {
$authorUrl = null;
$authorName = $plugin->author;
}
$security = [
[
'url' => 'https://wordpress.org/about/security/',
],
];
$sections = $plugin->ac_raw_metadata['sections'] ?? null;
$releases = [
[
'version' => $plugin->version,
'artifacts' => [
'package' => [
[
'url' => $plugin->download_link,
],
],
],
],
];
$tags = $plugin->tags()->pluck('name')->toArray();
$packageInfo = app()->make(PackageDIDService::class);
$did = $packageInfo->generateWebDid(PackageType::PLUGIN->value, $plugin->slug);
$ret = [
'did' => $did,
'type' => PackageType::PLUGIN->value,
'origin' => Origin::WP->value,
'slug' => $plugin->slug,
'name' => $plugin->name,
'description' => $plugin->description,
'download_url' => $plugin->download_link,
'version' => $plugin->version,
'license' => $plugin->business_model === 'commercial' ? 'proprietary' : 'GPL', // @todo - proper license
'raw_metadata' => $plugin->ac_raw_metadata,
'authors' => [
[
'name' => $authorName,
'url' => $authorUrl,
],
],
'security' => $security,
'releases' => $releases,
'tags' => $tags,
];
if ($sections) {
$ret['sections'] = $sections;
}
return $ret;
}
/**
* @param Theme $theme
* @return array<string, mixed>
*/
#[Transforms(Theme::class)]
public static function fromTheme(Theme $theme): array
{
$security = [
[
'url' => 'https://wordpress.org/about/security/',
],
];
$releases = [
[
'version' => $theme->version,
'artifacts' => [
'package' => [
[
'url' => $theme->download_link,
],
],
],
],
];
$sections = $theme->ac_raw_metadata['sections'] ?? null;
$tags = $theme->tags()->pluck('name')->toArray();
$packageInfo = app()->make(PackageDIDService::class);
$did = $packageInfo->generateWebDid(PackageType::THEME->value, $theme->slug);
$author = $theme->author;
$authors = $author ? [['name' => $author->user_nicename, 'url' => $author->author_url]] : [];
$ret = [
'did' => $did,
'type' => PackageType::THEME->value,
'origin' => Origin::WP->value,
'slug' => $theme->slug,
'name' => $theme->name,
'description' => $theme->description,
'download_url' => $theme->download_link,
'version' => $theme->version,
'license' => $theme->is_commercial ? 'proprietary' : 'GPL', // @todo - proper license
'raw_metadata' => $theme->ac_raw_metadata,
'authors' => $authors,
'security' => $security,
'releases' => $releases,
'tags' => $tags,
];
if ($sections) {
$ret['sections'] = $sections;
}
return $ret;
}
/** @return array<string, mixed> */
public static function rules(): array
{
return [
'did' => ['required', 'string'],
'type' => ['required', 'string'],
'slug' => ['nullable', 'string'],
'name' => ['nullable', 'string'],
'description' => ['nullable', 'string'],
'download_url' => ['required', 'string'],
'version' => ['required', 'string'],
'origin' => ['required', 'string', 'in:' . implode(',', Origin::values())],
'raw_metadata' => ['required', 'array'],
// 'security' => ['required', 'array'], // [chuck 2025-10-29] disabled entirely
'releases' => ['required', 'array'],
'tags' => ['sometimes', 'array'],
'sections' => ['sometimes', 'array'],
];
}
}