Skip to content

Commit

Permalink
Merge branch 'release/0.4.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
micheljung committed Apr 5, 2017
2 parents cf8daf3 + 95807c7 commit 46fe70f
Show file tree
Hide file tree
Showing 29 changed files with 527 additions and 103 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@ gradle-app.setting

# temporary server files
tmp
static/
1 change: 1 addition & 0 deletions .idea/codeStyleSettings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ before_install:
- curl -L https://github.com/docker/compose/releases/download/1.10.0/docker-compose-`uname -s`-`uname -m` > /tmp/docker-compose
- chmod +x /tmp/docker-compose
- sudo mv /tmp/docker-compose /usr/local/bin
- curl -sL https://github.com/jpm4j/jpm4j.installers/raw/master/dist/biz.aQute.jpm.run.jar >jpm4j.jar
- java -jar jpm4j.jar -u init
- ~/jpm/bin/jpm install com.codacy:codacy-coverage-reporter:assembly

install:
- git clone https://github.com/FAForever/faf-stack.git
Expand All @@ -30,7 +27,7 @@ after_success:
./gradlew pushDockerImage;
fi
- if [ "${TRAVIS_BRANCH}" == "develop" ]; then
~/jpm/bin/codacy-coverage-reporter -l Java -r build/reports/jacoco/test/jacocoTestReport.xml;
./gradlew jacocoTestReport sendCoverageToCodacy;
fi

before_cache:
Expand Down
17 changes: 16 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ apply plugin: 'org.springframework.boot'
apply plugin: 'propdeps'

group = 'micheljung'
version = '0.3.6'
version = '0.4.0'

sourceCompatibility = 1.8
targetCompatibility = 1.8
Expand All @@ -26,6 +26,7 @@ repositories {
mavenCentral()
maven { url "http://repo.jenkins-ci.org/public/" }
maven { url "https://jitpack.io" }
maven { url "http://dl.bintray.com/typesafe/maven-releases" }
}

compileJava.dependsOn(processResources)
Expand Down Expand Up @@ -57,6 +58,18 @@ jacocoTestReport {
}
}

// CODACY

configurations {
codacy
}

task sendCoverageToCodacy(type: JavaExec, dependsOn: jacocoTestReport) {
main = "com.codacy.CodacyCoverageReporter"
classpath = configurations.codacy
args = ["-l", "Java", "-r", "${buildDir}/reports/jacoco/test/jacocoTestReport.xml"]
}

// DOCKER

