From 756d299e2c328e448f7382573859af6c35439177 Mon Sep 17 00:00:00 2001 From: Benedikt Galbavy Date: Fri, 5 Jan 2024 16:32:46 +0100 Subject: [PATCH] Some rewrites + packages ~4h work --- src/Main.java | 27 ++- .../nanopenguin/mtcg/application/Package.java | 44 ++++ .../mtcg/application/SessionHandler.java | 8 +- src/at/nanopenguin/mtcg/application/User.java | 30 ++- .../service/InternalErrorService.java | 15 -- .../application/service/PackagesService.java | 28 ++- .../mtcg/application/service/UserService.java | 33 +-- .../application/service/schemas/Card.java | 4 +- src/at/nanopenguin/mtcg/db/DbQuery.java | 201 ++++++++++-------- src/at/nanopenguin/mtcg/db/Table.java | 4 +- 10 files changed, 248 insertions(+), 146 deletions(-) create mode 100644 src/at/nanopenguin/mtcg/application/Package.java delete mode 100644 src/at/nanopenguin/mtcg/application/service/InternalErrorService.java diff --git a/src/Main.java b/src/Main.java index 2d43d00..c04b4ff 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,5 +1,4 @@ -import at.nanopenguin.mtcg.application.service.TestService; -import at.nanopenguin.mtcg.application.service.UserService; +import at.nanopenguin.mtcg.application.service.*; import at.nanopenguin.mtcg.http.HttpMethod; import at.nanopenguin.mtcg.http.Router; import at.nanopenguin.mtcg.http.Server; @@ -18,24 +17,24 @@ public class Main { router.addRoute(HttpMethod.POST, "/sessions", new UserService(), new int[]{}); /* packages */ - router.addRoute(HttpMethod.POST, "/packages", new TestService(), new int[]{}); - router.addRoute(HttpMethod.POST, "/transaction/packages", new TestService(), new int[]{}); + router.addRoute(HttpMethod.POST, "/packages", new PackagesService(), new int[]{}); + router.addRoute(HttpMethod.POST, "/transaction/packages", new PackagesService(), new int[]{}); /* cards */ - router.addRoute(HttpMethod.GET, "/cards", new TestService(), new int[]{}); - router.addRoute(HttpMethod.GET, "/deck", new TestService(), new int[]{}); - router.addRoute(HttpMethod.PUT, "/deck", new TestService(), new int[]{}); + router.addRoute(HttpMethod.GET, "/cards", new CardsService(), new int[]{}); + router.addRoute(HttpMethod.GET, "/deck", new CardsService(), new int[]{}); + router.addRoute(HttpMethod.PUT, "/deck", new CardsService(), new int[]{}); /* game */ - router.addRoute(HttpMethod.GET, "/stats", new TestService(), new int[]{}); - router.addRoute(HttpMethod.GET, "/scoreboard", new TestService(), new int[]{}); - router.addRoute(HttpMethod.POST, "/battles", new TestService(), new int[]{}); + router.addRoute(HttpMethod.GET, "/stats", new GameService(), new int[]{}); + router.addRoute(HttpMethod.GET, "/scoreboard", new GameService(), new int[]{}); + router.addRoute(HttpMethod.POST, "/battles", new GameService(), new int[]{}); /* trading */ - router.addRoute(HttpMethod.GET, "/tradings", new TestService(), new int[]{}); - router.addRoute(HttpMethod.POST, "/tradings", new TestService(), new int[]{}); - router.addRoute(HttpMethod.DELETE, "/tradings/{tradingDealId}", new TestService(), new int[]{2}); - router.addRoute(HttpMethod.POST, "/tradings/{tradingDealId}", new TestService(), new int[]{2}); + router.addRoute(HttpMethod.GET, "/tradings", new TradingService(), new int[]{}); + router.addRoute(HttpMethod.POST, "/tradings", new TradingService(), new int[]{}); + router.addRoute(HttpMethod.DELETE, "/tradings/{tradingDealId}", new TradingService(), new int[]{2}); + router.addRoute(HttpMethod.POST, "/tradings/{tradingDealId}", new TradingService(), new int[]{2}); Server server = new Server(10001, 10, router); server.start(); diff --git a/src/at/nanopenguin/mtcg/application/Package.java b/src/at/nanopenguin/mtcg/application/Package.java new file mode 100644 index 0000000..de28afc --- /dev/null +++ b/src/at/nanopenguin/mtcg/application/Package.java @@ -0,0 +1,44 @@ +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 lombok.val; + +import java.sql.SQLException; +import java.util.List; +import java.util.UUID; + +public class Package { + public static boolean create(List cards) throws SQLException { + // TODO: assert package size == 5 + + val result = DbQuery.builder() + .command(SqlCommand.INSERT) + .table(Table.PACKAGES) + .parameter("cost", 5) + .returnColumn("uuid") + .executeQuery(); + if (result.isEmpty()) throw new SQLException(); // maybe change to different exception + if (!result.get(0).containsKey("uuid")) throw new SQLException(); + + UUID packageUuid = (UUID) result.get(0).get("uuid"); + + + for (val card : cards) { + val query = DbQuery.builder() + .command(SqlCommand.INSERT) + .table(Table.CARDS); + if (card.id() != null) query.parameter("uuid", card.id()); + if (query.parameter("damage", card.damage()) + .parameter("name", card.name()) + .parameter("package", packageUuid) + .executeUpdate() != 1){ + return false; + } + } + + return true; + } +} diff --git a/src/at/nanopenguin/mtcg/application/SessionHandler.java b/src/at/nanopenguin/mtcg/application/SessionHandler.java index f284a81..7531ee7 100644 --- a/src/at/nanopenguin/mtcg/application/SessionHandler.java +++ b/src/at/nanopenguin/mtcg/application/SessionHandler.java @@ -52,12 +52,16 @@ public final class SessionHandler { return uuid; } + public static UUID uuidFromHttpHeader(String headerValue) { + return headerValue == null ? null : UUID.fromString(headerValue.replaceFirst("^Bearer ", "")); + } + public boolean verifyUUID(UUID uuid) { return verifyUUID(uuid, false); } public boolean verifyUUID(UUID uuid, boolean requireAdmin) { - return Sessions.containsKey(uuid) && (!requireAdmin || Sessions.get(uuid).admin()); + return uuid != null && Sessions.containsKey(uuid) && (!requireAdmin || Sessions.get(uuid).admin()); } public boolean verifyUUID(UUID uuid, String username) { @@ -65,6 +69,6 @@ public final class SessionHandler { } public boolean verifyUUID(UUID uuid, String username, boolean allowAdmin) { - return Sessions.containsKey(uuid) && (username.equals(Sessions.get(uuid).username()) || (allowAdmin && Sessions.get(uuid).admin())); + return uuid != null && Sessions.containsKey(uuid) && (username.equals(Sessions.get(uuid).username()) || (allowAdmin && Sessions.get(uuid).admin())); } } diff --git a/src/at/nanopenguin/mtcg/application/User.java b/src/at/nanopenguin/mtcg/application/User.java index 3da4674..8c77a62 100644 --- a/src/at/nanopenguin/mtcg/application/User.java +++ b/src/at/nanopenguin/mtcg/application/User.java @@ -1,20 +1,46 @@ package at.nanopenguin.mtcg.application; import at.nanopenguin.mtcg.application.service.schemas.UserCredentials; +import at.nanopenguin.mtcg.application.service.schemas.UserData; import at.nanopenguin.mtcg.db.DbQuery; import at.nanopenguin.mtcg.db.SqlCommand; import at.nanopenguin.mtcg.db.Table; +import at.nanopenguin.mtcg.http.HttpStatus; +import at.nanopenguin.mtcg.http.Response; +import lombok.val; import java.sql.SQLException; public class User { - public static int create(UserCredentials userCredentials) throws SQLException { + public static boolean create(UserCredentials userCredentials) throws SQLException { return DbQuery.builder() .command(SqlCommand.INSERT) .table(Table.USERS) .parameter("username", userCredentials.username()) .parameter("password", userCredentials.password()) - .executeUpdate(); + .executeUpdate() == 1; } + + public static boolean update(String username, UserData userData) throws SQLException { + return DbQuery.builder() + .command(SqlCommand.UPDATE) + .table(Table.USERS) + .parameter("name", userData.name()) + .parameter("bio", userData.bio()) + .parameter("image", userData.image()) + .condition("username", username) + .executeUpdate() == 1; + } + + public static UserData retrieve(String username) throws SQLException { + val result = DbQuery.builder() + .command(SqlCommand.SELECT) + .table(Table.USERS) + .condition("username", username) + .executeQuery(); + if (result.isEmpty()) return null; + val row1 = result.get(0); + return new UserData((String) row1.get("name"), (String) row1.get("bio"), (String) row1.get("image")); + } } \ No newline at end of file diff --git a/src/at/nanopenguin/mtcg/application/service/InternalErrorService.java b/src/at/nanopenguin/mtcg/application/service/InternalErrorService.java deleted file mode 100644 index d598c2c..0000000 --- a/src/at/nanopenguin/mtcg/application/service/InternalErrorService.java +++ /dev/null @@ -1,15 +0,0 @@ -package at.nanopenguin.mtcg.application.service; - -import at.nanopenguin.mtcg.application.service.Service; -import at.nanopenguin.mtcg.http.HttpStatus; -import at.nanopenguin.mtcg.http.HttpRequest; -import at.nanopenguin.mtcg.http.Response; - -public class InternalErrorService implements Service { - /* For error in http server */ - - @Override - public Response handleRequest(HttpRequest request) { - return new Response(HttpStatus.INTERNAL, "application/json", ""); - } -} diff --git a/src/at/nanopenguin/mtcg/application/service/PackagesService.java b/src/at/nanopenguin/mtcg/application/service/PackagesService.java index b053c18..042264e 100644 --- a/src/at/nanopenguin/mtcg/application/service/PackagesService.java +++ b/src/at/nanopenguin/mtcg/application/service/PackagesService.java @@ -1,12 +1,22 @@ package at.nanopenguin.mtcg.application.service; +import at.nanopenguin.mtcg.application.Package; +import at.nanopenguin.mtcg.application.SessionHandler; +import at.nanopenguin.mtcg.application.service.schemas.Card; import at.nanopenguin.mtcg.http.HttpMethod; import at.nanopenguin.mtcg.http.HttpRequest; import at.nanopenguin.mtcg.http.HttpStatus; 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.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.UUID; public class PackagesService implements Service { @@ -14,11 +24,21 @@ public class PackagesService implements Service { public Response handleRequest(HttpRequest request) throws JsonProcessingException { try { if (request.getPath().split("/")[1].equals("packages") && request.getMethod() == HttpMethod.POST) { - return new Response(HttpStatus.NOT_IMPLEMENTED); // new ObjectMapper().readValue(request.getBody(), Card.class); + UUID uuid; + if ((uuid = SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization"))) == null) + return new Response(HttpStatus.UNAUTHORIZED); // TODO: unauthorized for invalid token + if (!SessionHandler.getInstance().verifyUUID(uuid, true)) + return new Response(HttpStatus.FORBIDDEN); + + return new Response(Package.create(new ObjectMapper().readValue(request.getBody(), new TypeReference>() {})) ? + HttpStatus.CREATED : + HttpStatus.CONFLICT); } if (String.join("/", Arrays.copyOfRange(request.getPath().split("/"), 1, 2)).equals("transactions/packages") && request.getMethod() == HttpMethod.POST) { - return new Response(HttpStatus.NOT_IMPLEMENTED); + return new Response(!SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization"))) ? + HttpStatus.UNAUTHORIZED : + HttpStatus.NOT_IMPLEMENTED); } return new Response(HttpStatus.NOT_FOUND); @@ -26,5 +46,9 @@ public class PackagesService implements Service { catch (ArrayIndexOutOfBoundsException e) { return new Response(HttpStatus.BAD_REQUEST); } + catch (SQLException e) { + System.out.println(e.getMessage()); + return new Response(HttpStatus.INTERNAL); + } } } diff --git a/src/at/nanopenguin/mtcg/application/service/UserService.java b/src/at/nanopenguin/mtcg/application/service/UserService.java index 6e60458..edc1278 100644 --- a/src/at/nanopenguin/mtcg/application/service/UserService.java +++ b/src/at/nanopenguin/mtcg/application/service/UserService.java @@ -35,38 +35,23 @@ public class UserService implements Service { return switch (request.getMethod()) { case GET -> { String username = request.getPath().split("/")[2]; - if (request.getHttpHeader("Authorization") == null || !SessionHandler.getInstance().verifyUUID(UUID.fromString(request.getHttpHeader("Authorization").replaceFirst("^Bearer ", "")), username, true)) + if (!SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization")), username, true)) yield new Response(HttpStatus.UNAUTHORIZED); - val result = DbQuery.builder() - .command(SqlCommand.SELECT) - .table(Table.USERS) - .condition("username", username) - .executeQuery(); - if (result.isEmpty()) yield new Response(HttpStatus.NOT_FOUND); - val row1 = result.get(0); - UserData userData = new UserData((String) row1.get("name"), (String) row1.get("bio"), (String) row1.get("image")); - yield new Response(HttpStatus.OK, "application/json", new ObjectMapper().writeValueAsString(userData)); + val userData = User.retrieve(username); + yield userData != null ? + new Response(HttpStatus.OK, "application/json", new ObjectMapper().writeValueAsString(userData)) : + new Response(HttpStatus.NOT_FOUND); } case POST -> { // register new user - int success = User.create(new ObjectMapper().readValue(request.getBody(), UserCredentials.class)); - yield new Response(success > 0 ? HttpStatus.CREATED : HttpStatus.CONFLICT); + UserCredentials userCredentials = new ObjectMapper().readValue(request.getBody(), UserCredentials.class); + yield new Response(User.create(userCredentials) ? HttpStatus.CREATED : HttpStatus.CONFLICT); } case PUT -> { String username = request.getPath().split("/")[2]; UserData userData = new ObjectMapper().readValue(request.getBody(), UserData.class); - if (request.getHttpHeader("Authorization") == null || !SessionHandler.getInstance().verifyUUID(UUID.fromString(request.getHttpHeader("Authorization").replaceFirst("^Bearer ", "")), username, true)) + if (!SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization")), username, true)) yield new Response(HttpStatus.UNAUTHORIZED); - if (DbQuery.builder() - .command(SqlCommand.UPDATE) - .table(Table.USERS) - .parameter("name", userData.name()) - .parameter("bio", userData.bio()) - .parameter("image", userData.image()) - .condition("username", username) - .executeUpdate() == 1) { - yield new Response(HttpStatus.OK); - } - yield new Response(HttpStatus.NOT_FOUND); + yield User.update(username, userData) ? new Response(HttpStatus.OK) : new Response(HttpStatus.NOT_FOUND); } default -> 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 7c63ff4..c34bc0e 100644 --- a/src/at/nanopenguin/mtcg/application/service/schemas/Card.java +++ b/src/at/nanopenguin/mtcg/application/service/schemas/Card.java @@ -2,6 +2,8 @@ package at.nanopenguin.mtcg.application.service.schemas; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import java.util.UUID; + @JsonIgnoreProperties(ignoreUnknown = true) -public record Card(String id, String name, Float damage) { +public record Card(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 026172b..fdb1fd6 100644 --- a/src/at/nanopenguin/mtcg/db/DbQuery.java +++ b/src/at/nanopenguin/mtcg/db/DbQuery.java @@ -8,6 +8,7 @@ import lombok.val; import java.sql.*; import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; @Builder public final class DbQuery { @@ -23,15 +24,24 @@ public final class DbQuery { private SortedMap parameters; @Singular private SortedMap conditions; + private String returnColumn; + public static class DbQueryBuilder { + public List> executeQuery() throws SQLException { DbQuery dbQuery = this.build(); - if (dbQuery.command != SqlCommand.SELECT) throw new SQLException(); - return dbQuery.read(); + if (dbQuery.command != SqlCommand.SELECT && dbQuery.returnColumn == null) throw new SQLException(); + return switch (dbQuery.command) { + case INSERT -> dbQuery.create(true); + case SELECT -> dbQuery.read(); + case UPDATE -> dbQuery.update(true); + default -> throw new SQLException(); + }; } public int executeUpdate() throws SQLException { + if (this.returnColumn != null) throw new SQLException(); DbQuery dbQuery = this.build(); return switch (dbQuery.command) { case INSERT -> dbQuery.create(); @@ -47,104 +57,125 @@ public final class DbQuery { } private String buildParameterizedString(@NonNull SortedMap parameters) { - if (!parameters.isEmpty()) { - return String.join(" = ?, ", parameters.keySet()) + " = ?"; + if (parameters.isEmpty()) { + return ""; } - return ""; + return String.join(" = ?, ", parameters.keySet()) + " = ?"; } - private int create() throws SQLException { - if (this.parameters.isEmpty()) throw new SQLException("No parameters provided for INSERT statement."); - + private static int executeUpdate(String sql, List parameterValues) throws SQLException { try (Connection connection = connect()) { - String columns = this.parameters.keySet().stream() - .filter(columnName -> columnName.matches("[a-zA-Z0-9_]+")) - .collect(Collectors.joining(", ")); - - String sql = String.format("INSERT INTO %s (%s) VALUES (%s) ON CONFLICT DO NOTHING;", table.table, columns, String.join(", ", Collections.nCopies(this.parameters.size(), "?"))); - // on conflict return int equals 0 - - try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) { - int i = 1; - for (val entry : this.parameters.entrySet()) { - preparedStatement.setObject(i++, entry.getValue()); - } - - return preparedStatement.executeUpdate(); - } - } - } - - private List> read() throws SQLException { - try (Connection connection = connect()) { - StringJoiner columnJoiner = new StringJoiner(", "); - if (this.columns.isEmpty()) { - columnJoiner.add("*"); - } - this.columns.forEach(columnJoiner::add); - - String sql = String.format("SELECT %s FROM %s%s;", columnJoiner, table.table, - this.conditions.isEmpty() ? "" : " WHERE (" + this.buildParameterizedString(this.conditions) + ")"); - - try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) { - int i = 1; - for (val entry : this.conditions.entrySet()) { - preparedStatement.setObject(i++, entry.getValue()); - } - - ResultSet resultSet = preparedStatement.executeQuery(); - - List> result = new ArrayList<>(); - while (resultSet.next()) { - Map row = new HashMap<>(); - for (i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) { - row.put(resultSet.getMetaData().getColumnName(i), resultSet.getObject(i)); - } - result.add(row); - } - - return result; - } - } - } - - private int update() throws SQLException { - if (this.parameters.isEmpty()) throw new SQLException(); - if (this.conditions.isEmpty()) throw new SQLException(); - - try (Connection connection = connect()) { - String sql = String.format("UPDATE %s SET %s WHERE (%s);", table.table, - this.buildParameterizedString(this.parameters), - this.buildParameterizedString(this.conditions)); - PreparedStatement preparedStatement = connection.prepareStatement(sql); - int i = 1; - for (val entry : this.parameters.entrySet()) { - preparedStatement.setObject(i++, entry.getValue()); - } - for (val entry : this.conditions.entrySet()) { - preparedStatement.setObject(i++, entry.getValue()); + for (val value : parameterValues) { + preparedStatement.setObject(i++, value); } return preparedStatement.executeUpdate(); } } - private int delete() throws SQLException { - if (this.conditions.isEmpty()) throw new SQLException(); + private static List> executeQuery(String sql, List parameterValues) throws SQLException { try (Connection connection = connect()) { - String sql = String.format("DELETE FROM %s WHERE (%s);", table.table, this.buildParameterizedString(this.conditions)); - - try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) { - int i = 1; - for (val entry : this.conditions.entrySet()) { - preparedStatement.setObject(i++, entry.getValue()); - } - - return preparedStatement.executeUpdate(); + PreparedStatement preparedStatement = connection.prepareStatement(sql); + int i = 1; + for (val value : parameterValues) { + preparedStatement.setObject(i++, value); } + + ResultSet resultSet = preparedStatement.executeQuery(); + + List> result = new ArrayList<>(); + while (resultSet.next()) { + Map row = new HashMap<>(); + for (i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) { + row.put(resultSet.getMetaData().getColumnName(i), resultSet.getObject(i)); + } + result.add(row); + } + + return result; } } + + private String getInsertStatement(boolean hasReturn) throws SQLException { + if (this.parameters.isEmpty()) throw new SQLException("No parameters provided for INSERT statement."); + + String columns = this.parameters.keySet().stream() + .filter(columnName -> columnName.matches("[a-zA-Z0-9_]+")) + .collect(Collectors.joining(", ")); + + return String.format("INSERT INTO %s (%s) VALUES (%s) ON CONFLICT DO NOTHING%s;", + table.table, + columns, + String.join(", ", Collections.nCopies(this.parameters.size(), "?")), + hasReturn ? " RETURNING " + this.returnColumn : ""); + } + + private int create() throws SQLException { + return executeUpdate(getInsertStatement(false), new ArrayList<>(this.parameters.values())); + } + + private List> create(boolean hasReturn) throws SQLException { + if (!hasReturn) { + // throw dev-is-stupid error + } + return executeQuery(getInsertStatement(true), new ArrayList<>(this.parameters.values())); + } + + private List> read() throws SQLException { + StringJoiner columnJoiner = new StringJoiner(", "); + if (this.columns.isEmpty()) { + columnJoiner.add("*"); + } + this.columns.forEach(columnJoiner::add); + + String sql = String.format("SELECT %s FROM %s%s;", + columnJoiner, + table.table, + this.conditions.isEmpty() ? "" : " WHERE (" + this.buildParameterizedString(this.conditions) + ")"); + + return executeQuery(sql, new ArrayList<>(this.conditions.values())); + } + + private String getUpdateStatement(boolean hasReturn) throws SQLException { + if (this.parameters.isEmpty()) throw new SQLException(); + if (this.conditions.isEmpty()) throw new SQLException(); + + return String.format("UPDATE %s SET %s WHERE (%s)%s;", + table.table, + this.buildParameterizedString(this.parameters), + this.buildParameterizedString(this.conditions), + hasReturn ? " RETURNING " + this.returnColumn : ""); + } + + private int update() throws SQLException { + return executeUpdate(getUpdateStatement(false), new ArrayList<>( + Stream.concat( + this.parameters.values().stream(), + this.conditions.values().stream() + ).collect(Collectors.toList()))); + } + + private List> update(boolean hasReturn) throws SQLException { + if (!hasReturn) { + // throw dev-is-stupid error + } + return executeQuery(getUpdateStatement(true), new ArrayList<>( + Stream.concat( + this.parameters.values().stream(), + this.conditions.values().stream() + ).collect(Collectors.toList()))); + } + + private int delete() throws SQLException { + if (this.conditions.isEmpty()) throw new SQLException(); + + String sql = String.format("DELETE FROM %s WHERE (%s);", + table.table, + this.buildParameterizedString(this.conditions)); + + return executeUpdate(sql, new ArrayList<>(this.conditions.values())); + } } diff --git a/src/at/nanopenguin/mtcg/db/Table.java b/src/at/nanopenguin/mtcg/db/Table.java index 8217aeb..bc57c5b 100644 --- a/src/at/nanopenguin/mtcg/db/Table.java +++ b/src/at/nanopenguin/mtcg/db/Table.java @@ -4,7 +4,9 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public enum Table { - USERS("users"); + USERS("users"), + CARDS("cards"), + PACKAGES("packages"); public final String table; }