MediaWiki master
SpecBasedModule.php
Go to the documentation of this file.
1<?php
2
4
15use Wikimedia\ObjectFactory\ObjectFactory;
16
42
43 private string $definitionFile;
44
45 private ?array $moduleDef = null;
46
47 private ?int $routeFileTimestamp = null;
48
49 private ?string $configHash = null;
50
54 public function __construct(
55 string $definitionFile,
56 Router $router,
57 string $pathPrefix,
59 BasicAuthorizerInterface $basicAuth,
60 ObjectFactory $objectFactory,
61 Validator $restValidator,
62 ErrorReporter $errorReporter,
63 HookContainer $hookContainer
64 ) {
65 parent::__construct(
66 $router,
69 $basicAuth,
70 $objectFactory,
71 $restValidator,
72 $errorReporter,
73 $hookContainer
74 );
75 $this->definitionFile = $definitionFile;
76 }
77
81 protected function getConfigHash(): string {
82 if ( $this->configHash === null ) {
83 $this->configHash = md5( json_encode( [
84 'class' => __CLASS__,
85 'version' => 1,
86 'fileTimestamps' => $this->getRouteFileTimestamp()
87 ] ) );
88 }
89 return $this->configHash;
90 }
91
95 private function getModuleDefinition(): 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 $localizer = new JsonLocalizer( $this->responseFactory );
126 $moduleDef = $localizer->localizeJson( $moduleDef );
127
128 $this->moduleDef = $moduleDef;
129 return $this->moduleDef;
130 }
131
135 private function getRouteFileTimestamp(): int {
136 if ( $this->routeFileTimestamp === null ) {
137 $this->routeFileTimestamp = filemtime( $this->definitionFile );
138 }
139 return $this->routeFileTimestamp;
140 }
141
147 public function getDefinedPaths(): array {
148 $paths = [];
149 $moduleDef = $this->getModuleDefinition();
150
151 foreach ( $moduleDef['paths'] as $path => $pSpec ) {
152 $paths[$path] = [];
153 foreach ( $pSpec as $method => $opSpec ) {
154 $paths[$path][] = strtoupper( $method );
155 }
156 }
157
158 return $paths;
159 }
160
161 protected function initRoutes(): void {
162 $moduleDef = $this->getModuleDefinition();
163
164 // The structure is similar to OpenAPI, see docs/rest/mwapi.1.0.json
165 foreach ( $moduleDef['paths'] as $path => $pathSpec ) {
166 foreach ( $pathSpec as $method => $opSpec ) {
167 $info = $this->makeRouteInfo( $path, $opSpec );
168 $this->addRoute( $method, $path, $info );
169 }
170 }
171 }
172
178 private function makeRouteInfo( string $path, array $opSpec ): array {
179 static $objectSpecKeys = [
180 'class',
181 'factory',
182 'services',
183 'optional_services',
184 'args',
185 ];
186
187 static $oasKeys = [
188 'parameters',
189 'responses',
190 'summary',
191 'description',
192 'tags',
193 'externalDocs',
194 ];
195
196 if ( isset( $opSpec['redirect'] ) ) {
197 // Redirect shorthand
198 $opSpec['handler'] = [
199 'class' => RedirectHandler::class,
200 'redirect' => $opSpec['redirect'],
201 ];
202 unset( $opSpec['redirect'] );
203 }
204
205 $handlerSpec = $opSpec['handler'] ?? null;
206 if ( !$handlerSpec ) {
207 throw new RouteDefinitionException( 'Missing handler spec' );
208 }
209
210 $info = [
211 'spec' => array_intersect_key( $handlerSpec, array_flip( $objectSpecKeys ) ),
212 'config' => array_diff_key( $handlerSpec, array_flip( $objectSpecKeys ) ),
213 'OAS' => array_intersect_key( $opSpec, array_flip( $oasKeys ) ),
214 'path' => $path,
215 ];
216
217 return $info;
218 }
219
220 public function getOpenApiInfo() {
221 $def = $this->getModuleDefinition();
222 return $def['info'] ?? [];
223 }
224
225}
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:81
Utility class for json localization needs in the REST API.
MatcherBasedModules respond to requests by matching the requested path against a list of known routes...
ResponseFactory $responseFactory
Definition Module.php:45
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, HookContainer $hookContainer)
getOpenApiInfo()
Return an array with data to be included in an OpenAPI "info" object describing this module.
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:30
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.