From f75293167ec81198492daa4360732a802c929780 Mon Sep 17 00:00:00 2001 From: jchrist Date: Mon, 9 Dec 2024 09:27:11 +0200 Subject: [PATCH] refactor(issues): Disable functionality missing from CLI - Disable editing issue comments - Disable removing issue reactions - Move changing issue title/description to CLI (not working yet!) Signed-off-by: jchrist --- .../issues/overview/IssueComponent.java | 20 +-- .../services/RadicleProjectApi.java | 98 ------------ .../services/RadicleProjectService.java | 6 +- .../radiclejetbrainsplugin/AbstractIT.java | 9 +- .../issues/OverviewTest.java | 146 ++++-------------- 5 files changed, 49 insertions(+), 230 deletions(-) diff --git a/src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/issues/overview/IssueComponent.java b/src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/issues/overview/IssueComponent.java index 9960b933..e775b700 100644 --- a/src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/issues/overview/IssueComponent.java +++ b/src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/issues/overview/IssueComponent.java @@ -132,20 +132,22 @@ public JComponent createCommentSection(List discussionList) { verticalPanel.add(replyPanel); panel.addToBottom(verticalPanel); var panelHandle = new EditablePanelHandler.PanelBuilder(radIssue.project, panel, - RadicleBundle.message("save"), new SingleValueModel<>(com.body), (field) -> { + RadicleBundle.message("save"), new SingleValueModel<>(com.body), f -> true).build(); + // TODO: disabling editing issue comment + /*(field) -> { var edited = api.editIssueComment(radIssue, field.getText(), com.id, field.getEmbedList()); final boolean success = edited != null; if (success) { issueModel.setValue(radIssue); } return success; - }).build(); - var contentPanel = panelHandle.panel; + }*/ var actionsPanel = CollaborationToolsUIUtilKt.HorizontalListPanel(CodeReviewCommentUIUtil.Actions.HORIZONTAL_GAP); - actionsPanel.add(CodeReviewCommentUIUtil.INSTANCE.createEditButton(e -> { + /* actionsPanel.add(CodeReviewCommentUIUtil.INSTANCE.createEditButton(e -> { panelHandle.showAndFocusEditor(); return null; - })); + })); */ + var contentPanel = panelHandle.panel; mainPanel.add(createTimeLineItem(contentPanel, actionsPanel, com.author.generateLabelText(), com.timestamp)); } return mainPanel; @@ -193,10 +195,7 @@ private JComponent getHeader() { RadicleBundle.message("issue.change.title"), new SingleValueModel<>(radIssue.title), (field) -> { // TODO: this will not work - // var edited = cli.changeIssueTitleDescription(radIssue, field.getText(), radIssue.getDescription()); - var issue = new RadIssue(radIssue); - issue.title = field.getText(); - var edited = api.changeIssueTitle(issue); + var edited = cli.changeIssueTitleDescription(radIssue, field.getText(), radIssue.getDescription()); final boolean success = edited != null; if (success) { issueModel.setValue(edited); @@ -271,7 +270,8 @@ public RadIssue addEmoji(Emoji emoji, String discussionId) { @Override public RadIssue removeEmoji(String emojiUnicode, String discussionId) { - return api.issueCommentReact(radIssue, discussionId, emojiUnicode, false); + //return api.issueCommentReact(radIssue, discussionId, emojiUnicode, false); + return cli.issueCommentReact(radIssue, discussionId, emojiUnicode, false); } @Override diff --git a/src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/services/RadicleProjectApi.java b/src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/services/RadicleProjectApi.java index c7209311..3761dad5 100644 --- a/src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/services/RadicleProjectApi.java +++ b/src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/services/RadicleProjectApi.java @@ -1,6 +1,5 @@ package network.radicle.jetbrains.radiclejetbrainsplugin.services; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; @@ -11,7 +10,6 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.serviceContainer.NonInjectable; -import git4idea.repo.GitRepository; import network.radicle.jetbrains.radiclejetbrainsplugin.RadicleBundle; import network.radicle.jetbrains.radiclejetbrainsplugin.actions.rad.RadSelf; import network.radicle.jetbrains.radiclejetbrainsplugin.config.RadicleProjectSettingsHandler; @@ -93,55 +91,6 @@ public SeedNodeInfo checkApi(SeedNode node, boolean showNotif) { } } - public RadIssue editIssueComment(RadIssue issue, String comment, String id, List embedList) { - var session = createAuthenticatedSession(); - if (session == null) { - return null; - } - try { - var issueReq = new HttpPatch(getHttpNodeUrl() + "/api/v1/projects/" + issue.projectId + "/issues/" + issue.id); - issueReq.setHeader("Authorization", "Bearer " + session.sessionId); - var patchIssueData = Map.of("type", "comment.edit", "id", id, "body", comment, "replyTo", issue.id, "embeds", embedList); - var json = MAPPER.writeValueAsString(patchIssueData); - issueReq.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON)); - var resp = makeRequest(issueReq, RadicleBundle.message("commentEditError"), RadicleBundle.message("commentDescError")); - if (!resp.isSuccess()) { - logger.warn("error editing comment: {} to issue:{} resp:{}", comment, issue, resp); - return null; - } - return issue; - } catch (Exception e) { - logger.warn("error editing issue comment: {}", issue, e); - } - return null; - } - - public String createPatch(String title, String description, List labels, String baseOid, String patchOid, GitRepository repo, String projectId) { - var session = createAuthenticatedSession(); - if (session == null) { - return null; - } - try { - var patchReq = new HttpPost(getHttpNodeUrl() + "/api/v1/projects/" + projectId + "/patches"); - patchReq.setHeader("Authorization", "Bearer " + session.sessionId); - var patchIssueData = Map.of("title", title, "description", description, "oid", patchOid, "target", baseOid, "labels", labels); - var json = MAPPER.writeValueAsString(patchIssueData); - patchReq.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON)); - var resp = makeRequest(patchReq, RadicleBundle.message("createPatchError")); - if (!resp.isSuccess()) { - logger.warn("error creating new patch title:{} description:{} base_oid:{} patch_oid:{} repo:{} projectId:{}", - title, description, baseOid, patchOid, repo, projectId); - return null; - } - var map = (Map) MAPPER.readValue(resp.body, new TypeReference<>() { }); - return map.get("id"); - } catch (Exception e) { - logger.warn("exception creating new patch title:{} description:{} base_oid:{} patch_oid:{} repo:{} projectId:{}", - title, description, baseOid, patchOid, repo, projectId, e); - } - return null; - } - public RadPatch deleteRevisionComment(RadPatch patch, String revId, String commentId) { var session = createAuthenticatedSession(); if (session == null) { @@ -189,53 +138,6 @@ public RadPatch patchCommentReact(RadPatch patch, String commendId, String revId return null; } - public RadIssue issueCommentReact(RadIssue issue, String discussionId, String reaction, boolean active) { - var session = createAuthenticatedSession(); - if (session == null) { - return null; - } - try { - var issueReq = new HttpPatch(getHttpNodeUrl() + "/api/v1/projects/" + issue.projectId + "/issues/" + issue.id); - issueReq.setHeader("Authorization", "Bearer " + session.sessionId); - var issueData = Map.of("type", "comment.react", "id", discussionId, "reaction", reaction, "active", active); - var json = MAPPER.writeValueAsString(issueData); - issueReq.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON)); - var resp = makeRequest(issueReq, RadicleBundle.message("reactionError")); - if (!resp.isSuccess()) { - logger.warn("error reacting to discussion:{} resp:{}", discussionId, resp); - return null; - } - return issue; - } catch (Exception e) { - logger.warn("error reacting to discussion: {}", discussionId, e); - } - - return null; - } - - public RadIssue changeIssueTitle(RadIssue issue) { - var session = createAuthenticatedSession(); - if (session == null) { - return null; - } - try { - var issueReq = new HttpPatch(getHttpNodeUrl() + "/api/v1/projects/" + issue.projectId + "/issues/" + issue.id); - issueReq.setHeader("Authorization", "Bearer " + session.sessionId); - var patchEditData = Map.of("type", "edit", "title", issue.title); - var json = MAPPER.writeValueAsString(patchEditData); - issueReq.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON)); - var resp = makeRequest(issueReq, RadicleBundle.message("issueTitleError")); - if (!resp.isSuccess()) { - logger.warn("received invalid response with status:{} and body:{} while editing patch: {}", - resp.status, resp.body, issue); - } - return issue; - } catch (Exception e) { - logger.warn("error changing issue title: {}", issue, e); - } - return null; - } - public RadPatch changePatchComment(String revisionId, String commentId, String body, RadPatch patch, List embedList) { var session = createAuthenticatedSession(); if (session == null) { diff --git a/src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/services/RadicleProjectService.java b/src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/services/RadicleProjectService.java index 59ebdf58..e73f36de 100644 --- a/src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/services/RadicleProjectService.java +++ b/src/main/java/network/radicle/jetbrains/radiclejetbrainsplugin/services/RadicleProjectService.java @@ -379,9 +379,9 @@ public ProcessOutput editPatchTitleDescription(GitRepository repo, String patchI } public ProcessOutput editIssueTitleDescription(GitRepository repo, String issueId, String title, String description) { - // TODO: this is not going to work, as `rad issue edit` does not support `--message` - return executeCommandFromFile(repo, List.of("issue", "edit", issueId, "--message", - ExecUtil.escapeUnixShellArgument(Strings.nullToEmpty(title) + "\n\n" + Strings.nullToEmpty(description)))); + // TODO: this is not going to work, as `rad issue edit` does not support `--title` and `--description` yet + return executeCommandFromFile(repo, List.of("issue", "edit", issueId, "--title", ExecUtil.escapeUnixShellArgument(Strings.nullToEmpty(title)), + "--description", ExecUtil.escapeUnixShellArgument(Strings.nullToEmpty(description)))); } public ProcessOutput addRemovePatchLabels(GitRepository root, String patchId, List addedLabels, List deletedLabels) { diff --git a/src/test/java/network/radicle/jetbrains/radiclejetbrainsplugin/AbstractIT.java b/src/test/java/network/radicle/jetbrains/radiclejetbrainsplugin/AbstractIT.java index 58f41309..fa84de9b 100644 --- a/src/test/java/network/radicle/jetbrains/radiclejetbrainsplugin/AbstractIT.java +++ b/src/test/java/network/radicle/jetbrains/radiclejetbrainsplugin/AbstractIT.java @@ -1,5 +1,6 @@ package network.radicle.jetbrains.radiclejetbrainsplugin; +import com.google.common.base.Strings; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.notification.Notification; import com.intellij.notification.Notifications; @@ -34,7 +35,6 @@ import network.radicle.jetbrains.radiclejetbrainsplugin.services.RadicleProjectApi; import network.radicle.jetbrains.radiclejetbrainsplugin.services.auth.AuthService; import org.apache.http.impl.client.CloseableHttpClient; -import org.assertj.core.util.Strings; import org.jetbrains.annotations.NotNull; import org.junit.After; import org.junit.Before; @@ -241,11 +241,8 @@ public void replaceCliService(String head) { var cliService = new RadicleCliService(myProject) { @Override public RadProject getRadRepo(GitRepository repo) { - if (Strings.isNullOrEmpty(head)) { - return new RadProject(RAD_PROJECT_ID, "test", "", "", PatchListPanelTest.getTestProjects().get(0).delegates); - } else { - return new RadProject(RAD_PROJECT_ID, "TestProject", "", "main", head, PatchListPanelTest.getTestProjects().get(0).delegates); - } + return new RadProject(RAD_PROJECT_ID, "TestProject", "TestProjectDescr", "main", + Strings.nullToEmpty(head), PatchListPanelTest.getTestProjects().get(0).delegates); } }; ServiceContainerUtil.replaceService(myProject, RadicleCliService.class, cliService, this.getTestRootDisposable()); diff --git a/src/test/java/network/radicle/jetbrains/radiclejetbrainsplugin/issues/OverviewTest.java b/src/test/java/network/radicle/jetbrains/radiclejetbrainsplugin/issues/OverviewTest.java index d735eb2b..971bac85 100644 --- a/src/test/java/network/radicle/jetbrains/radiclejetbrainsplugin/issues/OverviewTest.java +++ b/src/test/java/network/radicle/jetbrains/radiclejetbrainsplugin/issues/OverviewTest.java @@ -1,7 +1,5 @@ package network.radicle.jetbrains.radiclejetbrainsplugin.issues; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Strings; import com.intellij.collaboration.ui.SingleValueModel; import com.intellij.execution.configurations.GeneralCommandLine; @@ -31,21 +29,10 @@ import network.radicle.jetbrains.radiclejetbrainsplugin.models.RadIssue; import network.radicle.jetbrains.radiclejetbrainsplugin.models.Reaction; import network.radicle.jetbrains.radiclejetbrainsplugin.patches.PatchListPanelTest; -import network.radicle.jetbrains.radiclejetbrainsplugin.services.RadicleProjectApi; import network.radicle.jetbrains.radiclejetbrainsplugin.toolwindow.DragAndDropField; import network.radicle.jetbrains.radiclejetbrainsplugin.toolwindow.RadicleToolWindow; import network.radicle.jetbrains.radiclejetbrainsplugin.toolwindow.SelectionListCellRenderer; import network.radicle.jetbrains.radiclejetbrainsplugin.toolwindow.Utils; -import org.apache.http.StatusLine; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPatch; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.StringEntity; -import org.apache.http.message.BasicHeader; -import org.apache.http.protocol.HTTP; -import org.apache.http.util.EntityUtils; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -65,22 +52,13 @@ import java.io.IOException; import java.time.Instant; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.UUID; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import static network.radicle.jetbrains.radiclejetbrainsplugin.issues.IssueListPanelTest.getTestIssues; import static network.radicle.jetbrains.radiclejetbrainsplugin.issues.IssuePanel.DATE_TIME_FORMATTER; -import static network.radicle.jetbrains.radiclejetbrainsplugin.patches.PatchListPanelTest.getTestPatches; import static network.radicle.jetbrains.radiclejetbrainsplugin.patches.PatchListPanelTest.getTestProjects; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; @RunWith(JUnit4.class) public class OverviewTest extends AbstractIT { @@ -98,87 +76,14 @@ public class OverviewTest extends AbstractIT { private Embed imgEmbed; private String commentDesc; private String issueDesc; - private final BlockingQueue> response = new LinkedBlockingQueue<>(); + @Rule public TestName testName = new TestName(); @Before - public void beforeTest() throws IOException, InterruptedException { + public void beforeTest() throws InterruptedException { dummyComment = "Hello"; - var api = replaceApiService(); issue = createIssue(); - final var httpClient = api.getClient(); - final int[] statusCode = {200}; - when(httpClient.execute(any())).thenAnswer((i) -> { - var req = i.getArgument(0); - StringEntity se; - statusCode[0] = 200; - if ((req instanceof HttpPut) && ((HttpPut) req).getURI().getPath().contains(SESSIONS_URL)) { - se = new StringEntity("{}"); - } else if ((req instanceof HttpPatch) && ((HttpPatch) req).getURI().getPath().contains(ISSUES_URL + "/" + issue.id)) { - var obj = EntityUtils.toString(((HttpPatch) req).getEntity()); - var mapper = new ObjectMapper(); - Map map = mapper.readValue(obj, Map.class); - var action = (HashMap) map.get("action"); - //Assert that we send the correct payload to the api - if (map.get("type").equals("edit")) { - //Issue - assertThat(map.get("type")).isEqualTo("edit"); - assertThat(map.get("title")).isEqualTo(issue.title); - } else if (map.get("type").equals("assign") || map.get("type").equals("lifecycle") || map.get("type").equals("label") || - map.get("type").equals("comment.react") || map.get("type").equals("comment") || map.get("type").equals("comment.edit")) { - response.add(map); - } - // Return status code 400 in order to trigger the notification - if (dummyComment.equals("break") || issue.title.equals("break")) { - statusCode[0] = 400; - } - se = new StringEntity("{}"); - } else if ((req instanceof HttpPost) && ((HttpPost) req).getURI().getPath().contains(ISSUES_URL)) { - var obj = EntityUtils.toString(((HttpPost) req).getEntity()); - var mapper = new ObjectMapper(); - Map map = mapper.readValue(obj, Map.class); - response.add(map); - // We don't need to refresh the panel after the request - statusCode[0] = 400; - se = new StringEntity("{}"); - } else if ((req instanceof HttpGet) && ((HttpGet) req).getURI().getPath().contains(ISSUES_URL + "/" + issue.id)) { - issue.repo = null; - issue.project = null; - // Convert Reaction object to List> - var map = RadicleProjectApi.MAPPER.convertValue(issue, new TypeReference>() { }); - se = new StringEntity(RadicleProjectApi.MAPPER.writeValueAsString(map)); - } else if ((req instanceof HttpGet) && ((HttpGet) req).getURI().getPath().contains("/patches")) { - // request to fetch patches - se = new StringEntity(RadicleProjectApi.MAPPER.writeValueAsString(getTestPatches())); - } else if ((req instanceof HttpPost) && ((HttpPost) req).getURI().getPath().contains("/sessions")) { - var session = new RadicleProjectApi.Session("testId", "testPublicKey", "testSignature"); - se = new StringEntity(RadicleProjectApi.MAPPER.writeValueAsString(session)); - } else if ((req instanceof HttpGet) && ((HttpGet) req).getURI().getPath().endsWith(ISSUES_URL)) { - // request to fetch issues - var serializeIssues = RadicleProjectApi.MAPPER.convertValue(getTestIssues(), new TypeReference>>() { }); - for (var is : serializeIssues) { - var discussions = (ArrayList>) is.get("discussion"); - for (var disc : discussions) { - disc.remove("timestamp"); - disc.put("timestamp", Instant.now().getEpochSecond()); - } - } - se = new StringEntity(RadicleProjectApi.MAPPER.writeValueAsString(serializeIssues)); - } else if ((req instanceof HttpGet)) { - // request to fetch specific project - se = new StringEntity(RadicleProjectApi.MAPPER.writeValueAsString(getTestProjects().get(0))); - } else { - se = new StringEntity(""); - } - se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json")); - final var resp = mock(CloseableHttpResponse.class); - when(resp.getEntity()).thenReturn(se); - final var statusLine = mock(StatusLine.class); - when(resp.getStatusLine()).thenReturn(statusLine); - when(statusLine.getStatusCode()).thenReturn(statusCode[0]); - return resp; - }); setupWindow(); } @@ -566,26 +471,34 @@ public void testReactions() throws InterruptedException { } assertThat(cmds).anyMatch(cmd -> cmd.getCommandLineString().contains("rad issue react " + issue.id) && cmd.getCommandLineString().contains(issue.discussion.get(1).id)); - // var res = response.poll(5, TimeUnit.SECONDS); - /* assertThat(res.get("type")).isEqualTo("comment.react"); - assertThat(res.get("id")).isEqualTo(issue.discussion.get(1).id); - assertThat(res.get("reaction")).isEqualTo(emoji.unicode()); - assertThat((Boolean) res.get("active")).isTrue(); */ - + // test removing the reaction + // TODO: not yet implemented in CLI! + if (true) { + return; + } borderPanel = UIUtil.findComponentOfType(emojiJPanel, BorderLayoutPanel.class); var reactorsPanel = ((JPanel) borderPanel.getComponents()[1]).getComponents()[1]; var listeners = reactorsPanel.getMouseListeners(); listeners[0].mouseClicked(null); executeUiTasks(); - var res = response.poll(5, TimeUnit.SECONDS); - assertThat(res.get("type")).isEqualTo("comment.react"); - assertThat(res.get("id")).isEqualTo(issue.discussion.get(1).id); - assertThat(res.get("reaction")).isEqualTo(emoji.unicode()); - assertThat((Boolean) res.get("active")).isFalse(); + cmds = new ArrayList<>(); + radStub.commands.drainTo(cmds); + if (cmds.stream().filter(c -> c.getCommandLineString().contains("rad issue react " + issue.id)).findFirst().isEmpty()) { + var cmd = radStub.commands.poll(5, TimeUnit.SECONDS); + if (cmd != null) { + cmds.add(cmd); + } + } + assertThat(cmds).anyMatch(cmd -> cmd.getCommandLineString().contains("rad issue react " + issue.id) && + cmd.getCommandLineString().contains(issue.discussion.get(1).id)); } @Test public void testChangeTitle() throws InterruptedException { + if (true) { + // TODO: edit issue title/description is NOT implemented in CLI + return; + } var issueComponent = issueEditorProvider.getIssueComponent(); var titlePanel = issueComponent.getHeaderPanel(); var editBtn = UIUtil.findComponentOfType(titlePanel, InlineIconButton.class); @@ -765,7 +678,8 @@ public void testComment() throws InterruptedException { assertThat(not.getTitle()).isEqualTo(RadicleBundle.message("radCliError")); // Test edit issue functionality - response.clear(); + // TODO: Editing issue comment is not available from CLI + /* commentPanel = issueComponent.getCommentSection(); var editBtn = UIUtil.findComponentOfType(commentPanel, InlineIconButton.class); editBtn.getActionListener().actionPerformed(new ActionEvent(editBtn, 0, "")); @@ -779,10 +693,16 @@ public void testComment() throws InterruptedException { prBtn = prBtns.get(1); prBtn.doClick(); executeUiTasks(); - var res = response.poll(5, TimeUnit.SECONDS); - assertThat(res.get("id")).isEqualTo(issue.discussion.get(1).id); - assertThat(res.get("type")).isEqualTo("comment.edit"); - assertThat(res.get("body")).isEqualTo(editedComment); + List cmds = new ArrayList<>(); + radStub.commands.drainTo(cmds); + if (cmds.stream().filter(c -> c.getCommandLineString().contains("rad issue edit " + issue.id)).findFirst().isEmpty()) { + var cmd = radStub.commands.poll(5, TimeUnit.SECONDS); + if (cmd != null) { + cmds.add(cmd); + } + } + cmds.stream().filter(c -> c.getCommandLineString().contains("rad issue edit " + issue.id)).findFirst().orElseThrow(); + */ } @Test