diff --git a/build.gradle b/build.gradle index ba7c51c..e5ac60b 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { } group 'fr.mary.olivier' -version '1.0.8' +version '2.0.0' repositories { @@ -21,7 +21,7 @@ sourceSets { } } -task generateVersion { +tasks.register('generateVersion') { doFirst { def versionFile = file("${projectDir}/src/generated/java/fr/mary/olivier/aw/watcher/Version.java") versionFile.parentFile.mkdirs() @@ -57,32 +57,37 @@ clean { delete += sourceSets.generated.java.srcDirs + sourceSets.generated.resources.srcDirs + swaggerSources.activityWatcher.code.outputDir } -sourceCompatibility = 17 -targetCompatibility = 17 +sourceCompatibility = JavaVersion.VERSION_17 +targetCompatibility = JavaVersion.VERSION_17 dependencies { - compileOnly 'io.swagger:swagger-annotations:1.6.9' - compileOnly 'com.squareup.okhttp:okhttp:2.7.5' - compileOnly 'com.squareup.okhttp:logging-interceptor:2.7.5' - compileOnly 'com.google.code.gson:gson:2.10.1' - compileOnly 'io.gsonfire:gson-fire:1.8.3' - compileOnly 'org.threeten:threetenbp:1.6.5' - compileOnly 'javax.annotation:javax.annotation-api:1.3.2' - compileOnly 'org.openapitools:openapi-generator:6.3.0' - compileOnly 'org.apache.oltu.oauth2:org.apache.oltu.oauth2.common:1.0.2' - compileOnly 'org.apache.oltu.oauth2:org.apache.oltu.oauth2.client:1.0.2' - testCompileOnly 'junit:junit:4.12' + implementation 'io.swagger:swagger-annotations:1.6.9' + implementation 'com.squareup.okhttp:okhttp:1.6.0' + implementation 'com.squareup.okhttp3:logging-interceptor:4.10.0' + implementation 'com.google.code.gson:gson:2.10.1' + implementation 'io.gsonfire:gson-fire:1.8.3' + implementation 'javax.annotation:javax.annotation-api:1.3.2' + implementation 'javax.ws.rs:javax.ws.rs-api:2.1.1' + implementation 'org.openapitools:openapi-generator:6.3.0' + implementation 'org.projectlombok:lombok:1.18.24' + annotationProcessor 'org.projectlombok:lombok:1.18.24' + testImplementation 'junit:junit:4.12' swaggerCodegen 'org.openapitools:openapi-generator-cli:6.3.0' } intellij { - version.set("2022.3") + version.set('2022.3') setUpdateSinceUntilBuild(false) + plugins.add('Git4Idea') } patchPluginXml { setChangeNotes """ + 2.0.0
+ - Add support for IntelliJ 2022.3
+ - Change to HeatBeat event
+ - Add VCS informations
1.0.8
- Fix exceptions
- Fix file name recorded
diff --git a/src/main/java/fr/mary/olivier/aw/watcher/HeartBeatData.java b/src/main/java/fr/mary/olivier/aw/watcher/HeartBeatData.java new file mode 100755 index 0000000..e0b4c3d --- /dev/null +++ b/src/main/java/fr/mary/olivier/aw/watcher/HeartBeatData.java @@ -0,0 +1,27 @@ +package fr.mary.olivier.aw.watcher; + +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode() +@ToString(callSuper = true) +@Builder +@Data +public class HeartBeatData { + private String file; + private String fileFullPath; + private String project; + private String projectPath; + private String language; + private String editor; + private String editorVersion; + private String eventType; + private String branch; + private String commit; + private String state; + private String sourceUrl; + +} + diff --git a/src/main/java/fr/mary/olivier/aw/watcher/ReportActivity.java b/src/main/java/fr/mary/olivier/aw/watcher/ReportActivity.java index 01deb9d..694493c 100755 --- a/src/main/java/fr/mary/olivier/aw/watcher/ReportActivity.java +++ b/src/main/java/fr/mary/olivier/aw/watcher/ReportActivity.java @@ -11,8 +11,6 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.PlatformUtils; @@ -22,116 +20,47 @@ import fr.mary.olivier.aw.watcher.listener.RAEditorMouseListener; import fr.mary.olivier.aw.watcher.listener.RASaveListener; import fr.mary.olivier.aw.watcher.listener.RAVisibleAreaListener; -import fr.mary.olivier.aw.watcher.model.EditorActivityEvent; -import org.jetbrains.annotations.SystemIndependent; -import org.openapitools.client.ApiException; +import git4idea.GitUtil; +import git4idea.repo.GitRepositoryManager; import org.openapitools.client.api.DefaultApi; import org.openapitools.client.model.Bucket; import org.openapitools.client.model.CreateBucket; import org.openapitools.client.model.Event; -import org.threeten.bp.OffsetDateTime; -import java.math.BigDecimal; -import java.math.RoundingMode; +import java.awt.*; import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.List; +import java.time.OffsetDateTime; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; public class ReportActivity implements Disposable { private static final String ACTIVITY_WATCHER = "Activity Watcher"; private static final Logger LOG = Logger.getInstance(ReportActivity.class.getName()); private static final String TYPE = "app.editor.activity"; - private static final BigDecimal MAX_STAY_TIME = new BigDecimal(2 * 60); - private static final BigDecimal MAX_RETRY_TIME = new BigDecimal(30); private static final String AW_WATCHER = "aw-watcher-"; - private static String ide; - private static String ideVersion; - private static String bucketClientNamePrefix; - private static MessageBusConnection connection; - private static DefaultApi apiClient; + private static final String IDE_NAME = PlatformUtils.getPlatformPrefix(); + private static final String IDE_VERSION = ApplicationInfo.getInstance().getFullVersion(); + public static final int HEARTBEAT_PULSETIME = 20; + public static final int CHECK_CONNEXION_DELAY = 10; + private static final DefaultApi API_CLIENT = new DefaultApi(); private static Bucket bucket; - private static String lastFile = null; - private static BigDecimal lastTime = getCurrentTimestamp(); - private static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); - private static ScheduledFuture scheduledFixture; + private static final ScheduledExecutorService connexionScheduler = Executors.newScheduledThreadPool(1); + private static ScheduledFuture scheduledConnexion; private static boolean connexionFailedMessageAlreadySend = false; private static boolean connexionLost = false; - private static BigDecimal lastFailed = getCurrentTimestamp(); - private static List eventsToSend = new ArrayList<>(); - private static String lastFileType; - private static Project lastProject; + private static MessageBusConnection connection; + private static ReportActivity instance; + - ReportActivity() { + private ReportActivity() { LOG.info("Initializing ActivityWatcher plugin : Start"); - initIDEInfo(); setupConnexionToApi(); setupEventListeners(); - } - - private static void initIDEInfo() { - ideVersion = ApplicationInfo.getInstance().getFullVersion(); - ide = PlatformUtils.getPlatformPrefix(); - bucketClientNamePrefix = AW_WATCHER + ide; - } - - private static void setupConnexionToApi() { - final Runnable handler = () -> { - initClient(); - - if (bucket == null) { - LOG.info("Bucket null, no activity will be send."); - LOG.info("Initializing ActivityWatcher plugin : FAIL"); - if (!connexionFailedMessageAlreadySend) { - Notifications.Bus.notify(new Notification(ACTIVITY_WATCHER, ACTIVITY_WATCHER, - "Activity Watcher Server not found, server is started ?\n " + - "Will try to reconnect all 60s.", NotificationType.WARNING)); - connexionFailedMessageAlreadySend = true; - } - } else { - scheduledFixture.cancel(false); - Notifications.Bus.notify(new Notification(ACTIVITY_WATCHER, ACTIVITY_WATCHER, - "Activity Watcher Server Connected.", NotificationType.INFORMATION)); - LOG.info("Initializing ActivityWatcher plugin : OK"); - } - }; - scheduledFixture = scheduler.scheduleWithFixedDelay(handler, 0, 60, java.util.concurrent.TimeUnit.SECONDS); - } - - private static void initClient() { - apiClient = new DefaultApi(); - - String hostname; - try { - hostname = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException exp) { - LOG.error("Unable to get hostname", exp); - hostname = "unknown"; - } - - try { - bucket = apiClient.getBucketResource(bucketClientNamePrefix + "_" + hostname); - } catch (ApiException exp) { - CreateBucket nb = new CreateBucket(); - nb.setClient(bucketClientNamePrefix); - try { - nb.setHostname(InetAddress.getLocalHost().getHostName()); - } catch (UnknownHostException e1) { - nb.setHostname("unknown"); - } - nb.setType(TYPE); - try { - apiClient.postBucketResource(bucketClientNamePrefix + "_" + hostname, nb); - bucket = apiClient.getBucketResource(bucketClientNamePrefix + "_" + hostname); - } catch (ApiException expBis) { - LOG.warn("Unable to init bucket", expBis); - } - } + instance = this; } private static void setupEventListeners() { @@ -141,132 +70,143 @@ private static void setupEventListeners() { connection = bus.connect(); connection.subscribe(AppTopics.FILE_DOCUMENT_SYNC, new RASaveListener()); // Switch document or in document - EditorFactory.getInstance().getEventMulticaster().addDocumentListener(new RADocumentListener()); - EditorFactory.getInstance().getEventMulticaster().addEditorMouseListener(new RAEditorMouseListener()); - EditorFactory.getInstance().getEventMulticaster().addVisibleAreaListener(new RAVisibleAreaListener()); + EditorFactory.getInstance().getEventMulticaster().addDocumentListener(new RADocumentListener(), instance); + EditorFactory.getInstance().getEventMulticaster().addEditorMouseListener(new RAEditorMouseListener(), instance); + EditorFactory.getInstance().getEventMulticaster().addVisibleAreaListener(new RAVisibleAreaListener(), instance); }); } - private static boolean longEnougthToLog(BigDecimal now) { - return lastTime.add(MAX_STAY_TIME).compareTo(now) < 0; + public static void sendHeartBeat(VirtualFile file, Document document) { + sendHeartBeat(file, getProject(document)); } - private static boolean longEnougthToRetry() { - return lastFailed.add(MAX_RETRY_TIME).compareTo(getCurrentTimestamp()) < 0; + private static Project getProject(Document document) { + Editor[] editors = EditorFactory.getInstance().getEditors(document); + if (editors.length > 0) { + return editors[0].getProject(); + } + return null; } - public static void addAndSendEvent(final VirtualFile file, Project project, Class clazz) { - synchronized (eventsToSend) { - final BigDecimal time = getCurrentTimestamp(); - if (file == null || file.getPath().equals(lastFile) && !longEnougthToLog(time) - || eventsToSend.stream().anyMatch(e -> file.getPath().endsWith(((EditorActivityEvent) e.getData()).getFile()))) { + public static void sendHeartBeat(VirtualFile file, Project project) { + if (bucket != null && isAppActive() && !connexionLost) { + if (project == null || !project.isInitialized()) { return; } - if (lastFile == null){ - initLastFile(file, project, time); + if (file == null) { return; } - final BigDecimal duration = time.subtract(lastTime); - @SystemIndependent final String projectPath = lastProject != null && lastProject.getBasePath() != null ? lastProject.getBasePath() : ""; - Event event = new Event() - .duration(duration) - .data(new EditorActivityEvent( - lastFile.replace(projectPath,""), - lastProject != null ? lastProject.getName() : null, - projectPath, - lastFileType, - ide, ideVersion, clazz.getName())) - .timestamp(OffsetDateTime.now()); - eventsToSend.add(event); - sendAllEvents(); - initLastFile(file, project, time); - } - } - - private static void initLastFile(VirtualFile file, Project project, BigDecimal time) { - lastProject = project; - lastFileType = getLanguage(file); - lastFile = file.getPath(); - lastTime = time; - } + HeartBeatData.HeartBeatDataBuilder dataBuilder = HeartBeatData.builder() + .projectPath(project.getPresentableUrl()) + .editorVersion(IDE_VERSION) + .editor(IDE_NAME) + //.eventType(classz.getName()) disabled for heartbeat because data change and create multiples of event + .file(file.getPresentableName()) + .fileFullPath(file.getPath()) + .project(project.getName()) + .language(file.getFileType().getName()); + GitRepositoryManager repositoryManager = GitUtil.getRepositoryManager(project); + repositoryManager.getRepositories().stream().findFirst().ifPresent(r -> { + dataBuilder.branch(r.getCurrentBranchName()); + dataBuilder.commit(r.getCurrentRevision()); + dataBuilder.state(r.getState().name()); + r.getInfo().getRemotes().stream().findFirst().ifPresent(gitRemote -> dataBuilder.sourceUrl(gitRemote.getFirstUrl())); + }); + + HeartBeatData data = dataBuilder.build(); + Event event = new Event().data(data).timestamp(OffsetDateTime.now()); - static void eventSent(Event event) { - synchronized (eventsToSend) { - eventsToSend.remove(event); - } - } - - private static void sendAllEvents() { - if (bucket == null || ReportActivity.isConnexionLost() && !longEnougthToRetry()) { - return; - } - synchronized (eventsToSend) { - for (Event event : eventsToSend) { - try { - apiClient.postEventsResourceAsync(bucket.getId(), event, new ReportActivityCallBack(event)); - } catch (ApiException exp) { - // nothing - } + try { + API_CLIENT.postHeartbeatResource(bucket.getId(), event, "" + (HEARTBEAT_PULSETIME)); + } catch (Exception e) { + LOG.error("Unable to send heartbeat", e); + ReportActivity.connexionLost(); } } } - public static Project getProject(Document document) { - Editor[] editors = EditorFactory.getInstance().getEditors(document); - if (editors.length > 0) { - return editors[0].getProject(); + private static void initBucket() { + String bucketClientNamePrefix = AW_WATCHER + IDE_NAME; + String hostname; + try { + hostname = InetAddress.getLocalHost().getHostName(); + } catch (Exception exp) { + LOG.error("Unable to get hostname", exp); + hostname = "unknown"; } - return null; - } - static void connexionResume() { - if (ReportActivity.isConnexionLost()) { - ReportActivity.setConnexionLost(false); - Notifications.Bus.notify(new Notification(ReportActivity.ACTIVITY_WATCHER, ReportActivity.ACTIVITY_WATCHER, - "Activity Watcher Server is back!", NotificationType.INFORMATION)); + try { + bucket = API_CLIENT.getBucketResource(bucketClientNamePrefix + "_" + hostname); + } catch (Exception exp) { + CreateBucket nb = new CreateBucket(); + nb.setClient(bucketClientNamePrefix); + try { + nb.setHostname(InetAddress.getLocalHost().getHostName()); + } catch (Exception e1) { + nb.setHostname("unknown"); + } + nb.setType(TYPE); + try { + API_CLIENT.postBucketResource(bucketClientNamePrefix + "_" + hostname, nb); + bucket = API_CLIENT.getBucketResource(bucketClientNamePrefix + "_" + hostname); + } catch (Exception expBis) { + LOG.warn("Unable to init bucket", expBis); + connexionLost = true; + } } } - static void connexionLost() { - if (!ReportActivity.isConnexionLost()) { - Notifications.Bus.notify(new Notification(ReportActivity.ACTIVITY_WATCHER, ReportActivity.ACTIVITY_WATCHER, - "Activity Watcher Server connexion lost?\n " + - "Will try to re-send events when connexion back.", NotificationType.WARNING)); - } - ReportActivity.setConnexionLost(true); - lastFailed = getCurrentTimestamp(); - } + private static void setupConnexionToApi() { - private static synchronized boolean isConnexionLost() { - return connexionLost; - } + final Runnable handler = () -> { + if (bucket == null) { + initBucket(); + } - private static synchronized void setConnexionLost(boolean connexionLost) { - ReportActivity.connexionLost = connexionLost; - } + if (bucket == null && !connexionFailedMessageAlreadySend) { + connexionLost(); + } - private static String getLanguage(final VirtualFile file) { - FileType type = file.getFileType(); - return type.getName(); + if (bucket != null && connexionLost) { + connexionResume(); + } + }; + scheduledConnexion = connexionScheduler.scheduleWithFixedDelay(handler, 0, CHECK_CONNEXION_DELAY, TimeUnit.SECONDS); } - private static BigDecimal getCurrentTimestamp() { - return new BigDecimal(String.valueOf(System.currentTimeMillis() / 1000.0)).setScale(4, RoundingMode.HALF_UP); + private static boolean isAppActive() { + return KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow() != null; } @Override public void dispose() { - sendAllEvents(); // Try to send lasts event if connexion failed. try { connection.disconnect(); } catch (Exception e) { - LOG.error("Unable to disconnect", e); + LOG.error("Unable to disconnect to message bus", e); } try { - scheduledFixture.cancel(true); + scheduledConnexion.cancel(true); } catch (Exception e) { - LOG.error("Unable to cancel scheduler", e); + LOG.error("Unable to cancel schedulers", e); + } + } + + private static synchronized void connexionResume() { + if (connexionLost) { + connexionLost = false; + connexionFailedMessageAlreadySend = false; + Notifications.Bus.notify(new Notification(ReportActivity.ACTIVITY_WATCHER, ReportActivity.ACTIVITY_WATCHER, "Activity Watcher Server is back!", NotificationType.INFORMATION)); + } + } + + protected static synchronized void connexionLost() { + if (!connexionFailedMessageAlreadySend) { + connexionFailedMessageAlreadySend = true; + Notifications.Bus.notify(new Notification(ReportActivity.ACTIVITY_WATCHER, ReportActivity.ACTIVITY_WATCHER, "Activity Watcher Server connexion lost?\nWill try to re-send events when connexion back.", NotificationType.WARNING)); } + connexionLost = true; + bucket = null; } } diff --git a/src/main/java/fr/mary/olivier/aw/watcher/ReportActivityCallBack.java b/src/main/java/fr/mary/olivier/aw/watcher/ReportActivityCallBack.java deleted file mode 100755 index aedc05d..0000000 --- a/src/main/java/fr/mary/olivier/aw/watcher/ReportActivityCallBack.java +++ /dev/null @@ -1,40 +0,0 @@ -package fr.mary.olivier.aw.watcher; - - -import org.openapitools.client.ApiCallback; -import org.openapitools.client.ApiException; -import org.openapitools.client.model.Event; - -import java.util.List; -import java.util.Map; - -public class ReportActivityCallBack implements ApiCallback { - - private Event event; - - ReportActivityCallBack(Event event) { - this.event = event; - } - - @Override - public void onFailure(ApiException exp, int statusCode, Map> responseHeaders) { - ReportActivity.connexionLost(); - } - - @Override - public void onSuccess(Void result, int statusCode, Map> responseHeaders) { - ReportActivity.connexionResume(); - ReportActivity.eventSent(event); - } - - @Override - public void onUploadProgress(long bytesWritten, long contentLength, boolean done) { - // no action - - } - - @Override - public void onDownloadProgress(long bytesRead, long contentLength, boolean done) { - // no action - } -} diff --git a/src/main/java/fr/mary/olivier/aw/watcher/listener/RADocumentListener.java b/src/main/java/fr/mary/olivier/aw/watcher/listener/RADocumentListener.java old mode 100755 new mode 100644 index de8bf05..5e65d96 --- a/src/main/java/fr/mary/olivier/aw/watcher/listener/RADocumentListener.java +++ b/src/main/java/fr/mary/olivier/aw/watcher/listener/RADocumentListener.java @@ -19,6 +19,6 @@ public void beforeDocumentChange(@NotNull DocumentEvent documentEvent) { public void documentChanged(@NotNull DocumentEvent documentEvent) { Document document = documentEvent.getDocument(); VirtualFile file = FileDocumentManager.getInstance().getFile(document); - ReportActivity.addAndSendEvent(file, ReportActivity.getProject(document), RADocumentListener.class); + ReportActivity.sendHeartBeat(file, document); } } diff --git a/src/main/java/fr/mary/olivier/aw/watcher/listener/RAEditorMouseListener.java b/src/main/java/fr/mary/olivier/aw/watcher/listener/RAEditorMouseListener.java old mode 100755 new mode 100644 index 82530b5..e15658d --- a/src/main/java/fr/mary/olivier/aw/watcher/listener/RAEditorMouseListener.java +++ b/src/main/java/fr/mary/olivier/aw/watcher/listener/RAEditorMouseListener.java @@ -1,6 +1,5 @@ package fr.mary.olivier.aw.watcher.listener; - import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.event.EditorMouseEvent; import com.intellij.openapi.editor.event.EditorMouseListener; @@ -16,7 +15,7 @@ public void mousePressed(@NotNull EditorMouseEvent editorMouseEvent) { Editor editor = editorMouseEvent.getEditor(); VirtualFile file = FileDocumentManager.getInstance().getFile(editor.getDocument()); Project project = editor.getProject(); - ReportActivity.addAndSendEvent(file, project, RAEditorMouseListener.class); + ReportActivity.sendHeartBeat(file, project); } @Override diff --git a/src/main/java/fr/mary/olivier/aw/watcher/listener/RASaveListener.java b/src/main/java/fr/mary/olivier/aw/watcher/listener/RASaveListener.java old mode 100755 new mode 100644 index 8866553..ca76924 --- a/src/main/java/fr/mary/olivier/aw/watcher/listener/RASaveListener.java +++ b/src/main/java/fr/mary/olivier/aw/watcher/listener/RASaveListener.java @@ -1,6 +1,5 @@ package fr.mary.olivier.aw.watcher.listener; - import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileDocumentManagerListener; @@ -13,7 +12,7 @@ public class RASaveListener implements FileDocumentManagerListener { @Override public void beforeDocumentSaving(@NotNull Document document) { VirtualFile file = FileDocumentManager.getInstance().getFile(document); - ReportActivity.addAndSendEvent(file, ReportActivity.getProject(document), RASaveListener.class); + ReportActivity.sendHeartBeat(file, document); } @Override diff --git a/src/main/java/fr/mary/olivier/aw/watcher/listener/RAVisibleAreaListener.java b/src/main/java/fr/mary/olivier/aw/watcher/listener/RAVisibleAreaListener.java old mode 100755 new mode 100644 index 47587ff..d257d2f --- a/src/main/java/fr/mary/olivier/aw/watcher/listener/RAVisibleAreaListener.java +++ b/src/main/java/fr/mary/olivier/aw/watcher/listener/RAVisibleAreaListener.java @@ -1,6 +1,5 @@ package fr.mary.olivier.aw.watcher.listener; - import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.event.VisibleAreaEvent; import com.intellij.openapi.editor.event.VisibleAreaListener; @@ -16,6 +15,6 @@ public void visibleAreaChanged(@NotNull VisibleAreaEvent visibleAreaEvent) { Editor editor = visibleAreaEvent.getEditor(); VirtualFile file = FileDocumentManager.getInstance().getFile(editor.getDocument()); Project project = editor.getProject(); - ReportActivity.addAndSendEvent(file, project, RAVisibleAreaListener.class); + ReportActivity.sendHeartBeat(file, project); } } \ No newline at end of file diff --git a/src/main/java/fr/mary/olivier/aw/watcher/model/EditorActivityEvent.java b/src/main/java/fr/mary/olivier/aw/watcher/model/EditorActivityEvent.java deleted file mode 100755 index a950e20..0000000 --- a/src/main/java/fr/mary/olivier/aw/watcher/model/EditorActivityEvent.java +++ /dev/null @@ -1,106 +0,0 @@ -package fr.mary.olivier.aw.watcher.model; - -import com.google.gson.annotations.SerializedName; - -public class EditorActivityEvent { - @SerializedName("file") - private String file; - - @SerializedName("project") - private String project; - - @SerializedName("projectPath") - private String projectPath; - - @SerializedName("language") - private String language; - - @SerializedName("editor") - private String editor; - - @SerializedName("editorVersion") - private String editorVersion; - - @SerializedName("eventType") - private String eventType; - - public EditorActivityEvent(String file, String project, String projectPath, String language, String editor, String editorVersion, String eventType) { - this.file = file; - this.project = project; - this.projectPath = projectPath; - this.language = language; - this.editor = editor; - this.editorVersion = editorVersion; - this.eventType = eventType; - } - - public String getFile() { - return file; - } - - public void setFile(String file) { - this.file = file; - } - - public String getProject() { - return project; - } - - public void setProject(String project) { - this.project = project; - } - - public String getProjectPath() { - return projectPath; - } - - public void setProjectPath(String projectPath) { - this.projectPath = projectPath; - } - - public String getLanguage() { - return language; - } - - public void setLanguage(String language) { - this.language = language; - } - - public String getEditor() { - return editor; - } - - public void setEditor(String editor) { - this.editor = editor; - } - - public String getEditorVersion() { - return editorVersion; - } - - public void setEditorVersion(String editorVersion) { - this.editorVersion = editorVersion; - } - - public String getEventType() { - return eventType; - } - - public void setEventType(String eventType) { - this.eventType = eventType; - } - - @Override - public String toString() { - return "EditorActivityEvent{" + - "file='" + file + '\'' + - ", project='" + project + '\'' + - ", projectPath='" + projectPath + '\'' + - ", language='" + language + '\'' + - ", editor='" + editor + '\'' + - ", editorVersion='" + editorVersion + '\'' + - ", eventType='" + eventType + '\'' + - '}'; - } -} - diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 39661df..5682674 100755 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -2,16 +2,17 @@ fr.mary.olivier.aw-watcher Activity Watcher OlivierMARY - 1.0.8 + 2.0.0 - + com.intellij.modules.lang com.intellij.modules.platform + Git4Idea @@ -24,7 +25,7 @@ + text="Activity Watcher Informations" description="Activity watcher informations"> \ No newline at end of file diff --git a/src/main/resources/swagger.yaml b/src/main/resources/swagger.yaml index b957f41..b4bb598 100755 --- a/src/main/resources/swagger.yaml +++ b/src/main/resources/swagger.yaml @@ -1,387 +1,390 @@ -swagger: '2.0' -basePath: localhost:5600/api -paths: - /0/buckets/: - get: - responses: - '200': - description: Success - summary: 'Get dict {bucket_name: Bucket} of all buckets' - operationId: get_buckets_resource - tags: - - default - '/0/buckets/{bucket_id}': - parameters: - - name: bucket_id - in: path - required: true - type: string - get: - responses: - '200': - description: Success - schema: - $ref: '#/definitions/Bucket' - summary: Get metadata about bucket - operationId: get_bucket_resource - tags: - - default - post: - responses: - '200': - description: Success - summary: Create bucket - description: 'Returns True if successful, otherwise false if a bucket with the given ID already existed.' - operationId: post_bucket_resource - parameters: - - name: payload - required: true - in: body - schema: - $ref: '#/definitions/CreateBucket' - tags: - - default - delete: - responses: - '200': - description: Success - summary: Delete a bucket - operationId: delete_bucket_resource - parameters: - - in: query - description: Needs to be =1 to delete a bucket it non-testing mode - name: force - type: string - tags: - - default - '/0/buckets/{bucket_id}/events': - parameters: - - name: bucket_id - in: path - required: true - type: string - get: - responses: - '200': - description: Success - schema: - $ref: '#/definitions/Event' - summary: Get events from a bucket - operationId: get_events_resource - parameters: - - in: query - description: End date of events - name: end - type: string - - in: query - description: Start date of events - name: start - type: string - - in: query - description: the maximum number of requests to get - name: limit - type: string - tags: - - default - post: - responses: - '200': - description: Success - summary: Create events for a bucket - description: |- - Can handle both single events and multiple ones. - - Returns the inserted event when a single event was inserted, otherwise None. - operationId: post_events_resource - parameters: - - name: payload - required: true - in: body - schema: - $ref: '#/definitions/Event' - tags: - - default - '/0/buckets/{bucket_id}/events/count': - parameters: - - name: bucket_id - in: path - required: true - type: string - get: - responses: - '200': - description: Success - schema: - type: integer - summary: Get eventcount from a bucket - operationId: get_event_count_resource - parameters: - - in: query - description: End date of eventcount - name: end - type: string - - in: query - description: Start date of eventcount - name: start - type: string - tags: - - default - '/0/buckets/{bucket_id}/events/{event_id}': - parameters: - - name: bucket_id - in: path - required: true - type: string - - name: event_id - in: path - required: true - type: string - delete: - responses: - '200': - description: Success - summary: Delete a single event from a bucket - operationId: delete_event_resource - tags: - - default - '/0/buckets/{bucket_id}/export': - parameters: - - name: bucket_id - in: path - required: true - type: string - get: - responses: - '200': - description: Success - schema: - $ref: '#/definitions/Export' - summary: 'Export a bucket to a dataformat consistent across versions, including all events in it' - operationId: get_bucket_export_resource - tags: - - default - '/0/buckets/{bucket_id}/heartbeat': - parameters: - - name: bucket_id - in: path - required: true - type: string - post: - responses: - '200': - description: Success - summary: Heartbeats are useful when implementing watchers that simply keep - description: |- - track of a state, how long it's in that state and when it changes. - A single heartbeat always has a duration of zero. - - If the heartbeat was identical to the last (apart from timestamp), then the last event has its duration updated. - If the heartbeat differed, then a new event is created. - - Such as: - - Active application and window title - - Example: aw-watcher-window - - Currently open document/browser tab/playing song - - Example: wakatime - - Example: aw-watcher-web - - Example: aw-watcher-spotify - - Is the user active/inactive? - Send an event on some interval indicating if the user is active or not. - - Example: aw-watcher-afk - - Inspired by: https://wakatime.com/developers#heartbeats - operationId: post_heartbeat_resource - parameters: - - name: payload - required: true - in: body - schema: - $ref: '#/definitions/Event' - - in: query - description: Largest timewindow allowed between heartbeats for them to merge - name: pulsetime - type: string - tags: - - default - /0/export: - get: - responses: - '200': - description: Success - schema: - $ref: '#/definitions/Export' - summary: Exports all buckets and their events to a format consistent across versions - operationId: get_export_all_resource - tags: - - default - /0/import: - post: - responses: - '200': - description: Success - operationId: post_import_all_resource - parameters: - - name: payload - required: true - in: body - schema: - $ref: '#/definitions/Export' - tags: - - default - /0/info: - get: - responses: - '200': - description: Success - schema: - $ref: '#/definitions/Info' - summary: Get server info - operationId: get_info_resource - parameters: - - name: X-Fields - in: header - type: string - format: mask - description: An optional fields mask - tags: - - default - /0/log: - get: - responses: - '200': - description: Success - summary: Get the server log in json format - operationId: get_log_resource - tags: - - default - /0/query/: - post: - responses: - '200': - description: Success - operationId: post_query_resource - parameters: - - name: payload - required: true - in: body - schema: - $ref: '#/definitions/Query' - - in: query - description: Name of the query (required if using cache) - name: name - type: string - tags: - - default -info: - title: API - version: '1.0' -produces: - - application/json -consumes: - - application/json -tags: - - name: default - description: Default namespace -definitions: - Info: - properties: - hostname: - type: string - version: - type: string - testing: - type: boolean - type: object - CreateBucket: - required: - - client - - hostname - - type - properties: - client: - type: string - type: - type: string - hostname: - type: string - type: object - Bucket: - title: Bucket - description: The Bucket model that is used in ActivityWatch - type: object - required: - - id - - type - - client - - hostname - properties: - id: - description: The unique id for the bucket - type: string - name: - description: The readable and renameable name for the bucket - type: string - type: - description: The event type - type: string - client: - description: The name of the client that is reporting to the bucket - type: string - hostname: - description: The hostname of the machine on which the client is running - type: string - created: - description: The creation datetime of the bucket - type: string - format: date-time - data: - description: '' - type: object - events: - type: array - items: - $ref: '#/definitions/Event' - Event: - title: Event - description: The Event model that is used in ActivityWatch - type: object - required: - - timestamp - properties: - timestamp: - type: string - format: date-time - duration: - type: number - data: - type: object - Query: - required: - - query - - timeperiods - properties: - timeperiods: - type: array - description: List of periods to query - items: - type: string - query: - type: array - description: String list of query statements - items: - type: string - type: object - Export: - title: Export - description: The Export model that is used by ActivityWatch - type: object - properties: - buckets: - type: array - items: - $ref: '#/definitions/Bucket' -responses: - ParseError: - description: When a mask can't be parsed - MaskError: - description: When any error occurs on mask +swagger: '2.0' +basePath: /api +schemes: + - http +host: localhost:5600 +paths: + /0/buckets/: + get: + responses: + '200': + description: Success + summary: 'Get dict {bucket_name: Bucket} of all buckets' + operationId: get_buckets_resource + tags: + - default + '/0/buckets/{bucket_id}': + parameters: + - name: bucket_id + in: path + required: true + type: string + get: + responses: + '200': + description: Success + schema: + $ref: '#/definitions/Bucket' + summary: Get metadata about bucket + operationId: get_bucket_resource + tags: + - default + post: + responses: + '200': + description: Success + summary: Create bucket + description: 'Returns True if successful, otherwise false if a bucket with the given ID already existed.' + operationId: post_bucket_resource + parameters: + - name: payload + required: true + in: body + schema: + $ref: '#/definitions/CreateBucket' + tags: + - default + delete: + responses: + '200': + description: Success + summary: Delete a bucket + operationId: delete_bucket_resource + parameters: + - in: query + description: Needs to be =1 to delete a bucket it non-testing mode + name: force + type: string + tags: + - default + '/0/buckets/{bucket_id}/events': + parameters: + - name: bucket_id + in: path + required: true + type: string + get: + responses: + '200': + description: Success + schema: + $ref: '#/definitions/Event' + summary: Get events from a bucket + operationId: get_events_resource + parameters: + - in: query + description: End date of events + name: end + type: string + - in: query + description: Start date of events + name: start + type: string + - in: query + description: the maximum number of requests to get + name: limit + type: string + tags: + - default + post: + responses: + '200': + description: Success + summary: Create events for a bucket + description: |- + Can handle both single events and multiple ones. + + Returns the inserted event when a single event was inserted, otherwise None. + operationId: post_events_resource + parameters: + - name: payload + required: true + in: body + schema: + $ref: '#/definitions/Event' + tags: + - default + '/0/buckets/{bucket_id}/events/count': + parameters: + - name: bucket_id + in: path + required: true + type: string + get: + responses: + '200': + description: Success + schema: + type: integer + summary: Get eventcount from a bucket + operationId: get_event_count_resource + parameters: + - in: query + description: End date of eventcount + name: end + type: string + - in: query + description: Start date of eventcount + name: start + type: string + tags: + - default + '/0/buckets/{bucket_id}/events/{event_id}': + parameters: + - name: bucket_id + in: path + required: true + type: string + - name: event_id + in: path + required: true + type: string + delete: + responses: + '200': + description: Success + summary: Delete a single event from a bucket + operationId: delete_event_resource + tags: + - default + '/0/buckets/{bucket_id}/export': + parameters: + - name: bucket_id + in: path + required: true + type: string + get: + responses: + '200': + description: Success + schema: + $ref: '#/definitions/Export' + summary: 'Export a bucket to a dataformat consistent across versions, including all events in it' + operationId: get_bucket_export_resource + tags: + - default + '/0/buckets/{bucket_id}/heartbeat': + parameters: + - name: bucket_id + in: path + required: true + type: string + post: + responses: + '200': + description: Success + summary: Heartbeats are useful when implementing watchers that simply keep + description: |- + track of a state, how long it's in that state and when it changes. + A single heartbeat always has a duration of zero. + + If the heartbeat was identical to the last (apart from timestamp), then the last event has its duration updated. + If the heartbeat differed, then a new event is created. + + Such as: + - Active application and window title + - Example: aw-watcher-window + - Currently open document/browser tab/playing song + - Example: wakatime + - Example: aw-watcher-web + - Example: aw-watcher-spotify + - Is the user active/inactive? + Send an event on some interval indicating if the user is active or not. + - Example: aw-watcher-afk + + Inspired by: https://wakatime.com/developers#heartbeats + operationId: post_heartbeat_resource + parameters: + - name: payload + required: true + in: body + schema: + $ref: '#/definitions/Event' + - in: query + description: Largest timewindow allowed between heartbeats for them to merge + name: pulsetime + type: string + tags: + - default + /0/export: + get: + responses: + '200': + description: Success + schema: + $ref: '#/definitions/Export' + summary: Exports all buckets and their events to a format consistent across versions + operationId: get_export_all_resource + tags: + - default + /0/import: + post: + responses: + '200': + description: Success + operationId: post_import_all_resource + parameters: + - name: payload + required: true + in: body + schema: + $ref: '#/definitions/Export' + tags: + - default + /0/info: + get: + responses: + '200': + description: Success + schema: + $ref: '#/definitions/Info' + summary: Get server info + operationId: get_info_resource + parameters: + - name: X-Fields + in: header + type: string + format: mask + description: An optional fields mask + tags: + - default + /0/log: + get: + responses: + '200': + description: Success + summary: Get the server log in json format + operationId: get_log_resource + tags: + - default + /0/query/: + post: + responses: + '200': + description: Success + operationId: post_query_resource + parameters: + - name: payload + required: true + in: body + schema: + $ref: '#/definitions/Query' + - in: query + description: Name of the query (required if using cache) + name: name + type: string + tags: + - default +info: + title: API + version: '1.0' +produces: + - application/json +consumes: + - application/json +tags: + - name: default + description: Default namespace +definitions: + Info: + properties: + hostname: + type: string + version: + type: string + testing: + type: boolean + type: object + CreateBucket: + required: + - client + - hostname + - type + properties: + client: + type: string + type: + type: string + hostname: + type: string + type: object + Bucket: + title: Bucket + description: The Bucket model that is used in ActivityWatch + type: object + required: + - id + - type + - client + - hostname + properties: + id: + description: The unique id for the bucket + type: string + name: + description: The readable and renameable name for the bucket + type: string + type: + description: The event type + type: string + client: + description: The name of the client that is reporting to the bucket + type: string + hostname: + description: The hostname of the machine on which the client is running + type: string + created: + description: The creation datetime of the bucket + type: string + format: date-time + data: + description: '' + type: object + events: + type: array + items: + $ref: '#/definitions/Event' + Event: + title: Event + description: The Event model that is used in ActivityWatch + type: object + required: + - timestamp + properties: + timestamp: + type: string + format: date-time + duration: + type: number + data: + type: object + Query: + required: + - query + - timeperiods + properties: + timeperiods: + type: array + description: List of periods to query + items: + type: string + query: + type: array + description: String list of query statements + items: + type: string + type: object + Export: + title: Export + description: The Export model that is used by ActivityWatch + type: object + properties: + buckets: + type: array + items: + $ref: '#/definitions/Bucket' +responses: + ParseError: + description: When a mask can't be parsed + MaskError: + description: When any error occurs on mask