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 PullRequestData data; 43 44 private static class PullRequestData { 45 private Hash headHash; 46 PullRequest.State state = PullRequest.State.OPEN; 47 String body = ""; 48 String title = ""; 49 final List<Comment> comments = new ArrayList<>(); 50 final List<ReviewComment> reviewComments = new ArrayList<>(); 51 final Set<Check> checks = new HashSet<>(); 52 final Set<String> labels = new HashSet<>(); 53 final List<Review> reviews = new ArrayList<>(); 54 ZonedDateTime created = ZonedDateTime.now(); 55 ZonedDateTime lastUpdate = created; 56 } 57 58 private TestPullRequest(TestHostedRepository repository, String id, HostUserDetails author, HostUserDetails user, String targetRef, String sourceRef, PullRequestData data) { 59 this.repository = repository; 60 this.id = id; 61 this.author = author; 62 this.user = user; 63 this.targetRef = targetRef; 64 this.sourceRef = sourceRef; 65 this.data = data; 66 67 try { 68 var headHash = repository.localRepository().resolve(sourceRef).orElseThrow(); 69 if (!headHash.equals(data.headHash)) { 70 data.headHash = headHash; 71 data.lastUpdate = ZonedDateTime.now(); 72 } 73 } catch (IOException e) { 74 throw new UncheckedIOException(e); 75 } 76 } 77 78 static TestPullRequest createNew(TestHostedRepository repository, String id, String targetRef, String sourceRef, String title, List<String> body) { 79 var data = new PullRequestData(); 80 data.title = title; 81 data.body = String.join("\n", body); 82 var pr = new TestPullRequest(repository, id, repository.host().getCurrentUserDetails(), repository.host().getCurrentUserDetails(), targetRef, sourceRef, data); 83 return pr; 84 } 85 86 static TestPullRequest createFrom(TestHostedRepository repository, TestPullRequest other) { 87 var pr = new TestPullRequest(repository, other.id, other.author, repository.host().getCurrentUserDetails(), other.targetRef, other.sourceRef, other.data); 88 return pr; 89 } 90 91 @Override 92 public HostedRepository repository() { 93 return repository; 94 } 95 96 @Override 97 public String getId() { 98 return id; 99 } 100 101 @Override 102 public HostUserDetails getAuthor() { 103 return author; 104 } 105 106 @Override 107 public List<Review> getReviews() { 108 return new ArrayList<>(data.reviews); 109 } 110 111 @Override 112 public void addReview(Review.Verdict verdict, String body) { 113 try { 114 var review = new Review(repository.host().getCurrentUserDetails(), 115 verdict, repository.localRepository().resolve(sourceRef).orElseThrow(), 116 data.reviews.size(), 117 body); 118 119 data.reviews.add(review); 120 data.lastUpdate = ZonedDateTime.now(); 121 122 } catch (IOException e) { 123 throw new UncheckedIOException(e); 124 } 125 } 126 127 @Override 128 public ReviewComment addReviewComment(Hash base, Hash hash, String path, int line, String body) { 129 var comment = new ReviewComment(null, String.valueOf(data.reviewComments.size()), hash, path, line, String.valueOf(data.reviewComments.size()), body, user, ZonedDateTime.now(), ZonedDateTime.now()); 130 data.reviewComments.add(comment); 131 data.lastUpdate = ZonedDateTime.now(); 132 return comment; 133 } 134 135 @Override 136 public ReviewComment addReviewCommentReply(ReviewComment parent, String body) { 137 if (parent.parent().isPresent()) { 138 throw new RuntimeException("Can only reply to top-level review comments"); 139 } 140 var comment = new ReviewComment(parent, parent.threadId(), parent.hash(), parent.path(), parent.line(), String.valueOf(data.reviewComments.size()), body, user, ZonedDateTime.now(), ZonedDateTime.now()); 141 data.reviewComments.add(comment); 142 data.lastUpdate = ZonedDateTime.now(); 143 return comment; 144 } 145 146 @Override 147 public List<ReviewComment> getReviewComments() { 148 return new ArrayList<>(data.reviewComments); 149 } 150 151 @Override 152 public Hash getHeadHash() { 153 return data.headHash; 154 } 155 156 @Override 157 public String getSourceRef() { 158 return sourceRef; 159 } 160 161 @Override 162 public String getTargetRef() { 163 return targetRef; 164 } 165 166 @Override 167 public Hash getTargetHash() { 168 return repository.getBranchHash(targetRef); 169 } 170 171 @Override 172 public String getTitle() { 173 return data.title; 174 } 175 176 @Override 177 public void setTitle(String title) { 178 data.title = title; 179 data.lastUpdate = ZonedDateTime.now(); 180 } 181 182 @Override 183 public String getBody() { 184 return data.body; 185 } 186 187 @Override 188 public void setBody(String body) { 189 data.body = body; 190 data.lastUpdate = ZonedDateTime.now(); 191 } 192 193 @Override 194 public List<Comment> getComments() { 195 return new ArrayList<>(data.comments); 196 } 197 198 @Override 199 public Comment addComment(String body) { 200 var comment = new Comment(String.valueOf(data.comments.size()), 201 body, 202 user, 203 ZonedDateTime.now(), 204 ZonedDateTime.now()); 205 data.comments.add(comment); 206 data.lastUpdate = ZonedDateTime.now(); 207 return comment; 208 } 209 210 @Override 211 public Comment updateComment(String id, String body) { 212 var originalComment = data.comments.get(Integer.parseInt(id)); 213 var comment = new Comment(originalComment.id(), 214 body, 215 originalComment.author(), 216 originalComment.createdAt(), 217 ZonedDateTime.now()); 218 data.comments.remove(Integer.parseInt(id)); 219 data.comments.add(Integer.parseInt(id), comment); 220 data.lastUpdate = ZonedDateTime.now(); 221 return comment; 222 } 223 224 @Override 225 public ZonedDateTime getCreated() { 226 return data.created; 227 } 228 229 @Override 230 public ZonedDateTime getUpdated() { 231 return data.lastUpdate; 232 } 233 234 @Override 235 public Map<String, Check> getChecks(Hash hash) { 236 return data.checks.stream() 237 .filter(check -> check.hash().equals(hash)) 238 .collect(Collectors.toMap(Check::name, Function.identity())); 239 } 240 241 @Override 242 public void createCheck(Check check) { 243 var existing = data.checks.stream() 244 .filter(c -> check.name().equals(check.name())) 245 .findAny(); 246 existing.ifPresent(data.checks::remove); 247 data.checks.add(check); 248 data.lastUpdate = ZonedDateTime.now(); 249 } 250 251 @Override 252 public void updateCheck(Check updated) { 253 var existing = data.checks.stream() 254 .filter(check -> check.name().equals(updated.name())) 255 .findAny() 256 .orElseThrow(); 257 258 data.checks.remove(existing); 259 data.checks.add(updated); 260 data.lastUpdate = ZonedDateTime.now(); 261 } 262 263 @Override 264 public void setState(State state) { 265 data.state = state; 266 data.lastUpdate = ZonedDateTime.now(); 267 } 268 269 boolean isOpen() { 270 return data.state.equals(PullRequest.State.OPEN); 271 } 272 273 @Override 274 public void addLabel(String label) { 275 data.labels.add(label); 276 data.lastUpdate = ZonedDateTime.now(); 277 } 278 279 @Override 280 public void removeLabel(String label) { 281 data.labels.remove(label); 282 data.lastUpdate = ZonedDateTime.now(); 283 } 284 285 @Override 286 public List<String> getLabels() { 287 return new ArrayList<>(data.labels); 288 } 289 290 @Override 291 public URI getWebUrl() { 292 try { 293 return new URI(repository.getUrl().toString() + "/pr/" + getId()); 294 } catch (URISyntaxException e) { 295 throw new RuntimeException(e); 296 } 297 } 298 299 @Override 300 public List<HostUserDetails> getAssignees() { 301 throw new RuntimeException("not implemented yet"); 302 } 303 304 @Override 305 public void setAssignees(List<HostUserDetails> assignees) { 306 throw new RuntimeException("not implemented yet"); 307 } 308 }