1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.openjdk.skara.test; 24 25 import org.openjdk.skara.host.*; 26 import org.openjdk.skara.vcs.Hash; 27 28 import java.io.*; 29 import java.net.*; 30 import java.time.ZonedDateTime; 31 import java.util.*; 32 import java.util.function.Function; 33 import java.util.stream.Collectors; 34 35 public class TestPullRequest implements PullRequest { 36 private final TestHostedRepository repository; 37 private final String id; 38 private final HostUserDetails author; 39 private final HostUserDetails user; 40 private final String targetRef; 41 private final String sourceRef; 42 private final String title; 43 private final List<String> body; 44 private final PullRequestData data; 45 46 private static class PullRequestData { 47 private Hash headHash; 48 PullRequest.State state = PullRequest.State.OPEN; 49 String body = ""; 50 final List<Comment> comments = new ArrayList<>(); 51 final List<ReviewComment> reviewComments = new ArrayList<>(); 52 final Set<Check> checks = new HashSet<>(); 53 final Set<String> labels = new HashSet<>(); 54 final List<Review> reviews = new ArrayList<>(); 55 ZonedDateTime created = ZonedDateTime.now(); 56 ZonedDateTime lastUpdate = created; 57 } 58 59 private TestPullRequest(TestHostedRepository repository, String id, HostUserDetails author, HostUserDetails user, String targetRef, String sourceRef, String title, List<String> body, PullRequestData data) { 60 this.repository = repository; 61 this.id = id; 62 this.author = author; 63 this.user = user; 64 this.targetRef = targetRef; 65 this.sourceRef = sourceRef; 66 this.title = title; 67 this.body = body; 68 this.data = data; 69 70 try { 71 var headHash = repository.localRepository().resolve(sourceRef).orElseThrow(); 72 if (!headHash.equals(data.headHash)) { 73 data.headHash = headHash; 74 data.lastUpdate = ZonedDateTime.now(); 75 } 76 } catch (IOException e) { 77 throw new UncheckedIOException(e); 78 } 79 } 80 81 static TestPullRequest createNew(TestHostedRepository repository, String id, String targetRef, String sourceRef, String title, List<String> body) { 82 var data = new PullRequestData(); 83 data.body = String.join("\n", body); 84 var pr = new TestPullRequest(repository, id, repository.host().getCurrentUserDetails(), repository.host().getCurrentUserDetails(), targetRef, sourceRef, title, body, data); 85 return pr; 86 } 87 88 static TestPullRequest createFrom(TestHostedRepository repository, TestPullRequest other) { 89 var pr = new TestPullRequest(repository, other.id, other.author, repository.host().getCurrentUserDetails(), other.targetRef, other.sourceRef, other.title, other.body, other.data); 90 return pr; 91 } 92 93 @Override 94 public HostedRepository repository() { 95 return repository; 96 } 97 98 @Override 99 public String getId() { 100 return id; 101 } 102 103 @Override 104 public HostUserDetails getAuthor() { 105 return author; 106 } 107 108 @Override 109 public List<Review> getReviews() { 110 return new ArrayList<>(data.reviews); 111 } 112 113 @Override 114 public void addReview(Review.Verdict verdict, String body) { 115 try { 116 var review = new Review(repository.host().getCurrentUserDetails(), 117 verdict, repository.localRepository().resolve(sourceRef).orElseThrow(), 118 data.reviews.size(), 119 body); 120 121 data.reviews.add(review); 122 data.lastUpdate = ZonedDateTime.now(); 123 124 } catch (IOException e) { 125 throw new UncheckedIOException(e); 126 } 127 } 128 129 @Override 130 public ReviewComment addReviewComment(Hash base, Hash hash, String path, int line, String body) { 131 var comment = new ReviewComment(null, String.valueOf(data.reviewComments.size()), hash, path, line, String.valueOf(data.reviewComments.size()), body, user, ZonedDateTime.now(), ZonedDateTime.now()); 132 data.reviewComments.add(comment); 133 data.lastUpdate = ZonedDateTime.now(); 134 return comment; 135 } 136 137 @Override 138 public ReviewComment addReviewCommentReply(ReviewComment parent, String body) { 139 if (parent.parent().isPresent()) { 140 throw new RuntimeException("Can only reply to top-level review comments"); 141 } 142 var comment = new ReviewComment(parent, parent.threadId(), parent.hash(), parent.path(), parent.line(), String.valueOf(data.reviewComments.size()), body, user, ZonedDateTime.now(), ZonedDateTime.now()); 143 data.reviewComments.add(comment); 144 data.lastUpdate = ZonedDateTime.now(); 145 return comment; 146 } 147 148 @Override 149 public List<ReviewComment> getReviewComments() { 150 return new ArrayList<>(data.reviewComments); 151 } 152 153 @Override 154 public Hash getHeadHash() { 155 return data.headHash; 156 } 157 158 @Override 159 public String getSourceRef() { 160 return sourceRef; 161 } 162 163 @Override 164 public String getTargetRef() { 165 return targetRef; 166 } 167 168 @Override 169 public Hash getTargetHash() { 170 return repository.getBranchHash(targetRef); 171 } 172 173 @Override 174 public String getTitle() { 175 return title; 176 } 177 178 @Override 179 public String getBody() { 180 return data.body; 181 } 182 183 @Override 184 public void setBody(String body) { 185 data.body = body; 186 data.lastUpdate = ZonedDateTime.now(); 187 } 188 189 @Override 190 public List<Comment> getComments() { 191 return new ArrayList<>(data.comments); 192 } 193 194 @Override 195 public Comment addComment(String body) { 196 var comment = new Comment(String.valueOf(data.comments.size()), 197 body, 198 user, 199 ZonedDateTime.now(), 200 ZonedDateTime.now()); 201 data.comments.add(comment); 202 data.lastUpdate = ZonedDateTime.now(); 203 return comment; 204 } 205 206 @Override 207 public Comment updateComment(String id, String body) { 208 var originalComment = data.comments.get(Integer.parseInt(id)); 209 var comment = new Comment(originalComment.id(), 210 body, 211 originalComment.author(), 212 originalComment.createdAt(), 213 ZonedDateTime.now()); 214 data.comments.remove(Integer.parseInt(id)); 215 data.comments.add(Integer.parseInt(id), comment); 216 data.lastUpdate = ZonedDateTime.now(); 217 return comment; 218 } 219 220 @Override 221 public ZonedDateTime getCreated() { 222 return data.created; 223 } 224 225 @Override 226 public ZonedDateTime getUpdated() { 227 return data.lastUpdate; 228 } 229 230 @Override 231 public Map<String, Check> getChecks(Hash hash) { 232 return data.checks.stream() 233 .filter(check -> check.hash().equals(hash)) 234 .collect(Collectors.toMap(Check::name, Function.identity())); 235 } 236 237 @Override 238 public void createCheck(Check check) { 239 var existing = data.checks.stream() 240 .filter(c -> check.name().equals(check.name())) 241 .findAny(); 242 existing.ifPresent(data.checks::remove); 243 data.checks.add(check); 244 data.lastUpdate = ZonedDateTime.now(); 245 } 246 247 @Override 248 public void updateCheck(Check updated) { 249 var existing = data.checks.stream() 250 .filter(check -> check.name().equals(updated.name())) 251 .findAny() 252 .orElseThrow(); 253 254 data.checks.remove(existing); 255 data.checks.add(updated); 256 data.lastUpdate = ZonedDateTime.now(); 257 } 258 259 @Override 260 public void setState(State state) { 261 data.state = state; 262 data.lastUpdate = ZonedDateTime.now(); 263 } 264 265 boolean isOpen() { 266 return data.state.equals(PullRequest.State.OPEN); 267 } 268 269 @Override 270 public void addLabel(String label) { 271 data.labels.add(label); 272 data.lastUpdate = ZonedDateTime.now(); 273 } 274 275 @Override 276 public void removeLabel(String label) { 277 data.labels.remove(label); 278 data.lastUpdate = ZonedDateTime.now(); 279 } 280 281 @Override 282 public List<String> getLabels() { 283 return new ArrayList<>(data.labels); 284 } 285 286 @Override 287 public URI getWebUrl() { 288 try { 289 return new URI(repository.getUrl().toString() + "/pr/" + getId()); 290 } catch (URISyntaxException e) { 291 throw new RuntimeException(e); 292 } 293 } 294 295 @Override 296 public List<HostUserDetails> getAssignees() { 297 throw new RuntimeException("not implemented yet"); 298 } 299 300 @Override 301 public void setAssignees(List<HostUserDetails> assignees) { 302 throw new RuntimeException("not implemented yet"); 303 } 304 }