apply plugin: 'com.bmuschko.docker-remote-api'
Expand Down Expand Up @@ -180,4 +193,6 @@ dependencies {
testCompile("org.springframework.restdocs:spring-restdocs-mockmvc")
testCompile("org.springframework.security:spring-security-test")
testCompile("com.h2database:h2:${h2Version}")

codacy("com.github.codacy:codacy-coverage-reporter:-SNAPSHOT")
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ junitAddonsVersion=1.4
zohhakVersion=1.1.1
githubApiVersion=1.84
jgitVersionn=4.5.0.201609210915-r
fafCommonsVersion=76fb583d146082f3db68c0bece38a3ee28ead8e6
fafCommonsVersion=81da093d61b937a2433a5c14d39cf66c4b5f20f2
h2Version=1.4.193
jacksonDatatypeJsr310Version=2.8.6
mockitoVersion=2.7.0
62 changes: 18 additions & 44 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,47 @@
# Spring Boot based FAF-API Prototype

#### master
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/12eecd69a3cf4f6c96ffa043a7d70198)](https://www.codacy.com/app/micheljung/faf-java-api?utm_source=github.com&utm_medium=referral&utm_content=micheljung/faf-java-api&utm_campaign=badger)
[![Build Status](https://travis-ci.org/FAForever/downlords-faf-client.svg?branch=master)](https://travis-ci.org/FAForever/downlords-faf-client)

#### develop
[![Build Status](https://travis-ci.org/FAForever/faf-java-api.svg?branch=master)](https://travis-ci.org/FAForever/faf-java-api)
[![Coverage Status](https://coveralls.io/repos/github/FAForever/faf-java-api/badge.svg?branch=develop)](https://coveralls.io/github/FAForever/faf-java-api?branch=develop)

This is a prototype of a Spring Boot based API application for Forged Alliance Forever.

## How to run from source
## How to run

### From source

In order to run the application from source code:

1. Clone the repository
1. Import the project into IntelliJ
1. Import the project into IntelliJ. For some reason, IntelliJ deletes launch configurations after import. Please revert such deleted files first (Version Control (Alt+F9) -> Local Changes)
1. Configure your JDK 8 if you haven't already
1. Make sure you have the _IntelliJ Lombok plugin_ installed
1. Set up a [FAF database](https://github.com/FAForever/db).
1. Launch `FafApiApplication`

## How to run from binary

Either check out the source code and execute the run configuration `FafApiApplication`, or run it directly
from the published Docker image like so:

```
docker run --name faf-api \
-e DATABASE_ADDRESS=faf-db:3306 \
-e DATABASE_USERNAME=root \
-e DATABASE_PASSWORD=banana \
-e DATABASE_NAME=faf_test \
-e JWT_SECRET=banana \
--link faf-db
-d micheljung/faf-api:0.2.0-SNAPSHOT
```

To run in production, you probably want to create an environment file (e.g. `env.list`):
### From binary

```
DATABASE_ADDRESS=stable_faf-db_1
DATABASE_USERNAME=faf_lobby
DATABASE_PASSWORD=password
DATABASE_NAME=faf_lobby
API_PROFILE=prod
JWT_SECRET=<your secret>
```
Given the number of required configuration values, it's easiest to run the API using `faf-stack`:

And run with:
```
docker run --name faf-api \
--env-file ./env.list \
-d micheljung/faf-api:0.2.0-SNAPSHOT
```
docker-compose up -d faf-api

## Sample routes

* [List event definitions](http://localhost:8080/data/event_definition)
* [List 5 maps with more than 8 players](http://localhost:8080/data/map_version?filter=(maxPlayers=gt=8)&page[size]=5)
* [List UI mods, sorted by last updated ascending](http://localhost:8080/data/mod_version?filter=(type=='UI')&sort=-updateTime)
* List players: [http://localhost:8080/data/player](http://localhost:8080/data/player)
* List player events: [http://localhost:8080/data/player_event](http://localhost:8080/data/player_event)
* List replays: [http://localhost:8080/data/map](http://localhost:8080/data/map)
* API documentation: [http://localhost:8080/swagger-ui.html](http://localhost:8080/swagger-ui.html)
* [API documentation](http://localhost:8010)
* [List event definitions](http://localhost:8010/data/event)
* [List 5 maps with more than 8 players](http://localhost:8010/data/mapVersion?filter=(maxPlayers=gt=8)&page[size]=5)
* [List UI mods, sorted by last updated ascending](http://localhost:8010/data/modVersion?filter=(type=='UI')&sort=-updateTime)
* [List all players](http://localhost:8010/data/player)
* [List events of players](http://localhost:8010/data/playerEvent)
* [List replays](http://localhost:8010/data/game?filter=(endTime=isnull=true))

## Technology Stack

This project uses:

* Java 8 as the programming language
* [Spring Boot](https://projects.spring.io/spring-boot/) as a framework
* [Hibernate ORM](http://hibernate.org/orm/) as ORM mapper
* [Elide](http://elide.io/) to serve [JSON-API](http://jsonapi.org/) conform data
* [Gradle](https://gradle.org/) as a build automation tool
* [Docker](https://www.docker.com/) to deploy and run the application
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/com/faforever/api/config/FafApiProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public static class Map {
*/
private String downloadUrlFormat;
/**
* The directory in which map files are stored.
* The directory in which uploaded map files are stored.
*/
private Path targetDirectory = Paths.get("static/maps");
/**
Expand Down Expand Up @@ -93,6 +93,14 @@ public static class Map {
public static class Mod {
private String previewUrlFormat;
private String downloadUrlFormat;
/** Allowed file extensions of uploaded mods. */
private String[] allowedExtensions = {"zip"};
/** The directory in which uploaded mod files are stored. */
private Path targetDirectory = Paths.get("static/mods");
/** The directory in which thumbnails of uploaded mod files are stored. */
private Path thumbnailTargetDirectory = Paths.get("static/mod_thumbnails");
/** The maximum allowed length of a mod's name. */
private int maxNameLength = 100;
}

@Data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.Map;

@ControllerAdvice
// TODO implement a proper JSON-API error response
class GlobalControllerExceptionHandler {
private Map<String, Serializable> errorResponse(String title, String message) {
ImmutableMap<String, Serializable> error = ImmutableMap.of(
Expand All @@ -33,19 +34,14 @@ public Map<String, Serializable> processValidationException(ValidationException
return errorResponse(ErrorCode.VALIDATION_FAILED.getTitle(), ex.getMessage());
}

private String replaceArgs(String message, Object[] args) {
// http://stackoverflow.com/a/10995800
return MessageFormat.format(message.replace("'", "''"), args);
}

@ExceptionHandler(ApiException.class)
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
@ResponseBody
public ErrorResponse processApiException(ApiException ex) {
ErrorResponse response = new ErrorResponse();
Arrays.stream(ex.getErrors()).forEach(error -> {
ErrorCode code = error.getErrorCode();
response.addError(new ErrorResult(code.getTitle(), replaceArgs(code.getDetail(), error.getArgs())));
response.addError(new ErrorResult(code.getTitle(), MessageFormat.format(code.getDetail(), error.getArgs())));
});
return response;
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/faforever/api/data/domain/Map.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
Expand Down Expand Up @@ -47,7 +48,7 @@ public class Map {
private MapVersion latestVersion;

@Id
@GeneratedValue
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
public int getId() {
return id;
Expand Down
27 changes: 25 additions & 2 deletions src/main/java/com/faforever/api/data/domain/Mod.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@

import com.yahoo.elide.annotation.Include;
import lombok.Setter;
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.JoinColumnOrFormula;
import org.hibernate.annotations.JoinColumnsOrFormulas;
import org.hibernate.annotations.JoinFormula;
import org.hibernate.validator.constraints.NotEmpty;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.OffsetDateTime;
import java.util.List;

Expand All @@ -31,34 +40,48 @@ public class Mod {
private OffsetDateTime updateTime;
private List<ModVersion> versions;
private ModVersion latestVersion;
private Player uploader;

@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer getId() {
return id;
}

@Column(name = "display_name")
@Size(max = 100)
@NotNull
public String getDisplayName() {
return displayName;
}

@Column(name = "author")
@Size(max = 100)
@NotNull
public String getAuthor() {
return author;
}

@ManyToOne
@JoinColumn(name = "uploader")
public Player getUploader() {
return uploader;
}

@Column(name = "create_time")
public OffsetDateTime getCreateTime() {
return createTime;
}

@Column(name = "update_time")
@Formula(value = "(SELECT mod_version.update_time FROM mod_version WHERE mod_version.mod_id = id ORDER BY mod_version.version DESC LIMIT 1)")
public OffsetDateTime getUpdateTime() {
return updateTime;
}

@OneToMany(mappedBy = "mod")
@OneToMany(mappedBy = "mod", cascade = CascadeType.ALL, orphanRemoval = true)
@NotEmpty
@Valid
public List<ModVersion> getVersions() {
return versions;
}
Expand Down
13 changes: 11 additions & 2 deletions src/main/java/com/faforever/api/data/domain/ModVersion.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package com.faforever.api.data.domain;

import com.faforever.api.data.listeners.ModVersionEnricher;
import com.yahoo.elide.annotation.ComputedAttribute;
import com.yahoo.elide.annotation.Exclude;
import com.yahoo.elide.annotation.Include;
import lombok.Setter;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
Expand All @@ -19,9 +24,10 @@
@Table(name = "mod_version")
@Include(rootLevel = true, type = "modVersion")
@Setter
@EntityListeners(ModVersionEnricher.class)
public class ModVersion {

private int id;
private Integer id;
private String uid;
private ModType type;
private String description;
Expand All @@ -38,7 +44,8 @@ public class ModVersion {

@Id
@Column(name = "id")
public int getId() {
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer getId() {
return id;
}

Expand Down Expand Up @@ -69,6 +76,8 @@ public String getFilename() {
}

@Column(name = "icon")
// Excluded since I see no reason why this is even stored in the database.
@Exclude
public String getIcon() {
return icon;
}
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/faforever/api/data/domain/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@ public Clan getClan() {
}
return null;
}

@Override
public String toString() {
return "Player(" + getId() + ", " + getLogin() + ")";
}
}

0 comments on commit 46fe70f

Please sign in to comment.