Initial Commit
This commit is contained in:
commit
90c8512439
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.idea
|
||||
*.iml
|
||||
target
|
||||
config/production.yml
|
||||
|
||||
1
Procfile
Normal file
1
Procfile
Normal file
@ -0,0 +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
|
||||
45
README.md
Normal file
45
README.md
Normal file
@ -0,0 +1,45 @@
|
||||
BitHub
|
||||
=================
|
||||
|
||||
BitHub is a service that will automatically pay a percentage of Bitcoin funds for every submission to a GitHub repository.
|
||||
|
||||
Building
|
||||
-------------
|
||||
|
||||
$ git clone https://github.com/WhisperSystems/BitHub.git
|
||||
$ cd BitHub
|
||||
$ mvn3 package
|
||||
|
||||
Running
|
||||
-----------
|
||||
|
||||
1. Create a GitHub account for your BitHub server.
|
||||
1. Create a Coinbase account for your BitHub server.
|
||||
1. Add the above credentials to `config/sample.yml`
|
||||
1. Execute `$ java -jar target/BitHub-0.1.jar server config/yourconfig.yml`
|
||||
|
||||
Deploying To Heroku
|
||||
------------
|
||||
|
||||
1. `$ heroku create your_app_name`
|
||||
1. `$ heroku config:set GITHUB_USER=your_bithub_username`
|
||||
1. `$ heroku config:set GITHUB_TOKEN=your_bithub_authtoken`
|
||||
1. `$ heroku config:set GITHUB_REPOSITORIES="[\"https://github.com/youraccount/yourrepo\", \"https://github.com/youraccount/yourotherrepo\"]"`
|
||||
1. `$ heroku config:set COINBASE_API_KEY=your_api_key`
|
||||
1. `$ git remote add your_heroku_remote`
|
||||
1. `$ git push heroku master`
|
||||
|
||||
|
||||
Mailing list
|
||||
------------
|
||||
|
||||
Have a question? Ask on our mailing list!
|
||||
|
||||
whispersystems@lists.riseup.net
|
||||
|
||||
https://lists.riseup.net/www/info/whispersystems
|
||||
|
||||
Current BitHub Payment For Commit:
|
||||
=================
|
||||

