package com.capitalone.dashboard.collector;
import com.capitalone.dashboard.model.*;
import com.capitalone.dashboard.util.Supplier;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestOperations;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
@Component
public class DefaultSonarClient implements SonarClient {
private static final Log LOG = LogFactory.getLog(DefaultSonarClient.class);
private static final String URL_RESOURCES = "/api/resources?format=json";
private static final String URL_RESOURCE_DETAILS = "/api/resources?format=json&resource=%s&metrics=%s&includealerts=true";
private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
private static final String ID = "id";
private static final String NAME = "name";
private static final String KEY = "key";
private static final String VERSION = "version";
private static final String MSR = "msr";
private static final String ALERT = "alert";
private static final String ALERT_TEXT = "alert_text";
private static final String VALUE = "val";
private static final String FORMATTED_VALUE = "frmt_val";
private static final String STATUS_WARN = "WARN";
private static final String STATUS_ALERT = "ALERT";
private static final String DATE = "date";
private final RestOperations rest;
private final SonarSettings sonarSettings;
@Autowired
public DefaultSonarClient(Supplier<RestOperations> restOperationsSupplier, SonarSettings settings) {
this.rest = restOperationsSupplier.get();
this.sonarSettings = settings;
}
@Override
public List<SonarProject> getProjects(String instanceUrl) {
List<SonarProject> projects = new ArrayList<>();
String url = instanceUrl + URL_RESOURCES;
try {
for (Object obj : parseAsArray(url)) {
JSONObject prjData = (JSONObject) obj;
SonarProject project = new SonarProject();
project.setInstanceUrl(instanceUrl);
project.setProjectId(str(prjData, ID));
project.setProjectName(str(prjData, NAME));
projects.add((project));
}
} catch (ParseException e) {
LOG.error("Could not parse response from: " + url, e);
} catch (RestClientException rce) {
LOG.error(rce);
}
return projects;
}
@Override
public CodeQuality currentCodeQuality(SonarProject project) {
String url = String.format(
project.getInstanceUrl() + URL_RESOURCE_DETAILS, project.getProjectId(), sonarSettings.getMetrics());
try {
JSONArray jsonArray = parseAsArray(url);
if (!jsonArray.isEmpty()) {
JSONObject prjData = (JSONObject) jsonArray.get(0);
CodeQuality codeQuality = new CodeQuality();
codeQuality.setName(str(prjData, NAME));
codeQuality.setUrl(project.getInstanceUrl() + "/dashboard/index/" + project.getProjectId());
codeQuality.setType(CodeQualityType.StaticAnalysis);
codeQuality.setTimestamp(timestamp(prjData, DATE));
codeQuality.setVersion(str(prjData, VERSION));
for (Object metricObj : (JSONArray) prjData.get(MSR)) {
JSONObject metricJson = (JSONObject) metricObj;
CodeQualityMetric metric = new CodeQualityMetric(str(metricJson, KEY));
metric.setValue(metricJson.get(VALUE));
metric.setFormattedValue(str(metricJson, FORMATTED_VALUE));
metric.setStatus(metricStatus(str(metricJson, ALERT)));
metric.setStatusMessage(str(metricJson, ALERT_TEXT));
codeQuality.getMetrics().add(metric);
}
return codeQuality;
}
} catch (ParseException e) {
LOG.error("Could not parse response from: " + url, e);
} catch (RestClientException rce) {
LOG.error(rce);
}
return null;
}
private JSONArray parseAsArray(String url) throws ParseException {
return (JSONArray) new JSONParser().parse(rest.getForObject(url, String.class));
}
private long timestamp(JSONObject json, String key) {
Object obj = json.get(key);
if (obj != null) {
try {
return new SimpleDateFormat(DATE_FORMAT).parse(obj.toString()).getTime();
} catch (java.text.ParseException e) {
LOG.error(obj + " is not in expected format " + DATE_FORMAT, e);
}
}
return 0;
}
private String str(JSONObject json, String key) {
Object obj = json.get(key);
return obj == null ? null : obj.toString();
}
private Integer integer(JSONObject json, String key) {
Object obj = json.get(key);
return obj == null ? null : (Integer) obj;
}
private BigDecimal decimal(JSONObject json, String key) {
Object obj = json.get(key);
return obj == null ? null : new BigDecimal(obj.toString());
}
private Boolean bool(JSONObject json, String key) {
Object obj = json.get(key);
return obj == null ? null : Boolean.valueOf(obj.toString());
}
private CodeQualityMetricStatus metricStatus(String status) {
if (StringUtils.isBlank(status)) {
return CodeQualityMetricStatus.Ok;
}
switch(status) {
case STATUS_WARN: return CodeQualityMetricStatus.Warning;
case STATUS_ALERT: return CodeQualityMetricStatus.Alert;
default: return CodeQualityMetricStatus.Ok;
}
}
}