Refactored LocoProviderTest

This commit is contained in:
Mathieu Santostefano 2021-05-05 19:27:23 +02:00
parent e03ab57139
commit 40041db7cb
No known key found for this signature in database
GPG Key ID: EB610773AF2B5B5B
2 changed files with 385 additions and 329 deletions

View File

@ -59,19 +59,13 @@ final class LocoProvider implements ProviderInterface
$catalogue = $translatorBag->getCatalogues()[0]; $catalogue = $translatorBag->getCatalogues()[0];
} }
// Create keys on Loco
foreach ($catalogue->all() as $domain => $messages) { foreach ($catalogue->all() as $domain => $messages) {
$ids = []; $createdIds = $this->createAssets(array_keys($messages));
foreach ($messages as $id => $message) { if ($createdIds) {
$ids[] = $id; $this->tagsAssets($createdIds, $domain);
$this->createAsset($id);
}
if ($ids) {
$this->tagsAssets($ids, $domain);
} }
} }
// Push translations in all locales and tag them with domain
foreach ($translatorBag->getCatalogues() as $catalogue) { foreach ($translatorBag->getCatalogues() as $catalogue) {
$locale = $catalogue->getLocale(); $locale = $catalogue->getLocale();
@ -79,10 +73,9 @@ final class LocoProvider implements ProviderInterface
$this->createLocale($locale); $this->createLocale($locale);
} }
foreach ($catalogue->all() as $messages) { foreach ($catalogue->all() as $domain => $messages) {
foreach ($messages as $id => $message) { $ids = $this->getAssetsIds($domain);
$this->translateAsset($id, $message, $locale); $this->translateAssets(array_combine($ids, array_values($messages)), $locale);
}
} }
} }
} }
@ -91,13 +84,30 @@ final class LocoProvider implements ProviderInterface
{ {
$domains = $domains ?: ['*']; $domains = $domains ?: ['*'];
$translatorBag = new TranslatorBag(); $translatorBag = new TranslatorBag();
$responses = [];
foreach ($locales as $locale) { foreach ($locales as $locale) {
foreach ($domains as $domain) { foreach ($domains as $domain) {
$response = $this->client->request('GET', sprintf('export/locale/%s.xlf?filter=%s&status=translated', $locale, $domain)); $responses[] = [
'response' => $this->client->request('GET', sprintf('export/locale/%s.xlf', rawurlencode($locale)), [
'query' => [
'filter' => $domain,
'status' => 'translated',
],
]),
'locale' => $locale,
'domain' => $domain,
];
}
}
foreach ($responses as $response) {
$locale = $response['locale'];
$domain = $response['domain'];
$response = $response['response'];
if (404 === $response->getStatusCode()) { if (404 === $response->getStatusCode()) {
$this->logger->error(sprintf('Locale "%s" for domain "%s" does not exist in Loco.', $locale, $domain)); $this->logger->warning(sprintf('Locale "%s" for domain "%s" does not exist in Loco.', $locale, $domain));
continue; continue;
} }
@ -109,74 +119,107 @@ final class LocoProvider implements ProviderInterface
$translatorBag->addCatalogue($this->loader->load($responseContent, $locale, $domain)); $translatorBag->addCatalogue($this->loader->load($responseContent, $locale, $domain));
} }
}
return $translatorBag; return $translatorBag;
} }
public function delete(TranslatorBagInterface $translatorBag): void public function delete(TranslatorBagInterface $translatorBag): void
{ {
$deletedIds = []; $catalogue = $translatorBag->getCatalogue($this->defaultLocale);
foreach ($translatorBag->getCatalogues() as $catalogue) { if (!$catalogue) {
foreach ($catalogue->all() as $messages) { $catalogue = $translatorBag->getCatalogues()[0];
foreach ($messages as $id => $message) {
if (\in_array($id, $deletedIds, true)) {
continue;
} }
$this->deleteAsset($id); $responses = [];
$deletedIds[] = $id;
foreach (array_keys($catalogue->all()) as $domain) {
foreach ($this->getAssetsIds($domain) as $id) {
$responses[$id] = $this->client->request('DELETE', sprintf('assets/%s.json', $id));
} }
} }
foreach ($responses as $key => $response) {
if (403 === $response->getStatusCode()) {
$this->logger->error('The API key used does not have sufficient permissions to delete assets.');
}
if (200 !== $response->getStatusCode() && 404 !== $response->getStatusCode()) {
$this->logger->error(sprintf('Unable to delete translation key "%s" to Loco: "%s".', $key, $response->getContent(false)));
}
} }
} }
private function createAsset(string $id): void /**
* Returns array of internal Loco's unique ids.
*/
private function getAssetsIds(string $domain): array
{ {
$response = $this->client->request('POST', 'assets', [ $response = $this->client->request('GET', 'assets', ['query' => ['filter' => $domain]]);
if (200 !== $response->getStatusCode()) {
$this->logger->error(sprintf('Unable to get assets from Loco: "%s".', $response->getContent(false)));
}
return array_map(function ($asset) {
return $asset['id'];
}, $response->toArray(false));
}
private function createAssets(array $keys): array
{
$responses = $createdIds = [];
foreach ($keys as $key) {
$responses[$key] = $this->client->request('POST', 'assets', [
'body' => [ 'body' => [
'name' => $id, 'text' => $key,
'id' => $id,
'type' => 'text', 'type' => 'text',
'default' => 'untranslated', 'default' => 'untranslated',
], ],
]); ]);
}
if (409 === $response->getStatusCode()) { foreach ($responses as $key => $response) {
$this->logger->info(sprintf('Translation key "%s" already exists in Loco.', $id), [ if (201 !== $response->getStatusCode()) {
'id' => $id, $this->logger->error(sprintf('Unable to add new translation key "%s" to Loco: (status code: "%s") "%s".', $key, $response->getStatusCode(), $response->getContent(false)));
]); } else {
} elseif (201 !== $response->getStatusCode()) { $createdIds[] = $response->toArray(false)['id'];
$this->logger->error(sprintf('Unable to add new translation key "%s" to Loco: (status code: "%s") "%s".', $id, $response->getStatusCode(), $response->getContent(false)));
} }
} }
private function translateAsset(string $id, string $message, string $locale): void return $createdIds;
}
private function translateAssets(array $translations, string $locale): void
{ {
$response = $this->client->request('POST', sprintf('translations/%s/%s', $id, $locale), [ $responses = [];
foreach ($translations as $id => $message) {
$responses[$id] = $this->client->request('POST', sprintf('translations/%s/%s', $id, $locale), [
'body' => $message, 'body' => $message,
]); ]);
}
foreach ($responses as $id => $response) {
if (200 !== $response->getStatusCode()) { if (200 !== $response->getStatusCode()) {
$this->logger->error(sprintf('Unable to add translation message "%s" (for key: "%s" in locale "%s") to Loco: "%s".', $message, $id, $locale, $response->getContent(false))); $this->logger->error(sprintf('Unable to add translation for key "%s" in locale "%s" to Loco: "%s".', $id, $locale, $response->getContent(false)));
}
} }
} }
private function tagsAssets(array $ids, string $tag): void private function tagsAssets(array $ids, string $tag): void
{ {
$idsAsString = implode(',', array_unique($ids));
if (!\in_array($tag, $this->getTags(), true)) { if (!\in_array($tag, $this->getTags(), true)) {
$this->createTag($tag); $this->createTag($tag);
} }
$response = $this->client->request('POST', sprintf('tags/%s.json', $tag), [ $response = $this->client->request('POST', sprintf('tags/%s.json', $tag), [
'body' => $idsAsString, 'body' => implode(',', $ids),
]); ]);
if (200 !== $response->getStatusCode()) { if (200 !== $response->getStatusCode()) {
$this->logger->error(sprintf('Unable to add tag "%s" on translation keys "%s" to Loco: "%s".', $tag, $idsAsString, $response->getContent(false))); $this->logger->error(sprintf('Unable to tag assets with "%s" on Loco: "%s".', $tag, $response->getContent(false)));
} }
} }
@ -233,13 +276,4 @@ final class LocoProvider implements ProviderInterface
return $carry; return $carry;
}, []); }, []);
} }
private function deleteAsset(string $id): void
{
$response = $this->client->request('DELETE', sprintf('assets/%s.json', $id));
if (200 !== $response->getStatusCode()) {
$this->logger->error(sprintf('Unable to delete translation key "%s" to Loco: "%s".', $id, $response->getContent(false)));
}
}
} }

