Implemented deck configuration

~3h work
This commit is contained in:
Benedikt Galbavy 2024-01-07 15:26:53 +01:00
parent 2cc7a33c8c
commit e6bfbe81e3
6 changed files with 143 additions and 27 deletions

1
.idea/sqldialects.xml generated
View File

@ -2,6 +2,5 @@
<project version="4"> <project version="4">
<component name="SqlDialectMappings"> <component name="SqlDialectMappings">
<file url="file://$APPLICATION_CONFIG_DIR$/consoles/db/720ef4bc-36c4-4861-8776-49b639600223/console.sql" dialect="PostgreSQL" /> <file url="file://$APPLICATION_CONFIG_DIR$/consoles/db/720ef4bc-36c4-4861-8776-49b639600223/console.sql" dialect="PostgreSQL" />
<file url="file://$PROJECT_DIR$/setup.sql" dialect="PostgreSQL" />
</component> </component>
</project> </project>

View File

@ -0,0 +1,57 @@
package at.nanopenguin.mtcg.application;
import at.nanopenguin.mtcg.application.service.schemas.Card;
import at.nanopenguin.mtcg.db.DbQuery;
import at.nanopenguin.mtcg.db.SqlCommand;
import at.nanopenguin.mtcg.db.Table;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.val;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;
public class UserCards extends User {
public static Card[] get(UUID userUuid, boolean deckOnly) throws SQLException {
val dbQueryBuilder = DbQuery.builder()
.command(SqlCommand.SELECT)
.table(Table.CARDS)
.column("uuid AS id")
.column("name")
.column("damage")
.condition("owner", userUuid);
if (deckOnly) dbQueryBuilder.condition("deck", true);
ArrayList<Card> cards = new ArrayList<>();
for (val row : dbQueryBuilder.executeQuery()) {
cards.add(new ObjectMapper().convertValue(row, Card.class));
}
return cards.toArray(new Card[0]);
}
public static synchronized boolean setDeck(UUID[] cards, UUID userUuid) throws SQLException {
if (DbQuery.builder()
.command(SqlCommand.SELECT)
.table(Table.CARDS)
.condition("owner", userUuid)
.condition("uuid", Arrays.asList(cards))
.executeQuery().size() != 4)
return false;
DbQuery.builder()
.command(SqlCommand.UPDATE)
.table(Table.CARDS)
.parameter("deck", false)
.condition("owner", userUuid)
.executeUpdate();
DbQuery.builder()
.command(SqlCommand.UPDATE)
.table(Table.CARDS)
.parameter("deck", true)
.condition("uuid", Arrays.asList(cards))
.executeUpdate();
return true;
}
}

View File

@ -1,34 +1,57 @@
package at.nanopenguin.mtcg.application.service; package at.nanopenguin.mtcg.application.service;
import at.nanopenguin.mtcg.application.SessionHandler;
import at.nanopenguin.mtcg.application.TokenValidity;
import at.nanopenguin.mtcg.application.User;
import at.nanopenguin.mtcg.application.UserCards;
import at.nanopenguin.mtcg.http.HttpMethod; import at.nanopenguin.mtcg.http.HttpMethod;
import at.nanopenguin.mtcg.http.HttpRequest; import at.nanopenguin.mtcg.http.HttpRequest;
import at.nanopenguin.mtcg.http.HttpStatus; import at.nanopenguin.mtcg.http.HttpStatus;
import at.nanopenguin.mtcg.http.Response; import at.nanopenguin.mtcg.http.Response;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.val;
import java.sql.SQLException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class CardsService implements Service { public class CardsService implements Service {
@Override @Override
public Response handleRequest(HttpRequest request) throws JsonProcessingException { public Response handleRequest(HttpRequest request) throws JsonProcessingException, SQLException, ArrayIndexOutOfBoundsException {
try {
if (request.getPath().split("/")[1].equals("cards") && request.getMethod() == HttpMethod.GET) {
return new Response(HttpStatus.NOT_IMPLEMENTED);
}
if (request.getPath().split("/")[1].equals("deck")) { UUID authToken = SessionHandler.tokenFromHttpHeader(request.getHttpHeader("Authorization"));
return switch (request.getMethod()) { if (SessionHandler.getInstance().verifyUUID(authToken) != TokenValidity.VALID)
case GET -> new Response(HttpStatus.NOT_IMPLEMENTED); return new Response(HttpStatus.UNAUTHORIZED);
case PUT -> new Response(HttpStatus.NOT_IMPLEMENTED); // String[] array = new ObjectMapper().readValue(request.getBody(), String[].class) UUID userUuid = SessionHandler.getInstance().userUuidFromToken(authToken);
default -> new Response(HttpStatus.NOT_FOUND);
};
}
return new Response(HttpStatus.NOT_FOUND); if (request.getPath().split("/")[1].equals("cards") && request.getMethod() == HttpMethod.GET) {
} catch (ArrayIndexOutOfBoundsException e) { val result = UserCards.get(userUuid, false);
return new Response(HttpStatus.BAD_REQUEST); if (result.length == 0) return new Response(HttpStatus.NO_CONTENT);
return new Response(HttpStatus.OK, new ObjectMapper().writeValueAsString(result));
} }
if (request.getPath().split("/")[1].equals("deck")) {
return switch (request.getMethod()) {
case GET -> {
val result = UserCards.get(userUuid, true);
if (result.length == 0) yield new Response(HttpStatus.NO_CONTENT);
yield new Response(HttpStatus.OK, new ObjectMapper().writeValueAsString(result));
}
case PUT -> {
UUID[] cards = new ObjectMapper().readValue(request.getBody(), UUID[].class);
if (cards.length != 4)
yield new Response(HttpStatus.BAD_REQUEST);
yield new Response(UserCards.setDeck(cards, userUuid) ? HttpStatus.OK : HttpStatus.FORBIDDEN);
}
default -> new Response(HttpStatus.NOT_FOUND);
};
}
return new Response(HttpStatus.NOT_FOUND);
} }
} }

