Skip to content

Commit f1a6045

Browse files
jenkins-botGerrit Code Review
authored andcommitted
Merge "REST: Implement fallback in GetPropertyLabelWithFallback"
2 parents 2c73d8e + a31c113 commit f1a6045

File tree

12 files changed

+154
-38
lines changed

12 files changed

+154
-38
lines changed

repo/rest-api/specs/resources/labels/label-with-fallback-for-property.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"responses": {
1717
"200": { "$ref": "../../global/responses.json#/Label" },
1818
"304": { "$ref": "../../global/responses.json#/NotModified" },
19+
"307": { "$ref": "../../global/responses.json#/MovedTemporarily" },
1920
"400": { "$ref": "../../global/responses.json#/InvalidTermByLanguageInput" },
2021
"404": { "$ref": "../../global/responses.json#/ResourceNotFound" },
2122
"412": { "$ref": "../../global/responses.json#/PreconditionFailedError" },

repo/rest-api/src/Application/UseCases/GetPropertyLabelWithFallback/GetPropertyLabelWithFallback.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@
44

55
use Wikibase\Repo\RestApi\Application\UseCases\GetLatestPropertyRevisionMetadata;
66
use Wikibase\Repo\RestApi\Application\UseCases\UseCaseError;
7-
use Wikibase\Repo\RestApi\Domain\Services\PropertyLabelRetriever;
7+
use Wikibase\Repo\RestApi\Domain\Services\PropertyLabelWithFallbackRetriever;
88

99
/**
1010
* @license GPL-2.0-or-later
1111
*/
1212
class GetPropertyLabelWithFallback {
1313

14-
private PropertyLabelRetriever $labelRetriever;
14+
private PropertyLabelWithFallbackRetriever $labelRetriever;
1515
private GetLatestPropertyRevisionMetadata $getRevisionMetadata;
1616
private GetPropertyLabelWithFallbackValidator $validator;
1717

1818
public function __construct(
1919
GetPropertyLabelWithFallbackValidator $validator,
2020
GetLatestPropertyRevisionMetadata $getRevisionMetadata,
21-
PropertyLabelRetriever $labelRetriever
21+
PropertyLabelWithFallbackRetriever $labelRetriever
2222
) {
2323
$this->labelRetriever = $labelRetriever;
2424
$this->getRevisionMetadata = $getRevisionMetadata;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php declare( strict_types=1 );
2+
3+
namespace Wikibase\Repo\RestApi\Domain\Services;
4+
5+
use Wikibase\DataModel\Entity\PropertyId;
6+
use Wikibase\Repo\RestApi\Domain\ReadModel\Label;
7+
8+
/**
9+
* @license GPL-2.0-or-later
10+
*/
11+
interface PropertyLabelWithFallbackRetriever {
12+
13+
public function getLabel( PropertyId $propertyId, string $languageCode ): ?Label;
14+
15+
}

repo/rest-api/src/Infrastructure/DataAccess/FallbackLookupFactoryTermsRetriever.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
use Wikibase\Lib\Store\FallbackLabelDescriptionLookupFactory;
99
use Wikibase\Repo\RestApi\Domain\ReadModel\Label;
1010
use Wikibase\Repo\RestApi\Domain\Services\ItemLabelWithFallbackRetriever;
11+
use Wikibase\Repo\RestApi\Domain\Services\PropertyLabelWithFallbackRetriever;
1112

1213
/**
1314
* @license GPL-2.0-or-later
1415
*/
15-
class FallbackLookupFactoryTermsRetriever implements ItemLabelWithFallbackRetriever {
16+
class FallbackLookupFactoryTermsRetriever implements ItemLabelWithFallbackRetriever, PropertyLabelWithFallbackRetriever {
1617

1718
private FallbackLabelDescriptionLookupFactory $lookupFactory;
1819
private LanguageFactory $languageFactory;

repo/rest-api/src/RouteHandlers/GetPropertyLabelRouteHandler.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class GetPropertyLabelRouteHandler extends SimpleHandler {
2525

2626
private const PROPERTY_ID_PATH_PARAM = 'property_id';
2727
private const LANGUAGE_CODE_PATH_PARAM = 'language_code';
28+
public const ROUTE = '/wikibase/v0/entities/properties/{property_id}/labels/{language_code}';
2829

2930
private GetPropertyLabel $useCase;
3031
private MiddlewareHandler $middlewareHandler;

repo/rest-api/src/RouteHandlers/GetPropertyLabelWithFallbackRouteHandler.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,11 @@ public function run( ...$args ): Response {
6565

6666
public function runUseCase( string $propertyId, string $languageCode ): Response {
6767
try {
68-
$useCaseResponse = $this->useCase->execute( new GetPropertyLabelWithFallbackRequest( $propertyId, $languageCode ) );
69-
return $this->newSuccessResponse( $useCaseResponse );
68+
$response = $this->useCase->execute( new GetPropertyLabelWithFallbackRequest( $propertyId, $languageCode ) );
69+
70+
return $response->getLabel()->getLanguageCode() === $languageCode ?
71+
$this->newSuccessResponse( $response ) :
72+
$this->newLanguageFallbackResponse( $propertyId, $response->getLabel()->getLanguageCode() );
7073
} catch ( UseCaseError $e ) {
7174
return $this->responseFactory->newErrorResponseFromException( $e );
7275
}
@@ -113,4 +116,21 @@ public function checkPreconditions(): ?ResponseInterface {
113116
return null;
114117
}
115118

119+
private function newLanguageFallbackResponse( string $propertyId, string $fallbackLanguageCode ): Response {
120+
$httpResponse = $this->getResponseFactory()->create();
121+
$httpResponse->setHeader(
122+
'Location',
123+
$this->getRouter()->getRouteUrl(
124+
GetPropertyLabelRouteHandler::ROUTE,
125+
[
126+
self::PROPERTY_ID_PATH_PARAM => $propertyId,
127+
self::LANGUAGE_CODE_PATH_PARAM => $fallbackLanguageCode,
128+
]
129+
)
130+
);
131+
$httpResponse->setStatus( 307 );
132+
133+
return $httpResponse;
134+
}
135+
116136
}

repo/rest-api/src/RouteHandlers/openapi.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2593,6 +2593,9 @@
25932593
"304": {
25942594
"$ref": "#/components/responses/NotModified"
25952595
},
2596+
"307": {
2597+
"$ref": "#/components/responses/MovedTemporarily"
2598+
},
25962599
"400": {
25972600
"$ref": "#/components/responses/InvalidTermByLanguageInput"
25982601
},

repo/rest-api/src/WbRestApi.ServiceWiring.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,10 @@ function( MediaWikiServices $services ): ItemSerializationRequestValidatingDeser
691691
return new GetPropertyLabelWithFallback(
692692
WbRestApi::getValidatingRequestDeserializer( $services ),
693693
WbRestApi::getGetLatestPropertyRevisionMetadata( $services ),
694-
WbRestApi::getTermLookupEntityTermsRetriever( $services )
694+
new FallbackLookupFactoryTermsRetriever(
695+
$services->getLanguageFactory(),
696+
WikibaseRepo::getFallbackLabelDescriptionLookupFactory( $services )
697+
)
695698
);
696699
},
697700

repo/rest-api/tests/mocha/api-testing/GetPropertyLabelWithFallbackTest.js

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ const { assertValidError } = require( '../helpers/responseValidator' );
99
describe( newGetPropertyLabelWithFallbackRequestBuilder().getRouteDescription(), () => {
1010
let propertyId;
1111
const propertyEnLabel = `en-label-${utils.uniq()}`;
12+
const fallbackLanguageWithExistingLabel = 'en';
1213

1314
before( async () => {
1415
const testProperty = await createEntity( 'property', {
15-
labels: [ { language: 'en', value: propertyEnLabel } ],
16+
labels: [ { language: fallbackLanguageWithExistingLabel, value: propertyEnLabel } ],
1617
datatype: 'string'
1718
} );
1819
propertyId = testProperty.entity.id;
@@ -41,16 +42,35 @@ describe( newGetPropertyLabelWithFallbackRequestBuilder().getRouteDescription(),
4142
assert.strictEqual( response.body.message, 'The requested resource does not exist' );
4243
} );
4344

44-
it( 'responds 404 if the label does not exist', async () => {
45-
const languageCodeWithNoDefinedLabel = 'ko';
46-
const response = await newGetPropertyLabelWithFallbackRequestBuilder( propertyId, languageCodeWithNoDefinedLabel )
47-
.assertValidRequest()
48-
.makeRequest();
45+
it( 'responds 404 if the label does not exist in the requested or any fallback languages', async () => {
46+
const propertyWithoutFallback = await createEntity( 'property', {
47+
labels: { de: { language: 'de', value: `de-label-${utils.uniq()}` } },
48+
datatype: 'string'
49+
} );
50+
51+
const response = await newGetPropertyLabelWithFallbackRequestBuilder(
52+
propertyWithoutFallback.entity.id,
53+
'ko'
54+
).assertValidRequest().makeRequest();
4955

5056
assertValidError( response, 404, 'resource-not-found', { resource_type: 'label' } );
5157
assert.strictEqual( response.body.message, 'The requested resource does not exist' );
5258
} );
5359

60+
it( '307 - language fallback redirect', async () => {
61+
const languageCodeWithFallback = 'en-ca';
62+
63+
const response = await newGetPropertyLabelWithFallbackRequestBuilder( propertyId, languageCodeWithFallback )
64+
.assertValidRequest()
65+
.makeRequest();
66+
67+
expect( response ).to.have.status( 307 );
68+
69+
assert.isTrue( new URL( response.headers.location ).pathname.endsWith(
70+
`rest.php/wikibase/v0/entities/properties/${propertyId}/labels/${fallbackLanguageWithExistingLabel}`
71+
) );
72+
} );
73+
5474
it( '400 - invalid property ID', async () => {
5575
const response = await newGetPropertyLabelWithFallbackRequestBuilder( 'X123', 'en' )
5676
.assertInvalidRequest()

repo/rest-api/tests/mocha/openapi-validation/GetPropertyLabelWithFallbackTest.js

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,41 @@ describe( newGetPropertyLabelWithFallbackRequestBuilder().getRouteDescription(),
3838
expect( response ).to.satisfyApiSpec;
3939
} );
4040

41-
it( '400 Bad Request response is valid for an invalid property ID', async () => {
42-
const response = await newGetPropertyLabelWithFallbackRequestBuilder( 'X123', languageCode )
43-
.makeRequest();
44-
45-
expect( response ).to.have.status( 400 );
41+
it( '307 Temporary Redirect response is valid for a label with language fallback', async () => {
42+
const languageCodeWithFallback = 'en-ca';
43+
const response = await newGetPropertyLabelWithFallbackRequestBuilder(
44+
propertyId,
45+
languageCodeWithFallback
46+
).makeRequest();
47+
48+
expect( response ).to.have.status( 307 );
4649
expect( response ).to.satisfyApiSpec;
4750
} );
4851

49-
it( '404 Not Found response is valid for a non-existing property', async () => {
50-
const response = await newGetPropertyLabelWithFallbackRequestBuilder( 'P99999', languageCode )
52+
it( '400 Bad Request response is valid for an invalid property ID', async () => {
53+
const response = await newGetPropertyLabelWithFallbackRequestBuilder( 'X123', languageCode )
5154
.makeRequest();
5255

53-
expect( response ).to.have.status( 404 );
56+
expect( response ).to.have.status( 400 );
5457
expect( response ).to.satisfyApiSpec;
5558
} );
5659

57-
it( '404 Not Found response is valid if there is no label in the requested language', async () => {
58-
const response = await newGetPropertyLabelWithFallbackRequestBuilder( propertyId, 'ko' )
59-
.makeRequest();
60-
61-
expect( response ).to.have.status( 404 );
62-
expect( response ).to.satisfyApiSpec;
63-
} );
60+
it(
61+
'404 Not Found response is valid if there is no label in the requested or any fallback language',
62+
async () => {
63+
const propertyWithoutFallback = await createEntity( 'property', {
64+
labels: { de: { language: 'de', value: `de-label-${utils.uniq()}` } },
65+
datatype: 'string'
66+
} );
67+
68+
const response = await newGetPropertyLabelWithFallbackRequestBuilder(
69+
propertyWithoutFallback.entity.id,
70+
'ko'
71+
).makeRequest();
72+
73+
expect( response ).to.have.status( 404 );
74+
expect( response ).to.satisfyApiSpec;
75+
}
76+
);
6477

6578
} );

0 commit comments

Comments
 (0)