|
||||
|
||||
25
assembly.xml
Normal file
25
assembly.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
|
||||
<id>bin</id>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/config</directory>
|
||||
<outputDirectory>/config</outputDirectory>
|
||||
<includes>
|
||||
<include>*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>${project.name}-${project.version}.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
8
config/sample.yml
Normal file
8
config/sample.yml
Normal file
@ -0,0 +1,8 @@
|
||||
github:
|
||||
user: # Your BitHub instance's GitHub username.
|
||||
token: # Your BitHub instance's GitHub auth token.
|
||||
repositories:
|
||||
- # A list of repository URLs to support payouts for.
|
||||
|
||||
coinbase:
|
||||
apiKey: # Your Coinbase API key.
|
||||
BIN
design/badge-small.png
Normal file
BIN
design/badge-small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 705 B |
BIN
design/badge-small.xcf
Normal file
BIN
design/badge-small.xcf
Normal file
Binary file not shown.
BIN
design/badge.png
Normal file
BIN
design/badge.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
BIN
design/badge.xcf
Normal file
BIN
design/badge.xcf
Normal file
Binary file not shown.
175
pom.xml
Normal file
175
pom.xml
Normal file
@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<prerequisites>
|
||||
<maven>3.0.0</maven>
|
||||
</prerequisites>
|
||||
|
||||
<groupId>org.whispersystems.bithub</groupId>
|
||||
<artifactId>BitHub</artifactId>
|
||||
<version>0.1</version>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.yammer.dropwizard</groupId>
|
||||
<artifactId>dropwizard-core</artifactId>
|
||||
<version>0.6.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yammer.metrics</groupId>
|
||||
<artifactId>metrics-graphite</artifactId>
|
||||
<version>2.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yammer.dropwizard</groupId>
|
||||
<artifactId>dropwizard-auth</artifactId>
|
||||
<version>0.6.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yammer.dropwizard</groupId>
|
||||
<artifactId>dropwizard-jdbi</artifactId>
|
||||
<version>0.6.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yammer.dropwizard</groupId>
|
||||
<artifactId>dropwizard-client</artifactId>
|
||||
<version>0.6.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yammer.dropwizard</groupId>
|
||||
<artifactId>dropwizard-migrations</artifactId>
|
||||
<version>0.6.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yammer.dropwizard</groupId>
|
||||
<artifactId>dropwizard-testing</artifactId>
|
||||
<version>0.6.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey</groupId>
|
||||
<artifactId>jersey-json</artifactId>
|
||||
<version>1.17.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey.contribs</groupId>
|
||||
<artifactId>jersey-multipart</artifactId>
|
||||
<version>1.17.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>3.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yammer.dropwizard</groupId>
|
||||
<artifactId>dropwizard-views</artifactId>
|
||||
<version>0.6.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ocpsoft.prettytime</groupId>
|
||||
<artifactId>prettytime</artifactId>
|
||||
<version>2.1.3.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>1.6</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>true</createDependencyReducedPom>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>org.whispersystems.bithub.BithubService</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>assembly.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id> <!-- this is used for inheritance merges -->
|
||||
<phase>package</phase> <!-- bind to the packaging phase -->
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.yammer.dropwizard.config.Configuration;
|
||||
import org.whispersystems.bithub.config.BithubConfiguration;
|
||||
import org.whispersystems.bithub.config.CoinbaseConfiguration;
|
||||
import org.whispersystems.bithub.config.GithubConfiguration;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
public class BithubServerConfiguration extends Configuration {
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private GithubConfiguration github;
|
||||
|
||||
@Valid
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private CoinbaseConfiguration coinbase;
|
||||
|
||||
@JsonProperty
|
||||
@Valid
|
||||
private BithubConfiguration bithub = new BithubConfiguration();
|
||||
|
||||
|
||||
public GithubConfiguration getGithubConfiguration() {
|
||||
return github;
|
||||
}
|
||||
|
||||
public CoinbaseConfiguration getCoinbaseConfiguration() {
|
||||
return coinbase;
|
||||
}
|
||||
|
||||
public BithubConfiguration getBithubConfiguration() {
|
||||
return bithub;
|
||||
}
|
||||
|
||||
}
|
||||
70
src/main/java/org/whispersystems/bithub/BithubService.java
Normal file
70
src/main/java/org/whispersystems/bithub/BithubService.java
Normal file
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub;
|
||||
|
||||
import com.yammer.dropwizard.Service;
|
||||
import com.yammer.dropwizard.config.Bootstrap;
|
||||
import com.yammer.dropwizard.config.Environment;
|
||||
import com.yammer.dropwizard.views.ViewBundle;
|
||||
import org.whispersystems.bithub.client.CoinbaseClient;
|
||||
import org.whispersystems.bithub.client.GithubClient;
|
||||
import org.whispersystems.bithub.controllers.GithubController;
|
||||
import org.whispersystems.bithub.controllers.StatusController;
|
||||
import org.whispersystems.bithub.filters.CorsHeaderFilter;
|
||||
import org.whispersystems.bithub.mappers.IOExceptionMapper;
|
||||
import org.whispersystems.bithub.mappers.UnauthorizedHookExceptionMapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The main entry point for the service.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*/
|
||||
public class BithubService extends Service<BithubServerConfiguration> {
|
||||
|
||||
@Override
|
||||
public void initialize(Bootstrap<BithubServerConfiguration> bootstrap) {
|
||||
bootstrap.setName("bithub-server");
|
||||
bootstrap.addBundle(new ViewBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(BithubServerConfiguration config, Environment environment)
|
||||
throws Exception
|
||||
{
|
||||
String githubUser = config.getGithubConfiguration().getUser();
|
||||
String githubToken = config.getGithubConfiguration().getToken();
|
||||
List<String> githubRepositories = config.getGithubConfiguration().getRepositories();
|
||||
BigDecimal payoutRate = config.getBithubConfiguration().getPayoutRate();
|
||||
GithubClient githubClient = new GithubClient(githubUser, githubToken);
|
||||
CoinbaseClient coinbaseClient = new CoinbaseClient(config.getCoinbaseConfiguration().getApiKey());
|
||||
|
||||
environment.addFilter(new CorsHeaderFilter(), "/v1/status/*");
|
||||
environment.addResource(new GithubController(githubRepositories, githubClient, coinbaseClient, payoutRate));
|
||||
environment.addResource(new StatusController(coinbaseClient, payoutRate));
|
||||
environment.addProvider(new IOExceptionMapper());
|
||||
environment.addProvider(new UnauthorizedHookExceptionMapper());
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new BithubService().run(args);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.client;
|
||||
|
||||
import com.sun.jersey.api.client.Client;
|
||||
import com.sun.jersey.api.client.ClientHandlerException;
|
||||
import com.sun.jersey.api.client.UniformInterfaceException;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
import com.sun.jersey.api.client.config.ClientConfig;
|
||||
import com.sun.jersey.api.client.config.DefaultClientConfig;
|
||||
import com.sun.jersey.api.json.JSONConfiguration;
|
||||
|
||||
import org.whispersystems.bithub.entities.Author;
|
||||
import org.whispersystems.bithub.entities.BalanceResponse;
|
||||
import org.whispersystems.bithub.entities.BitcoinTransaction;
|
||||
import org.whispersystems.bithub.entities.BitcoinTransactionResponse;
|
||||
import org.whispersystems.bithub.entities.ExchangeRate;
|
||||
import org.whispersystems.bithub.entities.RecentTransactionsResponse;
|
||||
import org.whispersystems.bithub.entities.Transaction;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Handles interaction with the Coinbase API.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*/
|
||||
public class CoinbaseClient {
|
||||
|
||||
private static final String COINBASE_URL = "https://coinbase.com/";
|
||||
private static final String BALANCE_PATH = "/api/v1/account/balance";
|
||||
private static final String PAYMENT_PATH = "/api/v1/transactions/send_money";
|
||||
private static final String EXCHANGE_PATH = "/api/v1/currencies/exchange_rates";
|
||||
private static final String RECENT_TRANSACTIONS_PATH = "/api/v1/transactions";
|
||||
|
||||
private final String apiKey;
|
||||
private final Client client;
|
||||
|
||||
public CoinbaseClient(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
this.client = Client.create(getClientConfig());
|
||||
}
|
||||
|
||||
public List<Transaction> getRecentTransactions() throws IOException {
|
||||
try {
|
||||
return client.resource(COINBASE_URL)
|
||||
.path(RECENT_TRANSACTIONS_PATH)
|
||||
.queryParam("api_key", apiKey)
|
||||
.get(RecentTransactionsResponse.class).getTransactions();
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public BigDecimal getExchangeRate() throws IOException {
|
||||
try {
|
||||
WebResource resource = client.resource(COINBASE_URL)
|
||||
.path(EXCHANGE_PATH);
|
||||
|
||||
String btcToUsd = resource.accept(MediaType.APPLICATION_JSON)
|
||||
.get(ExchangeRate.class)
|
||||
.getBtc_to_usd();
|
||||
|
||||
return new BigDecimal(btcToUsd);
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPayment(Author author, BigDecimal amount, String url)
|
||||
throws TransferFailedException
|
||||
{
|
||||
try {
|
||||
WebResource resource = client.resource(COINBASE_URL)
|
||||
.path(PAYMENT_PATH)
|
||||
.queryParam("api_key", apiKey);
|
||||
|
||||
String note = "Commit payment:\n__" + author.getUsername() + "__ " + url;
|
||||
|
||||
BitcoinTransaction transaction = new BitcoinTransaction(author.getEmail(),
|
||||
amount.toPlainString(),
|
||||
note);
|
||||
|
||||
boolean success = resource.type(MediaType.APPLICATION_JSON_TYPE)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.entity(transaction)
|
||||
.post(BitcoinTransactionResponse.class)
|
||||
.isSuccess();
|
||||
|
||||
if (!success) {
|
||||
throw new TransferFailedException();
|
||||
}
|
||||
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
throw new TransferFailedException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public BigDecimal getAccountBalance() throws IOException {
|
||||
try {
|
||||
WebResource resource = client.resource(COINBASE_URL)
|
||||
.path(BALANCE_PATH)
|
||||
.queryParam("api_key", apiKey);
|
||||
|
||||
String amount = resource.accept(MediaType.APPLICATION_JSON)
|
||||
.get(BalanceResponse.class)
|
||||
.getAmount();
|
||||
|
||||
if (amount == null) {
|
||||
throw new IOException("Empty amount in response!");
|
||||
}
|
||||
|
||||
return new BigDecimal(amount);
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ClientConfig getClientConfig() {
|
||||
ClientConfig config = new DefaultClientConfig();
|
||||
config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.client;
|
||||
|
||||
import com.sun.jersey.api.client.Client;
|
||||
import com.sun.jersey.api.client.ClientHandlerException;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.UniformInterfaceException;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
import com.sun.jersey.api.client.config.ClientConfig;
|
||||
import com.sun.jersey.api.client.config.DefaultClientConfig;
|
||||
import com.sun.jersey.api.json.JSONConfiguration;
|
||||
import com.sun.jersey.core.util.Base64;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.bithub.entities.Commit;
|
||||
import org.whispersystems.bithub.entities.CommitComment;
|
||||
import org.whispersystems.bithub.entities.Repository;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
/**
|
||||
* Handles interaction with the GitHub API.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*/
|
||||
public class GithubClient {
|
||||
|
||||
private static final String GITHUB_URL = "https://api.github.com/";
|
||||
private static final String COMMENT_PATH = "/repos/%s/%s/commits/%s/comments";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(GithubClient.class);
|
||||
|
||||
private final String authorizationHeader;
|
||||
private final Client client;
|
||||
|
||||
public GithubClient(String user, String token) {
|
||||
this.authorizationHeader = getAuthorizationHeader(user, token);
|
||||
this.client = Client.create(getClientConfig());
|
||||
}
|
||||
|
||||
public void addCommitComment(Repository repository, Commit commit, String comment) {
|
||||
try {
|
||||
String path = String.format(COMMENT_PATH, repository.getOwner().getName(),
|
||||
repository.getName(), commit.getSha());
|
||||
|
||||
WebResource resource = client.resource(GITHUB_URL).path(path);
|
||||
ClientResponse response = resource.type(MediaType.APPLICATION_JSON_TYPE)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.header("Authorization", authorizationHeader)
|
||||
.entity(new CommitComment(comment))
|
||||
.post(ClientResponse.class);
|
||||
|
||||
if (response.getStatus() < 200 || response.getStatus() >=300) {
|
||||
logger.warn("Commit comment failed: " + response.getClientResponseStatus().getReasonPhrase());
|
||||
}
|
||||
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
logger.warn("Comment failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
private ClientConfig getClientConfig() {
|
||||
ClientConfig config = new DefaultClientConfig();
|
||||
config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private String getAuthorizationHeader(String user, String token) {
|
||||
return "Basic " + new String(Base64.encode(user + ":" + token));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.client;
|
||||
|
||||
public class TransferFailedException extends Throwable {
|
||||
|
||||
public TransferFailedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TransferFailedException(RuntimeException e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.config;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class BithubConfiguration {
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private String payout = "0.02";
|
||||
|
||||
public BigDecimal getPayoutRate() {
|
||||
return new BigDecimal(payout);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.config;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
public class CoinbaseConfiguration {
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private String apiKey;
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class GithubConfiguration {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(GithubConfiguration.class);
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private String user;
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private String token;
|
||||
|
||||
@JsonProperty
|
||||
private List<String> repositories;
|
||||
|
||||
@JsonProperty
|
||||
private String repositories_heroku;
|
||||
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public List<String> getRepositories() {
|
||||
if (repositories != null) {
|
||||
return repositories;
|
||||
}
|
||||
|
||||
if (repositories_heroku != null) {
|
||||
try {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
return mapper.readValue(repositories_heroku, new TypeReference<List<String>>() {});
|
||||
} catch (IOException e) {
|
||||
logger.warn("Error deserializing", e);
|
||||
}
|
||||
}
|
||||
|
||||
return new LinkedList<>();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,177 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.controllers;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yammer.metrics.annotation.Timed;
|
||||
import org.apache.commons.net.util.SubnetUtils;
|
||||
import org.apache.commons.net.util.SubnetUtils.SubnetInfo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.bithub.client.CoinbaseClient;
|
||||
import org.whispersystems.bithub.client.GithubClient;
|
||||
import org.whispersystems.bithub.client.TransferFailedException;
|
||||
import org.whispersystems.bithub.entities.Commit;
|
||||
import org.whispersystems.bithub.entities.PushEvent;
|
||||
import org.whispersystems.bithub.entities.Repository;
|
||||
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import javax.validation.ValidatorFactory;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.FormParam;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Handles incoming API calls from GitHub. These are currently only
|
||||
* PushEvent webhooks.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*/
|
||||
@Path("/v1/github")
|
||||
public class GithubController {
|
||||
|
||||
private static final String GITHUB_WEBOOK_CIDR = "192.30.252.0/22";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(GithubController.class);
|
||||
private final SubnetInfo trustedNetwork = new SubnetUtils(GITHUB_WEBOOK_CIDR).getInfo();
|
||||
|
||||
private final CoinbaseClient coinbaseClient;
|
||||
private final GithubClient githubClient;
|
||||
private final Set<String> repositories;
|
||||
private final BigDecimal payoutRate;
|
||||
|
||||
public GithubController(List<String> repositories,
|
||||
GithubClient githubClient,
|
||||
CoinbaseClient coinbaseClient,
|
||||
BigDecimal payoutRate)
|
||||
{
|
||||
this.coinbaseClient = coinbaseClient;
|
||||
this.githubClient = githubClient;
|
||||
this.repositories = new HashSet<>();
|
||||
this.payoutRate = payoutRate;
|
||||
|
||||
for (String repository : repositories) {
|
||||
this.repositories.add(repository.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@Timed
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
@Path("/commits/")
|
||||
public void handleCommits(@HeaderParam("X-Forwarded-For") String clientIp,
|
||||
@FormParam("payload") String eventString)
|
||||
throws IOException, UnauthorizedHookException
|
||||
{
|
||||
authenticate(clientIp);
|
||||
PushEvent event = getEventFromPayload(eventString);
|
||||
|
||||
if (!repositories.contains(event.getRepository().getUrl().toLowerCase())) {
|
||||
throw new UnauthorizedHookException("Not a valid repository: " + event.getRepository().getUrl());
|
||||
}
|
||||
|
||||
Repository repository = event.getRepository();
|
||||
List<Commit> commits = getQualifyingCommits(event);
|
||||
BigDecimal balance = coinbaseClient.getAccountBalance();
|
||||
|
||||
logger.info("Retrieved balance: " + balance.toPlainString());
|
||||
|
||||
sendPaymentsFor(repository, commits, balance);
|
||||
}
|
||||
|
||||
|
||||
private void sendPaymentsFor(Repository repository, List<Commit> commits, BigDecimal balance) {
|
||||
for (Commit commit : commits) {
|
||||
try {
|
||||
BigDecimal payout = balance.multiply(payoutRate);
|
||||
|
||||
if (isViablePaymentAmount(payout)) {
|
||||
coinbaseClient.sendPayment(commit.getAuthor(), payout, commit.getUrl());
|
||||
}
|
||||
|
||||
balance = balance.subtract(payout);
|
||||
githubClient.addCommitComment(repository, commit, getCommitCommentStringForPayment(payout));
|
||||
} catch (TransferFailedException e) {
|
||||
logger.warn("Transfer failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PushEvent getEventFromPayload(String payload) throws IOException {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
PushEvent event = objectMapper.readValue(payload, PushEvent.class);
|
||||
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
|
||||
Validator validator = factory.getValidator();
|
||||
|
||||
validator.validate(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
private List<Commit> getQualifyingCommits(PushEvent event) {
|
||||
List<Commit> commits = new LinkedList<>();
|
||||
Set<String> emails = new HashSet<>();
|
||||
|
||||
for (Commit commit : event.getCommits()) {
|
||||
logger.info(commit.getUrl());
|
||||
if (!emails.contains(commit.getAuthor().getEmail())) {
|
||||
logger.info("Unique author: "+ commit.getAuthor().getEmail());
|
||||
if (commit.getMessage() == null || !commit.getMessage().startsWith("Merge")) {
|
||||
logger.info("Not a merge commit...");
|
||||
emails.add(commit.getAuthor().getEmail());
|
||||
commits.add(commit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return commits;
|
||||
}
|
||||
|
||||
private boolean isViablePaymentAmount(BigDecimal payment) {
|
||||
return payment.compareTo(new BigDecimal(0)) == 1;
|
||||
}
|
||||
|
||||
private String getCommitCommentStringForPayment(BigDecimal payment) {
|
||||
if (isViablePaymentAmount(payment)) {
|
||||
payment = payment.setScale(2, RoundingMode.CEILING);
|
||||
return "Thanks! BitHub has sent payment of $" + payment.toPlainString() + "USD for this commit.";
|
||||
} else {
|
||||
return "Thanks! Unfortunately our BitHub balance is $0.00, so no payout can be made.";
|
||||
}
|
||||
}
|
||||
|
||||
private void authenticate(String clientIp) throws UnauthorizedHookException {
|
||||
if (clientIp == null) {
|
||||
throw new UnauthorizedHookException("No X-Forwarded-For!");
|
||||
}
|
||||
|
||||
if (!trustedNetwork.isInRange(clientIp)) {
|
||||
throw new UnauthorizedHookException("Untrusted IP: " + clientIp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.controllers;
|
||||
|
||||
import com.yammer.metrics.annotation.Timed;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.bithub.client.CoinbaseClient;
|
||||
import org.whispersystems.bithub.entities.Payment;
|
||||
import org.whispersystems.bithub.entities.Transaction;
|
||||
import org.whispersystems.bithub.util.Badge;
|
||||
import org.whispersystems.bithub.views.RecentTransactionsView;
|
||||
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Handles incoming API calls for BitHub instance status information.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*/
|
||||
@Path("/v1/status")
|
||||
public class StatusController {
|
||||
|
||||
private static final int UPDATE_FREQUENCY_MILLIS = 60 * 1000;
|
||||
|
||||
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
|
||||
private final Logger logger = LoggerFactory.getLogger(StatusController.class);
|
||||
|
||||
private final AtomicReference<CurrentPayment> cachedPaymentStatus;
|
||||
private final AtomicReference<RecentTransactionsView> cachedTransactions;
|
||||
private final BigDecimal payoutRate;
|
||||
|
||||
public StatusController(CoinbaseClient coinbaseClient, BigDecimal payoutRate) throws IOException {
|
||||
this.payoutRate = payoutRate;
|
||||
this.cachedPaymentStatus = new AtomicReference<>(createCurrentPaymentForBalance(coinbaseClient));
|
||||
this.cachedTransactions = new AtomicReference<>(createRecentTransactionsView(coinbaseClient));
|
||||
|
||||
initializeUpdates(coinbaseClient);
|
||||
}
|
||||
|
||||
@Timed
|
||||
@GET
|
||||
@Path("/transactions")
|
||||
public Response getTransactions(@QueryParam("format") @DefaultValue("html") String format)
|
||||
throws IOException
|
||||
{
|
||||
if (format.equals("json")) {
|
||||
return Response.ok(cachedTransactions.get(), MediaType.APPLICATION_JSON_TYPE).build();
|
||||
} else {
|
||||
return Response.ok(cachedTransactions.get(), MediaType.TEXT_HTML_TYPE).build();
|
||||
}
|
||||
}
|
||||
|
||||
@Timed
|
||||
@GET
|
||||
@Path("/payment/commit")
|
||||
public Response getCurrentCommitPrice(@QueryParam("format") @DefaultValue("png") String format)
|
||||
throws IOException
|
||||
{
|
||||
if (format.equals("json")) {
|
||||
return Response.ok(cachedPaymentStatus.get().getEntity(), MediaType.APPLICATION_JSON_TYPE).build();
|
||||
} else if (format.equals("png_small")) {
|
||||
return Response.ok(cachedPaymentStatus.get().getSmallBadge(), "image/png").build();
|
||||
} else {
|
||||
return Response.ok(cachedPaymentStatus.get().getBadge(), "image/png").build();
|
||||
}
|
||||
}
|
||||
|
||||
private CurrentPayment createCurrentPaymentForBalance(CoinbaseClient coinbaseClient)
|
||||
throws IOException
|
||||
{
|
||||
BigDecimal currentBalance = coinbaseClient.getAccountBalance();
|
||||
BigDecimal paymentBtc = currentBalance.multiply(payoutRate);
|
||||
BigDecimal exchangeRate = coinbaseClient.getExchangeRate();
|
||||
BigDecimal paymentUsd = paymentBtc.multiply(exchangeRate);
|
||||
|
||||
paymentUsd = paymentUsd.setScale(2, RoundingMode.CEILING);
|
||||
return new CurrentPayment(Badge.createFor(paymentUsd.toPlainString()),
|
||||
Badge.createSmallFor(paymentUsd.toPlainString()),
|
||||
new Payment(paymentUsd.toPlainString()));
|
||||
}
|
||||
|
||||
private RecentTransactionsView createRecentTransactionsView(CoinbaseClient coinbaseClient)
|
||||
throws IOException
|
||||
{
|
||||
List<Transaction> recentTransactions = coinbaseClient.getRecentTransactions();
|
||||
BigDecimal exchangeRate = coinbaseClient.getExchangeRate();
|
||||
|
||||
return new RecentTransactionsView(recentTransactions, exchangeRate);
|
||||
}
|
||||
|
||||
public void initializeUpdates(final CoinbaseClient coinbaseClient) {
|
||||
executor.scheduleAtFixedRate(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
CurrentPayment currentPayment = createCurrentPaymentForBalance(coinbaseClient);
|
||||
cachedPaymentStatus.set(currentPayment);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Failed to update badge", e);
|
||||
}
|
||||
}
|
||||
}, UPDATE_FREQUENCY_MILLIS, UPDATE_FREQUENCY_MILLIS, TimeUnit.MILLISECONDS);
|
||||
|
||||
executor.scheduleAtFixedRate(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
RecentTransactionsView view = createRecentTransactionsView(coinbaseClient);
|
||||
cachedTransactions.set(view);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Failed to update recent transactions", e);
|
||||
}
|
||||
}
|
||||
}, UPDATE_FREQUENCY_MILLIS, UPDATE_FREQUENCY_MILLIS, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private class CurrentPayment {
|
||||
private final byte[] badge;
|
||||
private final byte[] smallBadge;
|
||||
private final Payment entity;
|
||||
|
||||
private CurrentPayment(byte[] badge, byte[] smallBadge, Payment entity) {
|
||||
this.badge = badge;
|
||||
this.smallBadge = smallBadge;
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
private byte[] getBadge() {
|
||||
return badge;
|
||||
}
|
||||
|
||||
private byte[] getSmallBadge() {
|
||||
return smallBadge;
|
||||
}
|
||||
|
||||
private Payment getEntity() {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.controllers;
|
||||
|
||||
public class UnauthorizedHookException extends Throwable {
|
||||
public UnauthorizedHookException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
34
src/main/java/org/whispersystems/bithub/entities/Amount.java
Normal file
34
src/main/java/org/whispersystems/bithub/entities/Amount.java
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Amount {
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private String amount;
|
||||
|
||||
public String getAmount() {
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
|
||||
48
src/main/java/org/whispersystems/bithub/entities/Author.java
Normal file
48
src/main/java/org/whispersystems/bithub/entities/Author.java
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Author {
|
||||
|
||||
@JsonProperty
|
||||
private String name;
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private String email;
|
||||
|
||||
@JsonProperty
|
||||
private String username;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class BalanceResponse {
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private String amount;
|
||||
|
||||
public String getAmount() {
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class BitcoinTransaction {
|
||||
|
||||
@JsonProperty
|
||||
private String to;
|
||||
|
||||
@JsonProperty
|
||||
private String amount;
|
||||
|
||||
@JsonProperty
|
||||
private String notes;
|
||||
|
||||
public BitcoinTransaction(String to, String amount, String notes) {
|
||||
this.to = to;
|
||||
this.amount = amount;
|
||||
this.notes = notes;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class BitcoinTransactionResponse {
|
||||
|
||||
private boolean success;
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
}
|
||||
63
src/main/java/org/whispersystems/bithub/entities/Commit.java
Normal file
63
src/main/java/org/whispersystems/bithub/entities/Commit.java
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Commit {
|
||||
|
||||
@JsonProperty
|
||||
private String id;
|
||||
|
||||
@JsonProperty
|
||||
private String message;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private Author author;
|
||||
|
||||
@JsonProperty
|
||||
private String url;
|
||||
|
||||
@JsonProperty
|
||||
private boolean distinct;
|
||||
|
||||
public String getSha() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Author getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public boolean isDistinct() {
|
||||
return distinct;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class CommitComment {
|
||||
|
||||
@JsonProperty
|
||||
private String body;
|
||||
|
||||
public CommitComment(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class ExchangeRate {
|
||||
|
||||
@NotEmpty
|
||||
@JsonProperty
|
||||
private String btc_to_usd;
|
||||
|
||||
public String getBtc_to_usd() {
|
||||
return btc_to_usd;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class Payment {
|
||||
@JsonProperty
|
||||
private String payment;
|
||||
|
||||
public Payment(String payment) {
|
||||
this.payment = payment;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class PushEvent {
|
||||
|
||||
@JsonProperty
|
||||
private String head;
|
||||
|
||||
@JsonProperty
|
||||
private String ref;
|
||||
|
||||
@JsonProperty
|
||||
private int size;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
List<Commit> commits;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
Repository repository;
|
||||
|
||||
public Repository getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public String getHead() {
|
||||
return head;
|
||||
}
|
||||
|
||||
public String getRef() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public List<Commit> getCommits() {
|
||||
return commits;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class RecentTransactionsResponse {
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private List<TransactionWrapper> transactions;
|
||||
|
||||
public List<Transaction> getTransactions() {
|
||||
List<Transaction> rawTransactions = new LinkedList<Transaction>();
|
||||
|
||||
for (TransactionWrapper transactionWrapper : transactions) {
|
||||
rawTransactions.add(transactionWrapper.getTransaction());
|
||||
}
|
||||
|
||||
return rawTransactions;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Repository {
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private String url;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private Author owner;
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private String name;
|
||||
|
||||
public Author getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Transaction {
|
||||
|
||||
@JsonProperty(value = "created_at")
|
||||
@NotEmpty
|
||||
private String createdTime;
|
||||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private Amount amount;
|
||||
|
||||
@JsonProperty(value = "recipient_address")
|
||||
private String recipientAddress;
|
||||
|
||||
@JsonProperty
|
||||
private String notes;
|
||||
|
||||
public String getCreatedTime() {
|
||||
return createdTime;
|
||||
}
|
||||
|
||||
public String getAmount() {
|
||||
return amount.getAmount();
|
||||
}
|
||||
|
||||
public String getRecipientAddress() {
|
||||
return recipientAddress;
|
||||
}
|
||||
|
||||
public String getNotes() {
|
||||
return notes;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class TransactionWrapper {
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
private Transaction transaction;
|
||||
|
||||
public Transaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package org.whispersystems.bithub.filters;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class CorsHeaderFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
if (response instanceof HttpServletResponse) {
|
||||
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
httpServletResponse.addHeader("Access-Control-Allow-Origin", "*");
|
||||
if ("OPTIONS".equals(httpServletRequest.getMethod())) {
|
||||
httpServletResponse.addHeader("Access-Control-Allow-Headers", "Content-Type");
|
||||
}
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.mappers;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
import java.io.IOException;
|
||||
|
||||
@Provider
|
||||
public class IOExceptionMapper implements ExceptionMapper<IOException> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(IOExceptionMapper.class);
|
||||
|
||||
@Override
|
||||
public Response toResponse(IOException e) {
|
||||
logger.warn("IOExceptionMapper", e);
|
||||
return Response.status(503).build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.mappers;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.bithub.controllers.UnauthorizedHookException;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
@Provider
|
||||
public class UnauthorizedHookExceptionMapper implements ExceptionMapper<UnauthorizedHookException> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(IOExceptionMapper.class);
|
||||
|
||||
@Override
|
||||
public Response toResponse(UnauthorizedHookException e) {
|
||||
logger.warn("IOExceptionMapper", e);
|
||||
return Response.status(401).build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.util;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class AdvancedAtomicLong extends AtomicLong {
|
||||
|
||||
public AdvancedAtomicLong(long initial) {
|
||||
super(initial);
|
||||
}
|
||||
|
||||
public boolean setIfGreater(long compare, long update) {
|
||||
while(true) {
|
||||
long current = get();
|
||||
|
||||
if (compare > current) {
|
||||
if (compareAndSet(current, update)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
65
src/main/java/org/whispersystems/bithub/util/Badge.java
Normal file
65
src/main/java/org/whispersystems/bithub/util/Badge.java
Normal file
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.util;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Badge {
|
||||
|
||||
public static byte[] createFor(String price) throws IOException {
|
||||
byte[] badgeBackground = Resources.toByteArray(Resources.getResource("assets/badge.png"));
|
||||
BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(badgeBackground));
|
||||
Graphics2D graphics = bufferedImage.createGraphics();
|
||||
|
||||
graphics.setFont(new Font("OpenSans", Font.PLAIN, 34));
|
||||
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
|
||||
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
|
||||
graphics.drawString(price + " USD", 86, 45);
|
||||
graphics.dispose();
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(bufferedImage, "png", baos);
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
public static byte[] createSmallFor(String price) throws IOException {
|
||||
byte[] badgeBackground = Resources.toByteArray(Resources.getResource("assets/badge-small.png"));
|
||||
BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(badgeBackground));
|
||||
Graphics2D graphics = bufferedImage.createGraphics();
|
||||
|
||||
graphics.setFont(new Font("OpenSans", Font.PLAIN, 9));
|
||||
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
|
||||
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
|
||||
graphics.drawString(price + " USD", 22, 14);
|
||||
graphics.dispose();
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ImageIO.write(bufferedImage, "png", baos);
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.views;
|
||||
|
||||
import com.yammer.dropwizard.views.View;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.bithub.entities.Transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A rendered HTML view of recent BitHub transactions.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*/
|
||||
public class RecentTransactionsView extends View {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RecentTransactionsView.class);
|
||||
private final List<TransactionView> transactions = new LinkedList<>();
|
||||
|
||||
public RecentTransactionsView(List<Transaction> recentTransactions, BigDecimal exchangeRate) {
|
||||
super("recent_transactions.mustache");
|
||||
|
||||
for (Transaction transaction : recentTransactions) {
|
||||
try {
|
||||
if (isSentTransaction(transaction)) {
|
||||
transactions.add(new TransactionView(exchangeRate,
|
||||
transaction.getAmount(),
|
||||
transaction.getCreatedTime(),
|
||||
transaction.getNotes()));
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
logger.warn("Error parsing: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSentTransaction(Transaction transaction) {
|
||||
BigDecimal amount = new BigDecimal(transaction.getAmount());
|
||||
return amount.compareTo(new BigDecimal(0.0)) < 0;
|
||||
}
|
||||
|
||||
public List<TransactionView> getTransactions() {
|
||||
return transactions;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.views;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.ocpsoft.prettytime.PrettyTime;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
/**
|
||||
* A rendered HTML view of an individual BitHub transaction.
|
||||
*
|
||||
* @author Moxie Marlinspike
|
||||
*/
|
||||
public class TransactionView {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RecentTransactionsView.class);
|
||||
|
||||
private final String destination;
|
||||
private final String amount;
|
||||
private final String commitUrl;
|
||||
private final String commitSha;
|
||||
private final String timestamp;
|
||||
|
||||
public TransactionView(BigDecimal exchangeRate, String amount,
|
||||
String timestamp, String message)
|
||||
throws ParseException
|
||||
{
|
||||
this.amount = getAmountInDollars(exchangeRate, amount);
|
||||
this.destination = parseDestinationFromMessage(message);
|
||||
this.timestamp = parseTimestamp(timestamp);
|
||||
this.commitUrl = parseUrlFromMessage(message);
|
||||
this.commitSha = parseShaFromUrl(commitUrl);
|
||||
}
|
||||
|
||||
private String getAmountInDollars(BigDecimal exchangeRate, String amount) {
|
||||
return new BigDecimal(amount).abs()
|
||||
.multiply(exchangeRate)
|
||||
.setScale(2, RoundingMode.CEILING)
|
||||
.toPlainString();
|
||||
}
|
||||
|
||||
private String parseTimestamp(String timestamp) throws ParseException {
|
||||
int offendingColon = timestamp.lastIndexOf(':');
|
||||
String fixedTimestamp = timestamp.substring(0, offendingColon) + timestamp.substring(offendingColon + 1);
|
||||
return new PrettyTime().format(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(fixedTimestamp));
|
||||
}
|
||||
|
||||
private String parseDestinationFromMessage(String message) {
|
||||
message = StringEscapeUtils.unescapeHtml4(message);
|
||||
int startToken = message.indexOf("__");
|
||||
|
||||
if (startToken == -1) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
int endToken = message.indexOf("__", startToken + 1);
|
||||
|
||||
if (endToken == -1) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
return message.substring(startToken+2, endToken);
|
||||
}
|
||||
|
||||
private String parseUrlFromMessage(String message) throws ParseException {
|
||||
message = StringEscapeUtils.unescapeHtml4(message);
|
||||
int urlIndex = message.indexOf("https://");
|
||||
|
||||
return message.substring(urlIndex).trim();
|
||||
}
|
||||
|
||||
private String parseShaFromUrl(String url) throws ParseException {
|
||||
if (url == null) {
|
||||
throw new ParseException("No url", 0);
|
||||
}
|
||||
|
||||
String[] parts = url.split("/");
|
||||
String fullHash = parts[parts.length-1];
|
||||
|
||||
if (fullHash.length() < 8) {
|
||||
throw new ParseException("Not long enough", 0);
|
||||
}
|
||||
|
||||
return fullHash.substring(0, 8);
|
||||
}
|
||||
|
||||
public String getDestination() {
|
||||
return destination;
|
||||
}
|
||||
|
||||
public String getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public String getCommitUrl() {
|
||||
return commitUrl;
|
||||
}
|
||||
|
||||
public String getCommitSha() {
|
||||
return commitSha;
|
||||
}
|
||||
|
||||
public String getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
src/main/resources/assets/badge-small.png
Normal file
BIN
src/main/resources/assets/badge-small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 705 B |
BIN
src/main/resources/assets/badge.png
Normal file
BIN
src/main/resources/assets/badge.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
10
src/main/resources/banner.txt
Normal file
10
src/main/resources/banner.txt
Normal file
@ -0,0 +1,10 @@
|
||||
888888b. d8b 888 888 888 888
|
||||
888 "88b Y8P 888 888 888 888
|
||||
888 .88P 888 888 888 888
|
||||
8888888K. 888 888888 8888888888 888 888 88888b.
|
||||
888 "Y88b 888 888 888 888 888 888 888 "88b
|
||||
888 888 888 888 888 888 888 888 888 888
|
||||
888 d88P 888 Y88b. 888 888 Y88b 888 888 d88P
|
||||
8888888P" 888 "Y888 888 888 "Y88888 88888P"
|
||||
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src none">
|
||||
<meta charset="utf-8">
|
||||
|
||||
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
letter-spacing: .01rem;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-size: 22px;
|
||||
line-height: 1.5;
|
||||
color: #333332;
|
||||
}
|
||||
|
||||
sha {
|
||||
font-family: Consolas, "Liberation Mono", Courier, monospace;
|
||||
color: #4183c4;
|
||||
|
||||
margin: 2px;
|
||||
padding: 3px;
|
||||
border: 1px solid #ddd;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 3px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
ul { list-style-type: none;}
|
||||
|
||||
li { padding: 5px; padding-left: 20px;}
|
||||
|
||||
li:nth-child(even) { background-color: #ffffff; }
|
||||
li:nth-child(odd) { background-color: #eeeeee;}
|
||||
|
||||
a { text-decoration: none; color: #555; }
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<ul>
|
||||
{{#transactions}}
|
||||
<li>Sent ${{amount}} USD to <a href="https://github.com/{{destination}}">{{destination}}</a> for <a href="{{commitUrl}}"><sha>{{commitSha}}</sha></a> {{timestamp}}.</li>
|
||||
{{/transactions}}
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.whispersystems.bithub.tests.controllers;
|
||||
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.core.util.MultivaluedMapImpl;
|
||||
import com.yammer.dropwizard.testing.ResourceTest;
|
||||
import org.junit.Test;
|
||||
import org.whispersystems.bithub.client.CoinbaseClient;
|
||||
import org.whispersystems.bithub.client.GithubClient;
|
||||
import org.whispersystems.bithub.client.TransferFailedException;
|
||||
import org.whispersystems.bithub.controllers.GithubController;
|
||||
import org.whispersystems.bithub.entities.Author;
|
||||
import org.whispersystems.bithub.mappers.UnauthorizedHookExceptionMapper;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class GithubControllerTest extends ResourceTest {
|
||||
|
||||
private static final BigDecimal BALANCE = new BigDecimal(10.01);
|
||||
|
||||
private final CoinbaseClient coinbaseClient = mock(CoinbaseClient.class);
|
||||
private final GithubClient githubClient = mock(GithubClient.class);
|
||||
|
||||
private final List<String> repositories = new LinkedList<String>() {{
|
||||
add("https://github.com/moxie0/test");
|
||||
}};
|
||||
|
||||
@Override
|
||||
protected void setUpResources() throws Exception {
|
||||
when(coinbaseClient.getAccountBalance()).thenReturn(BALANCE);
|
||||
addResource(new GithubController(repositories, githubClient, coinbaseClient, new BigDecimal(0.02)));
|
||||
addProvider(new UnauthorizedHookExceptionMapper());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidRepository() throws Exception {
|
||||
String payloadValue = "{\"ref\":\"refs/heads/master\",\"after\":\"100e9859651b35a3505cc278e9a98a076f79940b\",\"before\":\"6626766348ab245bdb3351989f753bd6e792524a\",\"created\":false,\"deleted\":false,\"forced\":false,\"compare\":\"https://github.com/moxie0/tempt/compare/6626766348ab...100e9859651b\",\"commits\":[{\"id\":\"fd7daeb1de6d72220b1313a7f1112d43885013aa\",\"distinct\":true,\"message\":\"Update foo\",\"timestamp\":\"2013-12-14T11:27:00-08:00\",\"url\":\"https://github.com/moxie0/tempt/commit/fd7daeb1de6d72220b1313a7f1112d43885013aa\",\"author\":{\"name\":\"WhisperBTC\",\"email\":\"info@whispersystems.org\",\"username\":\"WhisperBTC\"},\"committer\":{\"name\":\"WhisperBTC\",\"email\":\"info@whispersystems.org\",\"username\":\"WhisperBTC\"},\"added\":[],\"removed\":[],\"modified\":[\"foo\"]},{\"id\":\"100e9859651b35a3505cc278e9a98a076f79940b\",\"distinct\":true,\"message\":\"Merge pull request #2 from WhisperBTC/patch-2\\n\\nUpdate foo\",\"timestamp\":\"2013-12-14T11:27:28-08:00\",\"url\":\"https://github.com/moxie0/tempt/commit/100e9859651b35a3505cc278e9a98a076f79940b\",\"author\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"committer\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"added\":[],\"removed\":[],\"modified\":[\"foo\"]}],\"head_commit\":{\"id\":\"100e9859651b35a3505cc278e9a98a076f79940b\",\"distinct\":true,\"message\":\"Merge pull request #2 from WhisperBTC/patch-2\\n\\nUpdate foo\",\"timestamp\":\"2013-12-14T11:27:28-08:00\",\"url\":\"https://github.com/moxie0/tempt/commit/100e9859651b35a3505cc278e9a98a076f79940b\",\"author\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"committer\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"added\":[],\"removed\":[],\"modified\":[\"foo\"]},\"repository\":{\"id\":15141344,\"name\":\"tempt\",\"url\":\"https://github.com/moxie0/tempt\",\"description\":\"test\",\"watchers\":1,\"stargazers\":1,\"forks\":1,\"fork\":false,\"size\":216,\"owner\":{\"name\":\"moxie0\",\"email\":\"moxie@thoughtcrime.org\"},\"private\":false,\"open_issues\":0,\"has_issues\":true,\"has_downloads\":true,\"has_wiki\":true,\"created_at\":1386866024,\"pushed_at\":1387049248,\"master_branch\":\"master\"},\"pusher\":{\"name\":\"moxie0\",\"email\":\"moxie@thoughtcrime.org\"}}";
|
||||
MultivaluedMapImpl post = new MultivaluedMapImpl();
|
||||
post.add("payload", payloadValue);
|
||||
ClientResponse response = client().resource("/v1/github/commits/")
|
||||
.header("X-Forwarded-For", "192.30.252.1")
|
||||
.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
|
||||
.post(ClientResponse.class, post);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(401);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidOrigin() throws Exception {
|
||||
String payloadValue = "{\"ref\":\"refs/heads/master\",\"after\":\"100e9859651b35a3505cc278e9a98a076f79940b\",\"before\":\"6626766348ab245bdb3351989f753bd6e792524a\",\"created\":false,\"deleted\":false,\"forced\":false,\"compare\":\"https://github.com/moxie0/tempt/compare/6626766348ab...100e9859651b\",\"commits\":[{\"id\":\"fd7daeb1de6d72220b1313a7f1112d43885013aa\",\"distinct\":true,\"message\":\"Update foo\",\"timestamp\":\"2013-12-14T11:27:00-08:00\",\"url\":\"https://github.com/moxie0/tempt/commit/fd7daeb1de6d72220b1313a7f1112d43885013aa\",\"author\":{\"name\":\"WhisperBTC\",\"email\":\"info@whispersystems.org\",\"username\":\"WhisperBTC\"},\"committer\":{\"name\":\"WhisperBTC\",\"email\":\"info@whispersystems.org\",\"username\":\"WhisperBTC\"},\"added\":[],\"removed\":[],\"modified\":[\"foo\"]},{\"id\":\"100e9859651b35a3505cc278e9a98a076f79940b\",\"distinct\":true,\"message\":\"Merge pull request #2 from WhisperBTC/patch-2\\n\\nUpdate foo\",\"timestamp\":\"2013-12-14T11:27:28-08:00\",\"url\":\"https://github.com/moxie0/tempt/commit/100e9859651b35a3505cc278e9a98a076f79940b\",\"author\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"committer\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"added\":[],\"removed\":[],\"modified\":[\"foo\"]}],\"head_commit\":{\"id\":\"100e9859651b35a3505cc278e9a98a076f79940b\",\"distinct\":true,\"message\":\"Merge pull request #2 from WhisperBTC/patch-2\\n\\nUpdate foo\",\"timestamp\":\"2013-12-14T11:27:28-08:00\",\"url\":\"https://github.com/moxie0/tempt/commit/100e9859651b35a3505cc278e9a98a076f79940b\",\"author\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"committer\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"added\":[],\"removed\":[],\"modified\":[\"foo\"]},\"repository\":{\"id\":15141344,\"name\":\"tempt\",\"url\":\"https://github.com/moxie0/test\",\"description\":\"test\",\"watchers\":1,\"stargazers\":1,\"forks\":1,\"fork\":false,\"size\":216,\"owner\":{\"name\":\"moxie0\",\"email\":\"moxie@thoughtcrime.org\"},\"private\":false,\"open_issues\":0,\"has_issues\":true,\"has_downloads\":true,\"has_wiki\":true,\"created_at\":1386866024,\"pushed_at\":1387049248,\"master_branch\":\"master\"},\"pusher\":{\"name\":\"moxie0\",\"email\":\"moxie@thoughtcrime.org\"}}";
|
||||
MultivaluedMapImpl post = new MultivaluedMapImpl();
|
||||
post.add("payload", payloadValue);
|
||||
ClientResponse response = client().resource("/v1/github/commits/")
|
||||
.header("X-Forwarded-For", "192.30.242.1")
|
||||
.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
|
||||
.post(ClientResponse.class, post);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(401);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidCommit() throws Exception, TransferFailedException {
|
||||
String payloadValue = "{\"ref\":\"refs/heads/master\",\"after\":\"bcf09f8b4a32921114587e4814a3f0849aa9900f\",\"before\":\"1b141aa068165dd1ed376f483cd5fdc2c64f32b1\",\"created\":false,\"deleted\":false,\"forced\":false,\"compare\":\"https://github.com/moxie0/tempt/compare/1b141aa06816...bcf09f8b4a32\",\"commits\":[{\"id\":\"ba1b681c71db4fcd461954b1bf344bc6e29411e5\",\"distinct\":true,\"message\":\"Update path\",\"timestamp\":\"2013-12-14T11:42:28-08:00\",\"url\":\"https://github.com/moxie0/tempt/commit/ba1b681c71db4fcd461954b1bf344bc6e29411e5\",\"author\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"committer\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"added\":[],\"removed\":[],\"modified\":[\"README.md\"]},{\"id\":\"bcf09f8b4a32921114587e4814a3f0849aa9900f\",\"distinct\":true,\"message\":\"Merge branch 'master' of github.com:moxie0/tempt\",\"timestamp\":\"2013-12-14T11:42:44-08:00\",\"url\":\"https://github.com/moxie0/tempt/commit/bcf09f8b4a32921114587e4814a3f0849aa9900f\",\"author\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"committer\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"added\":[],\"removed\":[],\"modified\":[]}],\"head_commit\":{\"id\":\"bcf09f8b4a32921114587e4814a3f0849aa9900f\",\"distinct\":true,\"message\":\"Merge branch 'master' of github.com:moxie0/tempt\",\"timestamp\":\"2013-12-14T11:42:44-08:00\",\"url\":\"https://github.com/moxie0/tempt/commit/bcf09f8b4a32921114587e4814a3f0849aa9900f\",\"author\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"committer\":{\"name\":\"Moxie Marlinspike\",\"email\":\"moxie@thoughtcrime.org\",\"username\":\"moxie0\"},\"added\":[],\"removed\":[],\"modified\":[]},\"repository\":{\"id\":15141344,\"name\":\"tempt\",\"url\":\"https://github.com/moxie0/test\",\"description\":\"test\",\"watchers\":1,\"stargazers\":1,\"forks\":1,\"fork\":false,\"size\":216,\"owner\":{\"name\":\"moxie0\",\"email\":\"moxie@thoughtcrime.org\"},\"private\":false,\"open_issues\":0,\"has_issues\":true,\"has_downloads\":true,\"has_wiki\":true,\"created_at\":1386866024,\"pushed_at\":1387050173,\"master_branch\":\"master\"},\"pusher\":{\"name\":\"moxie0\",\"email\":\"moxie@thoughtcrime.org\"}}";
|
||||
MultivaluedMapImpl post = new MultivaluedMapImpl();
|
||||
post.add("payload", payloadValue);
|
||||
ClientResponse response = client().resource("/v1/github/commits/")
|
||||
.header("X-Forwarded-For", "192.30.252.1")
|
||||
.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE)
|
||||
.post(ClientResponse.class, post);
|
||||
|
||||
verify(coinbaseClient).sendPayment(any(Author.class),
|
||||
eq(BALANCE.multiply(new BigDecimal(0.02))),
|
||||
anyString());
|
||||
}
|
||||
|
||||
}
|
||||
1
system.properties
Normal file
1
system.properties
Normal file
@ -0,0 +1 @@
|
||||
java.runtime.version=1.7
|
||||
Loading…
Reference in New Issue
Block a user