View File

@ -30,8 +30,7 @@ public class PackagesService implements Service {
case MISSING, INVALID -> new Response(HttpStatus.UNAUTHORIZED); case MISSING, INVALID -> new Response(HttpStatus.UNAUTHORIZED);
case FORBIDDEN -> new Response(HttpStatus.FORBIDDEN); case FORBIDDEN -> new Response(HttpStatus.FORBIDDEN);
case VALID -> new Response( case VALID -> new Response(
Package.create(new ObjectMapper().readValue(request.getBody(), new TypeReference<List<Card>>() { Package.create(new ObjectMapper().readValue(request.getBody(), new TypeReference<List<Card>>() {})) ?
})) ?
HttpStatus.CREATED : HttpStatus.CREATED :
HttpStatus.CONFLICT); HttpStatus.CONFLICT);
}; };

View File

@ -7,6 +7,7 @@ import lombok.val;
import java.sql.*; import java.sql.*;
import java.util.*; import java.util.*;
import java.util.concurrent.locks.Condition;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -80,11 +81,29 @@ public final class DbQuery {
return DriverManager.getConnection(connectionString); return DriverManager.getConnection(connectionString);
} }
private String buildParameterizedString(@NonNull SortedMap<String, Object> parameters) { private String buildParameterizedString(@NonNull SortedMap<String, Object> parameters, String separator) {
if (parameters.isEmpty()) { if (parameters.isEmpty()) {
return ""; return "";
} }
return String.join(" = ?, ", parameters.keySet()) + " = ?"; StringBuilder stringBuilder = new StringBuilder();
for (val parameter : parameters.entrySet()) {
if (!stringBuilder.isEmpty()) stringBuilder.append(separator);
if (parameter.getValue() instanceof List) {
stringBuilder
.append(parameter.getKey())
.append(" IN (")
.append(String.join("", Collections.nCopies(((List<?>) parameter.getValue()).size() - 1, "?, ")))
.append("?)");
continue;
}
stringBuilder
.append(parameter.getKey())
.append(" = ?");
}
return stringBuilder.toString();
} }
private static int executeUpdate(String sql, List<Object> parameterValues) throws SQLException { private static int executeUpdate(String sql, List<Object> parameterValues) throws SQLException {
@ -98,6 +117,11 @@ public final class DbQuery {
new PreparedStatementExecutor(connection.prepareStatement(sql)); new PreparedStatementExecutor(connection.prepareStatement(sql));
int i = 1; int i = 1;
for (val value : parameterValues) { for (val value : parameterValues) {
if (value instanceof List<?>) {
for (Object o : (List<?>) value)
statementExecutor.setObject(i++, o);
continue;
}
statementExecutor.setObject(i++, value); statementExecutor.setObject(i++, value);
} }
@ -116,6 +140,11 @@ public final class DbQuery {
new PreparedStatementExecutor(connection.prepareStatement(sql)); new PreparedStatementExecutor(connection.prepareStatement(sql));
int i = 1; int i = 1;
for (val value : parameterValues) { for (val value : parameterValues) {
if (value instanceof List<?>) {
for (Object o : (List<?>) value)
statementExecutor.setObject(i++, o);
continue;
}
statementExecutor.setObject(i++, value); statementExecutor.setObject(i++, value);
} }
@ -134,6 +163,11 @@ public final class DbQuery {
new PreparedStatementExecutor(connection.prepareStatement(sql)); new PreparedStatementExecutor(connection.prepareStatement(sql));
int i = 1; int i = 1;
for (val value : parameterValues) { for (val value : parameterValues) {
if (value instanceof List<?>) {
for (Object o : (List<?>) value)
statementExecutor.setObject(i++, o);
continue;
}
statementExecutor.setObject(i++, value); statementExecutor.setObject(i++, value);
} }
@ -157,7 +191,7 @@ public final class DbQuery {
if (this.parameters.isEmpty()) throw new SQLException("No parameters provided for INSERT statement."); if (this.parameters.isEmpty()) throw new SQLException("No parameters provided for INSERT statement.");
String columns = this.parameters.keySet().stream() String columns = this.parameters.keySet().stream()
.filter(columnName -> columnName.matches("[a-zA-Z0-9_]+")) .filter(columnName -> columnName.matches("^[a-zA-Z0-9_]+( AS [a-zA-Z0-9_]+)?$"))
.collect(Collectors.joining(", ")); .collect(Collectors.joining(", "));
return String.format("INSERT INTO %s (%s) VALUES (%s) ON CONFLICT DO NOTHING%s;", return String.format("INSERT INTO %s (%s) VALUES (%s) ON CONFLICT DO NOTHING%s;",
@ -188,7 +222,7 @@ public final class DbQuery {
String sql = String.format("SELECT %s FROM %s%s;", String sql = String.format("SELECT %s FROM %s%s;",
columnJoiner, columnJoiner,
table.table, table.table,
this.conditions.isEmpty() ? "" : " WHERE (" + this.buildParameterizedString(this.conditions) + ")"); this.conditions.isEmpty() ? "" : " WHERE " + this.buildParameterizedString(this.conditions, " AND "));
return executeQuery(sql, new ArrayList<>(this.conditions.values())); return executeQuery(sql, new ArrayList<>(this.conditions.values()));
} }
@ -197,10 +231,10 @@ public final class DbQuery {
if (this.parameters.isEmpty()) throw new SQLException(); if (this.parameters.isEmpty()) throw new SQLException();
if (this.conditions.isEmpty()) throw new SQLException(); if (this.conditions.isEmpty()) throw new SQLException();
return String.format("UPDATE %s SET %s WHERE (%s)%s;", return String.format("UPDATE %s SET %s WHERE %s%s;",
table.table, table.table,
this.buildParameterizedString(this.parameters), this.buildParameterizedString(this.parameters, ", "),
this.buildParameterizedString(this.conditions), this.buildParameterizedString(this.conditions, " AND "),
hasReturn ? " RETURNING " + this.returnColumn : ""); hasReturn ? " RETURNING " + this.returnColumn : "");
} }
@ -226,9 +260,9 @@ public final class DbQuery {
private int delete() throws SQLException { private int delete() throws SQLException {
if (this.conditions.isEmpty()) throw new SQLException(); if (this.conditions.isEmpty()) throw new SQLException();
String sql = String.format("DELETE FROM %s WHERE (%s);", String sql = String.format("DELETE FROM %s WHERE %s;",
table.table, table.table,
this.buildParameterizedString(this.conditions)); this.buildParameterizedString(this.conditions, " AND "));
return executeUpdate(sql, new ArrayList<>(this.conditions.values())); return executeUpdate(sql, new ArrayList<>(this.conditions.values()));
} }

View File

@ -9,6 +9,10 @@ public class Response {
private final String contentType; private final String contentType;
private final String content; private final String content;
public Response(HttpStatus httpStatus, String content) {
this(httpStatus, "application/json", content);
}
public Response(HttpStatus httpStatus, String contentType, String content) { public Response(HttpStatus httpStatus, String contentType, String content) {
this.httpStatus = httpStatus; this.httpStatus = httpStatus;