Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
75.00% covered (warning)
75.00%
3 / 4
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiFeatures
75.00% covered (warning)
75.00%
3 / 4
66.67% covered (warning)
66.67%
2 / 3
3.14
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 available
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 all
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace LambdaTwelve\OneRecord\Spec;
6
7use InvalidArgumentException;
8
9/**
10 * The one table of API features that depend on the negotiated API version.
11 *
12 * Serializers and endpoints ask here instead of comparing versions, so adding
13 * an edition means adding rows, and a 2.2 partner never receives a property
14 * that only exists in 2.3.
15 */
16final class ApiFeatures
17{
18    /** Action requests record when the current status started (api:hasRequestStatusSince). */
19    public const string REQUEST_STATUS_SINCE = Namespaces::API . 'hasRequestStatusSince';
20
21    /** Action requests expose previous statuses (api:hasRequestStatusHistory). */
22    public const string REQUEST_STATUS_HISTORY = Namespaces::API . 'hasRequestStatusHistory';
23
24    /** Errors carry a severity (api:hasSeverity). */
25    public const string ERROR_SEVERITY = Namespaces::API . 'hasSeverity';
26
27    /** Access delegations may expire (api:expiresAt on api:AccessDelegation). */
28    public const string ACCESS_DELEGATION_EXPIRY = Namespaces::API . 'AccessDelegation' . '|' . Namespaces::API . 'expiresAt';
29
30    /** Access delegations name exactly one organisation in api:isRequestedFor. */
31    public const string SINGLE_DELEGATE = Namespaces::API . 'AccessDelegation' . '|single-delegate';
32
33    /** POST /logistics-events creates one event on several objects (207 Multi-Status). */
34    public const string BULK_LOGISTICS_EVENTS = Namespaces::API . 'MultiStatusResponse';
35
36    /** Illegal action-request revocations answer 422 instead of 400. */
37    public const string REVOCATION_422 = Namespaces::API . 'ActionRequest' . '|revocation-422';
38
39    /** @var array<string, ApiVersion> feature => first version that has it */
40    private const array SINCE = [
41        self::REQUEST_STATUS_SINCE => ApiVersion::V2_3_0,
42        self::REQUEST_STATUS_HISTORY => ApiVersion::V2_3_0,
43        self::ERROR_SEVERITY => ApiVersion::V2_3_0,
44        self::ACCESS_DELEGATION_EXPIRY => ApiVersion::V2_3_0,
45        self::SINGLE_DELEGATE => ApiVersion::V2_3_0,
46        self::BULK_LOGISTICS_EVENTS => ApiVersion::V2_3_0,
47        self::REVOCATION_422 => ApiVersion::V2_3_0,
48    ];
49
50    private function __construct() {}
51
52    public static function available(ApiVersion $version, string $feature): bool
53    {
54        $since = self::SINCE[$feature] ?? throw new InvalidArgumentException(\sprintf('Unknown API feature "%s".', $feature));
55
56        return $version->isAtLeast($since);
57    }
58
59    /**
60     * @return list<string>
61     */
62    public static function all(): array
63    {
64        return array_keys(self::SINCE);
65    }
66}