MediaWiki master
SpecBasedModule.php
Go to the documentation of this file.
1<?php
2
4
13use Wikimedia\ObjectFactory\ObjectFactory;
14
40
41 private string $definitionFile;
42
43 private ?array $moduleDef = null;
44
45 private ?int $routeFileTimestamp = null;
46
47 private ?string $configHash = null;
48
52 public function __construct(
53 string $definitionFile,
54 Router $router,
55 string $pathPrefix,
57 BasicAuthorizerInterface $basicAuth,
58 ObjectFactory $objectFactory,
59 Validator $restValidator,
60 ErrorReporter $errorReporter
61 ) {
62 parent::__construct(
63 $router,
66 $basicAuth,
67 $objectFactory,
68 $restValidator,
69 $errorReporter
70 );
71 $this->definitionFile = $definitionFile;
72 }
73
79 protected function getConfigHash(): string {
80 if ( $this->configHash === null ) {
81 $this->configHash = md5( json_encode( [
82 'class' => __CLASS__,
83 'version' => 1,
84 'fileTimestamps' => $this->getRouteFileTimestamp()
85 ] ) );
86 }
87 return $this->configHash;
88 }
89
95 private function loadDefinition(): array {
96 if ( $this->moduleDef !== null ) {
97 return $this->moduleDef;
98 }
99
100 $this->routeFileTimestamp = filemtime( $this->definitionFile );
101 $moduleDef = $this->loadJsonFile( $this->definitionFile );
102
103 if ( !$moduleDef ) {
104 throw new ModuleConfigurationException(
105 'Malformed module definition file: ' . $this->definitionFile
106 );
107 }
108
109 if ( !isset( $moduleDef['mwapi'] ) ) {
110 throw new ModuleConfigurationException(
111 'Missing mwapi version field in ' . $this->definitionFile
112 );
113 }
114
115 // Require OpenAPI version 3.1 or compatible.
116 if ( !version_compare( $moduleDef['mwapi'], '1.0.999', '<=' ) ||
117 !version_compare( $moduleDef['mwapi'], '1.0.0', '>=' )
118 ) {
119 throw new ModuleConfigurationException(
120 "Unsupported openapi version {$moduleDef['mwapi']} in "
121 . $this->definitionFile
122 );
123 }
124
125 $this->moduleDef = $moduleDef;
126 return $this->moduleDef;
127 }
128
132 private function getRouteFileTimestamp(): int {
133 if ( $this->routeFileTimestamp === null ) {
134 $this->routeFileTimestamp = filemtime( $this->definitionFile );
135 }
136 return $this->routeFileTimestamp;
137 }
138
144 public function getDefinedPaths(): array {
145 $paths = [];
146 $moduleDef = $this->loadDefinition();
147
148 foreach ( $moduleDef['paths'] as $path => $pSpec ) {
149 $paths[$path] = [];
150 foreach ( $pSpec as $method => $opSpec ) {
151 $paths[$path][] = strtoupper( $method );
152 }
153 }
154
155 return $paths;
156 }
157
158 protected function initRoutes(): void {
159 $moduleDef = $this->loadDefinition();
160
161 // The structure is similar to OpenAPI, see docs/rest/mwapi.1.0.json
162 foreach ( $moduleDef['paths'] as $path => $pathSpec ) {
163 foreach ( $pathSpec as $method => $opSpec ) {
164 $info = $this->makeRouteInfo( $path, $opSpec );
165 $this->addRoute( $method, $path, $info );
166 }
167 }
168 }
169
175 private function makeRouteInfo( string $path, array $opSpec ): array {
176 static $objectSpecKeys = [
177 'class',
178 'factory',
179 'services',
180 'optional_services',
181 'args',
182 ];
183
184 static $oasKeys = [
185 'parameters',
186 'responses',
187 'summary',
188 'description',
189 'tags',
190 'externalDocs',
191 ];
192
193 if ( isset( $opSpec['redirect'] ) ) {
194 // Redirect shorthand
195 $opSpec['handler'] = [
196 'class' => RedirectHandler::class,
197 'redirect' => $opSpec['redirect'],
198 ];
199 unset( $opSpec['redirect'] );
200 }
201
202 $handlerSpec = $opSpec['handler'] ?? null;
203 if ( !$handlerSpec ) {
204 throw new RouteDefinitionException( 'Missing handler spec' );
205 }
206
207 $info = [
208 'spec' => array_intersect_key( $handlerSpec, array_flip( $objectSpecKeys ) ),
209 'config' => array_diff_key( $handlerSpec, array_flip( $objectSpecKeys ) ),
210 'OAS' => array_intersect_key( $opSpec, array_flip( $oasKeys ) ),
211 'path' => $path,
212 ];
213
214 return $info;
215 }
216}
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:81
MatcherBasedModules respond to requests by matching the requested path against a list of known routes...
ResponseFactory $responseFactory
Definition Module.php:41
A Module that is based on a module definition file similar to an OpenAPI spec.
initRoutes()
Initialize matchers by calling addRoute() for each known route.
__construct(string $definitionFile, Router $router, string $pathPrefix, ResponseFactory $responseFactory, BasicAuthorizerInterface $basicAuth, ObjectFactory $objectFactory, Validator $restValidator, ErrorReporter $errorReporter)
getConfigHash()
Get a config version hash for cache invalidation.
Exception indicating incorrect REST module configuration.
Generates standardized response objects.
The REST router is responsible for gathering module configuration, matching an input path against the...
Definition Router.php:29
Wrapper for ParamValidator.
Definition Validator.php:37
An interface used by Router to ensure that the client has "basic" access, i.e.
An ErrorReporter internally reports an error that happened during the handling of a request.