diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml
new file mode 100644
index 0000000..546db64
--- /dev/null
+++ b/.idea/sqldialects.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Main.java b/src/Main.java
index c04b4ff..9d2967a 100644
--- a/src/Main.java
+++ b/src/Main.java
@@ -18,7 +18,7 @@ public class Main {
/* packages */
router.addRoute(HttpMethod.POST, "/packages", new PackagesService(), new int[]{});
- router.addRoute(HttpMethod.POST, "/transaction/packages", new PackagesService(), new int[]{});
+ router.addRoute(HttpMethod.POST, "/transactions/packages", new PackagesService(), new int[]{});
/* cards */
router.addRoute(HttpMethod.GET, "/cards", new CardsService(), new int[]{});
diff --git a/src/at/nanopenguin/mtcg/Pair.java b/src/at/nanopenguin/mtcg/Pair.java
new file mode 100644
index 0000000..b5a0d97
--- /dev/null
+++ b/src/at/nanopenguin/mtcg/Pair.java
@@ -0,0 +1,4 @@
+package at.nanopenguin.mtcg;
+
+public record Pair(T left, U right) {
+}
diff --git a/src/at/nanopenguin/mtcg/application/Package.java b/src/at/nanopenguin/mtcg/application/Package.java
index 21c75c1..8cc4dd5 100644
--- a/src/at/nanopenguin/mtcg/application/Package.java
+++ b/src/at/nanopenguin/mtcg/application/Package.java
@@ -1,14 +1,16 @@
package at.nanopenguin.mtcg.application;
+import at.nanopenguin.mtcg.Pair;
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.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.val;
import java.sql.SQLException;
-import java.util.List;
-import java.util.UUID;
+import java.util.*;
public class Package {
public static boolean create(List cards) throws SQLException {
@@ -42,25 +44,51 @@ public class Package {
return true;
}
- public static boolean addToUser(UUID userUuid) throws SQLException {
- return DbQuery.builder()
+ public synchronized static Pair> addToUser(UUID userUuid) throws SQLException {
+ int coins = (int) DbQuery.builder()
+ .command(SqlCommand.SELECT)
+ .table(Table.USERS)
+ .column("coins")
+ .condition("uuid", userUuid)
+ .executeQuery()
+ .get(0)
+ .get("coins");
+ if (coins < 5) {
+ return new Pair<>(PurchaseStatus.NOT_ENOUGH_MONEY, null);
+ }
+
+ if ((long) DbQuery.builder()
+ .command(SqlCommand.SELECT)
+ .table(Table.PACKAGES)
+ .column("COUNT(*)")
+ .executeQuery()
+ .get(0)
+ .get("count") == 0) {
+ return new Pair<>(PurchaseStatus.NO_PACKAGE_AVAILABLE, null);
+ }
+
+ val result = DbQuery.builder()
.customSql("""
- DO $$
- DECLARE user_uuid uuid;
- DECLARE package_uuid uuid;
- DECLARE cost int;
- BEGIN
- cost = ?;
- user_uuid = ?::uuid;
- IF (SELECT coins FROM users WHERE uuid = user_uuid) >= cost THEN
- package_uuid = (SELECT uuid FROM packages ORDER BY created_at LIMIT 1);
- UPDATE cards SET owner = user_uuid WHERE package = package_uuid;
- UPDATE users SET coins = coins - cost WHERE uuid = user_uuid;
- DELETE FROM packages WHERE uuid = package_uuid;
- END IF;
- END $$;""")
- .value(5) // TODO: don't hardcode cost
+ UPDATE cards
+ SET owner = (?::uuid), package = null
+ WHERE package = (
+ SELECT uuid
+ FROM packages
+ ORDER BY created_at
+ LIMIT 1
+ )
+ RETURNING uuid AS id, name, damage;
+ """)
.value(userUuid)
- .executeUpdate() > 0;
+ .executeQuery();
+
+ DbQuery.builder()
+ .command(SqlCommand.UPDATE)
+ .table(Table.USERS)
+ .parameter("coins", coins - 5)
+ .condition("uuid", userUuid)
+ .executeUpdate();
+
+ return new Pair<>(PurchaseStatus.SUCCESS, new ObjectMapper().convertValue(result, new TypeReference>() {}));
}
}
diff --git a/src/at/nanopenguin/mtcg/application/PurchaseStatus.java b/src/at/nanopenguin/mtcg/application/PurchaseStatus.java
new file mode 100644
index 0000000..2fb7b0d
--- /dev/null
+++ b/src/at/nanopenguin/mtcg/application/PurchaseStatus.java
@@ -0,0 +1,7 @@
+package at.nanopenguin.mtcg.application;
+
+public enum PurchaseStatus {
+ SUCCESS,
+ NOT_ENOUGH_MONEY,
+ NO_PACKAGE_AVAILABLE
+}
diff --git a/src/at/nanopenguin/mtcg/application/SessionHandler.java b/src/at/nanopenguin/mtcg/application/SessionHandler.java
index 435d149..ebbf2be 100644
--- a/src/at/nanopenguin/mtcg/application/SessionHandler.java
+++ b/src/at/nanopenguin/mtcg/application/SessionHandler.java
@@ -58,7 +58,7 @@ public final class SessionHandler {
return uuid;
}
- public static UUID uuidFromHttpHeader(String headerValue) {
+ public static UUID tokenFromHttpHeader(String headerValue) {
return headerValue == null ? null : UUID.fromString(headerValue.replaceFirst("^Bearer ", ""));
}
@@ -85,4 +85,8 @@ public final class SessionHandler {
return TokenValidity.FORBIDDEN;
}
+
+ public UUID userUuidFromToken(UUID token) {
+ return this.sessions.containsKey(token) ? this.sessions.get(token).id() : null;
+ }
}
\ No newline at end of file
diff --git a/src/at/nanopenguin/mtcg/application/service/PackagesService.java b/src/at/nanopenguin/mtcg/application/service/PackagesService.java
index 337d978..ba56586 100644
--- a/src/at/nanopenguin/mtcg/application/service/PackagesService.java
+++ b/src/at/nanopenguin/mtcg/application/service/PackagesService.java
@@ -1,6 +1,7 @@
package at.nanopenguin.mtcg.application.service;
import at.nanopenguin.mtcg.application.Package;
+import at.nanopenguin.mtcg.application.PurchaseStatus;
import at.nanopenguin.mtcg.application.SessionHandler;
import at.nanopenguin.mtcg.application.TokenValidity;
import at.nanopenguin.mtcg.application.service.schemas.Card;
@@ -11,6 +12,7 @@ import at.nanopenguin.mtcg.http.Response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.val;
import java.sql.SQLException;
import java.util.Arrays;
@@ -21,10 +23,10 @@ public class PackagesService implements Service {
@Override
public Response handleRequest(HttpRequest request) throws JsonProcessingException, SQLException, ArrayIndexOutOfBoundsException {
+ UUID token = SessionHandler.tokenFromHttpHeader(request.getHttpHeader("Authorization"));
if (request.getPath().split("/")[1].equals("packages") && request.getMethod() == HttpMethod.POST) {
- UUID uuid;
- return switch (SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization")), true)) {
+ return switch (SessionHandler.getInstance().verifyUUID(token, true)) {
case MISSING, INVALID -> new Response(HttpStatus.UNAUTHORIZED);
case FORBIDDEN -> new Response(HttpStatus.FORBIDDEN);
case VALID -> new Response(
@@ -35,10 +37,17 @@ public class PackagesService implements Service {
};
}
- if (String.join("/", Arrays.copyOfRange(request.getPath().split("/"), 1, 2)).equals("transactions/packages") && request.getMethod() == HttpMethod.POST) {
- return new Response(SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization"))) != TokenValidity.VALID ?
- HttpStatus.UNAUTHORIZED :
- HttpStatus.NOT_IMPLEMENTED); // Package.addToUser();
+ if (String.join("/", Arrays.copyOfRange(request.getPath().split("/"), 1, 3)).equals("transactions/packages") && request.getMethod() == HttpMethod.POST) {
+ if (SessionHandler.getInstance().verifyUUID(token) != TokenValidity.VALID) return new Response(HttpStatus.UNAUTHORIZED);
+ val result = Package.addToUser(SessionHandler.getInstance().userUuidFromToken(token));
+ if (result.left() == PurchaseStatus.SUCCESS) {
+ return new Response(HttpStatus.OK, "application/json", new ObjectMapper().writeValueAsString(result.right()));
+ }
+ return new Response(switch (Package.addToUser(SessionHandler.getInstance().userUuidFromToken(token)).left()) {
+ case NO_PACKAGE_AVAILABLE -> HttpStatus.NOT_FOUND;
+ case NOT_ENOUGH_MONEY -> HttpStatus.FORBIDDEN;
+ default -> HttpStatus.INTERNAL;
+ });
}
return new Response(HttpStatus.NOT_FOUND);
diff --git a/src/at/nanopenguin/mtcg/application/service/UserService.java b/src/at/nanopenguin/mtcg/application/service/UserService.java
index 929ce94..cf2f390 100644
--- a/src/at/nanopenguin/mtcg/application/service/UserService.java
+++ b/src/at/nanopenguin/mtcg/application/service/UserService.java
@@ -32,7 +32,7 @@ public class UserService implements Service {
return switch (request.getMethod()) {
case GET -> {
String username = request.getPath().split("/")[2];
- if (SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization")), username, true) != TokenValidity.VALID)
+ if (SessionHandler.getInstance().verifyUUID(SessionHandler.tokenFromHttpHeader(request.getHttpHeader("Authorization")), username, true) != TokenValidity.VALID)
yield new Response(HttpStatus.UNAUTHORIZED);
val userData = User.retrieve(username);
yield userData != null ?
@@ -46,7 +46,7 @@ public class UserService implements Service {
case PUT -> {
String username = request.getPath().split("/")[2];
UserData userData = new ObjectMapper().readValue(request.getBody(), UserData.class);
- if (SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization")), username, true) != TokenValidity.FORBIDDEN)
+ if (SessionHandler.getInstance().verifyUUID(SessionHandler.tokenFromHttpHeader(request.getHttpHeader("Authorization")), username, true) != TokenValidity.VALID)
yield new Response(HttpStatus.UNAUTHORIZED);
yield User.update(username, userData) ? new Response(HttpStatus.OK) : new Response(HttpStatus.NOT_FOUND);
}
diff --git a/src/at/nanopenguin/mtcg/application/service/schemas/Card.java b/src/at/nanopenguin/mtcg/application/service/schemas/Card.java
index c34bc0e..ff93860 100644
--- a/src/at/nanopenguin/mtcg/application/service/schemas/Card.java
+++ b/src/at/nanopenguin/mtcg/application/service/schemas/Card.java
@@ -1,9 +1,10 @@
package at.nanopenguin.mtcg.application.service.schemas;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.UUID;
@JsonIgnoreProperties(ignoreUnknown = true)
-public record Card(UUID id, String name, Float damage) {
+public record Card(@JsonProperty("id") UUID id, String name, Float damage) {
}
diff --git a/src/at/nanopenguin/mtcg/db/DbQuery.java b/src/at/nanopenguin/mtcg/db/DbQuery.java
index fb02017..c017f7d 100644
--- a/src/at/nanopenguin/mtcg/db/DbQuery.java
+++ b/src/at/nanopenguin/mtcg/db/DbQuery.java
@@ -28,14 +28,15 @@ public final class DbQuery {
private String customSql;
@Singular
private List