Skip to content

Commit

Permalink
feat: allow message limits (#82)
Browse files Browse the repository at this point in the history
Signed-off-by: Andre Dietisheim <[email protected]>
  • Loading branch information
adietish committed Jun 18, 2024
1 parent eba5ef4 commit 8b25f9a
Show file tree
Hide file tree
Showing 39 changed files with 3,401 additions and 49 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ dependencies {
}

test {
// Discover and execute JUnit4-based tests
useJUnit()
// Discover and execute JUnit5-based tests
useJUnitPlatform()
afterSuite { desc, result ->
if (!desc.parent)
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ideaVersion = IC-2020.3
ideaVersion = IC-2022.1
# build number ranges
# https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html
sinceIdeaBuild=203
sinceIdeaBuild=211
projectVersion=1.2.0-SNAPSHOT
intellijPluginVersion=1.16.1
jetBrainsToken=invalid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
package com.redhat.devtools.intellij.telemetry.core;

import com.intellij.openapi.extensions.PluginDescriptor;
import com.redhat.devtools.intellij.telemetry.core.service.Environment;
import com.redhat.devtools.intellij.telemetry.core.service.Event;

public interface IMessageBroker {
void send(Event event);
void dispose();

interface IMessageBrokerFactory {
IMessageBroker create(boolean isDebug, PluginDescriptor descriptor);
IMessageBroker create(boolean isDebug, Environment environment, PluginDescriptor descriptor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class TelemetryConfiguration extends CompositeConfiguration {
private static final SaveableFileConfiguration FILE = new SaveableFileConfiguration(
Directories.RED_HAT.resolve("com.redhat.devtools.intellij.telemetry"));

private static TelemetryConfiguration INSTANCE = new TelemetryConfiguration();
private static final TelemetryConfiguration INSTANCE = new TelemetryConfiguration();

public static TelemetryConfiguration getInstance() {
return INSTANCE;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*******************************************************************************
* Copyright (c) 2024 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package com.redhat.devtools.intellij.telemetry.core.configuration.limits;

import com.intellij.openapi.diagnostic.Logger;
import com.redhat.devtools.intellij.telemetry.core.util.Directories;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

class Configurations {

private static final Logger LOGGER = Logger.getInstance(Configurations.class);
static final Path LOCAL = Directories.RED_HAT.resolve("telemetry-config.json");
static final String EMBEDDED = "/telemetry-config.json";
static final String REMOTE = "https://raw.githubusercontent.com/adietish/intellij-redhat-telemetry/issue-82/src/main/resources/telemetry-config.json";

private final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.build();

public String getRemote() {
Request request = new Request.Builder()
.url(REMOTE)
.addHeader("Content-Type", "application/json")
.build();
try (Response response = client.newCall(request).execute()) {
if (response.body() != null) {
Files.copy(response.body().byteStream(), LOCAL, StandardCopyOption.REPLACE_EXISTING);
}
return getLocal();
} catch (Throwable e) {
LOGGER.warn("Could not download remote limits configurations.", e);
return null;
}
}

public boolean localExists() {
return Files.exists(LOCAL);
}

public FileTime getLocalLastModified() {
try {
if (!Files.exists(LOCAL)) {
return null;
}
return Files.getLastModifiedTime(LOCAL);
} catch (Throwable e) {
return null;
}
}

public String getLocal() {
try {
return toString(Files.newInputStream(LOCAL));
} catch (IOException e) {
return null;
}
}

public String getEmbedded() {
return toString(Configurations.class.getResourceAsStream(EMBEDDED));
}

private String toString(InputStream in) {
if (in == null) {
return null;
}
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
return reader.lines().collect(Collectors.joining());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2024 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package com.redhat.devtools.intellij.telemetry.core.configuration.limits;

import com.intellij.openapi.util.text.StringUtil;

import java.util.Arrays;

public enum Enabled {
ALL("all"),
ERROR("error"),
CRASH("crash"),
OFF("off");

private final String value;

Enabled(String value) {
this.value = value;
}

private boolean hasValue(String value) {
if (StringUtil.isEmptyOrSpaces(value)) {
return this.value == null;
}
return value.equals(this.value);
}

public static Enabled safeValueOf(String value) {
return Arrays.stream(values())
.filter(instance -> instance.hasValue(value))
.findAny()
.orElse(ALL);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*******************************************************************************
* Copyright (c) 2024 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package com.redhat.devtools.intellij.telemetry.core.configuration.limits;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.Service;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.util.xmlb.XmlSerializerUtil;
import com.redhat.devtools.intellij.telemetry.core.service.Event;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Service
@State(
name = " com.redhat.devtools.intellij.telemetry.core.configuration.limits.EventCounts",
storages = @Storage(value = "eventCounts.xml")
)
public final class EventCounts implements PersistentStateComponent<EventCounts> {

public static EventCounts getInstance() {
return ApplicationManager.getApplication().getService(EventCounts.class);
}

private static final String COUNT_VALUES_SEPARATOR = ",";

public final Map<String, String> counts = new HashMap<>();

@Override
public EventCounts getState() {
return this;
}

@Override
public void loadState(@NotNull EventCounts state) {
XmlSerializerUtil.copyBean(state, this);
}

@Override
public void noStateLoaded() {
PersistentStateComponent.super.noStateLoaded();
}

@Override
public void initializeComponent() {
PersistentStateComponent.super.initializeComponent();
}

public Count get(Event event) {
if (event == null) {
return null;
}
String countString = counts.get(event.getName());
return toCount(countString);
}

public void put(Event event) {
Count count = newOrUpdateExisting(event);
put(event, count);
}

private Count newOrUpdateExisting(Event event) {
Count count = get(event);
if (count != null) {
// update existing
count = count.newOccurrence();
} else {
// create new
count = new Count();
}
return count;
}

private Count toCount(String string) {
if (StringUtils.isEmpty(string)) {
return null;
}
String[] split = string.split(COUNT_VALUES_SEPARATOR);
LocalDateTime lastOccurrence = toLastOccurrence(split[0]);
int total = toTotal(split[1]);
return new Count(lastOccurrence, total);
}

private LocalDateTime toLastOccurrence(String value) {
try {
long epochSeconds = Long.parseLong(value);
return LocalDateTime.ofEpochSecond(epochSeconds, 0, ZonedDateTime.now().getOffset());
} catch (NumberFormatException e) {
return null;
}
}

private int toTotal(String value) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return 0;
}
}

EventCounts put(Event event, Count count) {
if (event == null) {
return null;
}
counts.put(event.getName(), toString(count));
return this;
}

String toString(@NotNull Count count) {
long epochSecond = count.lastOccurrence.toEpochSecond(ZonedDateTime.now().getOffset());
return epochSecond + COUNT_VALUES_SEPARATOR + count.total;
}

public static class Count {
private final LocalDateTime lastOccurrence;
private final int total;

Count() {
this(LocalDateTime.now(), 1);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Count)) return false;
Count count = (Count) o;
return total == count.total && Objects.equals(lastOccurrence, count.lastOccurrence);
}

@Override
public int hashCode() {
return Objects.hash(lastOccurrence, total);
}

Count(LocalDateTime lastOccurrence, int total) {
this.lastOccurrence = lastOccurrence;
this.total = total;
}

public LocalDateTime getLastOccurrence() {
return lastOccurrence;
}

public int getTotal() {
return total;
}

public Count newOccurrence() {
return new Count(LocalDateTime.now(), total + 1);
}
}
}
Loading

0 comments on commit 8b25f9a

Please sign in to comment.