Package org.sonar.server.component.ws

Source Code of org.sonar.server.component.ws.ComponentAppAction$Period

/*
* 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.component.ws;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multiset;
import com.google.common.io.Resources;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.i18n.I18n;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Metric;
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.DateUtils;
import org.sonar.api.utils.Durations;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.api.web.NavigationSection;
import org.sonar.api.web.Page;
import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.component.SnapshotDto;
import org.sonar.core.measure.db.MeasureDto;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.properties.PropertyDto;
import org.sonar.core.properties.PropertyQuery;
import org.sonar.core.timemachine.Periods;
import org.sonar.server.db.DbClient;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.issue.IssueService;
import org.sonar.server.issue.RulesAggregation;
import org.sonar.server.rule.Rule;
import org.sonar.server.rule.RuleService;
import org.sonar.server.rule.index.RuleDoc;
import org.sonar.server.rule.index.RuleQuery;
import org.sonar.server.search.QueryContext;
import org.sonar.server.search.Result;
import org.sonar.server.ui.ViewProxy;
import org.sonar.server.ui.Views;
import org.sonar.server.user.UserSession;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import java.util.Date;
import java.util.List;
import java.util.Map;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;

public class ComponentAppAction implements RequestHandler {

  private static final String PARAM_KEY = "key";
  private static final String PARAM_PERIOD = "period";

  private final DbClient dbClient;

  private final IssueService issueService;
  private final Views views;
  private final RuleService ruleService;
  private final Periods periods;
  private final Durations durations;
  private final I18n i18n;

  public ComponentAppAction(DbClient dbClient, IssueService issueService, Views views, RuleService ruleService, Periods periods, Durations durations, I18n i18n) {
    this.dbClient = dbClient;
    this.issueService = issueService;
    this.views = views;
    this.ruleService = ruleService;
    this.periods = periods;
    this.durations = durations;
    this.i18n = i18n;
  }

  void define(WebService.NewController controller) {
    WebService.NewAction action = controller.createAction("app")
      .setDescription("Coverage data required for rendering the component viewer")
      .setSince("4.4")
      .setInternal(true)
      .setHandler(this)
      .setResponseExample(Resources.getResource(this.getClass(), "components-example-app.json"));

    action
      .createParam(PARAM_KEY)
      .setRequired(true)
      .setDescription("File key")
      .setExampleValue("org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/Plugin.java");

    action
      .createParam(PARAM_PERIOD)
      .setDescription("Period index in order to get differential measures")
      .setPossibleValues(1, 2, 3, 4, 5);
  }

  @Override
  public void handle(Request request, Response response) {
    String fileKey = request.mandatoryParam(PARAM_KEY);
    UserSession userSession = UserSession.get();

    JsonWriter json = response.newJsonWriter();
    json.beginObject();

    DbSession session = dbClient.openSession(false);
    try {
      ComponentDto component = dbClient.componentDao().getNullableByKey(session, fileKey);
      if (component == null) {
        throw new NotFoundException(String.format("Component '%s' does not exist", fileKey));
      }
      userSession.checkComponentPermission(UserRole.USER, fileKey);

      List<Period> periodList = periods(component.projectUuid(), session);
      Integer periodIndex = request.paramAsInt(PARAM_PERIOD);
      Date periodDate = periodDate(periodIndex, periodList);

      RulesAggregation rulesAggregation = issueService.findRulesByComponent(component.key(), periodDate, session);
      Multiset<String> severitiesAggregation = issueService.findSeveritiesByComponent(component.key(), periodDate, session);
      Map<String, MeasureDto> measuresByMetricKey = measuresByMetricKey(component, session);

      appendComponent(json, component, userSession, session);
      appendPermissions(json, component, userSession);
      appendPeriods(json, periodList);
      appendIssuesAggregation(json, rulesAggregation, severitiesAggregation);
      appendMeasures(json, measuresByMetricKey, severitiesAggregation, periodIndex);
      appendTabs(json, measuresByMetricKey);
      appendExtensions(json, component, userSession);
      appendManualRules(json);
    } finally {
      MyBatis.closeQuietly(session);
    }

    json.endObject();
    json.close();
  }

  private void appendComponent(JsonWriter json, ComponentDto component, UserSession userSession, DbSession session) {
    List<PropertyDto> propertyDtos = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
      .setKey("favourite")
      .setComponentId(component.getId())
      .setUserId(userSession.userId())
      .build(),
      session
      );
    boolean isFavourite = propertyDtos.size() == 1;

    json.prop("key", component.key());
    json.prop("path", component.path());
    json.prop("name", component.name());
    json.prop("longName", component.longName());
    json.prop("q", component.qualifier());

    ComponentDto subProject = nullableComponentById(component.subProjectId(), session);
    ComponentDto project = dbClient.componentDao().getByUuid(session, component.projectUuid());

    // Do not display sub project if sub project and project are the same
    boolean displaySubProject = subProject != null && !subProject.getId().equals(project.getId());

    json.prop("subProject", displaySubProject ? subProject.key() : null);
    json.prop("subProjectName", displaySubProject ? subProject.longName() : null);
    json.prop("project", project.key());
    json.prop("projectName", project.longName());

    json.prop("fav", isFavourite);
  }

  private void appendTabs(JsonWriter json, Map<String, MeasureDto> measuresByMetricKey) {
    List<String> tabs = newArrayList();
    if (measuresByMetricKey.get(CoreMetrics.SCM_AUTHORS_BY_LINE_KEY) != null) {
      tabs.add("scm");
    }
    if (hasCoverage(measuresByMetricKey)) {
      tabs.add("coverage");
    }
    if (measuresByMetricKey.get(CoreMetrics.DUPLICATED_LINES_KEY) != null) {
      tabs.add("duplications");
    }
    if (!tabs.isEmpty()) {
      json.name("tabs").beginArray().values(tabs).endArray();
    }
  }

  private boolean hasCoverage(Map<String, MeasureDto> measuresByMetricKey) {
    return measuresByMetricKey.get(CoreMetrics.OVERALL_COVERAGE_KEY) != null
      || measuresByMetricKey.get(CoreMetrics.IT_COVERAGE_KEY) != null
      || measuresByMetricKey.get(CoreMetrics.COVERAGE_KEY) != null;
  }

  private void appendPermissions(JsonWriter json, ComponentDto component, UserSession userSession) {
    boolean hasBrowsePermission = userSession.hasComponentPermission(UserRole.USER, component.key());
    json.prop("canMarkAsFavourite", userSession.isLoggedIn() && hasBrowsePermission);
    json.prop("canBulkChange", userSession.isLoggedIn());
    json.prop("canCreateManualIssue", userSession.isLoggedIn() && hasBrowsePermission);
  }

  private void appendMeasures(JsonWriter json, Map<String, MeasureDto> measuresByMetricKey, Multiset<String> severitiesAggregation, Integer periodIndex) {
    json.name("measures").beginObject();

    json.prop("fNcloc", formatMeasureOrVariation(measuresByMetricKey.get(CoreMetrics.NCLOC_KEY), periodIndex));
    json.prop("fCoverage", formatMeasureOrVariation(coverageMeasure(measuresByMetricKey), periodIndex));
    json.prop("fDuplicationDensity", formatMeasureOrVariation(measuresByMetricKey.get(CoreMetrics.DUPLICATED_LINES_DENSITY_KEY), periodIndex));
    json.prop("fDebt", formatMeasureOrVariation(measuresByMetricKey.get(CoreMetrics.TECHNICAL_DEBT_KEY), periodIndex));
    json.prop("fSqaleRating", formatMeasureOrVariation(measuresByMetricKey.get(CoreMetrics.SQALE_RATING_KEY), periodIndex));
    json.prop("fSqaleDebtRatio", formatMeasureOrVariation(measuresByMetricKey.get(CoreMetrics.SQALE_DEBT_RATIO_KEY), periodIndex));
    json.prop("fTests", formatMeasureOrVariation(measuresByMetricKey.get(CoreMetrics.TESTS_KEY), periodIndex));

    json.prop("fIssues", i18n.formatInteger(UserSession.get().locale(), severitiesAggregation.size()));
    for (String severity : severitiesAggregation.elementSet()) {
      json.prop("f" + StringUtils.capitalize(severity.toLowerCase()) + "Issues", i18n.formatInteger(UserSession.get().locale(), severitiesAggregation.count(severity)));
    }
    json.endObject();
  }

  private MeasureDto coverageMeasure(Map<String, MeasureDto> measuresByMetricKey) {
    MeasureDto overallCoverage = measuresByMetricKey.get(CoreMetrics.OVERALL_COVERAGE_KEY);
    MeasureDto itCoverage = measuresByMetricKey.get(CoreMetrics.IT_COVERAGE_KEY);
    MeasureDto utCoverage = measuresByMetricKey.get(CoreMetrics.COVERAGE_KEY);
    if (overallCoverage != null) {
      return overallCoverage;
    } else if (utCoverage != null) {
      return utCoverage;
    } else {
      return itCoverage;
    }
  }

  private void appendPeriods(JsonWriter json, List<Period> periodList) {
    json.name("periods").beginArray();
    for (Period period : periodList) {
      Date periodDate = period.date();
      json.beginArray()
        .value(period.index())
        .value(period.label())
        .value(periodDate != null ? DateUtils.formatDateTime(periodDate) : null)
        .endArray();
    }
    json.endArray();
  }

  private void appendIssuesAggregation(JsonWriter json, RulesAggregation rulesAggregation, Multiset<String> severitiesAggregation) {
    json.name("severities").beginArray();
    for (String severity : severitiesAggregation.elementSet()) {
      json.beginArray()
        .value(severity)
        .value(i18n.message(UserSession.get().locale(), "severity." + severity, null))
        .value(severitiesAggregation.count(severity))
        .endArray();
    }
    json.endArray();

    json.name("rules").beginArray();
    for (RulesAggregation.Rule rule : rulesAggregation.rules()) {
      json.beginArray()
        .value(rule.ruleKey().toString())
        .value(rule.name())
        .value(rulesAggregation.countRule(rule))
        .endArray();
    }
    json.endArray();
  }

  private void appendManualRules(JsonWriter json) {
    Result<Rule> result = ruleService.search(new RuleQuery().setRepositories(newArrayList(RuleDoc.MANUAL_REPOSITORY)), new QueryContext().setMaxLimit());
    if (result != null && !result.getHits().isEmpty()) {
      List<Rule> manualRules = result.getHits();
      json.name("manual_rules").beginArray();
      for (Rule manualRule : manualRules) {
        json.beginObject()
          .prop("key", manualRule.key().toString())
          .prop("name", manualRule.name())
          .endObject();
      }
      json.endArray();
    }
  }

  private void appendExtensions(JsonWriter json, ComponentDto component, UserSession userSession) {
    List<ViewProxy<Page>> extensionPages = views.getPages(NavigationSection.RESOURCE_TAB, component.scope(), component.qualifier(), component.language(), null);
    Map<String, String> extensions = extensions(extensionPages, component, userSession);
    if (!extensions.isEmpty()) {
      json.name("extensions").beginArray();
      for (Map.Entry<String, String> entry : extensions.entrySet()) {
        json.beginArray().value(entry.getKey()).value(entry.getValue()).endArray();
      }
      json.endArray();
    }
  }

  private Map<String, String> extensions(List<ViewProxy<Page>> extensions, ComponentDto component, UserSession userSession) {
    Map<String, String> result = newHashMap();
    List<String> providedExtensions = newArrayList("tests_viewer", "coverage", "duplications", "issues", "source");
    for (ViewProxy<Page> page : extensions) {
      if (!providedExtensions.contains(page.getId())) {
        addExtension(page, result, component, userSession);
      }
    }
    return result;
  }

  private void addExtension(ViewProxy<Page> page, Map<String, String> result, ComponentDto component, UserSession userSession) {
    if (page.getUserRoles().length == 0) {
      result.put(page.getId(), page.getTitle());
    } else {
      for (String userRole : page.getUserRoles()) {
        if (userSession.hasComponentPermission(userRole, component.key())) {
          result.put(page.getId(), page.getTitle());
        }
      }
    }
  }

  private List<Period> periods(String projectUuid, DbSession session) {
    List<Period> periodList = newArrayList();
    SnapshotDto snapshotDto = dbClient.resourceDao().getLastSnapshotByResourceUuid(projectUuid, session);
    if (snapshotDto != null) {
      for (int i = 1; i <= 5; i++) {
        String mode = snapshotDto.getPeriodMode(i);
        if (mode != null) {
          Date periodDate = snapshotDto.getPeriodDate(i);
          String label = periods.label(mode, snapshotDto.getPeriodModeParameter(i), periodDate);
          if (label != null) {
            periodList.add(new Period(i, label, periodDate));
          }
        }
      }
    }
    return periodList;
  }

  private Map<String, MeasureDto> measuresByMetricKey(ComponentDto component, DbSession session) {
    Map<String, MeasureDto> measuresByMetricKey = newHashMap();
    String fileKey = component.getKey();
    for (MeasureDto measureDto : dbClient.measureDao().findByComponentKeyAndMetricKeys(fileKey,
      newArrayList(CoreMetrics.NCLOC_KEY, CoreMetrics.COVERAGE_KEY, CoreMetrics.IT_COVERAGE_KEY, CoreMetrics.OVERALL_COVERAGE_KEY,
        CoreMetrics.DUPLICATED_LINES_KEY, CoreMetrics.DUPLICATED_LINES_DENSITY_KEY, CoreMetrics.TECHNICAL_DEBT_KEY, CoreMetrics.TESTS_KEY,
        CoreMetrics.SCM_AUTHORS_BY_LINE_KEY, CoreMetrics.SQALE_RATING_KEY, CoreMetrics.SQALE_DEBT_RATIO_KEY),
      session)) {
      measuresByMetricKey.put(measureDto.getKey().metricKey(), measureDto);
    }
    return measuresByMetricKey;
  }

  @CheckForNull
  private Date periodDate(@Nullable final Integer periodIndex, List<Period> periodList) {
    if (periodIndex != null) {
      Period period = Iterables.find(periodList, new Predicate<Period>() {
        @Override
        public boolean apply(@Nullable Period input) {
          return input != null && periodIndex.equals(input.index());
        }
      }, null);
      return period != null ? period.date() : null;
    }
    return null;
  }

  @CheckForNull
  private ComponentDto nullableComponentById(@Nullable Long componentId, DbSession session) {
    if (componentId != null) {
      return dbClient.componentDao().getById(componentId, session);
    }
    return null;
  }
  @CheckForNull
  private String formatMeasureOrVariation(@Nullable MeasureDto measure, @Nullable Integer periodIndex) {
    if (periodIndex == null) {
      return formatMeasure(measure);
    } else {
      return formatVariation(measure, periodIndex);
    }
  }

  @CheckForNull
  private String formatMeasure(@Nullable MeasureDto measure) {
    if (measure != null) {
      Metric metric = CoreMetrics.getMetric(measure.getKey().metricKey());
      Metric.ValueType metricType = metric.getType();
      Double value = measure.getValue();
      String data = measure.getData();

      if (metricType.equals(Metric.ValueType.FLOAT) && value != null) {
        return i18n.formatDouble(UserSession.get().locale(), value);
      }
      if (metricType.equals(Metric.ValueType.INT) && value != null) {
        return i18n.formatInteger(UserSession.get().locale(), value.intValue());
      }
      if (metricType.equals(Metric.ValueType.PERCENT) && value != null) {
        return i18n.formatDouble(UserSession.get().locale(), value) + "%";
      }
      if (metricType.equals(Metric.ValueType.WORK_DUR) && value != null) {
        return durations.format(UserSession.get().locale(), durations.create(value.longValue()), Durations.DurationFormat.SHORT);
      }
      if ((metricType.equals(Metric.ValueType.STRING) || metricType.equals(Metric.ValueType.RATING)) && data != null) {
        return data;
      }
    }
    return null;
  }

  @CheckForNull
  private String formatVariation(@Nullable MeasureDto measure, Integer periodIndex) {
    if (measure != null) {
      Double variation = measure.getVariation(periodIndex);
      if (variation != null) {
        Metric metric = CoreMetrics.getMetric(measure.getKey().metricKey());
        Metric.ValueType metricType = metric.getType();
        if (metricType.equals(Metric.ValueType.FLOAT) || metricType.equals(Metric.ValueType.PERCENT)) {
          return i18n.formatDouble(UserSession.get().locale(), variation);
        }
        if (metricType.equals(Metric.ValueType.INT)) {
          return i18n.formatInteger(UserSession.get().locale(), variation.intValue());
        }
        if (metricType.equals(Metric.ValueType.WORK_DUR)) {
          return durations.format(UserSession.get().locale(), durations.create(variation.longValue()), Durations.DurationFormat.SHORT);
        }
      }
    }
    return null;
  }

  protected static class Period {
    Integer index;
    String label;
    Date date;

    protected Period(Integer index, String label, @Nullable Date date) {
      this.index = index;
      this.label = label;
      this.date = date;
    }

    public Integer index() {
      return index;
    }

    public String label() {
      return label;
    }

    @CheckForNull
    public Date date() {
      return date;
    }
  }

}
TOP

Related Classes of org.sonar.server.component.ws.ComponentAppAction$Period

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.