Skip to content

Commit 817e457

Browse files
committed
Expand repository definition to support FREEBIE/MONEYMONEY default.
Repositories can either be defined to have a mode of 'MONEYMONEY' (default), where every commit is paid out on unless it contains the tag 'FREEBIE', or can be defined to have a mode of 'FREEBIE', where commits aren't paid out unless they contain the tag 'MONEYMONEY'.
1 parent 266cfc6 commit 817e457

File tree

12 files changed

+328
-39
lines changed

12 files changed

+328
-39
lines changed

Procfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
web: java $JAVA_OPTS -Ddw.http.port=$PORT -Ddw.http.adminPort=$PORT -Ddw.github.user=$GITHUB_USER -Ddw.github.token=$GITHUB_TOKEN -Ddw.github.repositories_heroku="$GITHUB_REPOSITORIES" -Ddw.coinbase.apiKey=$COINBASE_API_KEY -jar target/BitHub-0.1.jar server
1+
web: java $JAVA_OPTS -Ddw.http.port=$PORT -Ddw.http.adminPort=$PORT -Ddw.github.user=$GITHUB_USER -Ddw.github.token=$GITHUB_TOKEN -Ddw.github.repositories_heroku="$GITHUB_REPOSITORIES" -Ddw.coinbase.apiKey=$COINBASE_API_KEY -Ddw.github.webhook.password=$GITHUB_WEBHOOK_PASSWORD -jar target/BitHub-0.1.jar server

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ Deploying To Heroku
3535
$ heroku create your_app_name
3636
$ heroku config:set GITHUB_USER=your_bithub_username
3737
$ heroku config:set GITHUB_TOKEN=your_bithub_authtoken
38-
$ heroku config:set GITHUB_REPOSITORIES="[\"https://github.com/youraccount/yourrepo\", \"https://github.com/youraccount/yourotherrepo\"]"
38+
$ heroku config:set GITHUB_WEBHOOK_PASSWORD=your_webhook_password
39+
$ heroku config:set GITHUB_REPOSITORIES="[{\"url\" : \"https://github.com/youraccount/yourrepo\"}, {\"url\" : \"https://github.com/youraccount/yourotherrepo\"}]"
3940
$ heroku config:set COINBASE_API_KEY=your_api_key
4041
$ git remote add your_heroku_remote
4142
$ git push heroku master

config/sample.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
github:
22
user: # Your BitHub instance's GitHub username.
33
token: # Your BitHub instance's GitHub auth token.
4-
repositories:
5-
- # A list of repository URLs to support payouts for.
64
webhook:
75
password: # HTTP basic auth. The username defaults to "bithub".
6+
repositories: # A list of repository URLs to support payouts for.
7+
- url: # A repository's URL
8+
mode: # Either MONEYMONEY (default) or FREEBIE.
9+
# The former will pay out on every commit, unless
10+
# FREEBIE is specified in the message. The latter will
11+
# only pay out if MONEYMONEY is specified in the message.
812

913
coinbase:
1014
apiKey: # Your Coinbase API key.

