Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.94% covered (success)
93.94%
31 / 33
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
ServerBuilder
93.94% covered (success)
93.94%
31 / 33
50.00% covered (danger)
50.00%
2 / 4
6.01
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
 routes
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
2
 check
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 build
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace LambdaTwelve\OneRecord\Server;
6
7use LambdaTwelve\OneRecord\Server\Endpoint\AccessDelegationsEndpoint;
8use LambdaTwelve\OneRecord\Server\Endpoint\ActionRequestEndpoint;
9use LambdaTwelve\OneRecord\Server\Endpoint\AuditTrailEndpoint;
10use LambdaTwelve\OneRecord\Server\Endpoint\BulkLogisticsEventsEndpoint;
11use LambdaTwelve\OneRecord\Server\Endpoint\ChangeLogisticsObjectEndpoint;
12use LambdaTwelve\OneRecord\Server\Endpoint\CreateLogisticsObjectEndpoint;
13use LambdaTwelve\OneRecord\Server\Endpoint\LogisticsEventEndpoint;
14use LambdaTwelve\OneRecord\Server\Endpoint\LogisticsEventsEndpoint;
15use LambdaTwelve\OneRecord\Server\Endpoint\LogisticsObjectEndpoint;
16use LambdaTwelve\OneRecord\Server\Endpoint\NotificationsEndpoint;
17use LambdaTwelve\OneRecord\Server\Endpoint\ServerInformationEndpoint;
18use LambdaTwelve\OneRecord\Server\Endpoint\SubscriptionsEndpoint;
19use LambdaTwelve\OneRecord\Server\Endpoint\VerificationEndpoint;
20use LambdaTwelve\OneRecord\Spec\ApiVersion;
21
22/**
23 * Wires every endpoint of the API onto a router and returns the handler. A
24 * host calls this once with its Services; the in-memory server for tests and
25 * bin/serve calls it too.
26 */
27final class ServerBuilder
28{
29    private function __construct() {}
30
31    /**
32     * The route table: every endpoint the server answers, with every method it
33     * handles. Hosts whose framework wants named routes iterate this instead of
34     * copying the patterns; a new endpoint in the SDK then needs no host change.
35     *
36     * @return list<Route>
37     */
38    public static function routes(bool $bulkLogisticsEvents = false): array
39    {
40        $routes = [
41            new Route('server-information', ['GET', 'HEAD'], '/'),
42            new Route('logistics-objects.create', ['POST'], '/logistics-objects'),
43            new Route('logistics-object', ['GET', 'HEAD', 'PATCH', 'POST'], '/logistics-objects/{id}'),
44            new Route('logistics-object.audit-trail', ['GET', 'HEAD'], '/logistics-objects/{id}/audit-trail'),
45            new Route('logistics-object.events', ['POST', 'GET', 'HEAD'], '/logistics-objects/{id}/logistics-events'),
46            new Route('logistics-object.event', ['GET', 'HEAD'], '/logistics-objects/{id}/logistics-events/{eventId}'),
47            new Route('notifications', ['POST'], '/notifications'),
48            new Route('subscriptions', ['GET', 'HEAD', 'POST'], '/subscriptions'),
49            new Route('access-delegations', ['POST'], '/access-delegations'),
50            new Route('action-request', ['GET', 'HEAD', 'PATCH', 'DELETE'], '/action-requests/{id}'),
51        ];
52        if ($bulkLogisticsEvents) {
53            $routes[] = new Route('logistics-events.bulk', ['POST'], '/logistics-events', ApiVersion::V2_3_0);
54        }
55
56        return $routes;
57    }
58
59    /**
60     * The wiring findings Services logs at construction, for a host's status
61     * page (Drupal's hook_requirements, Laravel's about command): empty when
62     * nothing is amiss.
63     *
64     * @return list<string>
65     */
66    public static function check(Services $services): array
67    {
68        return $services->checks();
69    }
70
71    public static function build(Services $services): OneRecordServer
72    {
73        $router = new Router();
74        // One Route may be served by several endpoint classes (GET, PATCH and POST on an object differ).
75        $router->add(['GET', 'HEAD'], '/', new ServerInformationEndpoint($services));
76        $router->add(['POST'], '/logistics-objects', new CreateLogisticsObjectEndpoint($services));
77        $router->add(['GET', 'HEAD'], '/logistics-objects/{id}', new LogisticsObjectEndpoint($services));
78        $router->add(['PATCH'], '/logistics-objects/{id}', new ChangeLogisticsObjectEndpoint($services));
79        $router->add(['POST'], '/logistics-objects/{id}', new VerificationEndpoint($services));
80        $router->add(['GET', 'HEAD'], '/logistics-objects/{id}/audit-trail', new AuditTrailEndpoint($services));
81        $router->add(['POST', 'GET', 'HEAD'], '/logistics-objects/{id}/logistics-events', new LogisticsEventsEndpoint($services));
82        $router->add(['GET', 'HEAD'], '/logistics-objects/{id}/logistics-events/{eventId}', new LogisticsEventEndpoint($services));
83        $router->add(['POST'], '/notifications', new NotificationsEndpoint($services));
84        $router->add(['GET', 'HEAD', 'POST'], '/subscriptions', new SubscriptionsEndpoint($services));
85        $router->add(['POST'], '/access-delegations', new AccessDelegationsEndpoint($services));
86        $router->add(['GET', 'HEAD', 'PATCH', 'DELETE'], '/action-requests/{id}', new ActionRequestEndpoint($services));
87        if ($services->config->bulkLogisticsEvents) {
88            $router->add(['POST'], '/logistics-events', new BulkLogisticsEventsEndpoint($services), ApiVersion::V2_3_0);
89        }
90
91        return new OneRecordServer($services->config, $router, $services->authenticator, $services->responder, $services->logger, $services->unitOfWork);
92    }
93}