Added basic CRUD operations

This commit is contained in:
2025-12-11 19:07:30 +01:00
parent 7c93577132
commit 46f2cf2d55
30 changed files with 615 additions and 139 deletions

60
api/compose-dev.yaml Normal file
View File

@@ -0,0 +1,60 @@
services:
boardmate-api:
build: .
container_name: boardmate-api
ports:
- "8000:8080"
- "5005:5005"
environment:
SPRING_DATA_MONGODB_URI: "mongodb://board-mate-user:apx820kcng@mongodb:27017/board-mate-db"
JAVA_TOOL_OPTIONS: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"
depends_on:
- mongodb
- elasticsearch
elasticsearch:
image: 'docker.elastic.co/elasticsearch/elasticsearch:7.17.10'
environment:
- 'ELASTIC_PASSWORD=secret'
- 'discovery.type=single-node'
- 'xpack.security.enabled=false'
ports:
- '8100:9200'
- '8101:9300'
grafana-lgtm:
image: 'grafana/otel-lgtm:latest'
ports:
- '8200:3000'
- '8201:4317'
- '8202:4318'
prometheus:
image: 'prom/prometheus:latest'
container_name: prometheus
ports:
- "8300:9090"
volumes:
- ./prometheus.yaml:/etc/prometheus/prometheus.yml
mongodb:
image: mongo:latest
environment:
- MONGO_INITDB_DATABASE=board-mate-db
- MONGO_INITDB_ROOT_PASSWORD=secret
- MONGO_INITDB_ROOT_USERNAME=root
ports:
- "8400:27017"
volumes:
- ./mongo-data:/data/db
- ./mongo-init:/docker-entrypoint-initdb.d
#mongo-express:
# image: mongo-express:latest
# depends_on:
# - mongodb
# ports:
# - "8401:8081"
# environment:
# - ME_CONFIG_MONGODB_SERVER=mongodb
# - ME_CONFIG_MONGODB_PORT=27017
# - ME_CONFIG_MONGODB_ADMINUSERNAME=root
# - ME_CONFIG_MONGODB_ADMINPASSWORD=secret
# - ME_CONFIG_MONGODB_AUTH_DATABASE=admin

View File

@@ -4,8 +4,6 @@ services:
container_name: boardmate-api
ports:
- "8000:8080"
environment:
- SPRING_BOOT_DOCKER_COMPOSE_TCP_CONNECT_PORT=8080
depends_on:
- mongodb
- elasticsearch
@@ -36,14 +34,14 @@ services:
mongodb:
image: mongo:latest
environment:
- MONGO_INITDB_DATABASE=my-database
- MONGO_INITDB_DATABASE=board-mate-db
- MONGO_INITDB_ROOT_PASSWORD=secret
- MONGO_INITDB_ROOT_USERNAME=root
ports:
- "8400:27017"
volumes:
- ./mongo-data:/data/db
- ./mongo-init:/docker-entrypoint-initdb.d
#mongo-express:
# image: mongo-express:latest
# depends_on:

View File

@@ -0,0 +1,11 @@
db = db.getSiblingDB("board-mate-db");
db.createCollection("games");
db.createUser({
user: "board-mate-user",
pwd: "apx820kcng",
roles: [
{ role: "readWrite", db: "board-mate-db" }
]
});

73
api/populate.json Normal file
View File

@@ -0,0 +1,73 @@
[{
"_id": {
"$oid": "6939d212ad0a2074aeab3a51"
},
"time": {
"increment": 0,
"value": 10
},
"players": {
"white": {
"name": "Aude Vaiselle"
},
"black": {
"name": "Nat Action"
}
},
"moves": [
"e4",
"c6",
"d4",
"d5"
]
},
{
"_id": {
"$oid": "6939d09fad0a2074aeab3a4f"
},
"time": {
"increment": 0,
"value": 10
},
"players": {
"white": {
"name": "Aude Vaiselle"
},
"black": {
"name": "Nat Action"
}
},
"moves": [
"d4",
"d5",
"c4",
"c6"
]
},
{
"_id": {
"$oid": "6939ce8cad0a2074aeab3a4e"
},
"time": {
"increment": 0,
"value": 10
},
"players": {
"white": {
"name": "Aude Vaiselle"
},
"black": {
"name": "Nat Action"
}
},
"moves": [
"e4",
"e5",
"kf3",
"kc6",
"d4"
]
}
]

View File

@@ -0,0 +1,11 @@
package be.naaturel.boardmateapi.common.exceptions;
public class RepoException extends Exception{
public RepoException(String message, Exception innerException){
super(message, innerException);
}
public RepoException(String message){
super(message);
}
}

View File