View File

@ -4,6 +4,7 @@ namespace Symfony\Component\Translation\Bridge\Loco\Tests;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\Component\Translation\Bridge\Loco\LocoProvider; use Symfony\Component\Translation\Bridge\Loco\LocoProvider;
use Symfony\Component\Translation\Loader\ArrayLoader; use Symfony\Component\Translation\Loader\ArrayLoader;
use Symfony\Component\Translation\Loader\LoaderInterface; use Symfony\Component\Translation\Loader\LoaderInterface;
@ -22,195 +23,201 @@ class LocoProviderTest extends ProviderTestCase
return new LocoProvider($client, $loader, $logger, $defaultLocale, $endpoint); return new LocoProvider($client, $loader, $logger, $defaultLocale, $endpoint);
} }
public function toStringProvider(): iterable
{
yield [
$this->createProvider($this->getClient()->withOptions([
'base_uri' => 'https://localise.biz/api/',
'headers' => [
'Authorization' => 'Loco API_KEY',
],
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'localise.biz/api/'),
'loco://localise.biz/api/',
];
yield [
$this->createProvider($this->getClient()->withOptions([
'base_uri' => 'https://example.com',
'headers' => [
'Authorization' => 'Loco API_KEY',
],
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'example.com'),
'loco://example.com',
];
yield [
$this->createProvider($this->getClient()->withOptions([
'base_uri' => 'https://example.com:99',
'headers' => [
'Authorization' => 'Loco API_KEY',
],
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'example.com:99'),
'loco://example.com:99',
];
}
public function testCompleteWriteProcess() public function testCompleteWriteProcess()
{ {
$createAssetResponse = $this->createMock(ResponseInterface::class);
$createAssetResponse->expects($this->exactly(4))
->method('getStatusCode')
->willReturn(201);
$getLocalesResponse = $this->createMock(ResponseInterface::class);
$getLocalesResponse->expects($this->exactly(4))
->method('getStatusCode')
->willReturn(200);
$getLocalesResponse->expects($this->exactly(2))
->method('getContent')
->with(false)
->willReturn('[{"code":"en"}]');
$createLocaleResponse = $this->createMock(ResponseInterface::class);
$createLocaleResponse->expects($this->exactly(2))
->method('getStatusCode')
->willReturn(201);
$translateAssetResponse = $this->createMock(ResponseInterface::class);
$translateAssetResponse->expects($this->exactly(8))
->method('getStatusCode')
->willReturn(200);
$getTagsEmptyResponse = $this->createMock(ResponseInterface::class);
$getTagsEmptyResponse->expects($this->exactly(2))
->method('getStatusCode')
->willReturn(200);
$getTagsEmptyResponse->expects($this->once())
->method('getContent')
->with(false)
->willReturn('[]');
$getTagsNotEmptyResponse = $this->createMock(ResponseInterface::class);
$getTagsNotEmptyResponse->expects($this->exactly(2))
->method('getStatusCode')
->willReturn(200);
$getTagsNotEmptyResponse->expects($this->once())
->method('getContent')
->with(false)
->willReturn('["messages"]');
$createTagResponse = $this->createMock(ResponseInterface::class);
$createTagResponse->expects($this->exactly(4))
->method('getStatusCode')
->willReturn(201);
$tagAssetResponse = $this->createMock(ResponseInterface::class);
$tagAssetResponse->expects($this->exactly(4))
->method('getStatusCode')
->willReturn(200);
$expectedAuthHeader = 'Authorization: Loco API_KEY'; $expectedAuthHeader = 'Authorization: Loco API_KEY';
$responses = [ $responses = [
'createAsset1' => function (string $method, string $url, array $options = []) use ($createAssetResponse, $expectedAuthHeader): ResponseInterface { 'createAsset1' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$expectedBody = http_build_query([ $expectedBody = http_build_query([
'name' => 'a', 'text' => 'a',
'id' => 'a',
'type' => 'text', 'type' => 'text',
'default' => 'untranslated', 'default' => 'untranslated',
]); ]);
$this->assertEquals('POST', $method); $this->assertSame('POST', $method);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertEquals($expectedBody, $options['body']); $this->assertSame($expectedBody, $options['body']);
return $createAssetResponse; return new MockResponse('{"id": "1337"}', ['http_code' => 201]);
}, },
'getTags1' => function (string $method, string $url, array $options = []) use ($getTagsEmptyResponse, $expectedAuthHeader): ResponseInterface { 'getTags1' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertEquals('GET', $method); $this->assertSame('GET', $method);
$this->assertEquals('https://localise.biz/api/tags.json', $url); $this->assertSame('https://localise.biz/api/tags.json', $url);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
return $getTagsEmptyResponse; return new MockResponse('[]');
}, },
'createTag1' => function (string $method, string $url, array $options = []) use ($createTagResponse, $expectedAuthHeader): ResponseInterface { 'createTag1' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertEquals('POST', $method); $this->assertSame('POST', $method);
$this->assertEquals('https://localise.biz/api/tags.json', $url); $this->assertSame('https://localise.biz/api/tags.json', $url);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertEquals(http_build_query(['name' => 'messages']), $options['body']); $this->assertSame(http_build_query(['name' => 'messages']), $options['body']);
return $createTagResponse; return new MockResponse('', ['http_code' => 201]);
}, },
'tagAsset1' => function (string $method, string $url, array $options = []) use ($tagAssetResponse, $expectedAuthHeader): ResponseInterface { 'tagAsset1' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertEquals('POST', $method); $this->assertSame('POST', $method);
$this->assertEquals('https://localise.biz/api/tags/messages.json', $url); $this->assertSame('https://localise.biz/api/tags/messages.json', $url);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertEquals('a', $options['body']); $this->assertSame('1337', $options['body']);
return $tagAssetResponse; return new MockResponse();
}, },
'createAsset2' => function (string $method, string $url, array $options = []) use ($createAssetResponse, $expectedAuthHeader): ResponseInterface { 'createAsset2' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$expectedBody = http_build_query([ $expectedBody = http_build_query([
'name' => 'post.num_comments', 'text' => 'post.num_comments',
'id' => 'post.num_comments',
'type' => 'text', 'type' => 'text',
'default' => 'untranslated', 'default' => 'untranslated',
]); ]);
$this->assertEquals('POST', $method); $this->assertSame('POST', $method);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertEquals($expectedBody, $options['body']); $this->assertSame($expectedBody, $options['body']);
return $createAssetResponse; return new MockResponse('{"id": "1234"}', ['http_code' => 201]);
}, },
'getTags2' => function (string $method, string $url, array $options = []) use ($getTagsNotEmptyResponse, $expectedAuthHeader): ResponseInterface { 'getTags2' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertEquals('GET', $method); $this->assertSame('GET', $method);
$this->assertEquals('https://localise.biz/api/tags.json', $url); $this->assertSame('https://localise.biz/api/tags.json', $url);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
return $getTagsNotEmptyResponse; return new MockResponse('["messages"]');
}, },
'createTag2' => function (string $method, string $url, array $options = []) use ($createTagResponse, $expectedAuthHeader): ResponseInterface { 'createTag2' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertEquals('POST', $method); $this->assertSame('POST', $method);
$this->assertEquals('https://localise.biz/api/tags.json', $url); $this->assertSame('https://localise.biz/api/tags.json', $url);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertEquals(http_build_query(['name' => 'validators']), $options['body']); $this->assertSame(http_build_query(['name' => 'validators']), $options['body']);
return $createTagResponse; return new MockResponse('', ['http_code' => 201]);
}, },
'tagAsset2' => function (string $method, string $url, array $options = []) use ($tagAssetResponse, $expectedAuthHeader): ResponseInterface { 'tagAsset2' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertEquals('POST', $method); $this->assertSame('POST', $method);
$this->assertEquals('https://localise.biz/api/tags/validators.json', $url); $this->assertSame('https://localise.biz/api/tags/validators.json', $url);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertEquals('post.num_comments', $options['body']); $this->assertSame('1234', $options['body']);
return $tagAssetResponse; return new MockResponse();
}, },
'getLocales1' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertSame('GET', $method);
$this->assertSame('https://localise.biz/api/locales', $url);
$this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
'getLocales1' => function (string $method, string $url, array $options = []) use ($getLocalesResponse, $expectedAuthHeader): ResponseInterface { return new MockResponse('[{"code":"en"}]');
$this->assertEquals('GET', $method);
$this->assertEquals('https://localise.biz/api/locales', $url);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
return $getLocalesResponse;
}, },
'getAssetsIds1' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertSame('GET', $method);
$this->assertSame('https://localise.biz/api/assets?filter=messages', $url);
$this->assertSame(['filter' => 'messages'], $options['query']);
$this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
'translateAsset1' => function (string $method, string $url, array $options = []) use ($translateAssetResponse, $expectedAuthHeader): ResponseInterface { return new MockResponse('[{"id":"1337"}]');
$this->assertEquals('POST', $method);
$this->assertEquals('https://localise.biz/api/translations/a/en', $url);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertEquals('trans_en_a', $options['body']);
return $translateAssetResponse;
}, },
'translateAsset2' => function (string $method, string $url, array $options = []) use ($translateAssetResponse, $expectedAuthHeader): ResponseInterface { 'translateAsset1' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertEquals('POST', $method); $this->assertSame('POST', $method);
$this->assertEquals('https://localise.biz/api/translations/post.num_comments/en', $url); $this->assertSame('https://localise.biz/api/translations/1337/en', $url);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertEquals('{count, plural, one {# comment} other {# comments}}', $options['body']); $this->assertSame('trans_en_a', $options['body']);
return $translateAssetResponse; return new MockResponse();
}, },
'getAssetsIds2' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertSame('GET', $method);
$this->assertSame('https://localise.biz/api/assets?filter=validators', $url);
$this->assertSame(['filter' => 'validators'], $options['query']);
$this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
'getLocales2' => function (string $method, string $url, array $options = []) use ($getLocalesResponse, $expectedAuthHeader): ResponseInterface { return new MockResponse('[{"id":"1234"}]');
$this->assertEquals('GET', $method);
$this->assertEquals('https://localise.biz/api/locales', $url);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
return $getLocalesResponse;
}, },
'translateAsset2' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertSame('POST', $method);
$this->assertSame('https://localise.biz/api/translations/1234/en', $url);
$this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertSame('{count, plural, one {# comment} other {# comments}}', $options['body']);
'createLocale1' => function (string $method, string $url, array $options = []) use ($createLocaleResponse, $expectedAuthHeader): ResponseInterface { return new MockResponse();
$this->assertEquals('POST', $method);
$this->assertEquals('https://localise.biz/api/locales', $url);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertEquals('code=fr', $options['body']);
return $createLocaleResponse;
}, },
'getLocales2' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertSame('GET', $method);
$this->assertSame('https://localise.biz/api/locales', $url);
$this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
'translateAsset3' => function (string $method, string $url, array $options = []) use ($translateAssetResponse, $expectedAuthHeader): ResponseInterface { return new MockResponse('[{"code":"en"}]');
$this->assertEquals('POST', $method);
$this->assertEquals('https://localise.biz/api/translations/a/fr', $url);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertEquals('trans_fr_a', $options['body']);
return $translateAssetResponse;
}, },
'translateAsset4' => function (string $method, string $url, array $options = []) use ($translateAssetResponse, $expectedAuthHeader): ResponseInterface { 'createLocale1' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertEquals('POST', $method); $this->assertSame('POST', $method);
$this->assertEquals('https://localise.biz/api/translations/post.num_comments/fr', $url); $this->assertSame('https://localise.biz/api/locales', $url);
$this->assertEquals($expectedAuthHeader, $options['normalized_headers']['authorization'][0]); $this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertEquals('{count, plural, one {# commentaire} other {# commentaires}}', $options['body']); $this->assertSame('code=fr', $options['body']);
return $translateAssetResponse; return new MockResponse('', ['http_code' => 201]);
},
'getAssetsIds3' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertSame('GET', $method);
$this->assertSame('https://localise.biz/api/assets?filter=messages', $url);
$this->assertSame(['filter' => 'messages'], $options['query']);
$this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
return new MockResponse('[{"id":"1337"}]');
},
'translateAsset3' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertSame('POST', $method);
$this->assertSame('https://localise.biz/api/translations/1337/fr', $url);
$this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertSame('trans_fr_a', $options['body']);
return new MockResponse();
},
'getAssetsIds4' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertSame('GET', $method);
$this->assertSame('https://localise.biz/api/assets?filter=validators', $url);
$this->assertSame(['filter' => 'validators'], $options['query']);
$this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
return new MockResponse('[{"id":"1234"}]');
},
'translateAsset4' => function (string $method, string $url, array $options = []) use ($expectedAuthHeader): ResponseInterface {
$this->assertSame('POST', $method);
$this->assertSame('https://localise.biz/api/translations/1234/fr', $url);
$this->assertSame($expectedAuthHeader, $options['normalized_headers']['authorization'][0]);
$this->assertSame('{count, plural, one {# commentaire} other {# commentaires}}', $options['body']);
return new MockResponse();
}, },
]; ];
@ -226,106 +233,126 @@ class LocoProviderTest extends ProviderTestCase
$provider = $this->createProvider((new MockHttpClient($responses))->withOptions([ $provider = $this->createProvider((new MockHttpClient($responses))->withOptions([
'base_uri' => 'https://localise.biz/api/', 'base_uri' => 'https://localise.biz/api/',
'headers' => [ 'headers' => ['Authorization' => 'Loco API_KEY'],
'Authorization' => 'Loco API_KEY',
],
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'localise.biz/api/'); ]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'localise.biz/api/');
$provider->write($translatorBag); $provider->write($translatorBag);
} }
/** /**
* @dataProvider getLocoResponsesForOneLocaleAndOneDomain * @dataProvider getResponsesForOneLocaleAndOneDomain
*/ */
public function testReadForOneLocaleAndOneDomain(string $locale, string $domain, string $responseContent, TranslatorBag $expectedTranslatorBag) public function testReadForOneLocaleAndOneDomain(string $locale, string $domain, string $responseContent, TranslatorBag $expectedTranslatorBag)
{ {
$response = $this->createMock(ResponseInterface::class);
$response->expects($this->once())
->method('getContent')
->willReturn($responseContent);
$response->expects($this->exactly(2))
->method('getStatusCode')
->willReturn(200);
$loader = $this->getLoader(); $loader = $this->getLoader();
$loader->expects($this->once()) $loader->expects($this->once())
->method('load') ->method('load')
->willReturn($expectedTranslatorBag->getCatalogue($locale)); ->willReturn((new XliffFileLoader())->load($responseContent, $locale, $domain));
$locoProvider = $this->createProvider((new MockHttpClient($response))->withOptions([ $provider = $this->createProvider((new MockHttpClient(new MockResponse($responseContent)))->withOptions([
'base_uri' => 'https://localise.biz/api/', 'base_uri' => 'https://localise.biz/api/',
'headers' => [ 'headers' => [
'Authorization' => 'Loco API_KEY', 'Authorization' => 'Loco API_KEY',
], ],
]), $loader, $this->getLogger(), $this->getDefaultLocale(), 'localise.biz/api/'); ]), $loader, $this->getLogger(), $this->getDefaultLocale(), 'localise.biz/api/');
$translatorBag = $locoProvider->read([$domain], [$locale]); $translatorBag = $provider->read([$domain], [$locale]);
// We don't want to assert equality of metadata here, due to the ArrayLoader usage.
foreach ($translatorBag->getCatalogues() as $catalogue) {
$catalogue->deleteMetadata('', '');
}
$this->assertEquals($expectedTranslatorBag->getCatalogues(), $translatorBag->getCatalogues()); $this->assertEquals($expectedTranslatorBag->getCatalogues(), $translatorBag->getCatalogues());
} }
/** /**
* @dataProvider getLocoResponsesForManyLocalesAndManyDomains * @dataProvider getResponsesForManyLocalesAndManyDomains
*/ */
public function testReadForManyLocalesAndManyDomains(array $locales, array $domains, array $responseContents, array $expectedTranslatorBags) public function testReadForManyLocalesAndManyDomains(array $locales, array $domains, array $responseContents, TranslatorBag $expectedTranslatorBag)
{ {
$responses = [];
$consecutiveLoadArguments = [];
$consecutiveLoadReturns = [];
foreach ($locales as $locale) { foreach ($locales as $locale) {
foreach ($domains as $domain) { foreach ($domains as $domain) {
$response = $this->createMock(ResponseInterface::class); $responses[] = new MockResponse($responseContents[$locale][$domain]);
$response->expects($this->once()) $consecutiveLoadArguments[] = [$responseContents[$locale][$domain], $locale, $domain];
->method('getContent') $consecutiveLoadReturns[] = (new XliffFileLoader())->load($responseContents[$locale][$domain], $locale, $domain);
->willReturn($responseContents[$domain][$locale]); }
$response->expects($this->exactly(2)) }
->method('getStatusCode')
->willReturn(200);
$locoProvider = new LocoProvider((new MockHttpClient($response))->withOptions([ $loader = $this->getLoader();
$loader->expects($this->exactly(\count($consecutiveLoadArguments)))
->method('load')
->withConsecutive(...$consecutiveLoadArguments)
->willReturnOnConsecutiveCalls(...$consecutiveLoadReturns);
$provider = $this->createProvider((new MockHttpClient($responses))->withOptions([
'base_uri' => 'https://localise.biz/api/', 'base_uri' => 'https://localise.biz/api/',
'headers' => [ 'headers' => [
'Authorization' => 'Loco API_KEY', 'Authorization' => 'Loco API_KEY',
], ],
]), new XliffFileLoader(), $this->getLogger(), $this->getDefaultLocale(), 'localise.biz/api/'); ]), $loader, $this->getLogger(), $this->getDefaultLocale(), 'localise.biz/api/');
$translatorBag = $locoProvider->read([$domain], [$locale]); $translatorBag = $provider->read($domains, $locales);
// We don't want to assert equality of metadata here, due to the ArrayLoader usage. // We don't want to assert equality of metadata here, due to the ArrayLoader usage.
$translatorBag->getCatalogue($locale)->deleteMetadata('foo', ''); foreach ($translatorBag->getCatalogues() as $catalogue) {
$catalogue->deleteMetadata('', '');
$this->assertEquals($expectedTranslatorBags[$domain]->getCatalogue($locale), $translatorBag->getCatalogue($locale));
}
}
} }
public function toStringProvider(): iterable $this->assertEquals($expectedTranslatorBag->getCatalogues(), $translatorBag->getCatalogues());
}
public function testDeleteProcess()
{ {
yield [ $translatorBag = new TranslatorBag();
new LocoProvider($this->getClient()->withOptions([ $translatorBag->addCatalogue(new MessageCatalogue('en', [
'base_uri' => 'https://localise.biz/api/', 'messages' => ['a' => 'trans_en_a'],
'headers' => [ 'validators' => ['post.num_comments' => '{count, plural, one {# comment} other {# comments}}'],
'Authorization' => 'Loco API_KEY', ]));
], $translatorBag->addCatalogue(new MessageCatalogue('fr', [
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'localise.biz/api/'), 'messages' => ['a' => 'trans_fr_a'],
'loco://localise.biz/api/', 'validators' => ['post.num_comments' => '{count, plural, one {# commentaire} other {# commentaires}}'],
]; ]));
yield [ $provider = $this->createProvider(
new LocoProvider($this->getClient()->withOptions([ new MockHttpClient([
'base_uri' => 'https://example.com', function (string $method, string $url, array $options = []): ResponseInterface {
'headers' => [ $this->assertSame('GET', $method);
'Authorization' => 'Loco API_KEY', $this->assertSame('https://localise.biz/api/assets?filter=messages', $url);
], $this->assertSame(['filter' => 'messages'], $options['query']);
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'example.com'),
'loco://example.com',
];
yield [ return new MockResponse('[{"id":"1337"}]');
new LocoProvider($this->getClient()->withOptions([ },
'base_uri' => 'https://example.com:99', function (string $method, string $url): MockResponse {
'headers' => [ $this->assertSame('DELETE', $method);
'Authorization' => 'Loco API_KEY', $this->assertSame('https://localise.biz/api/assets/1337.json', $url);
],
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'example.com:99'), return new MockResponse();
'loco://example.com:99', },
]; function (string $method, string $url, array $options = []): ResponseInterface {
$this->assertSame('GET', $method);
$this->assertSame('https://localise.biz/api/assets?filter=validators', $url);
$this->assertSame(['filter' => 'validators'], $options['query']);
return new MockResponse('[{"id":"1234"}]');
},
function (string $method, string $url): MockResponse {
$this->assertSame('DELETE', $method);
$this->assertSame('https://localise.biz/api/assets/1234.json', $url);
return new MockResponse();
},
], 'https://localise.biz/api/'),
$this->getLoader(),
$this->getLogger(),
$this->getDefaultLocale(),
'localise.biz/api/'
);
$provider->delete($translatorBag);
} }
public function getLocoResponsesForOneLocaleAndOneDomain(): \Generator public function getResponsesForOneLocaleAndOneDomain(): \Generator
{ {
$arrayLoader = new ArrayLoader(); $arrayLoader = new ArrayLoader();
@ -333,7 +360,7 @@ class LocoProviderTest extends ProviderTestCase
$expectedTranslatorBagEn->addCatalogue($arrayLoader->load([ $expectedTranslatorBagEn->addCatalogue($arrayLoader->load([
'index.hello' => 'Hello', 'index.hello' => 'Hello',
'index.greetings' => 'Welcome, {firstname}!', 'index.greetings' => 'Welcome, {firstname}!',
], 'en', 'messages')); ], 'en'));
yield ['en', 'messages', <<<'XLIFF' yield ['en', 'messages', <<<'XLIFF'
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -363,7 +390,7 @@ XLIFF
$expectedTranslatorBagFr->addCatalogue($arrayLoader->load([ $expectedTranslatorBagFr->addCatalogue($arrayLoader->load([
'index.hello' => 'Bonjour', 'index.hello' => 'Bonjour',
'index.greetings' => 'Bienvenue, {firstname} !', 'index.greetings' => 'Bienvenue, {firstname} !',
], 'fr', 'messages')); ], 'fr'));
yield ['fr', 'messages', <<<'XLIFF' yield ['fr', 'messages', <<<'XLIFF'
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -390,26 +417,24 @@ XLIFF
]; ];
} }
public function getLocoResponsesForManyLocalesAndManyDomains(): \Generator public function getResponsesForManyLocalesAndManyDomains(): \Generator
{ {
$arrayLoader = new ArrayLoader(); $arrayLoader = new ArrayLoader();
$expectedTranslatorBagMessages = new TranslatorBag(); $expectedTranslatorBag = new TranslatorBag();
$expectedTranslatorBagMessages->addCatalogue($arrayLoader->load([ $expectedTranslatorBag->addCatalogue($arrayLoader->load([
'index.hello' => 'Hello', 'index.hello' => 'Hello',
'index.greetings' => 'Welcome, {firstname}!', 'index.greetings' => 'Welcome, {firstname}!',
], 'en', 'messages')); ], 'en'));
$expectedTranslatorBagMessages->addCatalogue($arrayLoader->load([ $expectedTranslatorBag->addCatalogue($arrayLoader->load([
'index.hello' => 'Bonjour', 'index.hello' => 'Bonjour',
'index.greetings' => 'Bienvenue, {firstname} !', 'index.greetings' => 'Bienvenue, {firstname} !',
], 'fr', 'messages')); ], 'fr'));
$expectedTranslatorBag->addCatalogue($arrayLoader->load([
$expectedTranslatorBagValidators = new TranslatorBag();
$expectedTranslatorBagValidators->addCatalogue($arrayLoader->load([
'firstname.error' => 'Firstname must contains only letters.', 'firstname.error' => 'Firstname must contains only letters.',
'lastname.error' => 'Lastname must contains only letters.', 'lastname.error' => 'Lastname must contains only letters.',
], 'en', 'validators')); ], 'en', 'validators'));
$expectedTranslatorBagValidators->addCatalogue($arrayLoader->load([ $expectedTranslatorBag->addCatalogue($arrayLoader->load([
'firstname.error' => 'Le prénom ne peut contenir que des lettres.', 'firstname.error' => 'Le prénom ne peut contenir que des lettres.',
'lastname.error' => 'Le nom de famille ne peut contenir que des lettres.', 'lastname.error' => 'Le nom de famille ne peut contenir que des lettres.',
], 'fr', 'validators')); ], 'fr', 'validators'));
@ -418,8 +443,8 @@ XLIFF
['en', 'fr'], ['en', 'fr'],
['messages', 'validators'], ['messages', 'validators'],
[ [
'messages' => [ 'en' => [
'en' => <<<'XLIFF' 'messages' => <<<'XLIFF'
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd"> <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
<file original="https://localise.biz/user/symfony-translation-provider" source-language="en" datatype="database" tool-id="loco"> <file original="https://localise.biz/user/symfony-translation-provider" source-language="en" datatype="database" tool-id="loco">
@ -440,30 +465,7 @@ XLIFF
</xliff> </xliff>
XLIFF XLIFF
, ,
'fr' => <<<'XLIFF' 'validators' => <<<'XLIFF'
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
<file original="https://localise.biz/user/symfony-translation-provider" source-language="en" datatype="database" tool-id="loco">
<header>
<tool tool-id="loco" tool-name="Loco" tool-version="1.0.25 20201211-1" tool-company="Loco"/>
</header>
<body>
<trans-unit id="loco:5fd89b853ee27904dd6c5f67" resname="index.hello" datatype="plaintext">
<source>index.hello</source>
<target state="translated">Bonjour</target>
</trans-unit>
<trans-unit id="loco:5fd89b8542e5aa5cc27457e2" resname="index.greetings" datatype="plaintext" extradata="loco:format=icu">
<source>index.greetings</source>
<target state="translated">Bienvenue, {firstname} !</target>
</trans-unit>
</body>
</file>
</xliff>
XLIFF
,
],
'validators' => [
'en' => <<<'XLIFF'
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd"> <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
<file original="https://localise.biz/user/symfony-translation-provider" source-language="en" datatype="database" tool-id="loco"> <file original="https://localise.biz/user/symfony-translation-provider" source-language="en" datatype="database" tool-id="loco">
@ -484,7 +486,30 @@ XLIFF
</xliff> </xliff>
XLIFF XLIFF
, ,
'fr' => <<<'XLIFF' ],
'fr' => [
'messages' => <<<'XLIFF'
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
<file original="https://localise.biz/user/symfony-translation-provider" source-language="en" datatype="database" tool-id="loco">
<header>
<tool tool-id="loco" tool-name="Loco" tool-version="1.0.25 20201211-1" tool-company="Loco"/>
</header>
<body>
<trans-unit id="loco:5fd89b853ee27904dd6c5f67" resname="index.hello" datatype="plaintext">
<source>index.hello</source>
<target state="translated">Bonjour</target>
</trans-unit>
<trans-unit id="loco:5fd89b8542e5aa5cc27457e2" resname="index.greetings" datatype="plaintext" extradata="loco:format=icu">
<source>index.greetings</source>
<target state="translated">Bienvenue, {firstname} !</target>
</trans-unit>
</body>
</file>
</xliff>
XLIFF
,
'validators' => <<<'XLIFF'
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd"> <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
<file original="https://localise.biz/user/symfony-translation-provider" source-language="en" datatype="database" tool-id="loco"> <file original="https://localise.biz/user/symfony-translation-provider" source-language="en" datatype="database" tool-id="loco">
@ -507,10 +532,7 @@ XLIFF
, ,
], ],
], ],
[ $expectedTranslatorBag,
'messages' => $expectedTranslatorBagMessages,
'validators' => $expectedTranslatorBagValidators,
],
]; ];
} }
} }