Skip to content

Commit be49eb2

Browse files
committed
Merge pull request hub4j#368
2 parents 57c36f4 + fb47067 commit be49eb2

6 files changed

Lines changed: 341 additions & 0 deletions

File tree

src/main/java/org/kohsuke/github/GHRepository.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,19 @@ public GHNotificationStream listNotifications() {
15401540
return new GHNotificationStream(root,getApiTailUrl("/notifications"));
15411541
}
15421542

1543+
/**
1544+
* <a href="https://developer.github.com/v3/repos/traffic/#views">https://developer.github.com/v3/repos/traffic/#views</a>
1545+
*/
1546+
public GHRepositoryViewTraffic getViewTraffic() throws IOException{
1547+
return root.retrieve().to(getApiTailUrl("/traffic/views"), GHRepositoryViewTraffic.class);
1548+
}
1549+
1550+
/**
1551+
* <a href="https://developer.github.com/v3/repos/traffic/#clones">https://developer.github.com/v3/repos/traffic/#clones</a>
1552+
*/
1553+
public GHRepositoryCloneTraffic getCloneTraffic() throws IOException{
1554+
return root.retrieve().to(getApiTailUrl("/traffic/clones"), GHRepositoryCloneTraffic.class);
1555+
}
15431556

15441557
@Override
15451558
public int hashCode() {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.kohsuke.github;
2+
3+
import java.util.Date;
4+
import java.util.List;
5+
6+
/**
7+
* Repository clone statistics.
8+
*
9+
* @see GHRepository#getCloneTraffic()
10+
*/
11+
public class GHRepositoryCloneTraffic extends GHRepositoryTrafficInfo {
12+
private List<DayInfo> clones;
13+
14+
/*package*/ GHRepositoryCloneTraffic() {
15+
}
16+
17+
/*package*/ GHRepositoryCloneTraffic(Integer count, Integer uniques, List<DayInfo> clones) {
18+
super(count, uniques);
19+
this.clones = clones;
20+
}
21+
22+
public List<DayInfo> getClones() {
23+
return clones;
24+
}
25+
26+
public List<DayInfo> getDailyInfo() {
27+
return getClones();
28+
}
29+
30+
public static class DayInfo extends GHRepositoryTrafficInfo.DayInfo {
31+
/*package*/ DayInfo() {
32+
}
33+
34+
/*package*/ DayInfo(String timestamp, int count, int uniques) {
35+
super(timestamp, count, uniques);
36+
}
37+
38+
/*package*/ DayInfo(Date timestamp, int count, int uniques) {
39+
super(timestamp, count, uniques);
40+
}
41+
}
42+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package org.kohsuke.github;
2+
3+
import java.util.Date;
4+
import java.util.List;
5+
6+
public abstract class GHRepositoryTrafficInfo implements TrafficInfo {
7+
private int count;
8+
private int uniques;
9+
10+
/*package*/ GHRepositoryTrafficInfo() {
11+
}
12+
13+
/*package*/ GHRepositoryTrafficInfo(int count, int uniques) {
14+
this.count = count;
15+
this.uniques = uniques;
16+
}
17+
18+
public int getCount() {
19+
return count;
20+
}
21+
22+
public int getUniques() {
23+
return uniques;
24+
}
25+
26+
public abstract List<? extends DayInfo> getDailyInfo();
27+
28+
public static abstract class DayInfo implements TrafficInfo {
29+
private Date timestamp;
30+
private int count;
31+
private int uniques;
32+
33+
public Date getTimestamp() {
34+
return timestamp;
35+
}
36+
37+
public int getCount() {
38+
return count;
39+
}
40+
41+
public int getUniques() {
42+
return uniques;
43+
}
44+
45+
/*package*/ DayInfo() {
46+
}
47+
48+
/*package*/ DayInfo(String timestamp, Integer count, Integer uniques) {
49+
this.timestamp = GitHub.parseDate(timestamp);
50+
this.count = count;
51+
this.uniques = uniques;
52+
}
53+
54+
/*package*/ DayInfo(Date timestamp, Integer count, Integer uniques) {
55+
this.timestamp = timestamp;
56+
this.count = count;
57+
this.uniques = uniques;
58+
}
59+
}
60+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.kohsuke.github;
2+
3+
import java.util.Date;
4+
import java.util.List;
5+
6+
/**
7+
* Repository view statistics.
8+
*
9+
* @see GHRepository#getViewTraffic()
10+
*/
11+
public class GHRepositoryViewTraffic extends GHRepositoryTrafficInfo {
12+
private List<Daily> views;
13+
14+
/*package*/ GHRepositoryViewTraffic() {
15+
}
16+
17+
/*package*/ GHRepositoryViewTraffic(int count, int uniques, List<Daily> views) {
18+
super(count, uniques);
19+
this.views = views;
20+
}
21+
22+
public List<Daily> getViews() {
23+
return views;
24+
}
25+
26+
public List<Daily> getDailyInfo() {
27+
return getViews();
28+
}
29+
30+
public static class Daily extends GHRepositoryTrafficInfo.DayInfo {
31+
/*package*/ Daily() {
32+
}
33+
34+
/*package*/ Daily(String timestamp, int count, int uniques) {
35+
super(timestamp, count, uniques);
36+
}
37+
38+
/*package*/ Daily(Date timestamp, int count, int uniques) {
39+
super(timestamp, count, uniques);
40+
}
41+
}
42+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.kohsuke.github;
2+
3+
/**
4+
* @author Kohsuke Kawaguchi
5+
*/
6+
public interface TrafficInfo {
7+
/**
8+
* Total count of hits.
9+
*/
10+
int getCount();
11+
12+
/**
13+
* Unique visitors.
14+
*/
15+
int getUniques();
16+
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package org.kohsuke.github;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import org.apache.commons.io.IOUtils;
5+
import org.junit.Assert;
6+
import org.junit.Test;
7+
import org.kohsuke.github.GHRepositoryCloneTraffic.DayInfo;
8+
import org.kohsuke.github.GHRepositoryViewTraffic.Daily;
9+
import org.mockito.Mockito;
10+
11+
import java.io.IOException;
12+
import java.io.InputStream;
13+
import java.net.HttpURLConnection;
14+
import java.net.URL;
15+
import java.text.SimpleDateFormat;
16+
import java.util.Arrays;
17+
import java.util.Iterator;
18+
import java.util.List;
19+
import java.util.TimeZone;
20+
21+
public class RepositoryTrafficTest {
22+
final private String login = "kohsuke", repositoryName = "github-api";
23+
24+
@SuppressWarnings("unchecked")
25+
private <T extends GHRepositoryTrafficInfo> void checkResponse(T expected, T actual){
26+
Assert.assertEquals(expected.getCount(), actual.getCount());
27+
Assert.assertEquals(expected.getUniques(), actual.getUniques());
28+
29+
List<? extends GHRepositoryTrafficInfo.DayInfo> expectedList = expected.getDailyInfo();
30+
List<? extends GHRepositoryTrafficInfo.DayInfo> actualList = actual.getDailyInfo();
31+
Iterator<? extends GHRepositoryTrafficInfo.DayInfo> expectedIt;
32+
Iterator<? extends GHRepositoryTrafficInfo.DayInfo> actualIt;
33+
34+
Assert.assertEquals(expectedList.size(), actualList.size());
35+
expectedIt = expectedList.iterator();
36+
actualIt = actualList.iterator();
37+
38+
while(expectedIt.hasNext() && actualIt.hasNext()) {
39+
GHRepositoryTrafficInfo.DayInfo expectedDayInfo = expectedIt.next();
40+
GHRepositoryTrafficInfo.DayInfo actualDayInfo = actualIt.next();
41+
Assert.assertEquals(expectedDayInfo.getCount(), actualDayInfo.getCount());
42+
Assert.assertEquals(expectedDayInfo.getUniques(), actualDayInfo.getUniques());
43+
Assert.assertEquals(expectedDayInfo.getTimestamp(), actualDayInfo.getTimestamp());
44+
}
45+
}
46+
47+
private <T extends GHRepositoryTrafficInfo> void testTraffic(T expectedResult) throws IOException{
48+
SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
49+
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
50+
ObjectMapper mapper = new ObjectMapper().setDateFormat(dateFormat);
51+
String mockedResponse = mapper.writeValueAsString(expectedResult);
52+
53+
54+
GitHub gitHub = GitHub.connect(login, null);
55+
GitHub gitHubSpy = Mockito.spy(gitHub);
56+
GHRepository repo = gitHubSpy.getUser(login).getRepository(repositoryName);
57+
58+
59+
// accessing traffic info requires push access to the repo
60+
// since we don't have that, let the mocking begin...
61+
62+
HttpConnector connectorSpy = Mockito.spy(gitHubSpy.getConnector());
63+
Mockito.doReturn(connectorSpy).when(gitHubSpy).getConnector();
64+
65+
66+
// also known as the "uc" in the Requester class
67+
HttpURLConnection mockHttpURLConnection = Mockito.mock(HttpURLConnection.class);
68+
69+
70+
// needed for Requester.setRequestMethod
71+
Mockito.doReturn("GET").when(mockHttpURLConnection).getRequestMethod();
72+
73+
74+
// this covers calls on "uc" in Requester.setupConnection and Requester.buildRequest
75+
URL trafficURL = new URL(
76+
"https://api.github.com/repos/"+login+"/"+repositoryName+"/traffic/" +
77+
((expectedResult instanceof GHRepositoryViewTraffic) ? "views" : "clones")
78+
);
79+
Mockito.doReturn(mockHttpURLConnection).when(connectorSpy).connect(Mockito.eq(trafficURL));
80+
81+
82+
// make Requester.parse work
83+
Mockito.doReturn(200).when(mockHttpURLConnection).getResponseCode();
84+
Mockito.doReturn("OK").when(mockHttpURLConnection).getResponseMessage();
85+
InputStream stubInputStream = IOUtils.toInputStream(mockedResponse, "UTF-8");
86+
Mockito.doReturn(stubInputStream).when(mockHttpURLConnection).getInputStream();
87+
88+
if(expectedResult instanceof GHRepositoryViewTraffic){
89+
GHRepositoryViewTraffic views = repo.getViewTraffic();
90+
checkResponse(expectedResult, views);
91+
}
92+
else if(expectedResult instanceof GHRepositoryCloneTraffic) {
93+
GHRepositoryCloneTraffic clones = repo.getCloneTraffic();
94+
checkResponse(expectedResult, clones);
95+
}
96+
}
97+
98+
@Test
99+
public void testGetViews() throws IOException{
100+
GHRepositoryViewTraffic expectedResult = new GHRepositoryViewTraffic(
101+
21523359,
102+
65534,
103+
Arrays.asList(
104+
new Daily("2016-10-10T00:00:00Z", 3, 2),
105+
new Daily("2016-10-11T00:00:00Z", 9, 4),
106+
new Daily("2016-10-12T00:00:00Z", 27, 8),
107+
new Daily("2016-10-13T00:00:00Z", 81, 16),
108+
new Daily("2016-10-14T00:00:00Z", 243, 32),
109+
new Daily("2016-10-15T00:00:00Z", 729, 64),
110+
new Daily("2016-10-16T00:00:00Z", 2187, 128),
111+
new Daily("2016-10-17T00:00:00Z", 6561, 256),
112+
new Daily("2016-10-18T00:00:00Z", 19683, 512),
113+
new Daily("2016-10-19T00:00:00Z", 59049, 1024),
114+
new Daily("2016-10-20T00:00:00Z", 177147, 2048),
115+
new Daily("2016-10-21T00:00:00Z", 531441, 4096),
116+
new Daily("2016-10-22T00:00:00Z", 1594323, 8192),
117+
new Daily("2016-10-23T00:00:00Z", 4782969, 16384),
118+
new Daily("2016-10-24T00:00:00Z", 14348907, 32768)
119+
)
120+
);
121+
testTraffic(expectedResult);
122+
}
123+
124+
@Test
125+
public void testGetClones() throws IOException{
126+
GHRepositoryCloneTraffic expectedResult = new GHRepositoryCloneTraffic(
127+
1500,
128+
455,
129+
Arrays.asList(
130+
new DayInfo("2016-10-10T00:00:00Z", 10,3),
131+
new DayInfo("2016-10-11T00:00:00Z", 20,6),
132+
new DayInfo("2016-10-12T00:00:00Z", 30,5),
133+
new DayInfo("2016-10-13T00:00:00Z", 40,7),
134+
new DayInfo("2016-10-14T00:00:00Z", 50,11),
135+
new DayInfo("2016-10-15T00:00:00Z", 60,12),
136+
new DayInfo("2016-10-16T00:00:00Z", 70,19),
137+
new DayInfo("2016-10-17T00:00:00Z", 170,111),
138+
new DayInfo("2016-10-18T00:00:00Z", 180,70),
139+
new DayInfo("2016-10-19T00:00:00Z", 190,10),
140+
new DayInfo("2016-10-20T00:00:00Z", 200,18),
141+
new DayInfo("2016-10-21T00:00:00Z", 210,8),
142+
new DayInfo("2016-10-22T00:00:00Z", 220,168),
143+
new DayInfo("2016-10-23T00:00:00Z", 5,2),
144+
new DayInfo("2016-10-24T00:00:00Z", 45,5)
145+
)
146+
);
147+
testTraffic(expectedResult);
148+
}
149+
150+
@Test
151+
public void testGetTrafficStatsAccessFailureDueToInsufficientPermissions() throws IOException {
152+
String errorMsg = "Exception should be thrown, since we don't have permission to access repo traffic info.";
153+
GitHub gitHub = GitHub.connect(login, null);
154+
GHRepository repo = gitHub.getUser(login).getRepository(repositoryName);
155+
try {
156+
repo.getViewTraffic();
157+
Assert.fail(errorMsg);
158+
}
159+
catch (HttpException ex){
160+
}
161+
try {
162+
repo.getCloneTraffic();
163+
Assert.fail(errorMsg);
164+
}
165+
catch (HttpException ex){
166+
}
167+
}
168+
}

0 commit comments

Comments
 (0)