Commit eb5bac74 authored by Roman Vasiliev's avatar Roman Vasiliev
Browse files

Merge branch 'rvasiliev/error-reporting'

parents 45da7c3f 3652c7bb
Showing with 132 additions and 32 deletions
+132 -32
......@@ -20,6 +20,8 @@ public abstract class AbstractMessage {
private SubmittedReportInfo mySubmissionInfo;
private String myAdditionalInfo;
private Integer myAssigneeId;
private boolean myAssigneeVisible;
private Long myDevelopersTimestamp;
public abstract @NotNull Throwable getThrowable();
public abstract @NotNull String getThrowableText();
......@@ -96,6 +98,22 @@ public abstract class AbstractMessage {
myAssigneeId = assigneeId;
}
boolean isAssigneeVisible() {
return myAssigneeVisible;
}
void setAssigneeVisible(boolean assigneeVisible) {
myAssigneeVisible = assigneeVisible;
}
public @Nullable Long getDevelopersTimestamp() {
return myDevelopersTimestamp;
}
public void setDevelopersTimestamp(@Nullable Long developersTimestamp) {
myDevelopersTimestamp = developersTimestamp;
}
/** @deprecated use {@link #getIncludedAttachments()} instead (to be removed in IDEA 2020) */
@Deprecated
public List<Attachment> getAttachments() {
......
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.diagnostic;
import com.intellij.util.xmlb.annotations.Attribute;
import com.intellij.util.xmlb.annotations.Tag;
import org.jetbrains.annotations.Nullable;
@Tag("developer")
public class Developer {
public static final Developer NULL = new Developer(-1, "<none>");
@Attribute("id")
private final int myId;
@Attribute("name")
private final String myName;
@SuppressWarnings("unused") // need for xml serialization
private Developer() {
this(0, "");
}
public Developer(int id, String name) {
myId = id;
myName = name;
......
......@@ -5,33 +5,69 @@ import com.intellij.credentialStore.CredentialAttributes
import com.intellij.credentialStore.Credentials
import com.intellij.credentialStore.SERVICE_NAME_PREFIX
import com.intellij.ide.passwordSafe.PasswordSafe
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.RoamingType
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.intellij.openapi.components.*
import com.intellij.util.io.decodeBase64
import com.intellij.util.xmlb.XmlSerializer
import org.jdom.Element
@State(name = "ErrorReportConfigurable", storages = [(Storage(value = "other.xml", deprecated = true, roamingType = RoamingType.DISABLED))])
internal class ErrorReportConfigurable : PersistentStateComponent<OldState> {
@State(name = "ErrorReportConfigurable", storages = [Storage(value = "other.xml", deprecated = true, roamingType = RoamingType.DISABLED), Storage(value = "errorReporting.xml")])
internal class ErrorReportConfigurable : PersistentStateComponent<Element> {
companion object {
@JvmStatic
val SERVICE_NAME = "$SERVICE_NAME_PREFIX — JetBrains Account"
@JvmStatic
val instance: ErrorReportConfigurable
get() = ServiceManager.getService(ErrorReportConfigurable::class.java)
@JvmStatic
fun getCredentials() = PasswordSafe.instance.get(CredentialAttributes(SERVICE_NAME))
}
override fun getState() = OldState()
private var myState: State? = null
var developer: Developers?
get() = myState?.let { Developers(it.developers.toList(), it.timestamp) }
set(value) {
myState = value?.let { State(it.developers.toList(), it.timestamp) }
}
override fun getState(): Element? {
return myState?.let { XmlSerializer.serialize(it) }
}
override fun loadState(element: Element) {
loadOldState(element)
myState = XmlSerializer.deserialize(element, State::class.java)
}
private fun loadOldState(element: Element) {
val state = XmlSerializer.deserialize(element, OldState::class.java)
override fun loadState(state: OldState) {
if (!state.ITN_LOGIN.isNullOrEmpty() || !state.ITN_PASSWORD_CRYPT.isNullOrEmpty()) {
PasswordSafe.instance.set(CredentialAttributes(SERVICE_NAME, state.ITN_LOGIN), Credentials(state.ITN_LOGIN, state.ITN_PASSWORD_CRYPT!!.decodeBase64()))
}
}
private data class State(var developers: List<Developer>, var timestamp: Long) {
@Suppress("unused")
private constructor(): this(emptyList(), 0) // need for xml serialization
}
@Suppress("PropertyName")
private class OldState {
var ITN_LOGIN: String? = null
var ITN_PASSWORD_CRYPT: String? = null
}
}
@Suppress("PropertyName")
internal class OldState {
var ITN_LOGIN: String? = null
var ITN_PASSWORD_CRYPT: String? = null
internal data class Developers(val developers: List<Developer>, val timestamp: Long) {
companion object {
private const val UPDATE_INTERVAL = 24L * 60 * 60 * 1000 // 24 hours
}
fun isUpToDateAt(timestamp: Long): Boolean {
return (timestamp - this.timestamp < UPDATE_INTERVAL) && developers.isNotEmpty()
}
}
\ No newline at end of file
......@@ -61,7 +61,7 @@ class ITNProxy {
private static final NotNullLazyValue<Map<String, String>> TEMPLATE = AtomicNotNullLazyValue.createValue(() -> {
Map<String, String> template = new LinkedHashMap<>();
template.put("protocol.version", "1");
template.put("protocol.version", "1.1");
template.put("os.name", SystemInfo.OS_NAME);
template.put("java.version", SystemInfo.JAVA_VERSION);
template.put("java.vm.vendor", SystemInfo.JAVA_VENDOR);
......@@ -254,6 +254,10 @@ class ITNProxy {
if (messageObj.getAssigneeId() != null) {
append(builder, "assignee.id", Integer.toString(messageObj.getAssigneeId()));
}
append(builder, "assignee.list.visible", Boolean.toString(messageObj.isAssigneeVisible()));
if (messageObj.getDevelopersTimestamp() != null) {
append(builder, "assignee.list.timestamp", Long.toString(messageObj.getDevelopersTimestamp()));
}
}
return builder.toString().getBytes(StandardCharsets.UTF_8);
......
......@@ -30,7 +30,6 @@ import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
......@@ -74,15 +73,15 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
private static final String ACCEPTED_NOTICES_KEY = "exception.accepted.notices";
private static final String ACCEPTED_NOTICES_SEPARATOR = ":";
private static final String DISABLE_PLUGIN_URL = "#disable";
private static List<Developer> ourDevelopersList = Collections.emptyList();
private static final String EA_PLUGIN_ID = "com.intellij.sisyphus";
private final MessagePool myMessagePool;
private final Project myProject;
private final boolean myInternalMode;
private final boolean myAssigneeVisible;
private final Set<String> myAcceptedNotices;
private final List<MessageCluster> myMessageClusters = new ArrayList<>(); // exceptions with the same stacktrace
private int myIndex, myLastIndex = -1;
private Long myDevelopersTimestamp;
private JLabel myCountLabel;
private JTextComponent myInfoLabel;
......@@ -101,14 +100,14 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
super(project, true);
myMessagePool = messagePool;
myProject = project;
myInternalMode = ApplicationManager.getApplication().isInternal();
myAssigneeVisible = ApplicationManager.getApplication().isInternal() || PluginManager.isPluginInstalled(PluginId.getId(EA_PLUGIN_ID));
setTitle(DiagnosticBundle.message("error.list.title"));
setModal(false);
init();
setCancelButtonText(CommonBundle.message("close.action.name"));
if (myInternalMode) {
if (myAssigneeVisible) {
loadDevelopersList();
}
......@@ -123,30 +122,45 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
}
private void loadDevelopersList() {
if (!ourDevelopersList.isEmpty()) {
myAssigneeCombo.setModel(new CollectionComboBoxModel<>(ourDevelopersList));
ErrorReportConfigurable configurable = ErrorReportConfigurable.getInstance();
Developers developers = configurable.getDeveloper();
if (developers != null && developers.isUpToDateAt(System.currentTimeMillis())) {
setDevelopers(developers);
}
else {
new Task.Backgroundable(null, "Loading Developers List", true) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
try {
List<Developer> developers = ITNProxy.fetchDevelopers(indicator);
//noinspection AssignmentToStaticFieldFromInstanceMethod
ourDevelopersList = developers;
Developers updatedDevelopers = new Developers(ITNProxy.fetchDevelopers(indicator), System.currentTimeMillis());
UIUtil.invokeLaterIfNeeded(() -> {
configurable.setDeveloper(updatedDevelopers);
if (isShowing()) {
myAssigneeCombo.setModel(new CollectionComboBoxModel<>(developers));
setDevelopers(updatedDevelopers);
}
});
}
catch (UnknownHostException e) {
LOG.debug(e);
UIUtil.invokeLaterIfNeeded(() -> {
if (isShowing()) {
setDevelopers(developers);
}
});
}
catch (UnknownHostException e) { LOG.debug(e); }
catch (IOException e) { LOG.warn(e); }
}
}.queue();
}
}
private void setDevelopers(@Nullable Developers developers) {
if (developers != null) {
myAssigneeCombo.setModel(new CollectionComboBoxModel<>(developers.getDevelopers()));
myDevelopersTimestamp = developers.getTimestamp();
}
}
private int selectMessage(@Nullable LogMessage defaultMessage) {
if (defaultMessage != null) {
for (int i = 0; i < myMessageClusters.size(); i++) {
......@@ -255,7 +269,7 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
}
});
if (myInternalMode) {
if (myAssigneeVisible) {
myAssigneeCombo = new ComboBox<>();
myAssigneeCombo.setRenderer(new ListCellRendererWrapper<Developer>() {
@Override
......@@ -312,7 +326,7 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
attachmentsPanel.add(scrollPane(myAttachmentArea, 500, 350), BorderLayout.CENTER);
JPanel accountRow = new JPanel(new BorderLayout());
if (myInternalMode) accountRow.add(myAssigneePanel, BorderLayout.WEST);
if (myAssigneeVisible) accountRow.add(myAssigneePanel, BorderLayout.WEST);
accountRow.add(myCredentialsLabel, BorderLayout.EAST);
myNoticePanel = new JPanel(new GridBagLayout());
myNoticePanel.add(new JBLabel(UIUtil.getBalloonWarningIcon()), new GridBagConstraints(0, 0, 1, 1, 0, 0, NORTH, NONE, JBUI.insets(7, 0, 0, 5), 0, 0));
......@@ -341,7 +355,7 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
@Override
protected Action[] createActions() {
List<Action> actions = new ArrayList<>();
if (myInternalMode && myProject != null && !myProject.isDefault()) {
if (myAssigneeVisible && myProject != null && !myProject.isDefault()) {
AnAction action = ActionManager.getInstance().getAction("Unscramble");
if (action != null) {
actions.add(new AnalyzeAction(action));
......@@ -411,7 +425,7 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
updateLabels(cluster);
updateDetails(cluster);
if (myInternalMode) {
if (myAssigneeVisible) {
updateAssigneePanel(cluster);
}
updateCredentialsPanel(submitter);
......@@ -541,8 +555,13 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
myAssigneeCombo.setSelectedIndex(-1);
}
else {
Condition<Developer> lookup = d -> Objects.equals(assignee, d.getId());
myAssigneeCombo.setSelectedIndex(ContainerUtil.indexOf(ourDevelopersList, lookup));
int assigneeIndex = getAssigneeIndex(assignee);
if (assigneeIndex != -1) {
myAssigneeCombo.setSelectedIndex(assigneeIndex);
}
else {
cluster.first.setAssigneeId(null);
}
}
}
else {
......@@ -550,6 +569,16 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
}
}
private int getAssigneeIndex(Integer assigneeId) {
for (int index = 0; index < myAssigneeCombo.getItemCount(); index++) {
if (Objects.equals(assigneeId, myAssigneeCombo.getItemAt(index).getId())) {
return index;
}
}
return -1;
}
private void updateCredentialsPanel(ErrorReportSubmitter submitter) {
if (submitter instanceof ITNReporter) {
myCredentialsLabel.setVisible(true);
......@@ -571,6 +600,8 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene
if (submitter == null) return false;
AbstractMessage message = cluster.first;
message.setAssigneeVisible(myAssigneeVisible);
message.setDevelopersTimestamp(myDevelopersTimestamp);
message.setSubmitting(true);
String notice = submitter.getPrivacyNoticeText();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment