*/
package org.sonar.server.qualityprofile.ws;
+import com.google.common.base.Function;
import com.google.common.base.Predicate;
-import java.util.ArrayList;
+import com.google.common.collect.Sets;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.sonar.server.qualityprofile.QProfileLookup;
import org.sonarqube.ws.client.qualityprofile.SearchWsRequest;
-import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.FluentIterable.from;
import static java.lang.String.format;
import static org.sonar.server.ws.WsUtils.checkRequest;
private final QProfileFactory profileFactory;
private final DbClient dbClient;
private final ComponentFinder componentFinder;
+ private final IsLanguageKnown isLanguageKnown;
public SearchDataLoader(Languages languages, QProfileLookup profileLookup, QProfileLoader profileLoader, QProfileFactory profileFactory, DbClient dbClient,
ComponentFinder componentFinder) {
this.profileFactory = profileFactory;
this.dbClient = dbClient;
this.componentFinder = componentFinder;
+ this.isLanguageKnown = new IsLanguageKnown();
}
SearchData load(SearchWsRequest request) {
}
private List<QProfile> findProfiles(SearchWsRequest request) {
- List<QProfile> profiles;
+ Collection<QProfile> profiles;
if (askDefaultProfiles(request)) {
profiles = findDefaultProfiles(request);
} else if (hasComponentKey(request)) {
profiles = findAllProfiles(request);
}
- return orderProfiles(profiles);
+ return from(profiles).toSortedList(QProfileComparator.INSTANCE);
}
- private static List<QProfile> orderProfiles(List<QProfile> profiles) {
- return from(profiles)
- .toSortedList(QProfileComparator.INSTANCE);
- }
+ private Collection<QProfile> findDefaultProfiles(SearchWsRequest request) {
+ String profileName = request.getProfileName();
- private List<QProfile> findAllProfiles(SearchWsRequest request) {
- String language = request.getLanguage();
+ Set<String> languageKeys = getLanguageKeys();
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ Map<String, QProfile> qualityProfiles = new HashMap<>(languageKeys.size());
- List<QProfile> profiles = language != null ? profileLookup.profiles(language)
- : profileLookup.allProfiles();
+ Set<String> missingLanguageKeys = lookupByProfileName(dbSession, qualityProfiles, languageKeys, profileName);
+ Set<String> noDefaultProfileLanguageKeys = lookupDefaults(dbSession, qualityProfiles, missingLanguageKeys);
- return from(profiles)
- .filter(new IsLanguageKnown())
- .toList();
+ if (!noDefaultProfileLanguageKeys.isEmpty()) {
+ throw new IllegalStateException(format("No quality profile can been found on language(s) '%s'", noDefaultProfileLanguageKeys));
+ }
+
+ return qualityProfiles.values();
+ }
}
- private List<QProfile> findProjectProfiles(SearchWsRequest request) {
- String moduleKey = request.getProjectKey();
+ private Collection<QProfile> findProjectProfiles(SearchWsRequest request) {
+ String componentKey = request.getProjectKey();
String profileName = request.getProfileName();
- List<QProfile> profiles = new ArrayList<>();
+ Set<String> languageKeys = getLanguageKeys();
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ Map<String, QProfile> qualityProfiles = new HashMap<>(languageKeys.size());
+
+ // look up profiles by profileName (if any) for each language
+ Set<String> unresolvedLanguages = lookupByProfileName(dbSession, qualityProfiles, languageKeys, profileName);
+ // look up profile by componentKey for each language for which we don't have one yet
+ Set<String> stillUnresolvedLanguages = lookupByModuleKey(dbSession, qualityProfiles, unresolvedLanguages, componentKey);
+ // look up profile by default for each language for which we don't have one yet
+ Set<String> noDefaultProfileLanguages = lookupDefaults(dbSession, qualityProfiles, stillUnresolvedLanguages);
- DbSession dbSession = dbClient.openSession(false);
- try {
- for (Language language : languages.all()) {
- String languageKey = language.getKey();
- ComponentDto project = getProject(moduleKey, dbSession);
- profiles.add(getProfile(dbSession, languageKey, project.key(), profileName));
+ if (!noDefaultProfileLanguages.isEmpty()) {
+ throw new IllegalStateException(format("No quality profile can been found on language(s) '%s' for project '%s'", noDefaultProfileLanguages, componentKey));
}
- } finally {
- dbClient.closeSession(dbSession);
+
+ return qualityProfiles.values();
}
+ }
+
+ private List<QProfile> findAllProfiles(SearchWsRequest request) {
+ String language = request.getLanguage();
- return profiles;
+ if (language == null) {
+ return from(profileLookup.allProfiles()).filter(isLanguageKnown).toList();
+ }
+ return profileLookup.profiles(language);
+ }
+
+ private Set<String> lookupByProfileName(DbSession dbSession, Map<String, QProfile> qualityProfiles, Set<String> languageKeys, @Nullable String profileName) {
+ if (languageKeys.isEmpty() || profileName == null) {
+ return languageKeys;
+ }
+
+ addAllFromDto(qualityProfiles, profileFactory.getByNameAndLanguages(dbSession, profileName, languageKeys));
+ return difference(languageKeys, qualityProfiles.keySet());
+ }
+
+ private Set<String> lookupByModuleKey(DbSession dbSession, Map<String, QProfile> qualityProfiles, Set<String> languageKeys, @Nullable String moduleKey) {
+ if (languageKeys.isEmpty() || moduleKey == null) {
+ return languageKeys;
+ }
+
+ ComponentDto project = getProject(moduleKey, dbSession);
+ addAllFromDto(qualityProfiles, profileFactory.getByProjectAndLanguages(dbSession, project.getKey(), languageKeys));
+ return difference(languageKeys, qualityProfiles.keySet());
}
private ComponentDto getProject(String moduleKey, DbSession session) {
ComponentDto module = componentFinder.getByKey(session, moduleKey);
- if (!module.isRootProject()) {
- return dbClient.componentDao().selectOrFailByUuid(session, module.projectUuid());
- } else {
+ if (module.isRootProject()) {
return module;
}
+ return dbClient.componentDao().selectOrFailByUuid(session, module.projectUuid());
}
- private List<QProfile> findDefaultProfiles(SearchWsRequest request) {
- String profileName = request.getProfileName();
- List<QProfile> profiles = new ArrayList<>();
+ private Set<String> lookupDefaults(DbSession dbSession, Map<String, QProfile> qualityProfiles, Set<String> languageKeys) {
+ if (languageKeys.isEmpty()) {
+ return languageKeys;
+ }
- DbSession dbSession = dbClient.openSession(false);
- try {
- for (Language language : languages.all()) {
- profiles.add(getDefaultProfile(dbSession, language.getKey(), profileName));
- }
- } finally {
- dbClient.closeSession(dbSession);
+ addAll(qualityProfiles, findDefaultProfiles(dbSession, languageKeys));
+ return difference(languageKeys, qualityProfiles.keySet());
+ }
+
+ private static <T> Set<T> difference(Set<T> languageKeys, Set<T> set2) {
+ return Sets.newHashSet(Sets.difference(languageKeys, set2));
+ }
+
+ private void addAllFromDto(Map<String, QProfile> qualityProfiles, Collection<QualityProfileDto> list) {
+ for (QualityProfileDto qualityProfileDto : list) {
+ qualityProfiles.put(qualityProfileDto.getLanguage(), QualityProfileDtoToQProfile.INSTANCE.apply(qualityProfileDto));
}
+ }
- return profiles;
- }
-
- private static QProfile profileDtoToQProfile(QualityProfileDto dto) {
- return new QProfile()
- .setKey(dto.getKey())
- .setName(dto.getName())
- .setLanguage(dto.getLanguage())
- .setDefault(dto.isDefault())
- .setRulesUpdatedAt(dto.getRulesUpdatedAt());
- }
-
- /**
- * First try to find a quality profile matching the given name (if provided) and current language
- * If no profile found, try to find the quality profile set on the project (if provided)
- * If still no profile found, try to find the default profile of the language
- * <p/>
- * Never return null because a default profile should always be set on each language
- */
- private QProfile getProfile(DbSession dbSession, String languageKey, @Nullable String projectKey, @Nullable String profileName) {
- QualityProfileDto profileDto = profileName != null ? profileFactory.getByNameAndLanguage(dbSession, profileName, languageKey) : null;
- if (profileDto == null && projectKey != null) {
- profileDto = profileFactory.getByProjectAndLanguage(dbSession, projectKey, languageKey);
+ private void addAll(Map<String, QProfile> qualityProfiles, Collection<QProfile> list) {
+ for (QProfile qProfile : list) {
+ qualityProfiles.put(qProfile.language(), qProfile);
}
- profileDto = profileDto != null ? profileDto : profileFactory.getDefault(dbSession, languageKey);
- checkState(profileDto != null, format("No quality profile can been found on language '%s' for project '%s'", languageKey, projectKey));
+ }
- return profileDtoToQProfile(profileDto);
+ private Set<String> getLanguageKeys() {
+ return from(Arrays.asList(languages.all())).transform(LanguageToKey.INSTANCE).toSet();
}
- private QProfile getDefaultProfile(DbSession dbSession, String languageKey, @Nullable String profileName) {
- QualityProfileDto profile = profileName != null ? profileFactory.getByNameAndLanguage(dbSession, profileName, languageKey) : null;
- if (profile == null) {
- profile = profileFactory.getDefault(dbSession, languageKey);
+ private List<QProfile> findDefaultProfiles(Set<String> languageKeys) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ return findDefaultProfiles(dbSession, languageKeys);
}
- checkState(profile != null, format("No quality profile can been found on language '%s'", languageKey));
+ }
- return profileDtoToQProfile(profile);
+ private List<QProfile> findDefaultProfiles(final DbSession dbSession, Set<String> languageKeys) {
+ return from(profileFactory.getDefaults(dbSession, languageKeys))
+ .transform(QualityProfileDtoToQProfile.INSTANCE)
+ .toList();
}
private static void validateRequest(SearchWsRequest request) {
checkRequest(!isDefault || !hasComponentKey, "The default parameter cannot be provided at the same time than the component key");
}
+ private static boolean askDefaultProfiles(SearchWsRequest request) {
+ return request.getDefaults();
+ }
+
private static boolean hasProfileName(SearchWsRequest request) {
return request.getProfileName() != null;
}
return request.getProjectKey() != null;
}
- private static Boolean askDefaultProfiles(SearchWsRequest request) {
- return request.getDefaults();
- }
-
private static boolean hasLanguage(SearchWsRequest request) {
return request.getLanguage() != null;
}
}
}
+ private enum LanguageToKey implements Function<Language, String> {
+ INSTANCE;
+
+ @Override
+ @Nonnull
+ public String apply(@Nonnull Language input) {
+ return input.getKey();
+ }
+ }
+
+ private enum QualityProfileDtoToQProfile implements Function<QualityProfileDto, QProfile> {
+ INSTANCE;
+
+ @Override
+ @Nonnull
+ public QProfile apply(@Nonnull QualityProfileDto input) {
+ return new QProfile()
+ .setKey(input.getKey())
+ .setName(input.getName())
+ .setLanguage(input.getLanguage())
+ .setDefault(input.isDefault())
+ .setRulesUpdatedAt(input.getRulesUpdatedAt());
+ }
+
+ }
+
private class IsLanguageKnown implements Predicate<QProfile> {
@Override
public boolean apply(@Nonnull QProfile profile) {
*/
package org.sonar.db.qualityprofile;
+import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.sonar.api.utils.System2;
import org.sonar.db.Dao;
+import org.sonar.db.DatabaseUtils;
import org.sonar.db.DbSession;
import org.sonar.db.MyBatis;
import org.sonar.db.RowNotFoundException;
}
/**
- * @deprecated Replaced by
- * {@link #selectAll(DbSession)}
+ * @deprecated Replaced by {@link #selectAll(DbSession)}
*/
@Deprecated
public List<QualityProfileDto> selectAll() {
}
@CheckForNull
- public QualityProfileDto selectDefaultProfile(DbSession session, String language) {
+ public List<QualityProfileDto> selectDefaultProfiles(final DbSession session, Collection<String> languageKeys) {
+ return DatabaseUtils.executeLargeInputs(languageKeys, new Function<List<String>, List<QualityProfileDto>>() {
+ @Override
+ @Nonnull
+ public List<QualityProfileDto> apply(@Nonnull List<String> input) {
+ return mapper(session).selectDefaultProfiles(input);
+ }
+ });
+ }
+
+ @CheckForNull
+ public QualityProfileDto selectDefaultProfile(final DbSession session, String language) {
return mapper(session).selectDefaultProfile(language);
}
return mapper(session).selectByProjectAndLanguage(projectKey, language);
}
+ public List<QualityProfileDto> selectByProjectAndLanguages(final DbSession session, final String projectKey, Collection<String> languageKeys) {
+ return DatabaseUtils.executeLargeInputs(languageKeys, new Function<List<String>, List<QualityProfileDto>>() {
+ @Override
+ @Nonnull
+ public List<QualityProfileDto> apply(@Nonnull List<String> input) {
+ return mapper(session).selectByProjectAndLanguages(projectKey, input);
+ }
+ });
+ }
+
public List<QualityProfileDto> selectByLanguage(String language) {
DbSession session = mybatis.openSession(false);
try {
return mapper(session).selectByNameAndLanguage(name, language);
}
+ public List<QualityProfileDto> selectByNameAndLanguages(final String name, Collection<String> languageKeys, final DbSession session) {
+ return DatabaseUtils.executeLargeInputs(languageKeys, new Function<List<String>, List<QualityProfileDto>>() {
+ @Override
+ @Nonnull
+ public List<QualityProfileDto> apply(@Nonnull List<String> input) {
+ return mapper(session).selectByNameAndLanguages(name, input);
+ }
+ });
+ }
+
public List<ComponentDto> selectProjects(String profileName, String language) {
DbSession session = mybatis.openSession(false);
try {
private QualityProfileMapper mapper(DbSession session) {
return session.getMapper(QualityProfileMapper.class);
}
-
}
import org.sonar.core.util.UtcDateUtils;
import org.sonar.db.DbTester;
+import static com.google.common.collect.ImmutableList.of;
+import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
assertThat(dao.selectDefaultProfile("js")).isNull();
}
+ @Test
+ public void get_default_profiles() {
+ dbTester.prepareDbUnit(getClass(), "shared.xml");
+
+ List<QualityProfileDto> java = dao.selectDefaultProfiles(dbTester.getSession(), singletonList("java"));
+ assertThat(java).extracting("key").containsOnly("java_sonar_way");
+
+ assertThat(dao.selectDefaultProfiles(dbTester.getSession(), singletonList("js"))).isEmpty();
+ assertThat(dao.selectDefaultProfiles(dbTester.getSession(), of("java", "js"))).extracting("key").containsOnly("java_sonar_way");
+ assertThat(dao.selectDefaultProfiles(dbTester.getSession(), of("js", "java"))).extracting("key").containsOnly("java_sonar_way");
+ }
+
@Test
public void get_by_name_and_language() {
dbTester.prepareDbUnit(getClass(), "shared.xml");
assertThat(dao.selectByNameAndLanguage("Sonar Way", "unknown", dbTester.getSession())).isNull();
}
+ @Test
+ public void get_by_name_and_languages() {
+ dbTester.prepareDbUnit(getClass(), "shared.xml");
+
+ List<QualityProfileDto> dtos = dao.selectByNameAndLanguages("Sonar Way", singletonList("java"), dbTester.getSession());
+ assertThat(dtos).hasSize(1);
+ QualityProfileDto dto = dtos.iterator().next();
+ assertThat(dto.getId()).isEqualTo(1);
+ assertThat(dto.getName()).isEqualTo("Sonar Way");
+ assertThat(dto.getLanguage()).isEqualTo("java");
+ assertThat(dto.getParentKee()).isNull();
+
+ assertThat(dao.selectByNameAndLanguages("Sonar Way", singletonList("unknown"), dbTester.getSession())).isEmpty();
+ assertThat(dao.selectByNameAndLanguages("Sonar Way", of("java", "unknown"), dbTester.getSession())).extracting("id").containsOnly(1);
+ }
+
@Test
public void find_by_language() {
dbTester.prepareDbUnit(getClass(), "select_by_language.xml");
assertThat(dao.selectByProjectAndLanguage(dbTester.getSession(), "org.codehaus.sonar:sonar", "unkown")).isNull();
assertThat(dao.selectByProjectAndLanguage(dbTester.getSession(), "unknown", "java")).isNull();
}
+
+ @Test
+ public void select_by_project_key_and_languages() {
+ dbTester.prepareDbUnit(getClass(), "projects.xml");
+
+ List<QualityProfileDto> dto = dao.selectByProjectAndLanguages(dbTester.getSession(), "org.codehaus.sonar:sonar", singletonList("java"));
+ assertThat(dto).extracting("id").containsOnly(1);
+
+ assertThat(dao.selectByProjectAndLanguages(dbTester.getSession(), "org.codehaus.sonar:sonar", singletonList("unkown"))).isEmpty();
+ assertThat(dao.selectByProjectAndLanguages(dbTester.getSession(), "org.codehaus.sonar:sonar", of("java", "unkown"))).extracting("id").containsOnly(1);
+ assertThat(dao.selectByProjectAndLanguages(dbTester.getSession(), "unknown", singletonList("java"))).isEmpty();
+ }
}