From 4b5d21345cf7b72e2ecb1397327fe64962e90e0c Mon Sep 17 00:00:00 2001 From: Francois G Date: Sat, 25 Feb 2023 19:40:16 +0100 Subject: [PATCH] Refactor spaghetti code I wrote + save source code in file system instead than in db --- .../peeratcode/model/Completion.java | 62 ++++++ .../peeratcode/model/Player.java | 34 +-- .../peeratcode/model/Puzzle.java | 2 +- .../repository/DatabaseRepository.java | 200 +++++++++--------- .../peeratcode/routes/PuzzleResponse.java | 130 ++++++++---- .../peeratcode/routes/Register.java | 21 +- 6 files changed, 287 insertions(+), 162 deletions(-) create mode 100644 src/be/jeffcheasey88/peeratcode/model/Completion.java diff --git a/src/be/jeffcheasey88/peeratcode/model/Completion.java b/src/be/jeffcheasey88/peeratcode/model/Completion.java new file mode 100644 index 0000000..2259136 --- /dev/null +++ b/src/be/jeffcheasey88/peeratcode/model/Completion.java @@ -0,0 +1,62 @@ +package be.jeffcheasey88.peeratcode.model; + +public class Completion { + private int puzzleId; + private int playerId; + private int tries; + private String fileName; + private byte[] code; + private int score; + + public Completion(int playerId, int puzzleId, String fileName, int score) { + this(playerId, puzzleId, -1, 1, fileName, score, null); + } + + public Completion(int playerId, int puzzleId, int idCompletion, int tries, String fileName, int score) { + this(playerId, puzzleId, idCompletion, tries, fileName, score, null); + } + + public Completion(int playerId, int puzzleId, int idCompletion, int tries, String fileName, int score, + byte[] file) { + this.playerId = playerId; + this.puzzleId = puzzleId; + this.tries = tries; + this.fileName = fileName; + this.score = score; + this.code = file; + } + + public int getPuzzleId() { + return puzzleId; + } + + public int getPlayerId() { + return playerId; + } + + public int getTries() { + return tries; + } + + public void addTry() { + this.tries++; + updateScore(); + } + private void updateScore() { + if (tries > 1) { + score = score * (1-((tries-1)/10)); + } + } + + public String getFileName() { + return fileName; + } + + public byte[] getCode() { + return code; + } + + public int getScore() { + return score; + } +} diff --git a/src/be/jeffcheasey88/peeratcode/model/Player.java b/src/be/jeffcheasey88/peeratcode/model/Player.java index f53e845..2eb0ada 100644 --- a/src/be/jeffcheasey88/peeratcode/model/Player.java +++ b/src/be/jeffcheasey88/peeratcode/model/Player.java @@ -1,15 +1,16 @@ package be.jeffcheasey88.peeratcode.model; public class Player { - + public static final String PATH_TO_CODE = "/home/%s/peer-at-source/"; + private String pseudo; private String email; private String firstname; private String lastname; private String description; private String sgroup; - - public Player(String pseudo, String email, String firstname, String lastname, String description, String sgroup){ + + public Player(String pseudo, String email, String firstname, String lastname, String description, String sgroup) { this.pseudo = pseudo; this.email = email; this.firstname = firstname; @@ -17,29 +18,32 @@ public class Player { this.description = description; this.sgroup = sgroup; } - - public String getPseudo(){ + + public String getPseudo() { return this.pseudo; } - - public String getEmail(){ + + public String getEmail() { return this.email; } - - public String getFirstname(){ + + public String getFirstname() { return this.firstname; } - - public String getLastname(){ + + public String getLastname() { return this.lastname; } - - public String getDescription(){ + + public String getDescription() { return this.description; } - - public String getGroup(){ + + public String getGroup() { return this.sgroup; } + public String getPathToSourceCode() { + return String.format(PATH_TO_CODE, pseudo); + } } diff --git a/src/be/jeffcheasey88/peeratcode/model/Puzzle.java b/src/be/jeffcheasey88/peeratcode/model/Puzzle.java index 191600a..1311278 100644 --- a/src/be/jeffcheasey88/peeratcode/model/Puzzle.java +++ b/src/be/jeffcheasey88/peeratcode/model/Puzzle.java @@ -58,7 +58,7 @@ public class Puzzle { this.verify = regex; } - private int getScoreMax(){ + public int getScoreMax(){ return this.scoreMax; } diff --git a/src/be/jeffcheasey88/peeratcode/repository/DatabaseRepository.java b/src/be/jeffcheasey88/peeratcode/repository/DatabaseRepository.java index 9ad6e79..d70c3d7 100644 --- a/src/be/jeffcheasey88/peeratcode/repository/DatabaseRepository.java +++ b/src/be/jeffcheasey88/peeratcode/repository/DatabaseRepository.java @@ -14,6 +14,8 @@ import com.password4j.Password; import be.jeffcheasey88.peeratcode.Configuration; import be.jeffcheasey88.peeratcode.model.Chapter; +import be.jeffcheasey88.peeratcode.model.Completion; +import be.jeffcheasey88.peeratcode.model.Player; import be.jeffcheasey88.peeratcode.model.Puzzle; public class DatabaseRepository { @@ -26,34 +28,47 @@ public class DatabaseRepository { private static final String REGISTER_QUERY = "INSERT INTO players (pseudo, email, passwd, firstname, lastname, description, sgroup, avatar) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; private static final String CHECK_PASSWORD = "SELECT id_player, passwd FROM players WHERE pseudo=?"; private static final String SCORE = "SELECT score FROM completions WHERE fk_player = ? AND fk_puzzle = ?"; - private static final String GET_PUZZLE_SOLUTION = "SELECT soluce FROM puzzles WHERE id_puzzle=?"; - private static final String GET_PUZZLE_SCORE_MAX = "SELECT score_max FROM puzzles WHERE id_puzzle=?"; - private static final String GET_PLAYER_ID_BY_PSEUDO = "SELECT id_player FROM players WHERE pseudo=?"; - private static final String GET_PUZZLE_NB_TRIES_AND_SCORE_BY_PLAYER = "SELECT tries, score FROM completions WHERE fk_puzzle = ? AND fk_player = ?"; + private static final String GET_COMPLETION = "SELECT id_completion, tries, fileName, score FROM completions WHERE fk_puzzle = ? AND fk_player = ?"; + private static final String GET_PLAYER = "SELECT * FROM players WHERE id_player = ?"; private static final String INSERT_COMPLETION = "INSERT INTO completions (fk_puzzle, fk_player, tries, code, fileName, score) values (?, ?, ?, ?, ?, ?)"; - private static final String UPDATE_COMPLETION = "UPDATE completions SET tries = ?, code = ?, filename = ?, score = ?"; + private static final String UPDATE_COMPLETION = "UPDATE completions SET tries = ?, filename = ?, score = ? WHERE fk_puzzle = ? AND fk_player = ?"; private Connection con; private Configuration config; - public DatabaseRepository(Configuration config){ + public DatabaseRepository(Configuration config) { this.config = config; } - - private void ensureConnection() throws SQLException{ - if(con == null || (!con.isValid(5))){ - this.con = DriverManager.getConnection("jdbc:mysql://"+config.getDbHost()+":"+config.getDbPort()+"/"+config.getDbDatabase()+"",config.getDbUser(), config.getDbPassword()); + + private void ensureConnection() throws SQLException { + if (con == null || (!con.isValid(5))) { + this.con = DriverManager.getConnection( + "jdbc:mysql://" + config.getDbHost() + ":" + config.getDbPort() + "/" + config.getDbDatabase() + "", + config.getDbUser(), config.getDbPassword()); } } private Puzzle makePuzzle(ResultSet puzzleResult) throws SQLException { - return new Puzzle(puzzleResult.getInt("id_puzzle"), puzzleResult.getString("name"), puzzleResult.getString("content"), null,"",0); + return new Puzzle(puzzleResult.getInt("id_puzzle"), puzzleResult.getString("name"), + puzzleResult.getString("content"), null, "", 0); } private Chapter makeChapter(ResultSet chapterResult) throws SQLException { return new Chapter(chapterResult.getInt("id_chapter"), chapterResult.getString("name")); } + private Completion makeCompletion(int playerId, int puzzleId, ResultSet completionResult) throws SQLException { + return new Completion(playerId, puzzleId, completionResult.getInt("id_completion"), + completionResult.getInt("tries"), completionResult.getString("fileName"), + completionResult.getInt("score"), null); + } + + private Player makePlayer(ResultSet playerResult) throws SQLException { + return new Player(playerResult.getString("pseudo"), playerResult.getString("email"), + playerResult.getString("firstName"), playerResult.getString("LastName"), + playerResult.getString("description"), playerResult.getString("sgroup")); + } + private List getPuzzlesInChapter(int id) throws SQLException { List puzzles = new ArrayList<>(); ensureConnection(); @@ -86,21 +101,52 @@ public class DatabaseRepository { } return null; } - - public int getScore(int user, int puzzle){ + + public int getScore(int user, int puzzle) { try { + ensureConnection(); PreparedStatement stmt = this.con.prepareStatement(SCORE); stmt.setInt(1, user); stmt.setInt(2, puzzle); - + ResultSet result = stmt.executeQuery(); - if(result.next()) result.getInt("score"); - }catch(Exception e){ + if (result.next()) + result.getInt("score"); + } catch (Exception e) { e.printStackTrace(); } return -1; } + public Completion getCompletion(int playerId, int puzzleId) { + try { + PreparedStatement completionsStmt = con.prepareStatement(GET_COMPLETION); + completionsStmt.setInt(1, puzzleId); + completionsStmt.setInt(2, playerId); + ResultSet result = completionsStmt.executeQuery(); + if (result.next()) { + return makeCompletion(playerId, puzzleId, result); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public Player getPlayer(int idPlayer) { + try { + PreparedStatement completionsStmt = con.prepareStatement(GET_PLAYER); + completionsStmt.setInt(1, idPlayer); + ResultSet result = completionsStmt.executeQuery(); + if (result.next()) { + return makePlayer(result); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + /** * Get a specific chapter * @@ -190,11 +236,12 @@ public class DatabaseRepository { * @param firstname The firstname of the user * @param lastname The lastname of the user * @param description The description of the user - * @param sgroup The group of the user + * @param sgroup The group of the user * @param avatar The avatar of the user * @return True if the user was registered, false if an error occurred */ - public int register(String pseudo, String email, String password, String firstname, String lastname, String description, String sgroup, String avatar) { + public int register(String pseudo, String email, String password, String firstname, String lastname, + String description, String sgroup, String avatar) { Hash hash = Password.hash(password).withArgon2(); try { ensureConnection(); @@ -207,9 +254,10 @@ public class DatabaseRepository { statement.setString(6, description); statement.setString(7, sgroup); statement.setString(8, avatar); - if(statement.executeUpdate() == 1){ + if (statement.executeUpdate() == 1) { ResultSet inserted = statement.getGeneratedKeys(); - if(inserted.next()) return inserted.getInt("id_player"); + if (inserted.next()) + return inserted.getInt("id_player"); } } catch (SQLException e) { e.printStackTrace(); @@ -220,7 +268,7 @@ public class DatabaseRepository { /** * Login a user * - * @param username The username of the user + * @param username The username of the user * @param password The password of the user * @return id the id of the user, -1 if not login successefuly */ @@ -232,97 +280,51 @@ public class DatabaseRepository { ResultSet result = statement.executeQuery(); if (result.next()) { String hashedPassword = result.getString("passwd"); - if(Password.check(password, hashedPassword).withArgon2()) return result.getInt("id_player"); + if (Password.check(password, hashedPassword).withArgon2()) + return result.getInt("id_player"); } } catch (SQLException e) { } return -1; } - public byte[] getPuzzleSolution(int puzzleId) { + public Completion insertOrUpdatePuzzleResponse(int puzzleId, int userId, String fileName, byte[] code) { try { - PreparedStatement puzzleStmt = con.prepareStatement(GET_PUZZLE_SOLUTION); - puzzleStmt.setInt(1, puzzleId); - ResultSet result = puzzleStmt.executeQuery(); - if (result.next()) { - return result.getBytes("soluce"); - } - } catch (SQLException e) { - e.printStackTrace(); - } - return null; - } - - public int insertOrUpdatePuzzleResponse(int puzzleId, String pseudo, String fileName, byte[] code) { - try { - ensureConnection(); - int[] triesAndScore = getPuzzleNbTriesAndScore(puzzleId, pseudo); - int playerId = getPlayerIdByPseudo(pseudo); - int puzzleScoreMax = getPuzzleScoreMax(puzzleId); - if (triesAndScore[0] < 0) { - // Insert completions - PreparedStatement statement = con.prepareStatement(INSERT_COMPLETION); - statement.setInt(1, puzzleId); - statement.setInt(2, playerId); - statement.setInt(3, 0); - statement.setBytes(4, code); - statement.setString(5, fileName); - statement.setInt(6, puzzleScoreMax); - return puzzleScoreMax; + Puzzle currentPuzzle = getPuzzle(puzzleId); + Completion completion = getCompletion(userId, puzzleId); + if (completion == null) { + insertCompletion(new Completion(userId, puzzleId, fileName, currentPuzzle.getScoreMax())); } else { - // Update completions - int score = puzzleScoreMax * (((triesAndScore[0]-1)*10)/100); - PreparedStatement statement = con.prepareStatement(UPDATE_COMPLETION); - statement.setInt(1, triesAndScore[0]+1); - statement.setBytes(2, code); - statement.setString(3, fileName); - statement.setInt(4, score); - return score; - } - } catch (SQLException e) { - e.printStackTrace(); - } - return -1; - } - public int getPuzzleScoreMax(int puzzleId) { - try { - PreparedStatement puzzleStmt = con.prepareStatement(GET_PUZZLE_SCORE_MAX); - puzzleStmt.setInt(1, puzzleId); - ResultSet result = puzzleStmt.executeQuery(); - if (result.next()) { - return result.getInt("score_max"); - } - } catch (SQLException e) { - e.printStackTrace(); - } - return -1; - } - public int[] getPuzzleNbTriesAndScore(int puzzleId, String pseudo) { - try { - PreparedStatement puzzleStmt = con.prepareStatement(GET_PUZZLE_NB_TRIES_AND_SCORE_BY_PLAYER); - puzzleStmt.setInt(1, puzzleId); - ResultSet result = puzzleStmt.executeQuery(); - int[] res = new int[2]; - if (result.next()) { - res[0] = result.getInt("tries"); - res[1] = result.getInt("score"); + completion.addTry(); + updateCompletion(completion); } + return completion; } catch (SQLException e) { e.printStackTrace(); } return null; } - private int getPlayerIdByPseudo(String pseudo) { - try { - PreparedStatement puzzleStmt = con.prepareStatement(GET_PLAYER_ID_BY_PSEUDO); - puzzleStmt.setString(1, pseudo); - ResultSet result = puzzleStmt.executeQuery(); - if (result.next()) { - return result.getInt("id_player"); - } - } catch (SQLException e) { - e.printStackTrace(); - } - return -1; + + private void insertCompletion(Completion newCompletion) throws SQLException { + // Insert completions + PreparedStatement statement = con.prepareStatement(INSERT_COMPLETION); + statement.setInt(1, newCompletion.getPuzzleId()); + statement.setInt(2, newCompletion.getPlayerId()); + statement.setInt(3, newCompletion.getTries()); + statement.setBytes(4, newCompletion.getCode()); + statement.setString(5, newCompletion.getFileName()); + statement.setInt(6, newCompletion.getScore()); + statement.executeUpdate(); + } + + private void updateCompletion(Completion completionToUpdate) throws SQLException { + // Update completions + PreparedStatement statement = con.prepareStatement(UPDATE_COMPLETION); + statement.setInt(1, completionToUpdate.getTries()); + statement.setString(2, completionToUpdate.getFileName()); + statement.setInt(3, completionToUpdate.getScore()); + statement.setInt(4, completionToUpdate.getPuzzleId()); + statement.setInt(5, completionToUpdate.getPlayerId()); + statement.executeUpdate(); } } \ No newline at end of file diff --git a/src/be/jeffcheasey88/peeratcode/routes/PuzzleResponse.java b/src/be/jeffcheasey88/peeratcode/routes/PuzzleResponse.java index 75732ff..84b2575 100644 --- a/src/be/jeffcheasey88/peeratcode/routes/PuzzleResponse.java +++ b/src/be/jeffcheasey88/peeratcode/routes/PuzzleResponse.java @@ -1,11 +1,17 @@ package be.jeffcheasey88.peeratcode.routes; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.json.simple.JSONObject; +import be.jeffcheasey88.peeratcode.model.Completion; +import be.jeffcheasey88.peeratcode.model.Player; import be.jeffcheasey88.peeratcode.repository.DatabaseRepository; import be.jeffcheasey88.peeratcode.webserver.HttpReader; import be.jeffcheasey88.peeratcode.webserver.HttpUtil; @@ -22,54 +28,39 @@ public class PuzzleResponse implements Response { @Override public void exec(Matcher matcher, User user, HttpReader reader, HttpWriter writer) throws Exception { - if(user == null){ + if (user == null) { HttpUtil.responseHeaders(writer, 403, "Access-Control-Allow-Origin: *"); return; } - + HttpUtil.skipHeaders(reader); - int puzzleId = Integer.parseInt(matcher.group(1)); - byte[] response; - byte[] fileName; - byte[] sourceCode; - // Response - int hSize = reader.readInt(); - response = new byte[hSize]; - if (hSize == reader.read(response)) { - // File Name - hSize = reader.readInt(); - fileName = new byte[hSize]; - if (hSize == reader.read(fileName)) { - // Source Code - hSize = reader.readInt(); - sourceCode = new byte[hSize]; - if (hSize == reader.read(sourceCode)) { - int score = databaseRepo.insertOrUpdatePuzzleResponse(puzzleId, "Pseudo", fileName.toString(), sourceCode); - if (Arrays.equals(response, databaseRepo.getPuzzleSolution(puzzleId))) { - HttpUtil.responseHeaders(writer, 200, - "Access-Control-Allow-Origin: *", - "Content-Type: application/json"); - JSONObject responseJSON = new JSONObject(); - responseJSON.put("id", puzzleId); - responseJSON.put("score", score); - writer.write(responseJSON.toJSONString()); - writer.flush(); - writer.close(); - } else { - HttpUtil.responseHeaders(writer, 406, "Access-Control-Allow-Origin: *"); - } - } - else { - HttpUtil.responseHeaders(writer, 400, "Access-Control-Allow-Origin: *"); - } - } - else { - HttpUtil.responseHeaders(writer, 400, "Access-Control-Allow-Origin: *"); - } + ReceivedResponse received = new ReceivedResponse(matcher, reader); + saveSourceCode(received, databaseRepo.getPlayer(user.getId())); + JSONObject responseJSON = new JSONObject(); + Completion completion = databaseRepo.insertOrUpdatePuzzleResponse(received.getPuzzleId(), 3, + received.getFileName(), received.getSourceCode()); + if (Arrays.equals(received.getResponse(), databaseRepo.getPuzzle(received.getPuzzleId()).getSoluce())) { + HttpUtil.responseHeaders(writer, 200, "Access-Control-Allow-Origin: *", "Content-Type: application/json"); + responseJSON.put("score", completion.getScore()); + responseJSON.put("tries", completion.getTries()); + + } else if (completion != null) { + HttpUtil.responseHeaders(writer, 406, "Access-Control-Allow-Origin: *", "Content-Type: application/json"); + responseJSON.put("tries", completion.getTries()); + } else { + HttpUtil.responseHeaders(writer, 403, "Access-Control-Allow-Origin: *"); + return; } - HttpUtil.responseHeaders(writer, 403, "Access-Control-Allow-Origin: *"); + writer.write(responseJSON.toJSONString()); + writer.flush(); + writer.close(); + } + + private void saveSourceCode(ReceivedResponse received, Player player) throws IOException { + Path path = Paths.get(player.getPathToSourceCode() + received.getFileName()); + Files.write(path, received.getSourceCode()); } @Override @@ -82,3 +73,60 @@ public class PuzzleResponse implements Response { return "POST"; } } + +class ReceivedResponse { + private int puzzleId; + private byte[] response; + private String fileName; + private byte[] sourceCode; + + private HttpReader reader; + + public ReceivedResponse(Matcher matcher, HttpReader reader) throws Exception { + this.reader = reader; + puzzleId = Integer.parseInt(matcher.group(1)); + readResponse(); + readFileName(); + readSourceCode(); + } + + private void readResponse() throws Exception { + int hSize = reader.readInt(); + response = new byte[hSize]; + if (hSize != reader.read(response)) + response = null; + } + + private void readFileName() throws Exception { + byte[] tmpFileName; + int hSize = reader.readInt(); + tmpFileName = new byte[hSize]; + if (hSize == reader.read(tmpFileName)) + fileName = tmpFileName.toString(); + else + fileName = null; + } + + private void readSourceCode() throws Exception { + int hSize = reader.readInt(); + sourceCode = new byte[hSize]; + if (hSize != reader.read(sourceCode)) + sourceCode = null; + } + + public int getPuzzleId() { + return puzzleId; + } + + public byte[] getResponse() { + return response; + } + + public String getFileName() { + return fileName; + } + + public byte[] getSourceCode() { + return sourceCode; + } +} diff --git a/src/be/jeffcheasey88/peeratcode/routes/Register.java b/src/be/jeffcheasey88/peeratcode/routes/Register.java index cc9b318..80612a0 100644 --- a/src/be/jeffcheasey88/peeratcode/routes/Register.java +++ b/src/be/jeffcheasey88/peeratcode/routes/Register.java @@ -1,10 +1,14 @@ package be.jeffcheasey88.peeratcode.routes; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.json.simple.JSONObject; +import be.jeffcheasey88.peeratcode.model.Player; import be.jeffcheasey88.peeratcode.repository.DatabaseRepository; import be.jeffcheasey88.peeratcode.webserver.HttpReader; import be.jeffcheasey88.peeratcode.webserver.HttpUtil; @@ -49,10 +53,11 @@ public class Register implements Response { boolean emailAvailable = databaseRepo.checkEmailAvailability(email); if (pseudoAvailable && emailAvailable) { int id; - if ((id = databaseRepo.register(pseudo, email, password, firstname, lastname, description, group, avatar)) >= 0){ - HttpUtil.responseHeaders(writer, 200, - "Access-Control-Allow-Origin: *", - "Authorization: Bearer "+this.router.createAuthUser(id)); + if ((id = databaseRepo.register(pseudo, email, password, firstname, lastname, description, group, + avatar)) >= 0) { + HttpUtil.responseHeaders(writer, 200, "Access-Control-Allow-Origin: *", + "Authorization: Bearer " + this.router.createAuthUser(id)); + createFolderToSaveSourceCode(pseudo); return; } } else { @@ -67,13 +72,17 @@ public class Register implements Response { HttpUtil.responseHeaders(writer, 403, "Access-Control-Allow-Origin: *"); } + private void createFolderToSaveSourceCode(String pseudo) throws IOException { + Files.createDirectories(Paths.get(String.format(Player.PATH_TO_CODE, pseudo))); + } + @Override public Pattern getPattern() { return Pattern.compile("^\\/register$"); } - + @Override - public String getType(){ + public String getType() { return "POST"; }