In order to replace certain /api/rest_v1 endpoints, we
need to have something in MediaWiki that generates a compatible
responser. The MediaWiki REST API error format does not match
RESTbase. So the easiest approach seemed to be to
add a compatibility mode, triggered using the x-restbase-compat
header. This can be set via the gateway when rerouting the request.
Bug: T374136
Change-Id: I73934940d367be52941bd27861c248ab5bcfb5d2
And deprecated aliases for the the no namespaced classes.
ReplicatedBagOStuff that already is deprecated isn't moved.
Bug: T353458
Change-Id: Ie01962517e5b53e59b9721e9996d4f1ea95abb51
This splits RouteFileModule into two classes, ExtraRoutesModule and
SpecBasedModule.
ExtraRoutesModule has no module prefix and supports
only "flat" route definition files and additional routes from
extension.json.
SpecBasedModule represents a single module defined in a definition
file similar to an OpenAPI spec. The idea is that a full OpenAPI spec
can be generated by filling in any missing information based on
information provided by the Handler implementation. In particular, the
definition of parameters and request body schemas will be generated.
A JSON schema for the new file format is added under docs/rest/.
Support for the intermediate format introduced in Iebcde4645d4 is
removed. It was not included in a release and was not being used outside
core tests.
Bug: T366837
Change-Id: I4ce306b0997f80b78a3d901e38bbfa8445bed604
Mostly used find-and-replace:
Find:
/\*[\*\s]+@var (I?[A-Z](\w+)(?:Interface)?)[\s\*]+/\s*(private|protected|public) (\$[a-z]\w+;\n)((?=\s*/\*[\*\s]+@var (I?[A-Z](\w+)(?:Interface)?))\n|)
Replace with:
\3 \1 \4
More could be done, but to keep this patch reasonably sized, I only
changed the most obvious and unambiguously correct cases.
In some cases, I also removed redundant doc comments on the
constructor, and re-ordered the properties to match the constructor.
Change-Id: Ifa710fdf4d8d44a2d7244798b787a1b2a58c35a7
This fixes the handling for the .../rest.php and .../rest.php/ paths.
The handling for the first case (no "/" at the end) is hard-coded to
redirect to the second case (with a "/") at the end. The second case
can then be handled by a regular route definition.
Change-Id: I9782b17e524f96d7dff4a8e2524fce4ed0940379
When a "pretty" RestPath is configured for outside requests, we still
need the "ugly" path to work for backwards compatibility and internal
requests.
For example, the REST API could be available through /api/ as
well as /w/rest.php. This is equivalent to supporting page views
at /wiki/ as well as /w/index.php.
We can already set RestPath to /api, but then requests to /w/rest.php would fail. We need a way to support both at once.
Change-Id: I9ff8e7345e88b1a4235b6870ec04a87527ed271f
In Iebcde4645d472d2 I broke the way we generate metrics keys from
endpoint paths. Instead of using the declared paths with placeholders,
we were recording the actual paths, resulting in an explosion of metrics
keys.
This moves metrics logging from Router into Module, where the declared
path is available. This patch also introduces regression tests for the
issue.
Bug: T365111
Change-Id: I2c9ddfe6e28aaecd313356894f17033e2db59073
Modules group together endpoints by a shared prefix. The idea is that each module has its own version and can generated self-contained self-documentation. This allows clients to have clear expectations about the endpoints of each module, no matter what wiki they are accessing. So far, each wiki may be exposing a different set of endpoints, with no way to provide a spec that describes that set of endpoints in a way that would be consistent across wikis and stable over time.
Bug: T362480
Change-Id: Iebcde4645d472d27eee5a30adb6eee12cc7d046b
Currently, all latency measurments for non-error responses are conflated
into a single value for each endpoint. It would however be useful to be
able to count and time different non-error cases, such as
normalization redirects, wiki redirects, not-modified responses, etc.
NOTE: This will break some Grafana graphs, particularly on the REST API
dashboard: https://grafana.wikimedia.org/d/2pCoanoVk/mw-rest-api
Change-Id: If687be44e964ad5d90fbd4d67e54e6b5d5986fca
Make the Router class know about redirect definitions, so it is no
necessary to explcitly specify the RedirectHandler.
Bug: T361840
Change-Id: I2f3bce5b426639f1c4ecf55a6c003794019acd14
GET requests SHOULD NOT have a body. However, there seem to be quite a
few clients that send one anyway. Let's ignore it for now.
The next step would be to check if the body is empty, and fail if it
isn't.
Bug: T359509
Change-Id: I1c1b9de37e8e3b9e8c4500c0477e40c0a44db32d
This reverts commit 890558f1fa.
This restores Id584208d9b67d877606a0add1d71c9b1784cdb1b with some fixes.
Bug: T323786
Bug: T352742
Change-Id: Ib31c451ddd75b06c95a544c8a3d2a64b32264126
This is only enabled in development mode for now.
It's intended as a baseline for further development,
the feature is not ready for production.
Bug: T323786
Change-Id: Id584208d9b67d877606a0add1d71c9b1784cdb1b
Co-authored-by: Atieno <pnjira@wikimedia.org>
Make HTTP redirects returned by the REST API use relative
URLs in the Location header. This ensures interoperability with the WMF
service mesh, avoiding issues with internal services trying to resolve
redirects by accessing the canonical external URLs.
Note that relative URLs are allowed in the Location header per RFC 7231,
while the obsolete RFC 2616 required it to be absolute.
Bug: T349001
Change-Id: Ibb38c30eb66aaed14ab3ad01ffc42c348a0d07c8
Use this class to track the entry point and handler used for requests,
making it available for use in profiling, stats, and logging code.
This makes it possible for periodic and/or shutdown profiling callbacks
to know the basic action handler that applies to the request (if any).
Metric names can easily include this string along with MW_ENTRY_POINT
to create per-action profiling dashboards.
This info cannot otherwise be acquired from things like excimer stack
traces since the router and handler classes do not appear in the stack
during PRESEND deferred updates and variations like ApiMain/SpecialPage
"inclusion mode" would have to be detected somehow.
Bug: T330810
Change-Id: Icca5a7a343faeeb18652994c96752acb61a61fd1
This is a modified version of Ie282bc5b5f5df0bbd6a40c8362ba73fcbbf36c2e
which was reverted in 5c7cca8776.
Bug: T321969
Change-Id: I566d54a473aa51c4cdaada21a49d63c0624aab93
This reverts commit d32c260ed0.
Reason for revert: Timo has reservations, I'll submit an updated version later.
Change-Id: I71d4d61a879fda4dccfc105127446cfedde75a7b
This adds applyCacheControl() to the Handler base class.
REST handlers can override this method to apply cache control to the
response.
Change-Id: Ib31b91a0048dbc57fbcadfec6533e672c1567509
The ParsoidHanlder base class is used by the Transform endpoint in core,
which should redirect to core's public /page and /revision endpoints,
using URLs based on the CanonicalServer setting.
It is also used by the Page and Transform endpoints in the parsoid
extension, which need to redirect to private endpoints, using URLs based
on the InternalServer setting.
This patch allows that distinction to be made while using the standard
mechanism for determining route URLs provided by the REST framework. So
far, we had a work-around in place that duplicated that logic.
Bug: T311867
Depends-On: I85ac2f2a8626989e111265869ff301fc72dfaa15
Change-Id: I8e154ded4b93b1e7201fc302a968bd88211666bc
We need a way to determine the URL of routes that are not public,
and should be using $wgInternalServer rather than $wgCanonicalServer.
This patch also refactors some test code to make changes to the
constructor of the Router class less painful.
Bug: T311867
Change-Id: If1878365d019434bb862c643c2350b63479c9844
All the other ways of doing it were ridiculous and much harder to read,
and usually required repeating the needle expression (to get its
length). I found these occurrences by grepping for various expressions,
but I undoubtedly missed some.
I didn't try replacing the many instances of strpos(...) === 0 with
str_starts_with(...), because I think they're readable enough as-is
(although less efficient). Likewise I didn't try porting strpos(...) !==
false to str_contains(...). For case-insensitive comparisons, Tim
Starling requested that we stick with substr_compare() because it's more
efficient than calling strtolower().
On PHP < 8 these functions will be included with a polyfill via
vendor/autoload.php. This is included at the beginning of
includes/AutoLoader.php, so if our autoloader has been included the
polyfill will be available. This means it should be safe to call these
functions from any code that would not be usable without our autoloader.
Three uses that Tim Starling identified as being performance-sensitive
have been split out to a separate commit for porting after the switch to
PHP 8.
Change-Id: I113a8d052b6845852c15969a2f0e6fbbe3e9f8d9
ResponseFactory::createFromException already had support for arbitrary
exceptions, but Router was so far only using it for HttpExceptions,
leaving other kinds of exceptions uncaught.
In addition to catching all exceptions and generating an appropriate
JSON response for them, this patch introduces the ErrorReporter
interface, with an MWErrorReporter implementation which calls
MWExceptionHandler::rollbackMasterChangesAndLog(). This is how uncaught
errors are handled for requests coming in via api.php, so it seems
appropriate to use the same approach for requests coming in via
rest.php.
Bug: T285984
Change-Id: I0605a7693821ef58fac80ab67f51a742556a37fd
Creates an OPTIONS handler that handles any OPTIONS requests that are
not already handled by a handler. CORS has no mechanism to ensure the
user is authenticated, so the Router will reject cross-origin requests
from anon users.
This change allows authenticated users to make cross-origin
requests if they authenticate with OAuth or if
$wgRestAllowCrossOriginCookieAuth is enabled.
Bug: T232176
Bug: T262712
Change-Id: I128b4bdbec4f6bea35142153c951fd7b79617106
In HandlerTestTrait::getRouteUrl when a query param had a null value,
the query was not being appended to the url at all. Whereas, in
Router::getRouteUrl a null value would still append on the query key.
HandlerTestTrait::getRouteUrl will now append on query params, even if the value
is null. Additionally, Router::getRouteUrl() can accept path params.
Bug: T255582
Change-Id: I3610f6252f2f0e7ec95ca346a6bdcd774e5260f8
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
NOTE: once this is merged, also merge Ie7b47e6868cc on the OAuth repo,
to fix unit tests after a breaking change to Router's constructor
signature.
Bug: T230842
Change-Id: I8f5b92918a58e44a4f2d8c78d234d9f64c2d06bf
This tests validity of parameter definitions for the MW REST API, like
ApiStructureTest does for the Action API.
Bug: T243437
Change-Id: Iac9ca951d00573be6efe00cc07478c3581c84588