From f4ad9b35d07952755babba16556eca1caefc6d7b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Lievremont Date: Thu, 22 May 2014 17:34:22 +0200 Subject: [PATCH] SONAR-5334 Create WS to serialize all bundles for a given language --- .../java/org/sonar/core/i18n/DefaultI18n.java | 3 +- .../server/platform/ServerComponents.java | 129 +++--------------- .../org/sonar/server/platform/ws/L10nWs.java | 72 ++++++++++ .../sonar/server/platform/ws/L10nWsTest.java | 89 ++++++++++++ 4 files changed, 178 insertions(+), 115 deletions(-) create mode 100644 sonar-server/src/main/java/org/sonar/server/platform/ws/L10nWs.java create mode 100644 sonar-server/src/test/java/org/sonar/server/platform/ws/L10nWsTest.java diff --git a/sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java b/sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java index 0ce77a1baec..dc6de022b00 100644 --- a/sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java +++ b/sonar-core/src/main/java/org/sonar/core/i18n/DefaultI18n.java @@ -206,8 +206,7 @@ public class DefaultI18n implements I18n, ServerExtension, BatchExtension, Start return result; } - @VisibleForTesting - Set getPropertyKeys() { + public Set getPropertyKeys() { return propertyToBundles.keySet(); } diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 42eb906dccc..68df4edaf9e 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -52,13 +52,7 @@ import org.sonar.core.measure.db.MeasureFilterDao; import org.sonar.core.metric.DefaultMetricFinder; import org.sonar.core.notification.DefaultNotificationManager; import org.sonar.core.permission.PermissionFacade; -import org.sonar.core.persistence.DaoUtils; -import org.sonar.core.persistence.DatabaseVersion; -import org.sonar.core.persistence.DefaultDatabase; -import org.sonar.core.persistence.MyBatis; -import org.sonar.core.persistence.PreviewDatabaseFactory; -import org.sonar.core.persistence.SemaphoreUpdater; -import org.sonar.core.persistence.SemaphoresImpl; +import org.sonar.core.persistence.*; import org.sonar.core.preview.PreviewCache; import org.sonar.core.profiling.Profiling; import org.sonar.core.purge.PurgeProfiler; @@ -92,33 +86,11 @@ import org.sonar.server.db.DbClient; import org.sonar.server.db.EmbeddedDatabaseFactory; import org.sonar.server.db.migrations.DatabaseMigrations; import org.sonar.server.db.migrations.DatabaseMigrator; -import org.sonar.server.debt.DebtCharacteristicsXMLImporter; -import org.sonar.server.debt.DebtModelBackup; -import org.sonar.server.debt.DebtModelLookup; -import org.sonar.server.debt.DebtModelOperations; -import org.sonar.server.debt.DebtModelPluginRepository; -import org.sonar.server.debt.DebtModelService; -import org.sonar.server.debt.DebtModelXMLExporter; -import org.sonar.server.debt.DebtRulesXMLImporter; +import org.sonar.server.debt.*; import org.sonar.server.duplication.ws.DuplicationsWriter; import org.sonar.server.duplication.ws.DuplicationsWs; import org.sonar.server.es.ESNode; -import org.sonar.server.issue.ActionService; -import org.sonar.server.issue.AssignAction; -import org.sonar.server.issue.CommentAction; -import org.sonar.server.issue.DefaultIssueFinder; -import org.sonar.server.issue.InternalRubyIssueService; -import org.sonar.server.issue.IssueBulkChangeService; -import org.sonar.server.issue.IssueChangelogFormatter; -import org.sonar.server.issue.IssueChangelogService; -import org.sonar.server.issue.IssueCommentService; -import org.sonar.server.issue.IssueService; -import org.sonar.server.issue.IssueStatsFinder; -import org.sonar.server.issue.PlanAction; -import org.sonar.server.issue.PublicRubyIssueService; -import org.sonar.server.issue.ServerIssueStorage; -import org.sonar.server.issue.SetSeverityAction; -import org.sonar.server.issue.TransitionAction; +import org.sonar.server.issue.*; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.issue.actionplan.ActionPlanWs; import org.sonar.server.issue.filter.IssueFilterService; @@ -137,75 +109,27 @@ import org.sonar.server.permission.InternalPermissionService; import org.sonar.server.permission.InternalPermissionTemplateService; import org.sonar.server.permission.PermissionFinder; import org.sonar.server.permission.ws.PermissionsWs; +import org.sonar.server.platform.ws.L10nWs; import org.sonar.server.platform.ws.RestartHandler; import org.sonar.server.platform.ws.ServerWs; import org.sonar.server.platform.ws.SystemWs; -import org.sonar.server.plugins.BatchWs; -import org.sonar.server.plugins.InstalledPluginReferentialFactory; -import org.sonar.server.plugins.PluginDownloader; -import org.sonar.server.plugins.ServerExtensionInstaller; -import org.sonar.server.plugins.ServerPluginJarInstaller; -import org.sonar.server.plugins.ServerPluginJarsInstaller; -import org.sonar.server.plugins.ServerPluginRepository; -import org.sonar.server.plugins.UpdateCenterClient; -import org.sonar.server.plugins.UpdateCenterMatrixFactory; +import org.sonar.server.plugins.*; import org.sonar.server.qualitygate.QgateProjectFinder; import org.sonar.server.qualitygate.QualityGates; import org.sonar.server.qualitygate.RegisterQualityGates; -import org.sonar.server.qualitygate.ws.QGatesAppAction; -import org.sonar.server.qualitygate.ws.QGatesCopyAction; -import org.sonar.server.qualitygate.ws.QGatesCreateAction; -import org.sonar.server.qualitygate.ws.QGatesCreateConditionAction; -import org.sonar.server.qualitygate.ws.QGatesDeleteConditionAction; -import org.sonar.server.qualitygate.ws.QGatesDeselectAction; -import org.sonar.server.qualitygate.ws.QGatesDestroyAction; -import org.sonar.server.qualitygate.ws.QGatesListAction; -import org.sonar.server.qualitygate.ws.QGatesRenameAction; -import org.sonar.server.qualitygate.ws.QGatesSearchAction; -import org.sonar.server.qualitygate.ws.QGatesSelectAction; -import org.sonar.server.qualitygate.ws.QGatesSetAsDefaultAction; -import org.sonar.server.qualitygate.ws.QGatesShowAction; -import org.sonar.server.qualitygate.ws.QGatesUnsetDefaultAction; -import org.sonar.server.qualitygate.ws.QGatesUpdateConditionAction; -import org.sonar.server.qualitygate.ws.QGatesWs; -import org.sonar.server.qualityprofile.ActiveRuleService; -import org.sonar.server.qualityprofile.DefaultProfilesCache; -import org.sonar.server.qualityprofile.ProfilesManager; -import org.sonar.server.qualityprofile.QProfileActiveRuleOperations; -import org.sonar.server.qualityprofile.QProfileBackup; -import org.sonar.server.qualityprofile.QProfileLookup; -import org.sonar.server.qualityprofile.QProfileOperations; -import org.sonar.server.qualityprofile.QProfileProjectLookup; -import org.sonar.server.qualityprofile.QProfileProjectOperations; -import org.sonar.server.qualityprofile.QProfileRepositoryExporter; -import org.sonar.server.qualityprofile.QProfiles; -import org.sonar.server.qualityprofile.QualityProfileService; -import org.sonar.server.qualityprofile.RegisterQualityProfiles; -import org.sonar.server.qualityprofile.RuleActivationContextFactory; +import org.sonar.server.qualitygate.ws.*; +import org.sonar.server.qualityprofile.*; import org.sonar.server.qualityprofile.index.ActiveRuleIndex; import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer; import org.sonar.server.qualityprofile.persistence.ActiveRuleDao; -import org.sonar.server.qualityprofile.ws.BulkRuleActivationActions; -import org.sonar.server.qualityprofile.ws.ProfilesWs; -import org.sonar.server.qualityprofile.ws.QProfileRecreateBuiltInAction; -import org.sonar.server.qualityprofile.ws.QProfilesWs; -import org.sonar.server.qualityprofile.ws.RuleActivationActions; -import org.sonar.server.rule.DeprecatedRulesDefinition; -import org.sonar.server.rule.RubyRuleService; -import org.sonar.server.rule.RuleDefinitionsLoader; -import org.sonar.server.rule.RuleOperations; -import org.sonar.server.rule.RuleRepositories; +import org.sonar.server.qualityprofile.ws.*; +import org.sonar.server.rule.*; import org.sonar.server.rule2.RegisterRules; import org.sonar.server.rule2.RuleService; import org.sonar.server.rule2.index.RuleIndex; import org.sonar.server.rule2.index.RuleNormalizer; import org.sonar.server.rule2.persistence.RuleDao; -import org.sonar.server.rule2.ws.RuleMapping; -import org.sonar.server.rule2.ws.RulesWebService; -import org.sonar.server.rule2.ws.SearchAction; -import org.sonar.server.rule2.ws.SetNoteAction; -import org.sonar.server.rule2.ws.SetTagsAction; -import org.sonar.server.rule2.ws.TagsAction; +import org.sonar.server.rule2.ws.*; import org.sonar.server.search.IndexClient; import org.sonar.server.search.IndexQueue; import org.sonar.server.search.IndexQueueWorker; @@ -217,20 +141,7 @@ import org.sonar.server.source.ws.ScmAction; import org.sonar.server.source.ws.ScmWriter; import org.sonar.server.source.ws.ShowAction; import org.sonar.server.source.ws.SourcesWs; -import org.sonar.server.startup.CleanPreviewAnalysisCache; -import org.sonar.server.startup.CopyRequirementsFromCharacteristicsToRules; -import org.sonar.server.startup.GeneratePluginIndex; -import org.sonar.server.startup.GwtPublisher; -import org.sonar.server.startup.JdbcDriverDeployer; -import org.sonar.server.startup.LogServerId; -import org.sonar.server.startup.RegisterDashboards; -import org.sonar.server.startup.RegisterDebtModel; -import org.sonar.server.startup.RegisterMetrics; -import org.sonar.server.startup.RegisterNewMeasureFilters; -import org.sonar.server.startup.RegisterPermissionTemplates; -import org.sonar.server.startup.RegisterServletFilters; -import org.sonar.server.startup.RenameDeprecatedPropertyKeys; -import org.sonar.server.startup.ServerMetadataPersister; +import org.sonar.server.startup.*; import org.sonar.server.test.CoverageService; import org.sonar.server.test.ws.CoverageShowAction; import org.sonar.server.test.ws.CoverageWs; @@ -242,20 +153,9 @@ import org.sonar.server.ui.JRubyProfiling; import org.sonar.server.ui.PageDecorations; import org.sonar.server.ui.Views; import org.sonar.server.updatecenter.ws.UpdateCenterWs; -import org.sonar.server.user.DefaultUserService; -import org.sonar.server.user.DoPrivileged; -import org.sonar.server.user.GroupMembershipFinder; -import org.sonar.server.user.GroupMembershipService; -import org.sonar.server.user.NewUserNotifier; -import org.sonar.server.user.SecurityRealmFactory; +import org.sonar.server.user.*; import org.sonar.server.user.ws.UsersWs; -import org.sonar.server.util.BooleanTypeValidation; -import org.sonar.server.util.FloatTypeValidation; -import org.sonar.server.util.IntegerTypeValidation; -import org.sonar.server.util.StringListTypeValidation; -import org.sonar.server.util.StringTypeValidation; -import org.sonar.server.util.TextTypeValidation; -import org.sonar.server.util.TypeValidations; +import org.sonar.server.util.*; import org.sonar.server.ws.ListingWs; import org.sonar.server.ws.WebServiceEngine; @@ -473,6 +373,9 @@ class ServerComponents { pico.addSingleton(WebServiceEngine.class); pico.addSingleton(ListingWs.class); + // localization + pico.addSingleton(L10nWs.class); + // authentication pico.addSingleton(AuthenticationWs.class); diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ws/L10nWs.java b/sonar-server/src/main/java/org/sonar/server/platform/ws/L10nWs.java new file mode 100644 index 00000000000..f267b4acfc6 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/platform/ws/L10nWs.java @@ -0,0 +1,72 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +import org.apache.commons.lang.LocaleUtils; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.RequestHandler; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.core.i18n.DefaultI18n; +import org.sonar.server.user.UserSession; + +import java.util.Locale; + +public class L10nWs implements WebService { + + private final DefaultI18n i18n; + + public L10nWs(DefaultI18n i18n) { + this.i18n = i18n; + } + + @Override + public void define(Context context) { + NewController l10n = context.createController("api/l10n"); + l10n.setDescription("Localization") + .setSince("4.4") + .createAction("index") + .setInternal(true) + .setDescription("Get all localization messages for a given locale") + .setHandler(new RequestHandler() { + @Override + public void handle(Request request, Response response) throws Exception { + serializeMessages(request, response); + } + }).createParam("locale") + .setDescription("BCP47 language tag, used to override the browser Accept-Language header") + .setExampleValue("fr-CH"); + l10n.done(); + } + + protected void serializeMessages(Request request, Response response) { + Locale locale = UserSession.get().locale(); + String localeParam = request.param("locale"); + if (localeParam != null) { + locale = LocaleUtils.toLocale(localeParam); + } + JsonWriter json = response.newJsonWriter().beginObject(); + for (String messageKey: i18n.getPropertyKeys()) { + json.prop(messageKey, i18n.message(locale, messageKey, messageKey)); + } + json.endObject().close(); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/platform/ws/L10nWsTest.java b/sonar-server/src/test/java/org/sonar/server/platform/ws/L10nWsTest.java new file mode 100644 index 00000000000..219405875f2 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/platform/ws/L10nWsTest.java @@ -0,0 +1,89 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.ws; + +import com.google.common.collect.ImmutableSet; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.core.i18n.DefaultI18n; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.ws.WsTester; +import org.sonar.server.ws.WsTester.Result; + +import java.util.Locale; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class L10nWsTest { + + @Mock + DefaultI18n i18n; + + @Test + public void should_return_all_l10n_messages_using_accept_header() throws Exception { + Locale locale = Locale.PRC; + MockUserSession.set().setLocale(locale); + + String key1 = "key1"; + String key2 = "key2"; + String key3 = "key3"; + + when(i18n.getPropertyKeys()).thenReturn(ImmutableSet.of(key1, key2, key3)); + when(i18n.message(locale, key1, key1)).thenReturn(key1); + when(i18n.message(locale, key2, key2)).thenReturn(key2); + when(i18n.message(locale, key3, key3)).thenReturn(key3); + + Result result = new WsTester(new L10nWs(i18n)).newGetRequest("api/l10n", "index").execute(); + verify(i18n).getPropertyKeys(); + verify(i18n).message(locale, key1, key1); + verify(i18n).message(locale, key2, key2); + verify(i18n).message(locale, key3, key3); + + result.assertJson("{key1:'key1',key2:'key2',key3:'key3'}"); + } + + @Test + public void should_override_locale_when_locale_param_is_set() throws Exception { + Locale locale = Locale.PRC; + MockUserSession.set().setLocale(locale); + Locale override = Locale.JAPANESE; + + String key1 = "key1"; + String key2 = "key2"; + String key3 = "key3"; + + when(i18n.getPropertyKeys()).thenReturn(ImmutableSet.of(key1, key2, key3)); + when(i18n.message(override, key1, key1)).thenReturn(key1); + when(i18n.message(override, key2, key2)).thenReturn(key2); + when(i18n.message(override, key3, key3)).thenReturn(key3); + + Result result = new WsTester(new L10nWs(i18n)).newGetRequest("api/l10n", "index").setParam("locale", override.toString()).execute(); + verify(i18n).getPropertyKeys(); + verify(i18n).message(override, key1, key1); + verify(i18n).message(override, key2, key2); + verify(i18n).message(override, key3, key3); + + result.assertJson("{key1:'key1',key2:'key2',key3:'key3'}"); + } +} -- 2.39.5