src/main/java/org/whispersystems/bithub/BithubService.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.whispersystems.bithub.auth.GithubWebhookAuthenticator;
2626
import org.whispersystems.bithub.client.CoinbaseClient;
2727
import org.whispersystems.bithub.client.GithubClient;
28+
import org.whispersystems.bithub.config.RepositoryConfiguration;
2829
import org.whispersystems.bithub.controllers.GithubController;
2930
import org.whispersystems.bithub.controllers.StatusController;
3031
import org.whispersystems.bithub.filters.CorsHeaderFilter;
@@ -51,14 +52,14 @@ public void initialize(Bootstrap<BithubServerConfiguration> bootstrap) {
5152
public void run(BithubServerConfiguration config, Environment environment)
5253
throws Exception
5354
{
54-
String githubUser = config.getGithubConfiguration().getUser();
55-
String githubToken = config.getGithubConfiguration().getToken();
56-
String githubWebhookUser = config.getGithubConfiguration().getWebhookConfiguration().getUsername();
57-
String githubWebhookPwd = config.getGithubConfiguration().getWebhookConfiguration().getPassword();
58-
List<String> githubRepositories = config.getGithubConfiguration().getRepositories();
59-
BigDecimal payoutRate = config.getBithubConfiguration().getPayoutRate();
60-
GithubClient githubClient = new GithubClient(githubUser, githubToken);
61-
CoinbaseClient coinbaseClient = new CoinbaseClient(config.getCoinbaseConfiguration().getApiKey());
55+
String githubUser = config.getGithubConfiguration().getUser();
56+
String githubToken = config.getGithubConfiguration().getToken();
57+
String githubWebhookUser = config.getGithubConfiguration().getWebhookConfiguration().getUsername();
58+
String githubWebhookPwd = config.getGithubConfiguration().getWebhookConfiguration().getPassword();
59+
List<RepositoryConfiguration> githubRepositories = config.getGithubConfiguration().getRepositories();
60+
BigDecimal payoutRate = config.getBithubConfiguration().getPayoutRate();
61+
GithubClient githubClient = new GithubClient(githubUser, githubToken);
62+
CoinbaseClient coinbaseClient = new CoinbaseClient(config.getCoinbaseConfiguration().getApiKey());
6263

6364
environment.addFilter(new CorsHeaderFilter(), "/v1/status/*");
6465
environment.addResource(new GithubController(githubRepositories, githubClient, coinbaseClient, payoutRate));

src/main/java/org/whispersystems/bithub/config/GithubConfiguration.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public class GithubConfiguration {
4343
private String token;
4444

4545
@JsonProperty
46-
private List<String> repositories;
46+
private List<RepositoryConfiguration> repositories;
4747

4848
@JsonProperty
4949
private String repositories_heroku;
@@ -61,15 +61,15 @@ public String getToken() {
6161
return token;
6262
}
6363

64-
public List<String> getRepositories() {
64+
public List<RepositoryConfiguration> getRepositories() {
6565
if (repositories != null) {
6666
return repositories;
6767
}
6868

6969
if (repositories_heroku != null) {
7070
try {
7171
ObjectMapper mapper = new ObjectMapper();
72-
return mapper.readValue(repositories_heroku, new TypeReference<List<String>>() {});
72+
return mapper.readValue(repositories_heroku, new TypeReference<List<RepositoryConfiguration>>() {});
7373
} catch (IOException e) {
7474
logger.warn("Error deserializing", e);
7575
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.whispersystems.bithub.config;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import org.hibernate.validator.constraints.NotEmpty;
5+
6+
public class RepositoryConfiguration {
7+
8+
@JsonProperty
9+
@NotEmpty
10+
private String url;
11+
12+
@JsonProperty
13+
@NotEmpty
14+
private String mode = "MONEYMONEY";
15+
16+
public RepositoryConfiguration(String url, String mode) {
17+
this.url = url;
18+
this.mode = mode;
19+
}
20+
21+
public RepositoryConfiguration(String url) {
22+
this.url = url;
23+
}
24+
25+
public RepositoryConfiguration() {}
26+
27+
public String getUrl() {
28+
return url;
29+
}
30+
31+
public String getMode() {
32+
return mode;
33+
}
34+
}

src/main/java/org/whispersystems/bithub/controllers/GithubController.java

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.whispersystems.bithub.client.CoinbaseClient;
2929
import org.whispersystems.bithub.client.GithubClient;
3030
import org.whispersystems.bithub.client.TransferFailedException;
31+
import org.whispersystems.bithub.config.RepositoryConfiguration;
3132
import org.whispersystems.bithub.entities.Commit;
3233
import org.whispersystems.bithub.entities.PushEvent;
3334
import org.whispersystems.bithub.entities.Repository;
@@ -44,9 +45,11 @@
4445
import java.io.IOException;
4546
import java.math.BigDecimal;
4647
import java.math.RoundingMode;
48+
import java.util.HashMap;
4749
import java.util.HashSet;
4850
import java.util.LinkedList;
4951
import java.util.List;
52+
import java.util.Map;
5053
import java.util.Set;
5154

5255
/**
@@ -63,23 +66,24 @@ public class GithubController {
6366
private final Logger logger = LoggerFactory.getLogger(GithubController.class);
6467
private final SubnetInfo trustedNetwork = new SubnetUtils(GITHUB_WEBOOK_CIDR).getInfo();
6568

66-
private final CoinbaseClient coinbaseClient;
67-
private final GithubClient githubClient;
68-
private final Set<String> repositories;
69-
private final BigDecimal payoutRate;
69+
private final CoinbaseClient coinbaseClient;
70+
private final GithubClient githubClient;
71+
private final Map<String, String> repositories;
72+
private final BigDecimal payoutRate;
7073

71-
public GithubController(List<String> repositories,
74+
public GithubController(List<RepositoryConfiguration> repositories,
7275
GithubClient githubClient,
7376
CoinbaseClient coinbaseClient,
7477
BigDecimal payoutRate)
7578
{
7679
this.coinbaseClient = coinbaseClient;
7780
this.githubClient = githubClient;
78-
this.repositories = new HashSet<>();
81+
this.repositories = new HashMap<>();
7982
this.payoutRate = payoutRate;
8083

81-
for (String repository : repositories) {
82-
this.repositories.add(repository.toLowerCase());
84+
for (RepositoryConfiguration repository : repositories) {
85+
this.repositories.put(repository.getUrl().toLowerCase(),
86+
repository.getMode().toUpperCase());
8387
}
8488
}
8589

@@ -95,12 +99,14 @@ public void handleCommits(@Auth Authentication auth,
9599
authenticate(clientIp);
96100
PushEvent event = getEventFromPayload(eventString);
97101

98-
if (!repositories.contains(event.getRepository().getUrl().toLowerCase())) {
99-
throw new UnauthorizedHookException("Not a valid repository: " + event.getRepository().getUrl());
102+
if (!repositories.containsKey(event.getRepository().getUrl().toLowerCase())) {
103+
throw new UnauthorizedHookException("Not a valid repository: " +
104+
event.getRepository().getUrl());
100105
}
101106

102107
Repository repository = event.getRepository();
103-
List<Commit> commits = getQualifyingCommits(event);
108+
String defaultMode = repositories.get(repository.getUrl().toLowerCase());
109+
List<Commit> commits = getQualifyingCommits(event, defaultMode);
104110
BigDecimal balance = coinbaseClient.getAccountBalance();
105111
BigDecimal exchangeRate = coinbaseClient.getExchangeRate();
106112

@@ -141,15 +147,15 @@ private PushEvent getEventFromPayload(String payload) throws IOException {
141147
return event;
142148
}
143149

144-
private List<Commit> getQualifyingCommits(PushEvent event) {
150+
private List<Commit> getQualifyingCommits(PushEvent event, String defaultMode) {
145151
List<Commit> commits = new LinkedList<>();
146152
Set<String> emails = new HashSet<>();
147153

148154
for (Commit commit : event.getCommits()) {
149155
logger.info(commit.getUrl());
150156
if (!emails.contains(commit.getAuthor().getEmail())) {
151157
logger.info("Unique author: "+ commit.getAuthor().getEmail());
152-
if (commit.getMessage() == null || (!commit.getMessage().startsWith("Merge") && !commit.getMessage().contains("FREEBIE"))) {
158+
if (isViableMessage(commit.getMessage(), defaultMode)) {
153159
logger.info("Not a merge commit or freebie...");
154160

155161
emails.add(commit.getAuthor().getEmail());
@@ -161,6 +167,14 @@ private List<Commit> getQualifyingCommits(PushEvent event) {
161167
return commits;
162168
}
163169

170+
private boolean isViableMessage(String message, String defaultMode) {
171+
if (message == null || message.startsWith("Merge"))
172+
return false;
173+
174+
return (!message.contains("FREEBIE") && defaultMode.equals("MONEYMONEY")) ||
175+
(message.contains("MONEYMONEY") && defaultMode.equals("FREEBIE"));
176+
}
177+
164178
private boolean isViablePaymentAmount(BigDecimal payment) {
165179
return payment.compareTo(new BigDecimal(0)) == 1;
166180
}

src/main/java/org/whispersystems/bithub/controllers/StatusController.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.whispersystems.bithub.util.Badge;
2727
import org.whispersystems.bithub.views.RecentTransactionsView;
2828

29+
import javax.ws.rs.Consumes;
2930
import javax.ws.rs.DefaultValue;
3031
import javax.ws.rs.GET;
3132
import javax.ws.rs.Path;
@@ -69,14 +70,11 @@ public StatusController(CoinbaseClient coinbaseClient, BigDecimal payoutRate) th
6970
@Timed
7071
@GET
7172
@Path("/transactions")
72-
public Response getTransactions(@QueryParam("format") @DefaultValue("html") String format)
73+
@Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_HTML})
74+
public RecentTransactionsView getTransactions()
7375
throws IOException
7476
{
75-
if (format.equals("json")) {
76-
return Response.ok(cachedTransactions.get(), MediaType.APPLICATION_JSON_TYPE).build();
77-
} else {
78-
return Response.ok(cachedTransactions.get(), MediaType.TEXT_HTML_TYPE).build();
79-
}
77+
return cachedTransactions.get();
8078
}
8179

8280
@Timed

src/test/java/org/whispersystems/bithub/tests/controllers/GithubControllerTest.java

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,22 @@
2727
import org.whispersystems.bithub.client.CoinbaseClient;
2828
import org.whispersystems.bithub.client.GithubClient;
2929
import org.whispersystems.bithub.client.TransferFailedException;
30+
import org.whispersystems.bithub.config.RepositoryConfiguration;
3031
import org.whispersystems.bithub.controllers.GithubController;
3132
import org.whispersystems.bithub.entities.Author;
3233
import org.whispersystems.bithub.mappers.UnauthorizedHookExceptionMapper;
3334

3435
import javax.ws.rs.core.MediaType;
36+
import java.io.InputStream;
3537
import java.math.BigDecimal;
3638
import java.util.LinkedList;
3739
import java.util.List;
3840
import java.util.Scanner;
39-
import java.io.InputStream;
4041

4142
import static org.fest.assertions.api.Assertions.assertThat;
42-
import static org.mockito.Matchers.*;
43+
import static org.mockito.Matchers.any;
44+
import static org.mockito.Matchers.anyString;
45+
import static org.mockito.Matchers.eq;
4346
import static org.mockito.Mockito.*;
4447

4548
public class GithubControllerTest extends ResourceTest {
@@ -58,8 +61,9 @@ public class GithubControllerTest extends ResourceTest {
5861
private final String invalidUserAuthString = "Basic " + Base64.encodeBase64(("wrong:" + authPassword).getBytes());
5962
private final String invalidPasswordAuthString = "Basic " + Base64.encodeBase64((authUsername + ":wrong").getBytes());
6063

61-
private final List<String> repositories = new LinkedList<String>() {{
62-
add("https://github.com/moxie0/test");
64+
private final List<RepositoryConfiguration> repositories = new LinkedList<RepositoryConfiguration>() {{
65+
add(new RepositoryConfiguration("https://github.com/moxie0/test"));
66+
add(new RepositoryConfiguration("https://github.com/moxie0/optin", "FREEBIE"));
6367
}};
6468

6569
@Override
@@ -195,4 +199,37 @@ public void testValidMultipleCommitsMultipleAuthors() throws Exception, Transfer
195199
.multiply(new BigDecimal(0.02))), anyString());
196200
}
197201

202+
@Test
203+
public void testOptInCommit() throws Exception, TransferFailedException {
204+
String payloadValue = payload("/payloads/opt_in_commit.json");
205+
MultivaluedMapImpl post = new MultivaluedMapImpl();
206+
post.add("payload", payloadValue);
207+
ClientResponse response = client().resource("/v1/github/commits/")
208+
.header("X-Forwarded-For", "192.30.252.1")
209+
.header("Authorization", authString)
210+
.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
211+
.post(ClientResponse.class, post);
212+
213+
verify(coinbaseClient).sendPayment(any(Author.class),
214+
eq(BALANCE.multiply(new BigDecimal(0.02))),
215+
anyString());
216+
}
217+
218+
@Test
219+
public void testNoOptInCommit() throws Exception, TransferFailedException {
220+
String payloadValue = payload("/payloads/no_opt_in_commit.json");
221+
MultivaluedMapImpl post = new MultivaluedMapImpl();
222+
post.add("payload", payloadValue);
223+
ClientResponse response = client().resource("/v1/github/commits/")
224+
.header("X-Forwarded-For", "192.30.252.1")
225+
.header("Authorization", authString)
226+
.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
227+
.post(ClientResponse.class, post);
228+
229+
verify(coinbaseClient, never()).sendPayment(any(Author.class),
230+
any(BigDecimal.class),
231+
anyString());
232+
}
233+
234+
198235
}

src/test/java/org/whispersystems/bithub/tests/controllers/StatusControllerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public void testTransactionsHtml() throws Exception {
4949

5050
@Test
5151
public void testTransactionsJson() throws Exception {
52-
ClientResponse response = client().resource("/v1/status/transactions/").queryParam("format", "json")
52+
ClientResponse response = client().resource("/v1/status/transactions/").accept(MediaType.APPLICATION_JSON_TYPE)
5353
.get(ClientResponse.class);
5454

5555
assertThat(response.getStatus()).isEqualTo(200);

0 commit comments

Comments
 (0)