@@ -1,4 +1,4 @@
package be.naaturel.boardmateapi.models;
package be.naaturel.boardmateapi.common.models;
public class Coordinate {

View File

@@ -0,0 +1,72 @@
package be.naaturel.boardmateapi.common.models;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Game {
private final String whiteName;
private final String blackName;
private final int timeValue;
private final int increment;
private final List<Move> moves;
public Game(String whiteName, String blackName, int timeValue, int increment) {
this(whiteName, blackName, timeValue, increment, new ArrayList<>());
}
public Game(String whiteName, String blackName, int timeValue, int increment, Collection<Move> moves){
this.whiteName = whiteName;
this.blackName = blackName;
this.timeValue = timeValue;
this.increment = increment;
this.moves = new ArrayList<>(moves);
}
public String getBlackName() {
return blackName;
}
public String getWhiteName() {
return whiteName;
}
public int getTimeValue(){
return this.timeValue;
}
public int getIncrement(){
return this.increment;
}
public List<Move> getMoves() {
return new ArrayList<>(moves);
}
public void addMove(Move m) {
this.moves.add(m);
}
public String getPGN() {
StringBuilder builder = new StringBuilder();
int moveNumber = 1;
int movesInTurn = 1;
for(Move m : moves){
if(movesInTurn == 1) {
builder.append(String.format("%d.", moveNumber));
}
builder.append(String.format(" %s", m));
if(movesInTurn == 2){
moveNumber++;
movesInTurn = 1;
} else {
movesInTurn++;
}
}
return builder.toString();
}
}

View File

@@ -0,0 +1,46 @@
package be.naaturel.boardmateapi.common.models;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Set;
public class Move {
private final Piece piece;
private final Coordinate coordinate;
private final Set<MoveAttribute> attributes;
private final Piece promotionPiece;
public Move(Piece piece, Coordinate coordinate, Collection<MoveAttribute> attributes){
this(piece, null, coordinate, attributes);
}
public Move(Piece piece, Piece promotionPiece, Coordinate coordinate, Collection<MoveAttribute> attributes){
this.piece = piece;
this.coordinate = coordinate;
this.attributes = EnumSet.copyOf(attributes);
this.promotionPiece = promotionPiece;
}
private boolean hasAttribute(MoveAttribute attribute){
return attributes.contains(attribute);
}
@Override
public String toString(){
if(hasAttribute(MoveAttribute.CASTLE_KINGSIDE)) return "O-O";
if(hasAttribute(MoveAttribute.CASTLE_QUEENSIDE)) return "O-O-O";
StringBuilder bob = new StringBuilder();
bob.append(piece);
if(hasAttribute(MoveAttribute.CAPTURE)) bob.append("x");
bob.append(coordinate);
if(hasAttribute(MoveAttribute.PROMOTION) && promotionPiece != null) bob.append("=").append(promotionPiece);
if(hasAttribute(MoveAttribute.CHECKMATE)) bob.append("#");
else if(hasAttribute(MoveAttribute.CHECK)) bob.append("+");
return bob.toString();
}
}

View File

@@ -0,0 +1,11 @@
package be.naaturel.boardmateapi.common.models;
public enum MoveAttribute {
NORMAL,
CAPTURE,
CHECK,
CHECKMATE,
CASTLE_KINGSIDE,
CASTLE_QUEENSIDE,
PROMOTION
}

View File

@@ -0,0 +1,17 @@
package be.naaturel.boardmateapi.common.models;
public class Piece {
private String notation;
private String name;
public Piece(){
}
@Override
public String toString(){
return this.notation;
}
}

View File

@@ -15,4 +15,10 @@ public class AppConfigurations {
@Value("${sec.cors.authorizedHeader}")
public String[] authorizedHeaders;
@Value("${spring.data.mongodb.uri}")
public String connectionString;
@Value("${spring.data.mongodb.database}")
public String database;
}

View File

@@ -0,0 +1,23 @@
package be.naaturel.boardmateapi.configurations;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MongoConfig {
private final AppConfigurations conf;
@Autowired
public MongoConfig(AppConfigurations appConf) {
this.conf = appConf;
}
@Bean
public MongoClient mongoClient() {
return MongoClients.create(this.conf.connectionString);
}
}

View File

@@ -0,0 +1,23 @@
package be.naaturel.boardmateapi.configurations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Startup {
AppConfigurations conf;
@Autowired
public Startup(AppConfigurations appConf) {
this.conf = appConf;
}
@Bean
public String log(){
System.out.println(">>> Connection string : " + conf.connectionString);
System.out.println(">>> Database : " + conf.database);
return "logged";
}
}

View File

@@ -1,8 +1,8 @@
package be.naaturel.boardmateapi.controllers;
import be.naaturel.boardmateapi.models.Move;
import be.naaturel.boardmateapi.models.Party;
import be.naaturel.boardmateapi.services.PartyService;
import be.naaturel.boardmateapi.common.models.Move;
import be.naaturel.boardmateapi.common.models.Game;
import be.naaturel.boardmateapi.services.GameService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -11,18 +11,16 @@ import org.springframework.web.bind.annotation.*;
@RestController
public class PartyController {
private final PartyService service;
private final GameService service;
@Autowired
public PartyController(PartyService service){
public PartyController(GameService service){
this.service = service;
}
@GetMapping("/party/{id}")
public ResponseEntity<?> RetrieveParty(@PathVariable String id){
@GetMapping("/games/")
public ResponseEntity<?> retrieveAllGames(){
try{
service.retrieveParty(id);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.build();
@@ -31,10 +29,23 @@ public class PartyController {
}
}
@PostMapping("/create")
public ResponseEntity<?> CreateParty(@RequestBody Party party){
@GetMapping("/games/{id}")
public ResponseEntity<Game> retrieveGames(@PathVariable String id){
try{
service.create();
Game g = service.retrieveGame(id);
return ResponseEntity
.status(HttpStatus.OK)
.body(g);
} catch (Exception e){
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@PostMapping("/create")
public ResponseEntity<?> CreateParty(@RequestBody Game game){
try{
service.create(game);
return ResponseEntity.
status(HttpStatus.INTERNAL_SERVER_ERROR)
@@ -45,9 +56,9 @@ public class PartyController {
}
@PostMapping("/moves/add")
public ResponseEntity<?> AddMove(@RequestBody Move move){
public ResponseEntity<?> AddMove(@RequestBody String gameId, @RequestBody Move move){
try{
service.addMove();
service.addMove(gameId, move);
return ResponseEntity
.status(HttpStatus.OK)

View File

@@ -1,13 +0,0 @@
package be.naaturel.boardmateapi.models;
public class Move {
private Piece piece;
private Coordinate coordinate;
public Move(Piece piece, Coordinate coordinate){
this.piece = piece;
this.coordinate = coordinate;
}
}

View File

@@ -1,22 +0,0 @@
package be.naaturel.boardmateapi.models;
import java.util.ArrayList;
import java.util.List;
public class Party {
private final String whiteName;
private final String blackName;
private final int timeControl;
private final int increment;
private final List<Move> moves;
public Party(String whiteName, String blackName, int timeControl, int increment){
this.whiteName = whiteName;
this.blackName = blackName;
this.timeControl = timeControl;
this.increment = increment;
this.moves = new ArrayList<>();
}
}

View File

@@ -1,11 +0,0 @@
package be.naaturel.boardmateapi.models;
public class Piece {
public Piece(){
}
}

View File

@@ -1,9 +0,0 @@
package be.naaturel.boardmateapi.repository;
import be.naaturel.boardmateapi.models.Party;
public interface CustomPartyRepo {
public Party find(String id);
public String create();
public String registerMove();
}

View File

@@ -1,23 +0,0 @@
package be.naaturel.boardmateapi.repository;
import be.naaturel.boardmateapi.models.Party;
import org.springframework.stereotype.Repository;
@Repository
public class CustomPartyRepoImpl implements CustomPartyRepo{
@Override
public Party find(String id) {
return null;
}
@Override
public String create() {
return "";
}
@Override
public String registerMove() {
return "";
}
}

View File

@@ -0,0 +1,9 @@
package be.naaturel.boardmateapi.repository;
import be.naaturel.boardmateapi.repository.dtos.GameDto;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface GameRepo extends MongoRepository<GameDto, String> {
}

View File

@@ -1,11 +0,0 @@
package be.naaturel.boardmateapi.repository;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "parties")
public class PartyDto {
@Id
private String id;
private String[] moves;
}

View File

@@ -0,0 +1,60 @@
package be.naaturel.boardmateapi.repository.dtos;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@Document(collection = "games")
public class GameDto {
@Id
private String id;
@Field("players")
private Map<String, PlayerDto> players;
@Field("time")
private TimeControlDto timeControl;
@Field("moves")
private List<String> moves;
public String getId() {
return id;
}
public TimeControlDto getTimeControl() {
return timeControl;
}
public List<String> getMoves() {
return moves;
}
public Map<String, PlayerDto> getPlayers() {
return players;
}
public void setPlayers(Map<String, PlayerDto> players) {
this.players = players;
}
public void setTimeControl(TimeControlDto timeControl) {
this.timeControl = timeControl;
}
public void setMoves(Collection<String> moves) {
this.moves = new ArrayList<>(moves);
}
public void addPlayer(String color, PlayerDto player){
if(players.size() >= 2) return;
players.put(color, player);
}
public void addMove(String move){
moves.add(move);
}
}

View File

@@ -0,0 +1,17 @@
package be.naaturel.boardmateapi.repository.dtos;
import org.springframework.data.mongodb.core.mapping.Field;
public class PlayerDto {
@Field("name")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,26 @@
package be.naaturel.boardmateapi.repository.dtos;
import org.springframework.data.mongodb.core.mapping.Field;
public class TimeControlDto {
@Field("value")
private int value;
@Field("increment")
private int increment;
public int getValue() {
return value;
}
public int getIncrement() {
return increment;
}
public void setValue(int value) {
this.value = value;
}
public void setIncrement(int increment){
this.increment = increment;
}
}

View File

@@ -0,0 +1,45 @@
package be.naaturel.boardmateapi.repository.mappings;
import be.naaturel.boardmateapi.common.models.Game;
import be.naaturel.boardmateapi.common.models.Move;
import be.naaturel.boardmateapi.repository.dtos.GameDto;
import be.naaturel.boardmateapi.repository.dtos.PlayerDto;
import be.naaturel.boardmateapi.repository.dtos.TimeControlDto;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GameMapper {
public static GameDto toDto(Game game){
GameDto dto = new GameDto();
PlayerDto white = PlayerMapper.toDto(game.getWhiteName());
PlayerDto black = PlayerMapper.toDto(game.getBlackName());
Map<String, PlayerDto> players = new HashMap<>();
players.put("white", white);
players.put("black", black);
dto.setPlayers(players);
TimeControlDto timeControlDto = TimeControlMapper.toDto(game.getTimeValue(), game.getIncrement());
dto.setTimeControl(timeControlDto);
List<String> moves =
game.getMoves()
.stream()
.map(Move::toString)
.toList();
dto.setMoves(moves);
return dto;
}
public static Game toModel(GameDto dto){
String whiteName = dto.getPlayers().get("white").getName();
String blackName = dto.getPlayers().get("black").getName();
int timeValue = dto.getTimeControl().getValue();
int increment = dto.getTimeControl().getIncrement();
return new Game(whiteName, blackName, timeValue, increment);
}
}

View File

@@ -0,0 +1,11 @@
package be.naaturel.boardmateapi.repository.mappings;
import be.naaturel.boardmateapi.repository.dtos.PlayerDto;
public class PlayerMapper {
public static PlayerDto toDto(String playerName){
PlayerDto dto = new PlayerDto();
dto.setName(playerName);
return dto;
}
}

View File

@@ -0,0 +1,14 @@
package be.naaturel.boardmateapi.repository.mappings;
import be.naaturel.boardmateapi.repository.dtos.TimeControlDto;
public class TimeControlMapper {
public static TimeControlDto toDto(int control, int increment){
TimeControlDto dto = new TimeControlDto();
dto.setValue(control);
dto.setIncrement(increment);
return dto;
}
}

View File

@@ -0,0 +1,47 @@
package be.naaturel.boardmateapi.services;
import be.naaturel.boardmateapi.common.models.Game;
import be.naaturel.boardmateapi.common.models.Move;
import be.naaturel.boardmateapi.repository.GameRepo;
import be.naaturel.boardmateapi.repository.dtos.GameDto;
import be.naaturel.boardmateapi.repository.mappings.GameMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.Collection;
import java.util.Optional;
@Service
public class GameService {
private final GameRepo repo;
@Autowired
public GameService(GameRepo repo){
this.repo = repo;
}
public void save(Game game){
GameDto gameDto = GameMapper.toDto(game);
repo.save(gameDto);
}
public Game retrieveGame(String id){
Optional<GameDto> dtoOpt = repo.findById(id);
return dtoOpt.map(GameMapper::toModel).orElse(null);
}
public String create(Game game){
GameDto gameDto = GameMapper.toDto(game);
repo.save(gameDto);
return gameDto.getId();
}
public String addMove(@RequestBody String gameId, @RequestBody Move move) throws Exception {
Game g = retrieveGame(gameId);
g.addMove(move);
save(g);
return move.toString();
}
}

View File

@@ -1,30 +0,0 @@
package be.naaturel.boardmateapi.services;
import be.naaturel.boardmateapi.models.Party;
import be.naaturel.boardmateapi.repository.CustomPartyRepoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PartyService {
private final CustomPartyRepoImpl repo;
@Autowired
public PartyService(CustomPartyRepoImpl repo){
this.repo = repo;
}
public Party retrieveParty(String id){
return null;
}
public String create(){
return null;
}
public String addMove(){
return null;
}
}

View File

@@ -18,3 +18,7 @@ management.endpoint.prometheus.enabled=true
#=============DOCUMENTATION=============
springdoc.swagger-ui.path=/api-docs
springdoc.api-docs.path=/v1/api-docs
#=============DATABASE=============
spring.data.mongodb.uri=mongodb://board-mate-user:apx820kcng@mongodb:27017/board-mate-db
spring.data.mongodb.database=board-mate-db