All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.server.property.ws.IndexAction Maven / Gradle / Ivy

There is a newer version: 7.2.1
Show newest version
/*
 * SonarQube
 * Copyright (C) 2009-2018 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program 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.
 *
 * This program 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.property.ws;

import com.google.common.base.Splitter;
import com.google.common.collect.Multimap;
import com.google.common.collect.Ordering;
import com.google.common.collect.TreeMultimap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.server.user.UserSession;
import org.sonar.server.ws.WsAction;

import static org.apache.commons.lang.StringUtils.isEmpty;
import static org.sonar.api.PropertyType.PROPERTY_SET;
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.server.setting.ws.SettingsWsSupport.DOT_LICENSE;
import static org.sonar.server.setting.ws.SettingsWsSupport.DOT_SECURED;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;

public class IndexAction implements WsAction {

  private static final Splitter DOT_SPLITTER = Splitter.on(".").omitEmptyStrings();
  private static final Splitter COMMA_SPLITTER = Splitter.on(",");
  private static final String COMMA_ENCODED_VALUE = "%2C";

  public static final String PARAM_ID = "id";
  public static final String PARAM_COMPONENT = "resource";
  public static final String PARAM_FORMAT = "format";

  private final DbClient dbClient;
  private final UserSession userSession;
  private final PropertyDefinitions propertyDefinitions;

  public IndexAction(DbClient dbClient, UserSession userSession, PropertyDefinitions propertyDefinitions) {
    this.dbClient = dbClient;
    this.userSession = userSession;
    this.propertyDefinitions = propertyDefinitions;
  }

  @Override
  public void define(WebService.NewController context) {
    WebService.NewAction action = context.createAction("index")
      .setDescription("This web service is deprecated, please use api/settings/values instead.")
      .setDeprecatedSince("6.3")
      .setResponseExample(getClass().getResource("index-example.json"))
      .setSince("2.6")
      .setHandler(this);
    action.createParam(PARAM_ID)
      .setDescription("Setting key")
      .setExampleValue("sonar.test.inclusions");
    action.createParam(PARAM_COMPONENT)
      .setDescription("Component key or database id")
      .setExampleValue(KEY_PROJECT_EXAMPLE_001);
    action.createParam(PARAM_FORMAT)
      .setDescription("Only json response format is available")
      .setPossibleValues("json");
  }

  @Override
  public void handle(Request request, Response response) throws Exception {
    request.param(PARAM_FORMAT);
    JsonWriter json = response.newJsonWriter();
    json.beginArray();
    doHandle(json, request);
    json.endArray();
    json.close();
  }

  private void doHandle(JsonWriter json, Request request) {
    try (DbSession dbSession = dbClient.openSession(true)) {
      Optional component = loadComponent(dbSession, request);
      String key = request.param(PARAM_ID);
      List propertyDtos = loadProperties(dbSession, component, Optional.ofNullable(key));
      new ResponseBuilder(propertyDtos).build(json);
    }
  }

  private Optional loadComponent(DbSession dbSession, Request request) {
    String component = request.param(PARAM_COMPONENT);
    if (component == null) {
      return Optional.empty();
    }
    return loadComponent(dbSession, component);
  }

  private Optional loadComponent(DbSession dbSession, String component) {
    try {
      Long componentId = Long.parseLong(component);
      return Optional.ofNullable(dbClient.componentDao().selectById(dbSession, componentId).orNull());
    } catch (NumberFormatException e) {
      return Optional.ofNullable(dbClient.componentDao().selectByKey(dbSession, component).orNull());
    }
  }

  private List loadProperties(DbSession dbSession, Optional component, Optional key) {
    // List of settings must be kept in the following orders : default -> global -> component
    List propertyDtos = new ArrayList<>();
    propertyDtos.addAll(loadDefaultSettings(key));
    propertyDtos.addAll(loadGlobalSettings(dbSession, key));
    component.ifPresent(componentDto -> propertyDtos.addAll(loadComponentSettings(dbSession, key, componentDto).values()));
    return propertyDtos.stream().filter(isVisible(component)).collect(Collectors.toList());
  }

  Predicate isVisible(Optional component) {
    return propertyDto -> !propertyDto.getKey().endsWith(DOT_SECURED)
      || hasAdminPermission(component)
      || (propertyDto.getKey().contains(DOT_LICENSE) && userSession.isLoggedIn());
  }

  private boolean hasAdminPermission(Optional component) {
    return component
      .map(c -> userSession.hasComponentPermission(ADMIN, c))
      .orElse(userSession.isSystemAdministrator());
  }

  private List loadGlobalSettings(DbSession dbSession, Optional key) {
    if (key.isPresent()) {
      return dbClient.propertiesDao().selectGlobalPropertiesByKeys(dbSession, Collections.singleton(key.get()));
    }
    return dbClient.propertiesDao().selectGlobalProperties(dbSession);
  }

  /**
   * Return list of propertyDto by component uuid, sorted from project to lowest module
   */
  private Multimap loadComponentSettings(DbSession dbSession, Optional key, ComponentDto component) {
    List componentUuids = DOT_SPLITTER.splitToList(component.moduleUuidPath());
    List componentDtos = dbClient.componentDao().selectByUuids(dbSession, componentUuids);
    Set componentIds = componentDtos.stream().map(ComponentDto::getId).collect(Collectors.toSet());
    Map uuidsById = componentDtos.stream().collect(Collectors.toMap(ComponentDto::getId, ComponentDto::uuid));
    List properties = key.isPresent() ? dbClient.propertiesDao().selectPropertiesByKeysAndComponentIds(dbSession, Collections.singleton(key.get()), componentIds)
      : dbClient.propertiesDao().selectPropertiesByComponentIds(dbSession, componentIds);

    Multimap propertyDtosByUuid = TreeMultimap.create(Ordering.explicit(componentUuids), Ordering.arbitrary());
    for (PropertyDto propertyDto : properties) {
      Long componentId = propertyDto.getResourceId();
      String componentUuid = uuidsById.get(componentId);
      propertyDtosByUuid.put(componentUuid, propertyDto);
    }
    return propertyDtosByUuid;
  }

  private List loadDefaultSettings(Optional key) {
    return propertyDefinitions.getAll().stream()
      .filter(definition -> !key.isPresent() || key.get().equals(definition.key()))
      .filter(defaultProperty -> !isEmpty(defaultProperty.defaultValue()))
      .map(definition -> new PropertyDto().setKey(definition.key()).setValue(definition.defaultValue()))
      .collect(Collectors.toList());
  }

  private class ResponseBuilder {
    private final List propertyDtos;
    private final Map propertiesByKey = new HashMap<>();

    ResponseBuilder(List propertyDtos) {
      this.propertyDtos = propertyDtos;
    }

    void build(JsonWriter json) {
      processSettings();
      propertiesByKey.values().forEach(property -> {
        json.beginObject()
          .prop("key", property.key)
          .prop("value", property.value);
        if (!property.values.isEmpty()) {
          json.name("values").beginArray().values(property.values).endArray();
        }
        json.endObject();
      });
    }

    private void processSettings() {
      propertyDtos.forEach(setting -> {
        Property property = createProperty(setting.getKey());
        setValue(setting, property);
      });
    }

    private Property createProperty(String key) {
      return propertiesByKey.computeIfAbsent(key, k -> new Property(key));
    }

    private void setValue(PropertyDto propertyDto, Property property) {
      String value = propertyDto.getValue();
      property.setValue(value);
      PropertyDefinition definition = propertyDefinitions.get(propertyDto.getKey());
      if (definition != null && (definition.multiValues() || definition.type().equals(PROPERTY_SET))) {
        property.setValues(createValues(value));
      }
    }

    private List createValues(String value) {
      return COMMA_SPLITTER.splitToList(value).stream().map(v -> v.replace(COMMA_ENCODED_VALUE, ",")).collect(Collectors.toList());
    }
  }

  private static class Property {
    private final String key;
    private String value;
    private List values = new ArrayList<>();

    public Property(String key) {
      this.key = key;
    }

    public Property setValue(String value) {
      this.value = value;
      return this;
    }

    public Property setValues(List values) {
      this.values = values;
      return this;
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy