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 }